From 2edb1939d3ccdaa4b18ea4a7af2255e8ec7d51fa Mon Sep 17 00:00:00 2001 From: h0ngcha0 Date: Tue, 28 Nov 2023 19:59:34 +0100 Subject: [PATCH] Linting --- client/.eslintrc.json | 33 ++ client/package.json | 24 +- client/src/api.ts | 113 +++--- client/src/assets/icons/BeerIcon.js | 20 - client/src/assets/icons/BeerIcon.tsx | 42 ++ .../icons/{BitcoinIcon.js => BitcoinIcon.tsx} | 11 +- ...oinIconYellow.js => BitcoinIconYellow.tsx} | 31 +- .../assets/icons/{RawIcon.js => RawIcon.tsx} | 34 +- .../icons/{ScriptIcon.js => ScriptIcon.tsx} | 38 +- client/src/containers/App.tsx | 60 ++- client/src/containers/AppRouter.tsx | 74 ++-- client/src/index.css | 6 + client/src/index.tsx | 13 +- client/src/modules/Loading.tsx | 8 +- client/src/modules/PurpleColorButton.tsx | 26 +- client/src/modules/ScrollableTabs.tsx | 142 ++++--- .../interpreter/InterpreterComponent.tsx | 233 +++++------ .../interpreter/InterpreterContainer.tsx | 365 +++++++++--------- .../interpreter/ScriptInterpreterWebsocket.ts | 68 ++-- .../src/modules/transaction/ScriptElements.ts | 176 ++++++--- .../modules/transaction/ScriptOpCodeList.tsx | 55 +-- .../transaction/TransactionContainer.tsx | 313 ++++++++------- .../TransactionDetailsComponent.tsx | 283 +++++++------- .../transaction/TransactionRawComponent.tsx | 134 +++---- client/yarn.lock | 229 ++++++++++- 25 files changed, 1473 insertions(+), 1058 deletions(-) create mode 100644 client/.eslintrc.json delete mode 100644 client/src/assets/icons/BeerIcon.js create mode 100644 client/src/assets/icons/BeerIcon.tsx rename client/src/assets/icons/{BitcoinIcon.js => BitcoinIcon.tsx} (75%) rename client/src/assets/icons/{BitcoinIconYellow.js => BitcoinIconYellow.tsx} (72%) rename client/src/assets/icons/{RawIcon.js => RawIcon.tsx} (71%) rename client/src/assets/icons/{ScriptIcon.js => ScriptIcon.tsx} (67%) diff --git a/client/.eslintrc.json b/client/.eslintrc.json new file mode 100644 index 0000000..55161dc --- /dev/null +++ b/client/.eslintrc.json @@ -0,0 +1,33 @@ +{ + "plugins": ["security", "header", "jest"], + "extends": [ + "prettier", + "plugin:prettier/recommended", + "plugin:security/recommended", + "plugin:@typescript-eslint/recommended" + ], + "parser": "@typescript-eslint/parser", + "rules": { + "@typescript-eslint/require-await": ["error"], + "no-var": ["error"], + "object-curly-spacing": ["error", "always"] + }, + "overrides": [ + { + "files": ["*.ts"], + "rules": { + "security/detect-non-literal-fs-filename": "off" + } + } + ], + "parserOptions": { + "ecmaVersion": "es5", + "sourceType": "module", + "project": ["./tsconfig.json"] + }, + "env": { + "node": true, + "es6": true, + "jest/globals": true + } +} diff --git a/client/package.json b/client/package.json index 75002b2..94ddebb 100644 --- a/client/package.json +++ b/client/package.json @@ -12,7 +12,13 @@ "@types/react-helmet": "^6.1.9", "@types/react-router": "^5.1.20", "@types/react-router-dom": "^5.3.3", - "prettier": "^2.8.8", + "eslint": "^8.37.0", + "eslint-config-prettier": "^9.0.0", + "eslint-plugin-header": "^3.1.1", + "eslint-plugin-jest": "^27.2.1", + "eslint-plugin-prettier": "^5.0.1", + "eslint-plugin-security": "^1.7.1", + "prettier": "^3.1.0", "react-scripts": "^5.0.1", "typescript": "^4.9.5" }, @@ -44,15 +50,21 @@ "start": "./node_modules/.bin/react-scripts start", "build": "./node_modules/.bin/react-scripts build", "test": "./node_modules/.bin/react-scripts test --env=jsdom", - "eject": "./node_modules/.bin/react-scripts eject" + "eject": "./node_modules/.bin/react-scripts eject", + "lint": "eslint . --ext ts,.tsx", + "lint:fix": "eslint . --fix --ext .ts,.tsx" }, "resolutions": { "@types/react": "^18.2.0" }, - "eslintConfig": { - "extends": [ - "react-app" - ] + "prettier": { + "printWidth": 120, + "tabWidth": 2, + "useTabs": false, + "semi": false, + "singleQuote": true, + "bracketSameLine": false, + "trailingComma": "none" }, "browserslist": { "production": [ diff --git a/client/src/api.ts b/client/src/api.ts index 840d44c..ffa6cec 100644 --- a/client/src/api.ts +++ b/client/src/api.ts @@ -1,12 +1,12 @@ -import axios from 'axios'; +import axios from 'axios' interface InterpreterResultOut { - value?: boolean, + value?: boolean type: string } interface ScriptElement { - type: string, + type: string value: boolean } @@ -15,46 +15,46 @@ interface ScriptExecutionStage { } interface InterpreterStateOut { - scriptPubKey: ScriptElement[], - scriptSig: ScriptElement[], - currentScript: ScriptElement[], - scriptP2sh: ScriptElement[] | undefined, - scriptWitness: ScriptElement[] | undefined, - scriptWitnessStack: ScriptElement[] | undefined, - stack: ScriptElement[], - altStack: ScriptElement[], + scriptPubKey: ScriptElement[] + scriptSig: ScriptElement[] + currentScript: ScriptElement[] + scriptP2sh: ScriptElement[] | undefined + scriptWitness: ScriptElement[] | undefined + scriptWitnessStack: ScriptElement[] | undefined + stack: ScriptElement[] + altStack: ScriptElement[] stage: ScriptExecutionStage } export interface InterpreterOutcome { - result: InterpreterResultOut, - state: InterpreterStateOut, + result: InterpreterResultOut + state: InterpreterStateOut step: number | undefined } export interface OutPointRaw { - hash: string, + hash: string index: string } export interface TxInRaw { - previousOutput: OutPointRaw, - sigScript: string, + previousOutput: OutPointRaw + sigScript: string sequence: string } export interface TxInsRaw { - count: string, + count: string txIns: TxInRaw[] } export interface TxOutRaw { - value: string, + value: string pkScript: string } export interface TxOutsRaw { - count: string, + count: string txOuts: TxOutRaw[] } @@ -63,67 +63,70 @@ export interface TxWitnessRaw { } export interface TxWitnessesRaw { - count: number, + count: number txWitnesses: TxWitnessRaw[] } export interface TxRaw { - version: string, - flag?: string, - txIns: TxInsRaw, - txOuts: TxOutsRaw, - txWitnesses: TxWitnessesRaw[], - lockTime: String + version: string + flag?: string + txIns: TxInsRaw + txOuts: TxOutsRaw + txWitnesses: TxWitnessesRaw[] + lockTime: string } export interface TransactionInput { - prevHash: string, - outputIndex: number, - script?: string, - parsedScript?: ScriptElement[], - outputValue: number, - sequence: number, - scriptType: number, - addresses: string[], + prevHash: string + outputIndex: number + script?: string + parsedScript?: ScriptElement[] + outputValue: number + sequence: number + scriptType: number + addresses: string[] witness?: string[] } export interface TransactionOutput { - value: number, - script: number, - parsedScript?: ScriptElement[], - spentBy?: string[], - addresses: string[], + value: number + script: number + parsedScript?: ScriptElement[] + spentBy?: string[] + addresses: string[] scriptType: string } export interface Transaction { - hash: string, - hex: string, - txRaw?: TxRaw, - total: number, - size: number, - version: number, - lockTime: number, - inputs: TransactionInput[], + hash: string + hex: string + txRaw?: TxRaw + total: number + size: number + version: number + lockTime: number + inputs: TransactionInput[] outputs: TransactionOutput[] } function extractResponseData(response: { data: T }): T { - return response.data; + return response.data } export function interpretTransactionInput(transactionId: string, inputIndex: number) { - return axios.get(`/api/transaction/${transactionId}/input/${inputIndex}/interpret`) - .then(extractResponseData); + return axios.get(`/api/transaction/${transactionId}/input/${inputIndex}/interpret`).then(extractResponseData) } -export function interpretTransactionInputWithSteps(transactionId: string, inputIndex: number, step: number): Promise { - return axios.get(`/api/transaction/${transactionId}/input/${inputIndex}/interpret-with-steps/${step}`) - .then(extractResponseData); +export function interpretTransactionInputWithSteps( + transactionId: string, + inputIndex: number, + step: number +): Promise { + return axios + .get(`/api/transaction/${transactionId}/input/${inputIndex}/interpret-with-steps/${step}`) + .then(extractResponseData) } export function fetchTransaction(transactionId): Promise { - return axios.get(`/api/transaction/${transactionId}`) - .then(extractResponseData); + return axios.get(`/api/transaction/${transactionId}`).then(extractResponseData) } diff --git a/client/src/assets/icons/BeerIcon.js b/client/src/assets/icons/BeerIcon.js deleted file mode 100644 index 3ce7934..0000000 --- a/client/src/assets/icons/BeerIcon.js +++ /dev/null @@ -1,20 +0,0 @@ -import React from 'react'; - -const BeerIcon = props => - - - - - - - - - - - - - - - ; - -export default BeerIcon; \ No newline at end of file diff --git a/client/src/assets/icons/BeerIcon.tsx b/client/src/assets/icons/BeerIcon.tsx new file mode 100644 index 0000000..282436a --- /dev/null +++ b/client/src/assets/icons/BeerIcon.tsx @@ -0,0 +1,42 @@ +import React from 'react' + +const BeerIcon = (props) => { + return ( + + + + + + + + + + + + + + + + ) +} + +export default BeerIcon diff --git a/client/src/assets/icons/BitcoinIcon.js b/client/src/assets/icons/BitcoinIcon.tsx similarity index 75% rename from client/src/assets/icons/BitcoinIcon.js rename to client/src/assets/icons/BitcoinIcon.tsx index 9e4f31c..48afdcc 100644 --- a/client/src/assets/icons/BitcoinIcon.js +++ b/client/src/assets/icons/BitcoinIcon.tsx @@ -1,8 +1,9 @@ -import React from 'react'; +import React from 'react' -const BitcoinIcon = props => +const BitcoinIcon = (props) => ( - - ; + + +) -export default BitcoinIcon; \ No newline at end of file +export default BitcoinIcon diff --git a/client/src/assets/icons/BitcoinIconYellow.js b/client/src/assets/icons/BitcoinIconYellow.tsx similarity index 72% rename from client/src/assets/icons/BitcoinIconYellow.js rename to client/src/assets/icons/BitcoinIconYellow.tsx index 41c823d..f685b58 100644 --- a/client/src/assets/icons/BitcoinIconYellow.js +++ b/client/src/assets/icons/BitcoinIconYellow.tsx @@ -1,17 +1,24 @@ -import React from 'react'; +import React from 'react' -const BitcoinIconYellow = props => +const BitcoinIconYellow = (props) => ( - - - - + + + + - - - - - ; + + + + + +) -export default BitcoinIconYellow; \ No newline at end of file +export default BitcoinIconYellow diff --git a/client/src/assets/icons/RawIcon.js b/client/src/assets/icons/RawIcon.tsx similarity index 71% rename from client/src/assets/icons/RawIcon.js rename to client/src/assets/icons/RawIcon.tsx index 6b3cd34..858cea0 100644 --- a/client/src/assets/icons/RawIcon.js +++ b/client/src/assets/icons/RawIcon.tsx @@ -1,13 +1,9 @@ -import React from 'react'; +import React from 'react' -const RawIcon = props => - - + - - - ; + c3.18,16.79,5.516,32.083,7.646,48.02h0.427c2.125-15.937,5.311-32.716,8.274-49.707l11.686-58.63h32.928L454.207,378.729z" + /> + + + +) -export default RawIcon; \ No newline at end of file +export default RawIcon diff --git a/client/src/assets/icons/ScriptIcon.js b/client/src/assets/icons/ScriptIcon.tsx similarity index 67% rename from client/src/assets/icons/ScriptIcon.js rename to client/src/assets/icons/ScriptIcon.tsx index 32050c2..55a260a 100644 --- a/client/src/assets/icons/ScriptIcon.js +++ b/client/src/assets/icons/ScriptIcon.tsx @@ -1,30 +1,36 @@ -import React from 'react'; +import React from 'react' -const ScriptIcon = props => - - + - + - + - + - ; + L227.49,192.276z" + /> + +) -export default ScriptIcon; \ No newline at end of file +export default ScriptIcon diff --git a/client/src/containers/App.tsx b/client/src/containers/App.tsx index 2fba776..9eb674d 100644 --- a/client/src/containers/App.tsx +++ b/client/src/containers/App.tsx @@ -1,43 +1,33 @@ -import React, { Component } from 'react'; -import { - ThemeProvider, - Theme, - StyledEngineProvider, - createMuiTheme, - createTheme, - makeStyles, - adaptV4Theme, -} from '@mui/material/styles'; -import blue from '@mui/material/colors/blue'; -import Helmet from 'react-helmet'; -import 'flexboxgrid'; -import AppRouter from './AppRouter'; +import React, { Component } from 'react' +import { ThemeProvider, StyledEngineProvider, createTheme, adaptV4Theme } from '@mui/material/styles' +import blue from '@mui/material/colors/blue' +import Helmet from 'react-helmet' +import 'flexboxgrid' +import AppRouter from './AppRouter' -const theme = createTheme(adaptV4Theme({ +const theme = createTheme( + adaptV4Theme({ palette: { - primary: blue + primary: blue } -})); + }) +) class App extends Component { - state = {}; + state = {} - render() { - return ( - - , - - - - - - - ); - } + render() { + return ( + + , + + + + + + + ) + } } -export default App; +export default App diff --git a/client/src/containers/AppRouter.tsx b/client/src/containers/AppRouter.tsx index 21871b5..cf256f1 100644 --- a/client/src/containers/AppRouter.tsx +++ b/client/src/containers/AppRouter.tsx @@ -1,29 +1,51 @@ -import { HashRouter, Redirect, Route, Switch } from 'react-router-dom'; -import React from 'react'; -import InterpreterContainer from '../modules/interpreter/InterpreterContainer'; -import { TransactionContainer } from '../modules/transaction/TransactionContainer'; -import qs from 'qs'; +import { HashRouter, Redirect, Route, Switch } from 'react-router-dom' +import React from 'react' +import InterpreterContainer from '../modules/interpreter/InterpreterContainer' +import { TransactionContainer } from '../modules/transaction/TransactionContainer' +import qs from 'qs' const AppRouter = () => { - return ( - - - { - const queryParams = qs.parse(location.search, { ignoreQueryPrefix: true }); - const automatic = Boolean(queryParams.automatic); - const step = queryParams.step ? +queryParams.step : 0; - return (); - } + return ( + + + { + const queryParams = qs.parse(location.search, { ignoreQueryPrefix: true }) + const automatic = Boolean(queryParams.automatic) + const step = queryParams.step ? +queryParams.step : 0 + return ( + + ) + }} + /> + } + /> + + + + ) +} - } /> - - () - } /> - - - - ); -}; - -export default AppRouter; +export default AppRouter diff --git a/client/src/index.css b/client/src/index.css index 30681a6..7e54fb8 100644 --- a/client/src/index.css +++ b/client/src/index.css @@ -270,3 +270,9 @@ a.image { color: white; background: darkred; } + +.SearchBarComponent-root .search-bar { + width: '1000px'; + text-align: 'center'; + margin: '0 auto'; +} \ No newline at end of file diff --git a/client/src/index.tsx b/client/src/index.tsx index 95c69b7..9a29bd7 100644 --- a/client/src/index.tsx +++ b/client/src/index.tsx @@ -1,9 +1,6 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import App from './containers/App'; -import './index.css'; +import React from 'react' +import ReactDOM from 'react-dom' +import App from './containers/App' +import './index.css' -ReactDOM.render( - , - document.getElementById('root') -); +ReactDOM.render(, document.getElementById('root')) diff --git a/client/src/modules/Loading.tsx b/client/src/modules/Loading.tsx index ea292f5..aa544dc 100644 --- a/client/src/modules/Loading.tsx +++ b/client/src/modules/Loading.tsx @@ -1,12 +1,12 @@ -import React from 'react'; -import { CircularProgress } from '@mui/material'; +import React from 'react' +import { CircularProgress } from '@mui/material' function Loading() { return (
- ); + ) } -export default Loading; +export default Loading diff --git a/client/src/modules/PurpleColorButton.tsx b/client/src/modules/PurpleColorButton.tsx index 9e012a4..eba2af2 100644 --- a/client/src/modules/PurpleColorButton.tsx +++ b/client/src/modules/PurpleColorButton.tsx @@ -1,23 +1,23 @@ -import { withStyles } from 'tss-react/mui'; -import { blue, purple } from '@mui/material/colors'; -import Button from "@mui/material/Button"; +import { withStyles } from 'tss-react/mui' +import { blue, purple } from '@mui/material/colors' +import Button from '@mui/material/Button' -export const PurpleColorButton = withStyles(Button, theme => ({ +export const PurpleColorButton = withStyles(Button, (theme) => ({ root: { color: theme.palette.getContrastText(purple[500]), backgroundColor: purple[500], '&:hover': { - backgroundColor: purple[700], - }, - }, -})); + backgroundColor: purple[700] + } + } +})) -export const BlueColorButton = withStyles(Button, theme => ({ +export const BlueColorButton = withStyles(Button, (theme) => ({ root: { color: theme.palette.getContrastText(blue[500]), backgroundColor: blue[500], '&:hover': { - backgroundColor: blue[700], - }, - }, -})); + backgroundColor: blue[700] + } + } +})) diff --git a/client/src/modules/ScrollableTabs.tsx b/client/src/modules/ScrollableTabs.tsx index 50e87f4..4b336ca 100644 --- a/client/src/modules/ScrollableTabs.tsx +++ b/client/src/modules/ScrollableTabs.tsx @@ -1,87 +1,83 @@ -import React from 'react'; -import _ from 'lodash'; -import PropTypes from 'prop-types'; -import { makeStyles } from 'tss-react/mui'; -import Tabs from '@mui/material/Tabs'; -import Tab from '@mui/material/Tab'; -import Typography from '@mui/material/Typography'; -import Box from '@mui/material/Box'; +import React from 'react' +import _ from 'lodash' +import PropTypes from 'prop-types' +import { makeStyles } from 'tss-react/mui' +import Tabs from '@mui/material/Tabs' +import Tab from '@mui/material/Tab' +import Typography from '@mui/material/Typography' +import Box from '@mui/material/Box' function TabPanel(props) { - const { children, value, index, ...other } = props; + const { children, value, index, ...other } = props - return ( - - ); + return ( + + ) } TabPanel.propTypes = { - children: PropTypes.node, - index: PropTypes.any.isRequired, - value: PropTypes.any.isRequired, -}; + children: PropTypes.node, + index: PropTypes.any.isRequired, + value: PropTypes.any.isRequired +} function a11yProps(index: string) { - return { - id: `scrollable-auto-tab-${index}`, - 'aria-controls': `scrollable-auto-tabpanel-${index}`, - }; + return { + id: `scrollable-auto-tab-${index}`, + 'aria-controls': `scrollable-auto-tabpanel-${index}` + } } -const useStyles = makeStyles()(theme => ({ - root: { - flexGrow: 1, - width: '100%', - backgroundColor: theme.palette.background.paper, - }, -})); +const useStyles = makeStyles()((theme) => ({ + root: { + flexGrow: 1, + width: '100%', + backgroundColor: theme.palette.background.paper + } +})) export default function ScrollableTabs(props) { - //const { title1, children1, title2, children2, ...other } = props; - const { tabs } = props; - const { classes } = useStyles(); - const [value, setValue] = React.useState(0); + //const { title1, children1, title2, children2, ...other } = props; + const { tabs } = props + const { classes } = useStyles() + const [value, setValue] = React.useState(0) - const handleChange = (event, newValue) => { - setValue(newValue); - }; + const handleChange = (event, newValue) => { + setValue(newValue) + } - return ( -
- - { - _.map(tabs, (tab, index) => { - return ; - }) - } - - { - _.map(tabs, (tab, index) => { - return ( - - {tab.children} - - ); - }) - } -
- ); -} \ No newline at end of file + return ( +
+ + {_.map(tabs, (tab, index) => { + return + })} + + {_.map(tabs, (tab, index) => { + return ( + + {tab.children} + + ) + })} +
+ ) +} diff --git a/client/src/modules/interpreter/InterpreterComponent.tsx b/client/src/modules/interpreter/InterpreterComponent.tsx index 09a2657..89a6fdc 100644 --- a/client/src/modules/interpreter/InterpreterComponent.tsx +++ b/client/src/modules/interpreter/InterpreterComponent.tsx @@ -1,127 +1,138 @@ -import React from 'react'; +import React from 'react' -import Table from '@mui/material/Table'; -import TableBody from '@mui/material/TableBody'; -import TableCell from '@mui/material/TableCell'; -import TableHead from '@mui/material/TableHead'; -import TableRow from '@mui/material/TableRow'; -import _ from 'lodash'; -import ScriptOpCodeList from '../transaction/ScriptOpCodeList'; -import Typography from "@mui/material/Typography"; +import Table from '@mui/material/Table' +import TableBody from '@mui/material/TableBody' +import TableCell from '@mui/material/TableCell' +import TableHead from '@mui/material/TableHead' +import TableRow from '@mui/material/TableRow' +import _ from 'lodash' +import ScriptOpCodeList from '../transaction/ScriptOpCodeList' +import Typography from '@mui/material/Typography' import { InterpreterOutcome } from '../../api' interface InterpreterComponentProps { - interpretResult: InterpreterOutcome, - step: number + interpretResult: InterpreterOutcome + step: number } export const InterpreterComponent: React.FunctionComponent = (props) => { - const { interpretResult } = props; - const { scriptPubKey, currentScript, stack, altStack, stage } = interpretResult.state; - const result = interpretResult.result.type === 'Result' ? (interpretResult.result.value ? 'True' : 'False') : 'NoResult'; + const { interpretResult } = props + const { scriptPubKey, currentScript, stack, altStack, stage } = interpretResult.state + const result = + interpretResult.result.type === 'Result' ? (interpretResult.result.value ? 'True' : 'False') : 'NoResult' - const currentRemainingScript = () => { - if (result === 'NoResult') { - if (stage.type === 'ExecutingScriptSig') { - return currentScript.concat(scriptPubKey); - } else { - return currentScript; - } - } else { - return currentScript; - } - }; + const currentRemainingScript = () => { + if (result === 'NoResult') { + if (stage.type === 'ExecutingScriptSig') { + return currentScript.concat(scriptPubKey) + } else { + return currentScript + } + } else { + return currentScript + } + } - const executionDescriptionComponent = (scriptType) => { - return Executing {scriptType} [{props.step}]; - }; - - const executionDescription = () => { - if (result === 'NoResult') { - if (stage.type === 'ExecutingScriptSig') { - return executionDescriptionComponent("Script Sig"); - } else if (stage.type === 'ExecutingScriptPubKey') { - return executionDescriptionComponent("Script PubKey"); - } else if (stage.type === 'ExecutingScriptP2SH') { - return executionDescriptionComponent("Script P2SH"); - } else if (stage.type === 'ExecutingScriptWitness') { - return executionDescriptionComponent("Script Witness"); - } else { - return executionDescriptionComponent(stage.type); - } - } else { - return result === 'True' ? - Execution Succeeded : - Execution Failed; - } - }; + const executionDescriptionComponent = (scriptType) => { + return ( + + + Executing {scriptType} + {' '} + [{props.step}] + + ) + } + const executionDescription = () => { + if (result === 'NoResult') { + if (stage.type === 'ExecutingScriptSig') { + return executionDescriptionComponent('Script Sig') + } else if (stage.type === 'ExecutingScriptPubKey') { + return executionDescriptionComponent('Script PubKey') + } else if (stage.type === 'ExecutingScriptP2SH') { + return executionDescriptionComponent('Script P2SH') + } else if (stage.type === 'ExecutingScriptWitness') { + return executionDescriptionComponent('Script Witness') + } else { + return executionDescriptionComponent(stage.type) + } + } else { + return result === 'True' ? ( + Execution Succeeded + ) : ( + Execution Failed + ) + } + } - return ( -
-
{executionDescription()}
- - - - - Stack - - - + return ( +
+
{executionDescription()}
+
+ + + + + Stack + + + + - - - -
- -
-
-
-
-
- - - - - Script - - - + + + +
+ +
+
+
+
+
+ + + + + + Script + + + + - - - -
- -
-
-
-
-
+ + + +
+ +
+
+
+
+ - { - !_.isEmpty(altStack) ? ( - - - - Current Alt Stack - - + {!_.isEmpty(altStack) ? ( +
+ + + Current Alt Stack + + - - - -
- -
-
-
-
-
- ) : null - } -
- ) -}; + + + +
+ +
+
+
+
+ + ) : null} + + ) +} -export default InterpreterComponent; \ No newline at end of file +export default InterpreterComponent diff --git a/client/src/modules/interpreter/InterpreterContainer.tsx b/client/src/modules/interpreter/InterpreterContainer.tsx index 3576104..d93a6bf 100644 --- a/client/src/modules/interpreter/InterpreterContainer.tsx +++ b/client/src/modules/interpreter/InterpreterContainer.tsx @@ -1,196 +1,201 @@ -import React, { useEffect, useState } from 'react'; - -import InterpreterComponent from "./InterpreterComponent"; -import { interpretTransactionInputWithSteps } from '../../api'; -import desktopLogoImage from '../../assets/images/bitcoin-playground-desktop.png'; -import mobileLogoImage from '../../assets/images/bitcoin-playground-mobile.png'; -import Loading from '../Loading'; -import NavigateBeforeIcon from '@mui/icons-material/NavigateBefore'; -import NavigateNextIcon from '@mui/icons-material/NavigateNext'; -import ScriptInterpreterWebsocket from './ScriptInterpreterWebsocket'; -import Grid from "@mui/material/Grid/Grid"; +import React, { useEffect, useState, useCallback } from 'react' + +import InterpreterComponent from './InterpreterComponent' +import { interpretTransactionInputWithSteps } from '../../api' +import desktopLogoImage from '../../assets/images/bitcoin-playground-desktop.png' +import mobileLogoImage from '../../assets/images/bitcoin-playground-mobile.png' +import Loading from '../Loading' +import NavigateBeforeIcon from '@mui/icons-material/NavigateBefore' +import NavigateNextIcon from '@mui/icons-material/NavigateNext' +import ScriptInterpreterWebsocket from './ScriptInterpreterWebsocket' +import Grid from '@mui/material/Grid/Grid' import { InterpreterOutcome } from '../../api' interface InterpreterContainerProps { - transactionId: string, - inputIndex: number, - automatic: boolean, - step: number, - push: (x: any, y: any) => void + transactionId: string + inputIndex: number + automatic: boolean + step: number + push: (x: any, y: any) => void } interface InterpreterContainerState { - transaction: string | undefined, - interpretResult: InterpreterOutcome | undefined, - currentStep: number, - automatic: boolean, - inputIndex: number, - transactionId: string, - loading: boolean, - executingScript: boolean + transaction: string | undefined + interpretResult: InterpreterOutcome | undefined + currentStep: number + automatic: boolean + inputIndex: number + transactionId: string + loading: boolean + executingScript: boolean } export const InterpreterContainer: React.FunctionComponent = (props) => { - - const [state, setState] = useState({ - transaction: undefined, - interpretResult: undefined, - currentStep: props.step, - automatic: props.automatic, - inputIndex: props.inputIndex, - transactionId: props.transactionId, - loading: false, - executingScript: false - }); - - const scriptInterpreterWebsocket = new ScriptInterpreterWebsocket(); - - const interpretScript = (step) => { + const [state, setState] = useState({ + transaction: undefined, + interpretResult: undefined, + currentStep: props.step, + automatic: props.automatic, + inputIndex: props.inputIndex, + transactionId: props.transactionId, + loading: false, + executingScript: false + }) + + const scriptInterpreterWebsocket = new ScriptInterpreterWebsocket() + + const interpretScript = useCallback((step) => { + setState({ + ...state, + loading: true + }) + + interpretTransactionInputWithSteps(state.transactionId, state.inputIndex, step) + .then((interpretResponse) => { setState({ - ...state, - loading: true - }); - - interpretTransactionInputWithSteps(state.transactionId, state.inputIndex, step) - .then((interpretResponse) => { - setState({ - ...state, - currentStep: step, - loading: false, - interpretResult: interpretResponse - }); - }) - .catch((error) => { - // TODO: handle error - console.log(error); - }); - }; - - const interpretScriptWebsocket = () => { - const initialCallback = () => { - setState({ - ...state, - loading: true, - executingScript: true - }); - }; - - const closeConnectionCallback = () => { - setState(prevState => { - return { - ...prevState, - executingScript: false - } - }); - }; - - const onMessageCallback = (interpretResult) => { - setState({ - ...state, - currentStep: interpretResult.step, - interpretResult: interpretResult, - loading: false - }); - }; - - const interpreter = scriptInterpreterWebsocket.interpreterBuilder(initialCallback, onMessageCallback, closeConnectionCallback); - interpreter(state.transactionId, state.inputIndex) - }; - - useEffect(() => { - if (state.transactionId) { - if (state.automatic) { - interpretScriptWebsocket(); - } else { - const step = props.step ? props.step : 0; - interpretScript(step); - } - } - }, []); - - useEffect(() => { - if (!props.automatic && (props.step !== state.currentStep)) { - interpretScript(props.step); - } - }, [props.step]); - - const prevNextButtons = () => { - const calculatePrevStep = () => { - return state.currentStep > 0 ? - `/#/transaction/${state.transactionId}/input/${state.inputIndex}/interpret?step=${props.step - 1}` : undefined; - }; - - const calculateNextStep = () => { - const result = state.interpretResult ? state.interpretResult.result.value : false; - const step = props.step ? props.step : 0; - return (result !== true) ? - `/#/transaction/${state.transactionId}/input/${state.inputIndex}/interpret?step=${step + 1}` : undefined; - }; - - if (!state.automatic) { - const prevStep = calculatePrevStep(); - const nextStep = calculateNextStep(); - - const disabledIconStyle = { verticalAlign: "middle", fontSize: "16px", color: "grey" }; - const activeIconStyle = { verticalAlign: "middle", fontSize: "16px", color: "rgb(219, 56, 111)" }; - const prevStepClassName = prevStep === undefined ? "not-active" : ""; - const prevStepIconStyle = prevStep === undefined ? disabledIconStyle : activeIconStyle; - const nextStepClassName = nextStep === undefined ? "not-active" : ""; - const nextStepIconStyle = nextStep === undefined ? disabledIconStyle : activeIconStyle; - - return ( -
- - - - - - - - -
- ) - } else { - return null; + ...state, + currentStep: step, + loading: false, + interpretResult: interpretResponse + }) + }) + .catch((error) => { + // TODO: handle error + console.log(error) + }) + }, []) + + const interpretScriptWebsocket = useCallback(() => { + const initialCallback = () => { + setState({ + ...state, + loading: true, + executingScript: true + }) + } + + const closeConnectionCallback = () => { + setState((prevState) => { + return { + ...prevState, + executingScript: false } - }; - - const interpretState = () => { - if (state.loading) { - return ; - } else { - if (state.interpretResult) { - return ; - } else { - return undefined; - } - } - }; - - return ( -
-
- - Bitcoin Playground + }) + } + + const onMessageCallback = (interpretResult) => { + setState({ + ...state, + currentStep: interpretResult.step, + interpretResult: interpretResult, + loading: false + }) + } + + const interpreter = scriptInterpreterWebsocket.interpreterBuilder( + initialCallback, + onMessageCallback, + closeConnectionCallback + ) + interpreter(state.transactionId, state.inputIndex) + }, []) + + useEffect(() => { + if (state.transactionId) { + if (state.automatic) { + interpretScriptWebsocket() + } else { + const step = props.step ? props.step : 0 + interpretScript(step) + } + } + }, [props.step, state.automatic, interpretScript, interpretScriptWebsocket, state.transactionId]) + + useEffect(() => { + if (!props.automatic && props.step !== state.currentStep) { + interpretScript(props.step) + } + }, [props.step, props.automatic, state.currentStep, interpretScript]) + + const prevNextButtons = () => { + const calculatePrevStep = () => { + return state.currentStep > 0 + ? `/#/transaction/${state.transactionId}/input/${state.inputIndex}/interpret?step=${props.step - 1}` + : undefined + } + + const calculateNextStep = () => { + const result = state.interpretResult ? state.interpretResult.result.value : false + const step = props.step ? props.step : 0 + return result !== true + ? `/#/transaction/${state.transactionId}/input/${state.inputIndex}/interpret?step=${step + 1}` + : undefined + } + + if (!state.automatic) { + const prevStep = calculatePrevStep() + const nextStep = calculateNextStep() + + const disabledIconStyle = { verticalAlign: 'middle', fontSize: '16px', color: 'grey' } + const activeIconStyle = { verticalAlign: 'middle', fontSize: '16px', color: 'rgb(219, 56, 111)' } + const prevStepClassName = prevStep === undefined ? 'not-active' : '' + const prevStepIconStyle = prevStep === undefined ? disabledIconStyle : activeIconStyle + const nextStepClassName = nextStep === undefined ? 'not-active' : '' + const nextStepIconStyle = nextStep === undefined ? disabledIconStyle : activeIconStyle + + return ( +
+ + + + + +
+ + Next - {prevNextButtons()} - {interpretState()} -
+ +
+ +
- ); + ) + } else { + return null + } + } + + const interpretState = () => { + if (state.loading) { + return + } else { + if (state.interpretResult) { + return + } else { + return undefined + } + } + } + + return ( +
+
+ + Bitcoin Playground + + + Bitcoin Playground + + {prevNextButtons()} + {interpretState()} +
+
+ ) } -export default InterpreterContainer; \ No newline at end of file +export default InterpreterContainer diff --git a/client/src/modules/interpreter/ScriptInterpreterWebsocket.ts b/client/src/modules/interpreter/ScriptInterpreterWebsocket.ts index a522d0b..3507e82 100644 --- a/client/src/modules/interpreter/ScriptInterpreterWebsocket.ts +++ b/client/src/modules/interpreter/ScriptInterpreterWebsocket.ts @@ -1,44 +1,46 @@ -import URI from 'urijs'; +import URI from 'urijs' import { InterpreterOutcome } from '../../api' class ScriptInterpreterWebsocket { - webSocket: WebSocket | undefined; + webSocket: WebSocket | undefined closeConnectionFunctionBuilder = (callback: () => any) => () => { if (this.webSocket) { - this.webSocket.close(); - this.webSocket = undefined; + this.webSocket.close() + this.webSocket = undefined - callback(); + callback() + } + } + + interpreterBuilder = + ( + initialCallback: () => any, + onMessageCallback: (outcome: InterpreterOutcome) => any, + closeConnectionCallback: () => any + ) => + (transactionId, inputIndex) => { + const uri = new URI({ + protocol: window.location.protocol === 'https:' ? 'wss' : 'ws', + hostname: window.location.host, + path: `/api/transaction/${transactionId}/input/${inputIndex}/stream-interpret` + }) + + const closeConnectionFunction = this.closeConnectionFunctionBuilder(closeConnectionCallback) + closeConnectionFunction() + + initialCallback() + + this.webSocket = new WebSocket(uri.toString()) + + this.webSocket.onmessage = (event) => { + const interpretResult = JSON.parse(event.data) + onMessageCallback(interpretResult) + } + + this.webSocket.onclose = closeConnectionFunction } - }; - - interpreterBuilder = ( - initialCallback: () => any, - onMessageCallback: (outcome: InterpreterOutcome) => any, - closeConnectionCallback: () => any - ) => (transactionId, inputIndex) => { - const uri = new URI({ - protocol: window.location.protocol === 'https:' ? 'wss' : 'ws', - hostname: window.location.host, - path: `/api/transaction/${transactionId}/input/${inputIndex}/stream-interpret` - }); - - const closeConnectionFunction = this.closeConnectionFunctionBuilder(closeConnectionCallback); - closeConnectionFunction(); - - initialCallback(); - - this.webSocket = new WebSocket(uri.toString()); - - this.webSocket.onmessage = event => { - const interpretResult = JSON.parse(event.data); - onMessageCallback(interpretResult) - }; - - this.webSocket.onclose = closeConnectionFunction; - }; } -export default ScriptInterpreterWebsocket; \ No newline at end of file +export default ScriptInterpreterWebsocket diff --git a/client/src/modules/transaction/ScriptElements.ts b/client/src/modules/transaction/ScriptElements.ts index 9ce0311..1ee2154 100644 --- a/client/src/modules/transaction/ScriptElements.ts +++ b/client/src/modules/transaction/ScriptElements.ts @@ -1,76 +1,144 @@ -import _ from 'lodash'; +import _ from 'lodash' -const Values: string[] = [ - 'ScriptConstant', 'ScriptNum' -]; +const Values: string[] = ['ScriptConstant', 'ScriptNum'] const StackOps: string[] = [ - 'OP_TOALTSTACK', 'OP_FROMALTSTACK', 'OP_2DROP', 'OP_2DUP', 'OP_3DUP', 'OP_2OVER', - 'OP_2ROT', 'OP_2SWAP', 'OP_IFDUP', 'OP_DEPTH', 'OP_DROP', 'OP_DUP', 'OP_NIP', 'OP_OVER', - 'OP_PICK', 'OP_ROLL', 'OP_ROT', 'OP_SWAP', 'OP_TUCK' -]; + 'OP_TOALTSTACK', + 'OP_FROMALTSTACK', + 'OP_2DROP', + 'OP_2DUP', + 'OP_3DUP', + 'OP_2OVER', + 'OP_2ROT', + 'OP_2SWAP', + 'OP_IFDUP', + 'OP_DEPTH', + 'OP_DROP', + 'OP_DUP', + 'OP_NIP', + 'OP_OVER', + 'OP_PICK', + 'OP_ROLL', + 'OP_ROT', + 'OP_SWAP', + 'OP_TUCK' +] const CryptoOps: string[] = [ - 'OP_RIPEMD160', 'OP_SHA1', 'OP_SHA256', 'OP_HASH160', 'OP_HASH256', 'OP_CODESEPARATOR', - 'OP_CHECKSIG', 'OP_CHECKSIGVERIFY', 'OP_CHECKMULTISIG', 'OP_CHECKMULTISIGVERIFY' -]; + 'OP_RIPEMD160', + 'OP_SHA1', + 'OP_SHA256', + 'OP_HASH160', + 'OP_HASH256', + 'OP_CODESEPARATOR', + 'OP_CHECKSIG', + 'OP_CHECKSIGVERIFY', + 'OP_CHECKMULTISIG', + 'OP_CHECKMULTISIGVERIFY' +] const ConstantOps: string[] = [ - 'OP_0', 'OP_FALSE', 'OP_PUSHDATA1', 'OP_PUSHDATA2', 'OP_PUSHDATA4', 'OP_1NEGATE', - 'OP_1', 'OP_TRUE', 'OP_2', 'OP_3', 'OP_4', 'OP_5', 'OP_6', 'OP_7', 'OP_8', 'OP_9', 'OP_10', - 'OP_11', 'OP_12', 'OP_13', 'OP_14', 'OP_15', 'OP_16', 'OP_PUSHDATA' -]; + 'OP_0', + 'OP_FALSE', + 'OP_PUSHDATA1', + 'OP_PUSHDATA2', + 'OP_PUSHDATA4', + 'OP_1NEGATE', + 'OP_1', + 'OP_TRUE', + 'OP_2', + 'OP_3', + 'OP_4', + 'OP_5', + 'OP_6', + 'OP_7', + 'OP_8', + 'OP_9', + 'OP_10', + 'OP_11', + 'OP_12', + 'OP_13', + 'OP_14', + 'OP_15', + 'OP_16', + 'OP_PUSHDATA' +] -const BitwiseLogicOps: string[] = [ - 'OP_INVERT', 'OP_AND', 'OP_OR', 'OP_XOR', 'OP_EQUAL', 'OP_EQUALVERIFY' -]; +const BitwiseLogicOps: string[] = ['OP_INVERT', 'OP_AND', 'OP_OR', 'OP_XOR', 'OP_EQUAL', 'OP_EQUALVERIFY'] const ArithmeticOps: string[] = [ - 'OP_1ADD', 'OP_1SUB', 'OP_2MUL', 'OP_2DIV', 'OP_NEGATE', 'OP_ABS', 'OP_NOT', 'OP_0NOTEQUAL', - 'OP_ADD', 'OP_SUB', 'OP_MUL', 'OP_DIV', 'OP_MOD', 'OP_LSHIFT', 'OP_RSHIFT', 'OP_BOOLAND', - 'OP_BOOLOR', 'OP_NUMEQUAL', 'OP_NUMEQUALVERIFY', 'OP_NUMNOTEQUAL', 'OP_LESSTHAN', - 'OP_GREATERTHAN', 'OP_LESSTHANOREQUAL', 'OP_GREATERTHANOREQUAL', 'OP_MIN', 'OP_MAX', 'OP_WITHIN' -]; + 'OP_1ADD', + 'OP_1SUB', + 'OP_2MUL', + 'OP_2DIV', + 'OP_NEGATE', + 'OP_ABS', + 'OP_NOT', + 'OP_0NOTEQUAL', + 'OP_ADD', + 'OP_SUB', + 'OP_MUL', + 'OP_DIV', + 'OP_MOD', + 'OP_LSHIFT', + 'OP_RSHIFT', + 'OP_BOOLAND', + 'OP_BOOLOR', + 'OP_NUMEQUAL', + 'OP_NUMEQUALVERIFY', + 'OP_NUMNOTEQUAL', + 'OP_LESSTHAN', + 'OP_GREATERTHAN', + 'OP_LESSTHANOREQUAL', + 'OP_GREATERTHANOREQUAL', + 'OP_MIN', + 'OP_MAX', + 'OP_WITHIN' +] -const FlowControlOps: string[] = [ - 'OP_NOP', 'OP_IF', 'OP_NOTIF', 'OP_ELSE', 'OP_ENDIF', 'OP_VERIFY', 'OP_RETURN' -]; +const FlowControlOps: string[] = ['OP_NOP', 'OP_IF', 'OP_NOTIF', 'OP_ELSE', 'OP_ENDIF', 'OP_VERIFY', 'OP_RETURN'] -const LocktimeOps: string[] = [ - 'OP_CHECKLOCKTIMEVERIFY', 'OP_CHECKSEQUENCEVERIFY', 'OP_NOP2', 'OP_NOP3' -]; +const LocktimeOps: string[] = ['OP_CHECKLOCKTIMEVERIFY', 'OP_CHECKSEQUENCEVERIFY', 'OP_NOP2', 'OP_NOP3'] -const PseudoOps: string[] = [ - 'OP_PUBKEYHASH', 'OP_PUBKEY', 'OP_INVALIDOPCODE' -]; +const PseudoOps: string[] = ['OP_PUBKEYHASH', 'OP_PUBKEY', 'OP_INVALIDOPCODE'] const ReservedOps: string[] = [ - 'OP_RESERVED', 'OP_VER', 'OP_VERIF', 'OP_VERNOTIF', 'OP_RESERVED1', 'OP_RESERVED2', - 'OP_NOP1', 'OP_NOP4', 'OP_NOP5', 'OP_NOP6', 'OP_NOP7', 'OP_NOP8', 'OP_NOP9', 'OP_NOP10' -]; + 'OP_RESERVED', + 'OP_VER', + 'OP_VERIF', + 'OP_VERNOTIF', + 'OP_RESERVED1', + 'OP_RESERVED2', + 'OP_NOP1', + 'OP_NOP4', + 'OP_NOP5', + 'OP_NOP6', + 'OP_NOP7', + 'OP_NOP8', + 'OP_NOP9', + 'OP_NOP10' +] -const SpliceOps: string[] = [ - 'OP_CAT', 'OP_SUBSTR', 'OP_LEFT', 'OP_RIGHT', 'OP_SIZE' -]; +const SpliceOps: string[] = ['OP_CAT', 'OP_SUBSTR', 'OP_LEFT', 'OP_RIGHT', 'OP_SIZE'] const allElements = { - 'Values': Values, - 'StackOps': StackOps, - 'CryptoOps': CryptoOps, - 'ConstantOps': ConstantOps, - 'BitwiseLogicOps': BitwiseLogicOps, - 'ArithmeticOps': ArithmeticOps, - 'FlowControlOps': FlowControlOps, - 'LocktimeOps': LocktimeOps, - 'PseudoOps': PseudoOps, - 'ReservedOps': ReservedOps, - 'SpliceOps': SpliceOps -}; + Values: Values, + StackOps: StackOps, + CryptoOps: CryptoOps, + ConstantOps: ConstantOps, + BitwiseLogicOps: BitwiseLogicOps, + ArithmeticOps: ArithmeticOps, + FlowControlOps: FlowControlOps, + LocktimeOps: LocktimeOps, + PseudoOps: PseudoOps, + ReservedOps: ReservedOps, + SpliceOps: SpliceOps +} export function findElementType(scriptElement: string): string | undefined { const value = _.toPairs(allElements).find(([key, value]) => { - return _.includes(value, scriptElement); - }); + return _.includes(value, scriptElement) + }) - return value ? value[0] : undefined; -}; + return value ? value[0] : undefined +} diff --git a/client/src/modules/transaction/ScriptOpCodeList.tsx b/client/src/modules/transaction/ScriptOpCodeList.tsx index ab707fd..a637efa 100644 --- a/client/src/modules/transaction/ScriptOpCodeList.tsx +++ b/client/src/modules/transaction/ScriptOpCodeList.tsx @@ -1,32 +1,33 @@ -import _ from 'lodash'; -import { findElementType } from './ScriptElements'; +import _ from 'lodash' +import { findElementType } from './ScriptElements' const ScriptOpCodeList = ({ opCodes }) => { - return ( -
- { - _(opCodes) - .filter((opCode) => opCode.type !== 'OP_PUSHDATA') - .map((scriptElement, index) => { - const elementType = findElementType(scriptElement.type); - const className = `OpCode ${elementType}` + return ( +
+ {_(opCodes) + .filter((opCode) => opCode.type !== 'OP_PUSHDATA') + .map((scriptElement, index) => { + const elementType = findElementType(scriptElement.type) + const className = `OpCode ${elementType}` - if (scriptElement.type === 'ScriptConstant' && scriptElement.value === "0x") { - scriptElement = { - type: 'ScriptNum', - value: 0 - }; - } - - return ( -
- {_.includes(['ScriptConstant', 'ScriptNum'], scriptElement.type) ? scriptElement.value : scriptElement.type} -
- ); - }).value() + if (scriptElement.type === 'ScriptConstant' && scriptElement.value === '0x') { + scriptElement = { + type: 'ScriptNum', + value: 0 } -
- ) -}; + } + + return ( +
+ {_.includes(['ScriptConstant', 'ScriptNum'], scriptElement.type) + ? scriptElement.value + : scriptElement.type} +
+ ) + }) + .value()} +
+ ) +} -export default ScriptOpCodeList; \ No newline at end of file +export default ScriptOpCodeList diff --git a/client/src/modules/transaction/TransactionContainer.tsx b/client/src/modules/transaction/TransactionContainer.tsx index e847c0a..62bba16 100644 --- a/client/src/modules/transaction/TransactionContainer.tsx +++ b/client/src/modules/transaction/TransactionContainer.tsx @@ -1,167 +1,188 @@ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useState, useCallback } from 'react' -import desktopLogoImage from '../../assets/images/bitcoin-playground-desktop.png'; -import mobileLogoImage from '../../assets/images/bitcoin-playground-mobile.png'; -import btcRpcExplorerImage from '../../assets/images/btc-rpc-explorer.jpg'; -import { fetchTransaction } from '../../api'; -import TransactionDetailsComponent from "./TransactionDetailsComponent"; -import Loading from "../Loading"; -import { PurpleColorButton } from "../PurpleColorButton"; -import SearchBar from '@mkyy/mui-search-bar'; -import BitcoinIcon from '../../assets/icons/BitcoinIcon'; -import RawIcon from '../../assets/icons/RawIcon'; -import ScriptIcon from '../../assets/icons/ScriptIcon'; -import { Typography } from '@mui/material'; -import ScrollableTabs from "../ScrollableTabs"; -import TransactionRawComponent from "./TransactionRawComponent"; -import { Transaction } from '../../api'; +import desktopLogoImage from '../../assets/images/bitcoin-playground-desktop.png' +import mobileLogoImage from '../../assets/images/bitcoin-playground-mobile.png' +import btcRpcExplorerImage from '../../assets/images/btc-rpc-explorer.jpg' +import { fetchTransaction } from '../../api' +import TransactionDetailsComponent from './TransactionDetailsComponent' +import Loading from '../Loading' +import { PurpleColorButton } from '../PurpleColorButton' +import SearchBar from '@mkyy/mui-search-bar' +import BitcoinIcon from '../../assets/icons/BitcoinIcon' +import RawIcon from '../../assets/icons/RawIcon' +import ScriptIcon from '../../assets/icons/ScriptIcon' +import { Typography } from '@mui/material' +import ScrollableTabs from '../ScrollableTabs' +import TransactionRawComponent from './TransactionRawComponent' +import { Transaction } from '../../api' interface TransactionContainerProps { - transactionId: string, - push: (x: any, y: any) => void + transactionId: string + push: (x: any, y: any) => void } interface TransactionContainerState { - transactionId: string, - transaction: Transaction | undefined, - loading: boolean, - error: string | undefined, - executingScript: boolean + transactionId: string + transaction: Transaction | undefined + loading: boolean + error: string | undefined + executingScript: boolean } export const TransactionContainer: React.FunctionComponent = (props) => { - const [state, setState] = useState({ - transactionId: props.transactionId, - transaction: undefined, - loading: false, - error: undefined, - executingScript: false - }); + const [state, setState] = useState({ + transactionId: props.transactionId, + transaction: undefined, + loading: false, + error: undefined, + executingScript: false + }) - const loadTransaction = (transactionId: string) => { + const loadTransaction = useCallback((transactionId: string) => { + setState({ + ...state, + transaction: undefined, + transactionId: transactionId, + loading: true + }) + + fetchTransaction(transactionId) + .then((transaction) => { + setState({ + ...state, + loading: false, + error: undefined, + transaction: transaction + }) + }) + .catch((error) => { setState({ - ...state, - transaction: undefined, - transactionId: transactionId, - loading: true - }); + ...state, + loading: false, + error: error.response + }) + }) + }, []) - fetchTransaction(transactionId) - .then((transaction) => { - setState({ - ...state, - loading: false, - error: undefined, - transaction: transaction - }); - }) - .catch((error) => { - setState({ - ...state, - loading: false, - error: error.response - }); - }); - }; + useEffect(() => { + if (props.transactionId) { + loadTransaction(props.transactionId) + } + }, [props.transactionId, loadTransaction]) - useEffect(() => { - if (props.transactionId) { - loadTransaction(props.transactionId); - } - }, [props.transactionId]); + const handleSetTransactionId = (txId) => { + setState({ + ...state, + transactionId: txId + }) + } - const handleSetTransactionId = (txId) => { - setState({ - ...state, - transactionId: txId - }); - }; + const executeAfterFetchTransaction = (state, func) => { + if (state.error) { + if (state.error.status === 404) { + return ( +
+ {' '} + 404, transaction not found{' '} +
+ ) + } else { + return ( +
+ {state.error.status}, {state.error.statusText} +
+ ) + } + } else if (state.transaction) { + return func(state.transaction) + } + } - const executeAfterFetchTransaction = (state, func) => { - if (state.error) { - if (state.error.status === 404) { - return ( -
404, transaction not found
- ); - } else { - return
{state.error.status}, {state.error.statusText}
; - } - } else if (state.transaction) { - return func(state.transaction); - } - }; + const showRawTransaction = () => { + return executeAfterFetchTransaction(state, () => { + return state.transaction ? ( + + ) : ( +
+ {' '} + 404, transaction not found{' '} +
+ ) + }) + } - const showRawTransaction = () => { - return executeAfterFetchTransaction(state, () => { - return state.transaction ? : ( -
404, transaction not found
- ) - }); - }; + const showTransactionDetails = () => { + return executeAfterFetchTransaction(state, (transaction: Transaction) => ( + + + + + + BTC RPC EXPLORER + + + + )) + } - const showTransactionDetails = () => { - return executeAfterFetchTransaction(state, (transaction: Transaction) => ( + return ( +
+
+ Bitcoin Playground + Bitcoin Playground + handleSetTransactionId(newValue)} + disabled={state.executingScript} + width="500px" + height="40px" + style={{ maxWidth: '500px', textAlign: 'center', margin: '0 auto', boxShadow: '2px 2px 2px grey' }} + onSearch={() => { + loadTransaction(state.transactionId) + }} + /> +
+ {state.loading ? ( + + ) : ( - - - - - BTC RPC EXPLORER - - - - )); - }; + + loadTransaction(state.transactionId)} + > + Search + + - return ( -
-
- Bitcoin Playground - Bitcoin Playground - handleSetTransactionId(newValue)} - disabled={state.executingScript} - style={{ maxWidth: '500px', textAlign: 'center', margin: '0 auto' }} - onSearch={() => { - loadTransaction(state.transactionId); - }} +
+ , children: showTransactionDetails() }, + { title: , children: showRawTransaction() } + ]} /> -
- { - state.loading ? - : - ( - - - - loadTransaction(state.transactionId) - }> - Search - - - -
- - ), children: showTransactionDetails() }, - { title: (), children: showRawTransaction() } - ] - } /> -
-
- ) - } -
-
- +
+ + )}
- ); -} \ No newline at end of file +
+
+ ) +} diff --git a/client/src/modules/transaction/TransactionDetailsComponent.tsx b/client/src/modules/transaction/TransactionDetailsComponent.tsx index 16146fb..e7dcc44 100644 --- a/client/src/modules/transaction/TransactionDetailsComponent.tsx +++ b/client/src/modules/transaction/TransactionDetailsComponent.tsx @@ -1,148 +1,155 @@ -import _ from 'lodash'; +import _ from 'lodash' -import React from 'react'; -import Table from '@mui/material/Table'; -import TableBody from '@mui/material/TableBody'; -import TableCell from '@mui/material/TableCell'; -import TableHead from '@mui/material/TableHead'; -import TableRow from '@mui/material/TableRow'; -import Grid from "@mui/material/Grid/Grid"; -import HomeIcon from '@mui/icons-material/Home'; -import LockIcon from '@mui/icons-material/Lock'; -import LockOpenIcon from '@mui/icons-material/LockOpen'; -import DebugIcon from '@mui/icons-material/BugReport'; -import ScriptOpCodeList from "./ScriptOpCodeList"; -import BitcoinIcon from '../../assets/icons/BitcoinIcon'; -import Typography from "@mui/material/Typography"; -import { Transaction } from '../../api'; +import React from 'react' +import Table from '@mui/material/Table' +import TableBody from '@mui/material/TableBody' +import TableCell from '@mui/material/TableCell' +import TableHead from '@mui/material/TableHead' +import TableRow from '@mui/material/TableRow' +import Grid from '@mui/material/Grid/Grid' +import HomeIcon from '@mui/icons-material/Home' +import LockIcon from '@mui/icons-material/Lock' +import LockOpenIcon from '@mui/icons-material/LockOpen' +import DebugIcon from '@mui/icons-material/BugReport' +import ScriptOpCodeList from './ScriptOpCodeList' +import BitcoinIcon from '../../assets/icons/BitcoinIcon' +import Typography from '@mui/material/Typography' +import { Transaction } from '../../api' interface TransactionDetailsComponentProps { - transaction: Transaction + transaction: Transaction } export const TransactionDetailsComponent: React.FunctionComponent = (props) => { - const { transaction } = props; - const inputsLength = transaction.inputs.length; - return ( -
- { - inputsLength < 0 ? - null : - - - - - - - Paying from - - - + const { transaction } = props + const inputsLength = transaction.inputs.length + return ( +
+ {inputsLength < 0 ? null : ( + + +
+ + + + + Paying from + + + + - - { - _.map(transaction.inputs, (input, index) => { - return ( - - -
- - {_.head(input.addresses)} -
-
- - {input.outputValue / 100000000} BTC - Transaction - { - input.outputIndex ? output {input.outputIndex} : null - } -
-
- - ScriptSig - -
-
- -
-
- - - Interpret or - debug -
-
-
- ) - }) - } -
-
-
- - - - - - To - - - - - { - _.map(transaction.outputs, (output, index) => { - return ( - - -
- - {_.head(output.addresses) || 'No address'} -
-
- - {output.value / 100000000} BTC - { - output.spentBy ? Transaction : 'not spent yet' - } -
-
- - ScriptPubKey - -
-
- -
-
- -
- ) - }) - } -
-
-
-
- } -
- ) -}; + + {_.map(transaction.inputs, (input, index) => { + return ( + + +
+ + {_.head(input.addresses)} +
+
+ + {input.outputValue / 100000000} BTC -{' '} + Transaction + {input.outputIndex ? output {input.outputIndex} : null} +
+
+ + ScriptSig - +
+
+ +
+
+ + + + Interpret + {' '} + or + + + debug + +
+
+
+ ) + })} +
+ + + + + + + + + To + + + + + + {_.map(transaction.outputs, (output, index) => { + return ( + + +
+ + {_.head(output.addresses) || 'No address'} +
+
+ + {output.value / 100000000} BTC -{' '} + {output.spentBy ? ( + Transaction + ) : ( + 'not spent yet' + )} +
+
+ + ScriptPubKey -{' '} + +
+
+ +
+
+
+ ) + })} +
+
+
+ + )} +
+ ) +} const ScriptType = ({ scriptTypeRaw }) => { - if (scriptTypeRaw === "pay-to-pubkey-hash") { - return ( - P2PKH - ); - } if (scriptTypeRaw === "pay-to-script-hash") { - return ( - P2SH - ); - } if (scriptTypeRaw === 'null-data') { - return ( - NULL DATA - ); - } else if (scriptTypeRaw) { - return ( {scriptTypeRaw} ); - } else { - return null; - } -}; - + if (scriptTypeRaw === 'pay-to-pubkey-hash') { + return P2PKH + } + if (scriptTypeRaw === 'pay-to-script-hash') { + return P2SH + } + if (scriptTypeRaw === 'null-data') { + return NULL DATA + } else if (scriptTypeRaw) { + return {scriptTypeRaw} + } else { + return null + } +} -export default TransactionDetailsComponent; \ No newline at end of file +export default TransactionDetailsComponent diff --git a/client/src/modules/transaction/TransactionRawComponent.tsx b/client/src/modules/transaction/TransactionRawComponent.tsx index ad5a8b0..5d8e120 100644 --- a/client/src/modules/transaction/TransactionRawComponent.tsx +++ b/client/src/modules/transaction/TransactionRawComponent.tsx @@ -1,95 +1,85 @@ -import _ from 'lodash'; +import _ from 'lodash' -import React from 'react'; -import Typography from "@mui/material/Typography"; -import Tooltip from "@mui/material/Tooltip"; -import { Transaction } from '../../api'; +import React from 'react' +import Typography from '@mui/material/Typography' +import Tooltip from '@mui/material/Tooltip' +import { Transaction } from '../../api' interface TransactionRawComponentProps { transaction: Transaction } export const TransactionRawComponent: React.FunctionComponent = (props) => { - const { transaction } = props; - const txRaw = transaction.txRaw; + const { transaction } = props + const txRaw = transaction.txRaw return txRaw ? ( -
+
{txRaw.version} - { - txRaw.flag ? ( - - {txRaw.flag} - - ) : undefined - } + {txRaw.flag ? ( + + {txRaw.flag} + + ) : undefined} { {txRaw.txIns.count} } - { - _.map(txRaw.txIns.txIns, (txIn, index) => { - return ( - - - {txIn.previousOutput.hash} - - - {txIn.previousOutput.index} - - - {txIn.sigScript} - - - {txIn.sequence} - - - ) - }) - } + {_.map(txRaw.txIns.txIns, (txIn, index) => { + return ( + + + {txIn.previousOutput.hash} + + + {txIn.previousOutput.index} + + + {txIn.sigScript} + + + {txIn.sequence} + + + ) + })} { {txRaw.txOuts.count} } - { - _.map(txRaw.txOuts.txOuts, (txOut, index) => { - return ( - - - {txOut.value} - - - {txOut.pkScript} - - - ) - }) - } - { - _.map(txRaw.txWitnesses, (txWitnesses, inputIndex) => { - return ( - - - {txWitnesses.count} - - { - _.flatMap(txWitnesses.txWitnesses, (txWitness, witnessIndex) => { - return ( - - {txWitness.witness} - - ) - }) - } - - ) - }) - } + {_.map(txRaw.txOuts.txOuts, (txOut, index) => { + return ( + + + {txOut.value} + + + {txOut.pkScript} + + + ) + })} + {_.map(txRaw.txWitnesses, (txWitnesses, inputIndex) => { + return ( + + + {txWitnesses.count} + + {_.flatMap(txWitnesses.txWitnesses, (txWitness, witnessIndex) => { + return ( + + {txWitness.witness} + + ) + })} + + ) + })} { {txRaw.lockTime} @@ -97,7 +87,9 @@ export const TransactionRawComponent: React.FunctionComponent
- ) : (
Transaction raw data not available
); + ) : ( +
Transaction raw data not available
+ ) } -export default TransactionRawComponent; \ No newline at end of file +export default TransactionRawComponent diff --git a/client/yarn.lock b/client/yarn.lock index d32bb48..0084fc1 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -1853,6 +1853,18 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@pkgr/utils@^2.3.1": + version "2.4.2" + resolved "https://registry.yarnpkg.com/@pkgr/utils/-/utils-2.4.2.tgz#9e638bbe9a6a6f165580dc943f138fd3309a2cbc" + integrity sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw== + dependencies: + cross-spawn "^7.0.3" + fast-glob "^3.3.0" + is-glob "^4.0.3" + open "^9.1.0" + picocolors "^1.0.0" + tslib "^2.6.0" + "@pmmmwh/react-refresh-webpack-plugin@^0.5.3": version "0.5.11" resolved "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.11.tgz" @@ -2495,7 +2507,7 @@ semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/utils@5.62.0", "@typescript-eslint/utils@^5.58.0": +"@typescript-eslint/utils@5.62.0", "@typescript-eslint/utils@^5.10.0", "@typescript-eslint/utils@^5.58.0": version "5.62.0" resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz" integrity sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ== @@ -3161,6 +3173,11 @@ bfj@^7.0.2: jsonpath "^1.1.1" tryer "^1.0.1" +big-integer@^1.6.44: + version "1.6.52" + resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.52.tgz#60a887f3047614a8e1bffe5d7173490a97dc8c85" + integrity sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg== + big.js@^5.2.2: version "5.2.2" resolved "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz" @@ -3209,6 +3226,13 @@ boolbase@^1.0.0, boolbase@~1.0.0: resolved "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz" integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww== +bplist-parser@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/bplist-parser/-/bplist-parser-0.2.0.tgz#43a9d183e5bf9d545200ceac3e712f79ebbe8d0e" + integrity sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw== + dependencies: + big-integer "^1.6.44" + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" @@ -3263,6 +3287,13 @@ builtin-modules@^3.1.0: resolved "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz" integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw== +bundle-name@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bundle-name/-/bundle-name-3.0.0.tgz#ba59bcc9ac785fb67ccdbf104a2bf60c099f0e1a" + integrity sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw== + dependencies: + run-applescript "^5.0.0" + bytes@3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz" @@ -3883,6 +3914,24 @@ deepmerge@^4.2.2: resolved "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz" integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== +default-browser-id@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/default-browser-id/-/default-browser-id-3.0.0.tgz#bee7bbbef1f4e75d31f98f4d3f1556a14cea790c" + integrity sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA== + dependencies: + bplist-parser "^0.2.0" + untildify "^4.0.0" + +default-browser@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/default-browser/-/default-browser-4.0.0.tgz#53c9894f8810bf86696de117a6ce9085a3cbc7da" + integrity sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA== + dependencies: + bundle-name "^3.0.0" + default-browser-id "^3.0.0" + execa "^7.1.1" + titleize "^3.0.0" + default-gateway@^6.0.3: version "6.0.3" resolved "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz" @@ -3904,6 +3953,11 @@ define-lazy-prop@^2.0.0: resolved "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz" integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== +define-lazy-prop@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz#dbb19adfb746d7fc6d734a06b72f4a00d021255f" + integrity sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg== + define-properties@^1.1.3, define-properties@^1.1.4, define-properties@^1.2.0, define-properties@^1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz" @@ -4322,6 +4376,11 @@ escodegen@^2.0.0: optionalDependencies: source-map "~0.6.1" +eslint-config-prettier@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz#eb25485946dd0c66cd216a46232dc05451518d1f" + integrity sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw== + eslint-config-react-app@^7.0.1: version "7.0.1" resolved "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-7.0.1.tgz" @@ -4366,6 +4425,11 @@ eslint-plugin-flowtype@^8.0.3: lodash "^4.17.21" string-natural-compare "^3.0.1" +eslint-plugin-header@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-header/-/eslint-plugin-header-3.1.1.tgz#6ce512432d57675265fac47292b50d1eff11acd6" + integrity sha512-9vlKxuJ4qf793CmeeSrZUvVClw6amtpghq3CuWcB5cUNnWHQhgcqy5eF8oVKFk1G3Y/CbchGfEaw3wiIJaNmVg== + eslint-plugin-import@^2.25.3: version "2.29.0" resolved "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.0.tgz" @@ -4396,6 +4460,13 @@ eslint-plugin-jest@^25.3.0: dependencies: "@typescript-eslint/experimental-utils" "^5.0.0" +eslint-plugin-jest@^27.2.1: + version "27.6.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-27.6.0.tgz#e5c0cf735b3c8cad0ef9db5b565b2fc99f5e55ed" + integrity sha512-MTlusnnDMChbElsszJvrwD1dN3x6nZl//s4JD23BxB6MgR66TZlL064su24xEIS3VACfAoHV1vgyMgPw8nkdng== + dependencies: + "@typescript-eslint/utils" "^5.10.0" + eslint-plugin-jsx-a11y@^6.5.1: version "6.8.0" resolved "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.8.0.tgz" @@ -4418,6 +4489,14 @@ eslint-plugin-jsx-a11y@^6.5.1: object.entries "^1.1.7" object.fromentries "^2.0.7" +eslint-plugin-prettier@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.1.tgz#a3b399f04378f79f066379f544e42d6b73f11515" + integrity sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg== + dependencies: + prettier-linter-helpers "^1.0.0" + synckit "^0.8.5" + eslint-plugin-react-hooks@^4.3.0: version "4.6.0" resolved "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz" @@ -4445,6 +4524,13 @@ eslint-plugin-react@^7.27.1: semver "^6.3.1" string.prototype.matchall "^4.0.8" +eslint-plugin-security@^1.7.1: + version "1.7.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-security/-/eslint-plugin-security-1.7.1.tgz#0e9c4a471f6e4d3ca16413c7a4a51f3966ba16e4" + integrity sha512-sMStceig8AFglhhT2LqlU5r+/fn9OwsA72O5bBuQVTssPCdQAOQzL+oMn/ZcpeUY6KcNfLJArgcrsSULNjYYdQ== + dependencies: + safe-regex "^2.1.1" + eslint-plugin-testing-library@^5.0.1: version "5.11.1" resolved "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-5.11.1.tgz" @@ -4489,7 +4575,7 @@ eslint-webpack-plugin@^3.1.1: normalize-path "^3.0.0" schema-utils "^4.0.0" -eslint@^8.3.0: +eslint@^8.3.0, eslint@^8.37.0: version "8.54.0" resolved "https://registry.npmjs.org/eslint/-/eslint-8.54.0.tgz" integrity sha512-NY0DfAkM8BIZDVl6PgSa1ttZbx3xHgJzSNJKYcQglem6CppHyMhRIQkBVSSMaSRnLhig3jsDbEzOjwCVt4AmmA== @@ -4616,6 +4702,21 @@ execa@^5.0.0: signal-exit "^3.0.3" strip-final-newline "^2.0.0" +execa@^7.1.1: + version "7.2.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-7.2.0.tgz#657e75ba984f42a70f38928cedc87d6f2d4fe4e9" + integrity sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.1" + human-signals "^4.3.0" + is-stream "^3.0.0" + merge-stream "^2.0.0" + npm-run-path "^5.1.0" + onetime "^6.0.0" + signal-exit "^3.0.7" + strip-final-newline "^3.0.0" + exit@^0.1.2: version "0.1.2" resolved "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz" @@ -4673,6 +4774,11 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== +fast-diff@^1.1.2: + version "1.3.0" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.3.0.tgz#ece407fa550a64d638536cd727e129c61616e0f0" + integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== + fast-glob@^3.2.9, fast-glob@^3.3.0: version "3.3.2" resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz" @@ -4957,7 +5063,7 @@ get-package-type@^0.1.0: resolved "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== -get-stream@^6.0.0: +get-stream@^6.0.0, get-stream@^6.0.1: version "6.0.1" resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== @@ -5303,6 +5409,11 @@ human-signals@^2.1.0: resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== +human-signals@^4.3.0: + version "4.3.1" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-4.3.1.tgz#ab7f811e851fca97ffbd2c1fe9a958964de321b2" + integrity sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ== + iconv-lite@0.4.24: version "0.4.24" resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz" @@ -5474,6 +5585,11 @@ is-docker@^2.0.0, is-docker@^2.1.1: resolved "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz" integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== +is-docker@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-3.0.0.tgz#90093aa3106277d8a77a5910dbae71747e15a200" + integrity sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ== + is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" @@ -5510,6 +5626,13 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" +is-inside-container@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-inside-container/-/is-inside-container-1.0.0.tgz#e81fba699662eb31dbdaf26766a61d4814717ea4" + integrity sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA== + dependencies: + is-docker "^3.0.0" + is-map@^2.0.1: version "2.0.2" resolved "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz" @@ -5592,6 +5715,11 @@ is-stream@^2.0.0: resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz" integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== +is-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac" + integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA== + is-string@^1.0.5, is-string@^1.0.7: version "1.0.7" resolved "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz" @@ -6630,6 +6758,11 @@ mimic-fn@^2.1.0: resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== +mimic-fn@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc" + integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== + mini-css-extract-plugin@^2.4.5: version "2.7.6" resolved "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.6.tgz" @@ -6770,6 +6903,13 @@ npm-run-path@^4.0.1: dependencies: path-key "^3.0.0" +npm-run-path@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-5.1.0.tgz#bc62f7f3f6952d9894bd08944ba011a6ee7b7e00" + integrity sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q== + dependencies: + path-key "^4.0.0" + nth-check@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz" @@ -6906,6 +7046,13 @@ onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" +onetime@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-6.0.0.tgz#7c24c18ed1fd2e9bca4bd26806a33613c77d34b4" + integrity sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ== + dependencies: + mimic-fn "^4.0.0" + open@^8.0.9, open@^8.4.0: version "8.4.2" resolved "https://registry.npmjs.org/open/-/open-8.4.2.tgz" @@ -6915,6 +7062,16 @@ open@^8.0.9, open@^8.4.0: is-docker "^2.1.1" is-wsl "^2.2.0" +open@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/open/-/open-9.1.0.tgz#684934359c90ad25742f5a26151970ff8c6c80b6" + integrity sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg== + dependencies: + default-browser "^4.0.0" + define-lazy-prop "^3.0.0" + is-inside-container "^1.0.0" + is-wsl "^2.2.0" + optionator@^0.8.1: version "0.8.3" resolved "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz" @@ -7050,6 +7207,11 @@ path-key@^3.0.0, path-key@^3.1.0: resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== +path-key@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-4.0.0.tgz#295588dc3aee64154f877adb9d780b81c554bf18" + integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== + path-parse@^1.0.7: version "1.0.7" resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" @@ -7678,10 +7840,17 @@ prelude-ls@~1.1.2: resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz" integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w== -prettier@^2.8.8: - version "2.8.8" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" - integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== +prettier-linter-helpers@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" + integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== + dependencies: + fast-diff "^1.1.2" + +prettier@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.1.0.tgz#c6d16474a5f764ea1a4a373c593b779697744d5e" + integrity sha512-TQLvXjq5IAibjh8EpBIkNKxO749UEWABoiIZehEPiY4GNpVdhaFKqSTu+QrlU6D2dPAfubRmtJTi4K4YkQ5eXw== pretty-bytes@^5.3.0, pretty-bytes@^5.4.1: version "5.6.0" @@ -8104,6 +8273,11 @@ regex-parser@^2.2.11: resolved "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz" integrity sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q== +regexp-tree@~0.1.1: + version "0.1.27" + resolved "https://registry.yarnpkg.com/regexp-tree/-/regexp-tree-0.1.27.tgz#2198f0ef54518ffa743fe74d983b56ffd631b6cd" + integrity sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA== + regexp.prototype.flags@^1.5.0, regexp.prototype.flags@^1.5.1: version "1.5.1" resolved "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz" @@ -8253,6 +8427,13 @@ rollup@^2.43.1: optionalDependencies: fsevents "~2.3.2" +run-applescript@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/run-applescript/-/run-applescript-5.0.0.tgz#e11e1c932e055d5c6b40d98374e0268d9b11899c" + integrity sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg== + dependencies: + execa "^5.0.0" + run-parallel@^1.1.9: version "1.2.0" resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz" @@ -8289,6 +8470,13 @@ safe-regex-test@^1.0.0: get-intrinsic "^1.1.3" is-regex "^1.1.4" +safe-regex@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-2.1.1.tgz#f7128f00d056e2fe5c11e81a1324dd974aadced2" + integrity sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A== + dependencies: + regexp-tree "~0.1.1" + "safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": version "2.1.2" resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" @@ -8499,7 +8687,7 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" -signal-exit@^3.0.2, signal-exit@^3.0.3: +signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== @@ -8773,6 +8961,11 @@ strip-final-newline@^2.0.0: resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== +strip-final-newline@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd" + integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== + strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz" @@ -8885,6 +9078,14 @@ symbol-tree@^3.2.4: resolved "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== +synckit@^0.8.5: + version "0.8.5" + resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.8.5.tgz#b7f4358f9bb559437f9f167eb6bc46b3c9818fa3" + integrity sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q== + dependencies: + "@pkgr/utils" "^2.3.1" + tslib "^2.5.0" + tailwindcss@^3.0.2: version "3.3.5" resolved "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.5.tgz" @@ -9015,6 +9216,11 @@ tiny-warning@^1.0.0: resolved "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz" integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== +titleize@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/titleize/-/titleize-3.0.0.tgz#71c12eb7fdd2558aa8a44b0be83b8a76694acd53" + integrity sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ== + tmpl@1.0.5: version "1.0.5" resolved "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz" @@ -9086,7 +9292,7 @@ tslib@^1.8.1: resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.3: +tslib@^2.0.3, tslib@^2.5.0, tslib@^2.6.0: version "2.6.2" resolved "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz" integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== @@ -9270,6 +9476,11 @@ unquote@~1.1.1: resolved "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz" integrity sha512-vRCqFv6UhXpWxZPyGDh/F3ZpNv8/qo7w6iufLpQg9aKnQ71qM4B5KiI7Mia9COcjEhrO9LueHpMYjYzsWH3OIg== +untildify@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b" + integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw== + upath@^1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz"