diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 4a7ea3036a20398caf9a9daa984acb4a019f3090..0000000000000000000000000000000000000000 --- a/.editorconfig +++ /dev/null @@ -1,12 +0,0 @@ -root = true - -[*] -indent_style = space -indent_size = 2 -end_of_line = lf -charset = utf-8 -trim_trailing_whitespace = true -insert_final_newline = true - -[*.md] -trim_trailing_whitespace = false diff --git a/.eslintrc.js b/.eslintrc.js index 2dcd62350e35eb5873288596f01703f116397504..75cd94e1e7746aea5d1569fb2f08167d78adac81 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -3,6 +3,13 @@ module.exports = { rules: { // A temporary hack related to IDE not resolving correct package.json 'import/no-extraneous-dependencies': 'off', + 'no-console': 'off', + 'prettier/prettier': [ + 'error', + { + endOfLine: 'auto', + }, + ], }, parserOptions: { ecmaVersion: 2020, diff --git a/.gitignore b/.gitignore index 09c02bf81a5225dd66fab1d577c7f885d479bcaa..38e1fb2ce1376370d9f83352eeb75edea7d9d176 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ +loginDetails.json + +*.LICENSE + # Logs logs *.log @@ -48,3 +52,24 @@ npm-debug.log.* *.css.d.ts *.sass.d.ts *.scss.d.ts + +### Vim ### +# Swap +[._]*.s[a-v][a-z] +!*.svg # comment out if you don't need vector files +[._]*.sw[a-p] +[._]s[a-rt-v][a-z] +[._]ss[a-gi-z] +[._]sw[a-p] + +# Session +Session.vim +Sessionx.vim + +# Temporary +.netrwhist +*~ +# Auto-generated tag files +tags +# Persistent undo +[._]*.un~ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..22ac592e5fa9941276d03d106c06936419715fbc --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,37 @@ +image: node:lts-alpine + +stages: + - pre + - test + +cache: + key: ${CI_COMMIT_REF_SLUG} + paths: + - node_modules/ + +before_script: + - yarn install + - yarn build-main + - yarn build-renderer + +lint: + stage: test + script: + - yarn run lint +#lint:types: +# stage: test +# script: +# - npm run typecheck + +tests:unit: + stage: test + script: + - yarn run test +#expo-deployments: +# stage: deploy +# only: +# - master +# script: +# - echo fs.inotify.max_user_watches=524288 | tee -a /etc/sysctl.conf && sysctl -p +# - npx expo login -u $EXPO_USERNAME -p $EXPO_PASSWORD +# - npx expo publish --non-interactive diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000000000000000000000000000000000000..15918ed5eb934869c1d72629026e8c828ce246d0 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,7 @@ +{ + "trailingComma": "es5", + "tabWidth": 2, + "semi": false, + "singleQuote": true, + "endOfLine": "auto" +} diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 7b5f512b3ef4b9d9c9f581079f489335cfadb695..f4b0e3a890338930c0a4a24e656cabd16017409c 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,6 +1,12 @@ { "recommendations": [ "dbaeumer.vscode-eslint", - "EditorConfig.EditorConfig" + "EditorConfig.EditorConfig", + "esbenp.prettier-vscode", + "hwencc.html-tag-wrapper", + "coenraads.bracket-pair-colorizer", + "ms-vscode.vscode-typescript-tslint-plugin", + "visualstudioexptteam.vscodeintellicode", + "wayou.vscode-todo-highlight" ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index 63e5500f9ca72cf6edefb4f26a37bd70eaba77b1..aa04e679c831027c621517cf6afff9fb16269880 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -26,5 +26,6 @@ "test/**/__snapshots__": true, "yarn.lock": true, "*.{css,sass,scss}.d.ts": true - } + }, + "editor.tabSize": 2 } diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index a5b4f88f2d49f71347b2fa6073186df429bb62f2..0000000000000000000000000000000000000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,486 +0,0 @@ -# 1.3.0 - -- Fixes E2E tests ([#2516](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/2516)) -- Fixes preload entrypoint ([#2503](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/2503)) -- Downgrade to `electron@8` -- Bump dependencies to latest semver - -# 1.2.0 - -- Migrate to redux toolkit -- Lazy load routes with react suspense -- Drop support for azure-pipelines and use only github actions -- Bump all deps to latest semver -- Remove `test-e2e` script from tests (blocked on release of https://github.com/DevExpress/testcafe-browser-provider-electron/pull/65) -- Swap `typed-css-modules-webpack-plugin` for `typings-for-css-modules-loader` -- Use latest version of `eslint-config-erb` -- Remove unnecessary file extensions from ts exclude -- Add experimental support for vscode debugging -- Revert https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/2365 as default for users, provide as opt in option - -# 1.1.0 - -- Fix #2402 -- Simplify configs (https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/2406) - -# 1.0.0 - -- Migrate to TypeScript from Flow ([#2363](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/2363)) -- Use browserslist for `@babel/preset-env` targets ([#2368](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/2368)) -- Use preload script, disable `nodeIntegration` in renderer process for [improved security](https://www.electronjs.org/docs/tutorial/security#2-do-not-enable-nodejs-integration-for-remote-content) ([#2365](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/2365)) -- Add support for azure pipelines ([#2369](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/2369)) -- Disable sourcemaps in production - -# 0.18.1 (2019.12.12) - -- Fix HMR env bug ([#2343](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/2343)) -- Bump all deps to latest semver -- Bump to `electron@7` - -# 0.18.0 (2019.11.19) - -- Bump electron to `electron@6` (`electron@7` introduces breaking changes to testcafe end to end tests) -- Revert back to [two `package.json` structure](https://www.electron.build/tutorials/two-package-structure) -- Bump all deps to latest semver - -# 0.17.1 (2018.11.20) - -- Fix `yarn test-e2e` and testcafe for single package.json structure -- Fixes incorrect path in `yarn start` script -- Bumped deps -- Bump g++ in travis -- Change clone arguments to clone only master -- Change babel config to target current electron version - -For full change list, see https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/2021 - -# 0.17.0 (2018.10.30) - -- upgraded to `babel@7` (thanks to @vikr01 🎉🎉🎉) -- migrated from [two `package.json` structure](https://www.electron.build/tutorials/two-package-structure) (thanks to @HyperSprite!) -- initial auto update support (experimental) -- migrate from greenkeeper to [renovate](https://renovatebot.com) -- added issue template -- use `babel-preset-env` to target current electron version -- add [opencollective](https://opencollective.com/electron-react-boilerplate-594) banner message display in postinstall script (help support ERB 🙏) -- fix failing ci issues - -# 0.16.0 (2018.10.3) - -- removed unused dependencies -- migrate from `react-redux-router` to `connect-react-router` -- move webpack configs to `./webpack` dir -- use `g++` on travis when testing linux -- migrate from `spectron` to `testcafe` for e2e tests -- add linting support for config styles -- changed stylelint config -- temporarily disabled flow in appveyor to make ci pass -- added necessary infra to publish releases from ci - -# 0.15.0 (2018.8.25) - -- Performance: cache webpack uglify results -- Feature: add start minimized feature -- Feature: lint and fix styles with prettier and stylelint -- Feature: add greenkeeper support - -# 0.14.0 (2018.5.24) - -- Improved CI timings -- Migrated README commands to yarn from npm -- Improved vscode config -- Updated all dependencies to latest semver -- Fix `electron-rebuild` script bug -- Migrated to `mini-css-extract-plugin` from `extract-text-plugin` -- Added `optimize-css-assets-webpack-plugin` -- Run `prettier` on json, css, scss, and more filetypes - -# 0.13.3 (2018.5.24) - -- Add git precommit hook, when git commit will use `prettier` to format git add code -- Add format code function in `lint-fix` npm script which can use `prettier` to format project js code - -# 0.13.2 (2018.1.31) - -- Hot Module Reload (HMR) fixes -- Bumped all dependencies to latest semver -- Prevent error propagation of `CheckNativeDeps` script - -# 0.13.1 (2018.1.13) - -- Hot Module Reload (HMR) fixes -- Bumped all dependencies to latest semver -- Fixed electron-rebuild script -- Fixed tests scripts to run on all platforms -- Skip redux logs in console in test ENV - -# 0.13.0 (2018.1.6) - -#### Additions - -- Add native dependencies check on postinstall -- Updated all dependencies to latest semver - -# 0.12.0 (2017.7.8) - -#### Misc - -- Removed `babel-polyfill` -- Renamed and alphabetized npm scripts - -#### Breaking - -- Changed node dev `__dirname` and `__filename` to node built in fn's (https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/1035) -- Renamed `app/bundle.js` to `app/renderer.prod.js` for consistency -- Renamed `dll/vendor.js` to `dll/renderer.dev.dll.js` for consistency - -#### Additions - -- Enable node_modules cache on CI - -# 0.11.2 (2017.5.1) - -Yay! Another patch release. This release mostly includes refactorings and router bug fixes. Huge thanks to @anthonyraymond! - -⚠️ Windows electron builds are failing because of [this issue](https://github.com/electron/electron/issues/9321). This is not an issue with the boilerplate ⚠️ - -#### Breaking - -- **Renamed `./app/main.development.js` => `./app/main.{dev,prod}.js`:** [#963](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/963) - -#### Fixes - -- **Fixed reloading when not on `/` path:** [#958](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/958) [#949](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/949) - -#### Additions - -- **Added support for stylefmt:** [#960](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/960) - -# 0.11.1 (2017.4.23) - -You can now debug the production build with devtools like so: - -``` -DEBUG_PROD=true npm run package -``` - -🎉🎉🎉 - -#### Additions - -- **Added support for debugging production build:** [#fab245a](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/941/commits/fab245a077d02a09630f74270806c0c534a4ff95) - -#### Bug Fixes - -- **Fixed bug related to importing native dependencies:** [#933](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/933) - -#### Improvements - -- **Updated all deps to latest semver** - -# 0.11.0 (2017.4.19) - -Here's the most notable changes since `v0.10.0`. Its been about a year since a release has been pushed. Expect a new release to be published every 3-4 weeks. - -#### Breaking Changes - -- **Dropped support for node < 6** -- **Refactored webpack config files** -- **Migrate to two-package.json project structure** -- **Updated all devDeps to latest semver** -- **Migrated to Jest:** [#768](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/768) -- **Migrated to `react-router@4`** -- **Migrated to `electron-builder@4`** -- **Migrated to `webpack@2`** -- **Migrated to `react-hot-loader@3`** -- **Changed default live reload server PORT to `1212` from `3000`** - -#### Additions - -- **Added support for Yarn:** [#451](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/451) -- **Added support for Flow:** [#425](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/425) -- **Added support for stylelint:** [#911](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/911) -- **Added support for electron-builder:** [#876](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/876) -- **Added optional support for SASS:** [#880](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/880) -- **Added support for eslint-plugin-flowtype:** [#911](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/911) -- **Added support for appveyor:** [#280](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/280) -- **Added support for webpack dlls:** [#860](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/860) -- **Route based code splitting:** [#884](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/884) -- **Added support for Webpack Bundle Analyzer:** [#922](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/922) - -#### Improvements - -- **Parallelize renderer and main build processes when running `npm run build`** -- **Dynamically generate electron app menu** -- **Improved vscode integration:** [#856](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/856) - -#### Bug Fixes - -- **Fixed hot module replacement race condition bug:** [#917](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/917) [#920](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/920) - -# 0.10.0 (2016.4.18) - -#### Improvements - -- **Use Babel in main process with Webpack build:** [#201](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/201) -- **Change targets to built-in support by webpack:** [#197](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/197) -- **use es2015 syntax for webpack configs:** [#195](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/195) -- **Open application when webcontent is loaded:** [#192](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/192) -- **Upgraded dependencies** - -#### Bug fixed - -- **Fix `npm list electron-prebuilt` in package.js:** [#188](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/188) - -# 0.9.0 (2016.3.23) - -#### Improvements - -- **Added [redux-logger](https://github.com/fcomb/redux-logger)** -- **Upgraded [react-router-redux](https://github.com/reactjs/react-router-redux) to v4** -- **Upgraded dependencies** -- **Added `npm run dev` command:** [#162](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/162) -- **electron to v0.37.2** - -#### Breaking Changes - -- **css module as default:** [#154](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/154). -- **set default NODE_ENV to production:** [#140](https://github.com/electron-react-boilerplate/electron-react-boilerplate/issues/140) - -# 0.8.0 (2016.2.17) - -#### Bug fixed - -- **Fix lint errors** -- **Fix Webpack publicPath for production builds**: [#119](https://github.com/electron-react-boilerplate/electron-react-boilerplate/issues/119). -- **package script now chooses correct OS icon extension** - -#### Improvements - -- **babel 6** -- **Upgrade Dependencies** -- **Enable CSS source maps** -- **Add json-loader**: [#128](https://github.com/electron-react-boilerplate/electron-react-boilerplate/issues/128). -- **react-router 2.0 and react-router-redux 3.0** - -# 0.7.1 (2015.12.27) - -#### Bug fixed - -- **Fixed npm script on windows 10:** [#103](https://github.com/electron-react-boilerplate/electron-react-boilerplate/issues/103). -- **history and react-router version bump**: [#109](https://github.com/electron-react-boilerplate/electron-react-boilerplate/issues/109), [#110](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/110). - -#### Improvements - -- **electron 0.36** - -# 0.7.0 (2015.12.16) - -#### Bug fixed - -- **Fixed process.env.NODE_ENV variable in webpack:** [#74](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/74). -- **add missing object-assign**: [#76](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/76). -- **packaging in npm@3:** [#77](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/77). -- **compatibility in windows:** [#100](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/100). -- **disable chrome debugger in production env:** [#102](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/102). - -#### Improvements - -- **redux** -- **css-modules** -- **upgrade to react-router 1.x** -- **unit tests** -- **e2e tests** -- **travis-ci** -- **upgrade to electron 0.35.x** -- **use es2015** -- **check dev engine for node and npm** - -# 0.6.5 (2015.11.7) - -#### Improvements - -- **Bump style-loader to 0.13** -- **Bump css-loader to 0.22** - -# 0.6.4 (2015.10.27) - -#### Improvements - -- **Bump electron-debug to 0.3** - -# 0.6.3 (2015.10.26) - -#### Improvements - -- **Initialize ExtractTextPlugin once:** [#64](https://github.com/electron-react-boilerplate/electron-react-boilerplate/issues/64). - -# 0.6.2 (2015.10.18) - -#### Bug fixed - -- **Babel plugins production env not be set properly:** [#57](https://github.com/electron-react-boilerplate/electron-react-boilerplate/issues/57). - -# 0.6.1 (2015.10.17) - -#### Improvements - -- **Bump electron to v0.34.0** - -# 0.6.0 (2015.10.16) - -#### Breaking Changes - -- **From react-hot-loader to react-transform** - -# 0.5.2 (2015.10.15) - -#### Improvements - -- **Run tests with babel-register:** [#29](https://github.com/electron-react-boilerplate/electron-react-boilerplate/issues/29). - -# 0.5.1 (2015.10.12) - -#### Bug fixed - -- **Fix #51:** use `path.join(__dirname` instead of `./`. - -# 0.5.0 (2015.10.11) - -#### Improvements - -- **Simplify webpack config** see [#50](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/50). - -#### Breaking Changes - -- **webpack configs** -- **port changed:** changed default port from 2992 to 3000. -- **npm scripts:** remove `start-dev` and `dev-server`. rename `hot-dev-server` to `hot-server`. - -# 0.4.3 (2015.9.22) - -#### Bug fixed - -- **Fix #45 zeromq crash:** bump version of `electron-prebuilt`. - -# 0.4.2 (2015.9.15) - -#### Bug fixed - -- **run start-hot breaks chrome refresh(CTRL+R) (#42)**: bump `electron-debug` to `0.2.1` - -# 0.4.1 (2015.9.11) - -#### Improvements - -- **use electron-prebuilt version for packaging (#33)** - -# 0.4.0 (2015.9.5) - -#### Improvements - -- **update dependencies** - -# 0.3.0 (2015.8.31) - -#### Improvements - -- **eslint-config-airbnb** - -# 0.2.10 (2015.8.27) - -#### Features - -- **custom placeholder icon** - -#### Improvements - -- **electron-renderer as target:** via [webpack-target-electron-renderer](https://github.com/chentsulin/webpack-target-electron-renderer) - -# 0.2.9 (2015.8.18) - -#### Bug fixed - -- **Fix hot-reload** - -# 0.2.8 (2015.8.13) - -#### Improvements - -- **bump electron-debug** -- **babelrc** -- **organize webpack scripts** - -# 0.2.7 (2015.7.9) - -#### Bug fixed - -- **defaultProps:** fix typos. - -# 0.2.6 (2015.7.3) - -#### Features - -- **menu** - -#### Bug fixed - -- **package.js:** include webpack build. - -# 0.2.5 (2015.7.1) - -#### Features - -- **NPM Script:** support multi-platform -- **package:** `--all` option - -# 0.2.4 (2015.6.9) - -#### Bug fixed - -- **Eslint:** typo, [#17](https://github.com/electron-react-boilerplate/electron-react-boilerplate/issues/17) and improve `.eslintrc` - -# 0.2.3 (2015.6.3) - -#### Features - -- **Package Version:** use latest release electron version as default -- **Ignore Large peerDependencies** - -#### Bug fixed - -- **Npm Script:** typo, [#6](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/6) -- **Missing css:** [#7](https://github.com/electron-react-boilerplate/electron-react-boilerplate/pull/7) - -# 0.2.2 (2015.6.2) - -#### Features - -- **electron-debug** - -#### Bug fixed - -- **Webpack:** add `.json` and `.node` to extensions for imitating node require. -- **Webpack:** set `node_modules` to externals for native module support. - -# 0.2.1 (2015.5.30) - -#### Bug fixed - -- **Webpack:** #1, change build target to `atom`. - -# 0.2.0 (2015.5.30) - -#### Features - -- **Ignore:** `test`, `tools`, `release` folder and devDependencies in `package.json`. -- **Support asar** -- **Support icon** - -# 0.1.0 (2015.5.27) - -#### Features - -- **Webpack:** babel, react-hot, ... -- **Flux:** actions, api, components, containers, stores.. -- **Package:** darwin (osx), linux and win32 (windows) platform. diff --git a/LICENSE b/LICENSE deleted file mode 100644 index c2885de682427844aaf0382a124da8de9f14eeb4..0000000000000000000000000000000000000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015-present Electron React Boilerplate - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/README.md b/README.md index d0e655c60dd6e95d939e3586b9bfe78332e42665..7932189748901def0ae9ff69aa5e44a27657e424 100644 --- a/README.md +++ b/README.md @@ -1,53 +1,23 @@ -<img src="internals/img/erb-banner.png" width="100%" /> +# MySQL Query Profiler -<br> +## Requirements -<p> - Electron React Boilerplate uses <a href="https://electron.atom.io/">Electron</a>, <a href="https://facebook.github.io/react/">React</a>, <a href="https://github.com/reactjs/redux">Redux</a>, <a href="https://github.com/reactjs/react-router">React Router</a>, <a href="https://webpack.github.io/docs/">Webpack</a> and <a href="https://github.com/gaearon/react-hot-loader">React Hot Loader</a> for rapid application development (HMR). -</p> - -<br> - -<div align="center"> - <a href="https://facebook.github.io/react/"><img src="./internals/img/react-padded-90.png" /></a> - <a href="https://webpack.github.io/"><img src="./internals/img/webpack-padded-90.png" /></a> - <a href="https://redux.js.org/"><img src="./internals/img/redux-padded-90.png" /></a> - <a href="https://github.com/ReactTraining/react-router"><img src="./internals/img/react-router-padded-90.png" /></a> - <a href="https://eslint.org/"><img src="./internals/img/eslint-padded-90.png" /></a> - <a href="https://facebook.github.io/jest/"><img src="./internals/img/jest-padded-90.png" /></a> - <a href="https://yarnpkg.com/"><img src="./internals/img/yarn-padded-90.png" /></a> -</div> - -<hr /> -<br /> - -<div align="center"> - -[![Build Status][github-actions-status]][github-actions-url] -[![Dependency Status][david-image]][david-url] -[![DevDependency Status][david-dev-image]][david-dev-url] -[![Github Tag][github-tag-image]][github-tag-url] - -[](https://spectrum.chat/electron-react-blpt) -[](#backers) -[](#sponsors) -[![Good first issues open][good-first-issue-image]][good-first-issue-url] -[![StackOverflow][stackoverflow-img]][stackoverflow-url] - -</div> +- Node 10 or later +- Npm +- Yarn ## Install -- **If you have installation or compilation issues with this project, please see [our debugging guide](https://github.com/electron-react-boilerplate/electron-react-boilerplate/issues/400)** - First, clone the repo via git and install dependencies: ```bash -git clone --depth 1 --single-branch https://github.com/electron-react-boilerplate/electron-react-boilerplate.git your-project-name -cd your-project-name +git clone [project link] +cd mysql-query-profiler yarn ``` +Test database: [test_db](https://github.com/datacharmer/test_db) + ## Starting Development Start the app in the `dev` environment. This starts the renderer process in [**hot-module-replacement**](https://webpack.js.org/guides/hmr-react/) mode and starts a webpack dev server that sends hot updates to the renderer process: @@ -64,108 +34,4 @@ To package apps for the local platform: yarn package ``` -## Docs - -See our [docs and guides here](https://electron-react-boilerplate.js.org/docs/installation) - -## Donations - -**Donations will ensure the following:** - -- 🔨 Long term maintenance of the project -- 🛣 Progress on the [roadmap](https://electron-react-boilerplate.js.org/docs/roadmap) -- 🐛 Quick responses to bug reports and help requests - -## Backers - -Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/electron-react-boilerplate#backer)] - -<a href="https://opencollective.com/electron-react-boilerplate/backer/0/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/0/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/backer/1/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/1/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/backer/2/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/2/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/backer/3/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/3/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/backer/4/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/4/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/backer/5/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/5/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/backer/6/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/6/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/backer/7/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/7/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/backer/8/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/8/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/backer/9/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/9/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/backer/10/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/10/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/backer/11/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/11/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/backer/12/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/12/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/backer/13/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/13/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/backer/14/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/14/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/backer/15/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/15/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/backer/16/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/16/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/backer/17/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/17/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/backer/18/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/18/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/backer/19/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/19/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/backer/20/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/20/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/backer/21/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/21/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/backer/22/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/22/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/backer/23/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/23/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/backer/24/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/24/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/backer/25/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/25/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/backer/26/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/26/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/backer/27/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/27/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/backer/28/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/28/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/backer/29/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/29/avatar.svg"></a> - -## Sponsors - -Become a sponsor and get your logo on our README on Github with a link to your site. [[Become a sponsor](https://opencollective.com/electron-react-boilerplate-594#sponsor)] - -<a href="https://opencollective.com/electron-react-boilerplate/sponsor/0/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/0/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/sponsor/1/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/1/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/sponsor/2/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/2/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/sponsor/3/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/3/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/sponsor/4/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/4/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/sponsor/5/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/5/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/sponsor/6/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/6/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/sponsor/7/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/7/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/sponsor/8/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/8/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/sponsor/9/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/9/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/sponsor/10/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/10/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/sponsor/11/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/11/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/sponsor/12/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/12/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/sponsor/13/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/13/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/sponsor/14/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/14/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/sponsor/15/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/15/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/sponsor/16/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/16/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/sponsor/17/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/17/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/sponsor/18/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/18/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/sponsor/19/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/19/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/sponsor/20/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/20/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/sponsor/21/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/21/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/sponsor/22/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/22/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/sponsor/23/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/23/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/sponsor/24/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/24/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/sponsor/25/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/25/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/sponsor/26/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/26/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/sponsor/27/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/27/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/sponsor/28/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/28/avatar.svg"></a> -<a href="https://opencollective.com/electron-react-boilerplate/sponsor/29/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/29/avatar.svg"></a> - -## Maintainers - -- [Amila Welihinda](https://github.com/amilajack) -- [John Tran](https://github.com/jooohhn) -- [C. T. Lin](https://github.com/chentsulin) -- [Jhen-Jie Hong](https://github.com/jhen0409) - -## License - -MIT © [Electron React Boilerplate](https://github.com/electron-react-boilerplate) - -[github-actions-status]: https://github.com/electron-react-boilerplate/electron-react-boilerplate/workflows/Test/badge.svg -[github-actions-url]: https://github.com/electron-react-boilerplate/electron-react-boilerplate/actions -[github-tag-image]: https://img.shields.io/github/tag/electron-react-boilerplate/electron-react-boilerplate.svg?label=version -[github-tag-url]: https://github.com/electron-react-boilerplate/electron-react-boilerplate/releases/latest -[stackoverflow-img]: https://img.shields.io/badge/stackoverflow-electron_react_boilerplate-blue.svg -[stackoverflow-url]: https://stackoverflow.com/questions/tagged/electron-react-boilerplate -[david-image]: https://img.shields.io/david/electron-react-boilerplate/electron-react-boilerplate.svg -[david-url]: https://david-dm.org/electron-react-boilerplate/electron-react-boilerplate -[david-dev-image]: https://img.shields.io/david/dev/electron-react-boilerplate/electron-react-boilerplate.svg?label=devDependencies -[david-dev-url]: https://david-dm.org/electron-react-boilerplate/electron-react-boilerplate?type=dev -[good-first-issue-image]: https://img.shields.io/github/issues/electron-react-boilerplate/electron-react-boilerplate/good%20first%20issue.svg?label=good%20first%20issues -[good-first-issue-url]: https://github.com/electron-react-boilerplate/electron-react-boilerplate/issues?q=is%3Aopen+is%3Aissue+label%3A"good+first+issue" +See the scripts in package.json for more packaging options. diff --git a/app/.testcafe-electron-rc b/app/.testcafe-electron-rc deleted file mode 100644 index 708ef19adc8f3e47c732a88701b5232ba4d7def9..0000000000000000000000000000000000000000 --- a/app/.testcafe-electron-rc +++ /dev/null @@ -1,4 +0,0 @@ -{ - "mainWindowUrl": "./app.html", - "appPath": "." -} diff --git a/app/App.tsx b/app/App.tsx new file mode 100644 index 0000000000000000000000000000000000000000..39d56c50db9969f34f7989ecf0774987eaf9c099 --- /dev/null +++ b/app/App.tsx @@ -0,0 +1,71 @@ +import React from 'react'; +import { createMuiTheme, ThemeProvider } from '@material-ui/core'; +import { HashRouter as Router, Switch, Route } from 'react-router-dom'; +import { SqlManagerProvider } from './context/SqlManagerContext'; +import { LoadingProvider } from './context/LoadingContext'; +import routes from './routes'; +import LoginScreen from './containers/LoginScreen'; +import Dashboard from './containers/Dashboard'; +import { LoginDetailsProvider } from './context/LoginDetailsContext'; + +const theme = createMuiTheme({ + palette: { + type: 'dark', + primary: { + light: '#769CFF', + main: '#769CFF', + contrastText: '#FFFFFF', + }, + text: { + primary: 'rgba(255, 255, 255, 0,9)', + secondary: 'rgba(255, 255, 255, 0,8)', + disabled: 'rgba(255, 255, 255, 0,6)', + }, + background: { + default: '#404040', // Dark 2 + paper: '#4B4B4B', // Dark 3 + }, + }, + typography: { + fontFamily: ['Raleway', 'Arial'].join(','), + button: { + textTransform: 'capitalize', + }, + }, + spacing: 8, + props: { + MuiInputBase: { + margin: 'dense', + }, + MuiInputLabel: { + margin: 'dense', + }, + }, +}); + +export default function App() { + return ( + <div> + <ThemeProvider theme={theme}> + <SqlManagerProvider> + <LoadingProvider> + <LoginDetailsProvider> + <div className="content"> + <Router> + <Switch> + <Route exact path={routes.login}> + <LoginScreen /> + </Route> + <Route path={routes.dashboard}> + <Dashboard /> + </Route> + </Switch> + </Router> + </div> + </LoginDetailsProvider> + </LoadingProvider> + </SqlManagerProvider> + </ThemeProvider> + </div> + ); +} diff --git a/app/Routes.tsx b/app/Routes.tsx deleted file mode 100644 index aec0c9be12d59b4c36808cc125a21f8042e6f154..0000000000000000000000000000000000000000 --- a/app/Routes.tsx +++ /dev/null @@ -1,28 +0,0 @@ -/* eslint react/jsx-props-no-spreading: off */ -import React from 'react'; -import { Switch, Route } from 'react-router-dom'; -import routes from './constants/routes.json'; -import App from './containers/App'; -import HomePage from './containers/HomePage'; - -// Lazily load routes and code split with webpack -const LazyCounterPage = React.lazy(() => - import(/* webpackChunkName: "CounterPage" */ './containers/CounterPage') -); - -const CounterPage = (props: Record<string, any>) => ( - <React.Suspense fallback={<h1>Loading...</h1>}> - <LazyCounterPage {...props} /> - </React.Suspense> -); - -export default function Routes() { - return ( - <App> - <Switch> - <Route path={routes.COUNTER} component={CounterPage} /> - <Route path={routes.HOME} component={HomePage} /> - </Switch> - </App> - ); -} diff --git a/app/app.global.css b/app/app.global.css index aa238b94c2418c38e5d6442d397f2c407c52bae5..529eb368c19146638be71f9e840f411fa3ea3280 100644 --- a/app/app.global.css +++ b/app/app.global.css @@ -1,47 +1,18 @@ -/* +/* Global styles for the entire app + * * @NOTE: Prepend a `~` to css file paths that are in your node_modules * See https://github.com/webpack-contrib/sass-loader#imports */ -@import '~@fortawesome/fontawesome-free/css/all.css'; body { - position: relative; - color: white; + color: rgba(255, 255, 255, 0.9); height: 100vh; - background-color: #232c39; - background-image: linear-gradient( - 45deg, - rgba(0, 216, 255, 0.5) 10%, - rgba(0, 1, 127, 0.7) - ); - font-family: Arial, Helvetica, Helvetica Neue, serif; - overflow-y: hidden; -} - -h2 { + background-color: #404040; margin: 0; - font-size: 2.25rem; - font-weight: bold; - letter-spacing: -0.025em; - color: #fff; -} - -p { - font-size: 24px; -} - -li { - list-style: none; -} - -a { - color: white; - opacity: 0.75; - text-decoration: none; } -a:hover { - opacity: 1; - text-decoration: none; - cursor: pointer; +.content { + display: flex; + flex-flow: column nowrap; + justify-content: space-between; } diff --git a/app/app.html b/app/app.html index 8db3498f3b4e73cc4940c119cd833af70dc6054f..6307190b216ff19a4edaa0d2f31f2038b52b413b 100644 --- a/app/app.html +++ b/app/app.html @@ -1,8 +1,17 @@ +<!--This is equivalent to the usual index.html we're familiar with--> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Hello Electron React!</title> + <link + href="https://fonts.googleapis.com/css2?family=Raleway:wght@200;300;400;500;600;700&display=swap" + rel="stylesheet" + /> + <link + href="https://fonts.googleapis.com/css2?family=Roboto+Mono:wght@400;500;600&display=swap" + rel="stylesheet" + /> <script> (() => { if ( @@ -18,6 +27,7 @@ })(); </script> </head> + <body> <div id="root"></div> <script> @@ -40,7 +50,7 @@ if (scripts.length) { document.write( scripts - .map(script => `<script defer src="${script}"><\/script>`) + .map((script) => `<script defer src="${script}"><\/script>`) .join('') ); } diff --git a/app/app.icns b/app/app.icns deleted file mode 100644 index 4f3cbbafa578fbccdb3e7116d2592b782b2144b9..0000000000000000000000000000000000000000 Binary files a/app/app.icns and /dev/null differ diff --git a/app/components/Home.css b/app/components/Home.css deleted file mode 100644 index 978a02aa24360765f6054fb4d4a6ded7e64266d1..0000000000000000000000000000000000000000 --- a/app/components/Home.css +++ /dev/null @@ -1,14 +0,0 @@ -.container { - position: absolute; - top: 30%; - left: 10px; - text-align: center; -} - -.container h2 { - font-size: 5rem; -} - -.container a { - font-size: 1.4rem; -} diff --git a/app/components/Home.tsx b/app/components/Home.tsx deleted file mode 100644 index 2afb3ca261b80878cc6ececa297f69674a5b4a3c..0000000000000000000000000000000000000000 --- a/app/components/Home.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react'; -import { Link } from 'react-router-dom'; -import routes from '../constants/routes.json'; -import styles from './Home.css'; -import runSSH from '../utils/ssh'; - -export default function Home(): JSX.Element { - return ( - <div className={styles.container} data-tid="container"> - <h2>Home</h2> - <Link to={routes.COUNTER}>to Counter</Link> - </div> - ); -} diff --git a/app/components/Login.tsx b/app/components/Login.tsx new file mode 100644 index 0000000000000000000000000000000000000000..36166d56969e7b9f6a7cce80c76cafbfd2cfc41e --- /dev/null +++ b/app/components/Login.tsx @@ -0,0 +1,168 @@ +import React, { useState, useContext } from 'react'; +import { + TextField, + Button, + Paper, + Typography, + makeStyles, +} from '@material-ui/core'; +import { useHistory, useLocation } from 'react-router'; +import clsx from 'clsx'; +import { SqlManagerContext } from '../context/SqlManagerContext'; +import SqlManager from '../../backend/recorder/SqlManager'; +import { LoadingContext } from '../context/LoadingContext'; +import routes from '../routes'; +import { LoginDetailsContext } from '../context/LoginDetailsContext'; + +const useStyles = makeStyles((theme) => ({ + wrapper: { + display: 'flex', + flexFlow: 'column nowrap', + flexShrink: 1, + // alignItems: 'center', + maxWidth: 300 + theme.spacing(4), + }, + container: { + display: 'flex', + flexFlow: 'column nowrap', + flexGrow: 0, + flexShrink: 1, + width: 300, + padding: `${theme.spacing(1)}px ${theme.spacing(2)}px`, + }, + content: { + display: 'flex', + flexFlow: 'column nowrap', + }, + element: { + marginBottom: theme.spacing(1), + }, + title: { + textAlign: 'center', + marginBottom: theme.spacing(2), + }, +})); + +interface LoginProps { + onConnect?: () => void; +} + +const DefaultProps = { + onConnect: undefined, +}; + +export default function Login({ onConnect = undefined }: LoginProps) { + const sqlManagerContext = useContext(SqlManagerContext); + const loadingContext = useContext(LoadingContext); + const loginDetailsContext = useContext(LoginDetailsContext); + + const history = useHistory(); + const location = useLocation(); + + const [host, setHost] = useState( + loginDetailsContext?.loginDetails?.host || '' + ); + const [user, setUser] = useState( + loginDetailsContext?.loginDetails?.user || '' + ); + const [password, setPassword] = useState( + loginDetailsContext?.loginDetails?.password || '' + ); + const [port, setPort] = useState( + Number(loginDetailsContext?.loginDetails?.port || 33060) + ); + const [database, setDatabase] = useState( + loginDetailsContext?.loginDetails?.database || '' + ); + + async function handleConnectByLogin() { + let manager; + if (!sqlManagerContext?.sqlManager) { + manager = new SqlManager(); + sqlManagerContext?.setSqlManager(manager); + } else { + manager = sqlManagerContext.sqlManager; + } + try { + loadingContext?.setConnected(false); + loadingContext?.setConnecting(true); + await manager.connect({ host, user, password, port }, database); + loadingContext?.setConnecting(false); + loadingContext?.setConnected(true); + + if (location.pathname === routes.login) { + console.log('Routing to dashboard'); + history.push(routes.dashboard); + } else if (onConnect) { + onConnect(); + } + } catch (error) { + console.error(error); + loadingContext?.setConnecting(false); + loadingContext?.setConnected(false); + } + } + + // TODO: Show connection indicator (spinning circle) + // TODO: Show result of connection (success/failure) + + const classes = useStyles(); + + return ( + <div className={classes.wrapper}> + <Paper className={classes.container} elevation={1}> + <Typography className={classes.title} variant="h5"> + Connect to a database + </Typography> + <div className={clsx(classes.content, classes.element)}> + <TextField + className={classes.element} + variant="filled" + label="Host" + value={host} + onChange={(event) => setHost(event.target.value)} + /> + <TextField + className={classes.element} + variant="filled" + label="User" + value={user} + onChange={(event) => setUser(event.target.value)} + /> + <TextField + className={classes.element} + variant="filled" + label="Password" + type="password" + value={password} + onChange={(event) => setPassword(event.target.value)} + /> + <TextField + className={classes.element} + variant="filled" + label="Port" + value={port} + onChange={(event) => setPort(Number(event.target.value))} + /> + <TextField + className={classes.element} + variant="filled" + label="Database" + value={database} + onChange={(event) => setDatabase(event.target.value)} + /> + </div> + <Button + type="button" + variant="contained" + color="primary" + onClick={() => handleConnectByLogin()} + > + Connect + </Button> + </Paper> + </div> + ); +} + +Login.defaultProps = DefaultProps; diff --git a/app/components/MemoryChart.tsx b/app/components/MemoryChart.tsx new file mode 100644 index 0000000000000000000000000000000000000000..b86754e32784aec62ff5d75a1ba5c14ff7a3d861 --- /dev/null +++ b/app/components/MemoryChart.tsx @@ -0,0 +1,142 @@ +import React, { useState, ReactElement, ReactNode } from 'react'; +import { + Legend, + LineChart, + Line, + CartesianGrid, + XAxis, + YAxis, + Tooltip, + ResponsiveContainer, + Surface, + Symbols, + ContentRenderer, + LegendProps, +} from 'recharts'; +import _ from 'lodash'; +import { MemoryPerformance } from '../../backend/data-processor/DataProcessor'; + +interface Payload { + value: string; + id: number; + type: string; + color: string; +} + +const chartDataMock: MemoryPerformance = [ + { relative_time: 0, geom: 0, total: 235378 }, + { relative_time: 1, geom: 0, total: 263058 }, + { relative_time: 2, geom: 0, total: 263058 }, + { relative_time: 3, geom: 265, total: 263371 }, + { relative_time: 4, geom: 265, total: 263371 }, + { relative_time: 5, geom: 387, total: 263493 }, + { relative_time: 6, geom: 3267, total: 266373 }, + { relative_time: 7, geom: 3267, total: 266373 }, + { relative_time: 8, geom: 32067, total: 295173 }, + { relative_time: 9, geom: 32067, total: 295173 }, + { relative_time: 10, geom: 256503, total: 519609 }, + { relative_time: 11, geom: 320067, total: 726837 }, + { relative_time: 12, geom: 320067, total: 726837 }, + { relative_time: 13, geom: 1349729, total: 1756499 }, + { relative_time: 14, geom: 1442129, total: 1848899 }, + { relative_time: 15, geom: 1785089, total: 2191859 }, + { relative_time: 16, geom: 1980689, total: 2387459 }, + { relative_time: 17, geom: 1164423, total: 1571193 }, + { relative_time: 18, geom: 1418583, total: 1825353 }, + { relative_time: 19, geom: 1589703, total: 1996473 }, + { relative_time: 20, geom: 0, total: 216938 }, +]; + +export default function MemoryChart() { + const [disabled, setDisabled] = useState<Array<string>>([]); + const [chartData, setChartData] = useState<MemoryPerformance>(chartDataMock); + const [chartColors, setChartColors] = useState({ + geom: '#8884d8', + total: '#82ca9d', + }); + + function handleClick(dataKey: string) { + if (_.includes(disabled, dataKey)) { + setDisabled(disabled.filter((obj) => obj !== dataKey)); + } else { + setDisabled(disabled.concat(dataKey)); + } + } + + function renderCustomizedLegend(props: { + payload: Array<Payload>; + }): ReactNode { + const { payload } = props; + return ( + <div className="customized-legend"> + {payload.map((entry: { value: string; color: string }) => { + const { value, color } = entry; + const active = _.includes(disabled, value); + const style = { + marginRight: 10, + color: active ? '#AAA' : '#000', + }; + + return ( + <span + role="presentation" + className="legend-item" + onClick={() => handleClick(value)} + style={style} + key={value} + > + {/* viewBox="0 0 10 10" */} + <Surface width={10} height={10}> + <Symbols cx={5} cy={5} type="circle" size={50} fill={color} /> + {active && ( + <Symbols cx={5} cy={5} type="circle" size={25} fill="#FFF" /> + )} + </Surface> + <span>{value}</span> + </span> + ); + })} + </div> + ); + } + + return ( + <div className="highlight-bar-charts"> + <ResponsiveContainer height={400}> + <LineChart + width={800} + height={500} + data={chartData} + margin={{ top: 20, right: 30, left: 20, bottom: 5 }} + > + <CartesianGrid strokeDasharray="3 10" /> + <XAxis dataKey="relative_time" /> + <YAxis /> + <Tooltip /> + <Legend + verticalAlign="bottom" + height={36} + align="left" + payload={_.toPairs(chartColors).map((pair, i) => ({ + value: pair[0], + id: i, + type: 'line', + color: pair[1], + }))} + content={renderCustomizedLegend} + /> + {_.toPairs(chartColors) + .filter((pair) => !_.includes(disabled, pair[0])) + .map((pair) => ( + <Line + type="monotone" + dataKey={pair[0]} + key={pair[0]} + stroke={pair[1]} + /> + ))} + </LineChart> + </ResponsiveContainer> + </div> + ); +} diff --git a/app/components/QueryRecorder.tsx b/app/components/QueryRecorder.tsx new file mode 100644 index 0000000000000000000000000000000000000000..6aaa0a3c635736dfce0dc78425060bad04671bad --- /dev/null +++ b/app/components/QueryRecorder.tsx @@ -0,0 +1,151 @@ +import React, { useState, useContext } from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import { + Paper, + Button, + TextField, + Typography, + FormControlLabel, + Checkbox, +} from '@material-ui/core'; +import clsx from 'clsx'; +import { SqlManagerContext } from '../context/SqlManagerContext'; +import { DataProcessor } from '../../backend/data-processor/DataProcessor'; +import { RecordingContext } from '../context/RecordingContext'; + +const useStyles = makeStyles((theme) => ({ + container: { + maxWidth: 600 + theme.spacing(4), + }, + content: { + display: 'flex', + flexFlow: 'column nowrap', + paddingTop: theme.spacing(1), + paddingBottom: theme.spacing(1), + paddingLeft: theme.spacing(2), + paddingRight: theme.spacing(2), + }, + element: { + marginBottom: theme.spacing(1), + }, + input: { + display: 'flex', + flexFlow: 'column nowrap', + flexGrow: 1, + width: '600px', + }, + output: { + userSelect: 'none', + }, + code: { + fontFamily: 'Roboto Mono !important', + }, +})); + +export default function QueryRecorder() { + const [changedQuery, setChangedQuery] = useState(''); + const [output, setOutput] = useState<string>(''); + const [explainAnalyze, setExplainAnalyze] = useState(false); + + const manager = useContext(SqlManagerContext)?.sqlManager; + const recordingContext = useContext(RecordingContext); + + async function record() { + if (!manager) { + console.log('not connected'); + return; + } + + try { + const result = await manager.record(changedQuery, explainAnalyze); + console.log(result); + if (result?.error) { + console.log(`QueryRecorder: Result: ${result}`); + setOutput(JSON.stringify(result.error.info.msg, undefined, 2)); + } else { + setOutput(JSON.stringify(result?.result, undefined, 2)); + const memoryPerformance = DataProcessor.processMemoryPerformance( + result.memoryPerformance + ); + recordingContext?.setMemoryPerformance(memoryPerformance); + } + } catch (error) { + console.log(`QueryRecorder: Error: ${error}`); + } + } + + async function doOptimizerQuery() { + if (!manager) { + console.log('not connected'); + return; + } + const traceResult = await manager.executeWithTrace(changedQuery); + setOutput(JSON.stringify(traceResult.result, undefined, 2)); + console.log(traceResult.trace); + } + + const classes = useStyles(); + + return ( + <div className={classes.container}> + <Paper className={classes.content} elevation={1}> + <div className={classes.element}> + <Typography variant="h5">Record performance of a query</Typography> + </div> + <div className={clsx(classes.element, classes.input)}> + <TextField + multiline + InputProps={{ + classes: { + input: classes.code, + }, + }} + variant="filled" + label="Query" + rows={2} + rowsMax={10} + value={changedQuery} + onChange={(event) => setChangedQuery(event.target.value)} + /> + </div> + <div className={classes.element}> + <FormControlLabel + control={ + // eslint-disable-next-line react/jsx-wrap-multilines + <Checkbox + color="primary" + checked={explainAnalyze} + onChange={(event) => setExplainAnalyze(event.target.checked)} + /> + } + label="Explain analyze" + /> + </div> + <div className={classes.element}> + <Button variant="contained" color="primary" onClick={() => record()}> + Record + </Button> + </div> + <div className={classes.element}> + <Button variant="contained" onClick={() => doOptimizerQuery()}> + Optimizer + </Button> + </div> + <div className={clsx(classes.element, classes.input)}> + <TextField + className={classes.output} + InputProps={{ + classes: { + input: classes.code, + }, + }} + multiline + rows={10} + variant="outlined" + value={output} + /> + </div> + </Paper> + </div> + ); +} diff --git a/app/components/__tests__/Example.test.tsx b/app/components/__tests__/Example.test.tsx new file mode 100644 index 0000000000000000000000000000000000000000..498f1d1d51dcd1ef9f6e8b5aac6e6fee98d5ad81 --- /dev/null +++ b/app/components/__tests__/Example.test.tsx @@ -0,0 +1,5 @@ +describe('test', () => { + it('Renders without crashing', () => { + expect(true).toBeTruthy(); + }); +}); diff --git a/app/components/css.d.ts b/app/components/css.d.ts deleted file mode 100644 index af3d030048bbf98d7e4f7a52a75aff894e2aa0d5..0000000000000000000000000000000000000000 --- a/app/components/css.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -declare module '*.scss' { - const content: { [className: string]: string }; - export default content; -} - -declare module '*.css' { - const content: { [className: string]: string }; - export default content; -} diff --git a/app/constants/routes.json b/app/constants/routes.json deleted file mode 100644 index 00770b0c061f45390a4606bad55cb58885de4085..0000000000000000000000000000000000000000 --- a/app/constants/routes.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "HOME": "/", - "COUNTER": "/counter" -} diff --git a/app/containers/App.tsx b/app/containers/App.tsx deleted file mode 100644 index 48b5fac25ba07e4a6cfe149cbd79d09bc5276982..0000000000000000000000000000000000000000 --- a/app/containers/App.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import React, { ReactNode } from 'react'; -import runSSH from '../utils/ssh'; - -type Props = { - children: ReactNode; -}; - -export default function App(props: Props) { - runSSH(); - const { children } = props; - return <>{children}</>; -} diff --git a/app/containers/CounterPage.tsx b/app/containers/CounterPage.tsx deleted file mode 100644 index 2f70b68e081a29d596bc808c33ddcd0cf9881795..0000000000000000000000000000000000000000 --- a/app/containers/CounterPage.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import React from 'react'; -import Counter from '../features/counter/Counter'; - -export default function CounterPage() { - return <Counter />; -} diff --git a/app/containers/Dashboard.tsx b/app/containers/Dashboard.tsx new file mode 100644 index 0000000000000000000000000000000000000000..302cd0eb802fbf5ba858d36b460f05bc1234512b --- /dev/null +++ b/app/containers/Dashboard.tsx @@ -0,0 +1,58 @@ +import React, { useContext, useState } from 'react'; +import { Button, Dialog } from '@material-ui/core'; +import { useLocation, useHistory } from 'react-router'; +import QueryRecorder from '../components/QueryRecorder'; +import { LoadingContext } from '../context/LoadingContext'; +import routes from '../routes'; +import Login from '../components/Login'; +import { LoginDetailsContext } from '../context/LoginDetailsContext'; +import { RecordingProvider } from '../context/RecordingContext'; +import MemoryChart from '../components/MemoryChart'; + +export default function Dashboard() { + const [changeConnectionOpen, setChangeConnectionOpen] = useState(false); + + const loadingContext = useContext(LoadingContext); + const loginDetailsContext = useContext(LoginDetailsContext); + + const location = useLocation(); + const history = useHistory(); + + if ( + location.pathname === routes.dashboard && + !loadingContext?.connected && + !loadingContext?.connecting && + !changeConnectionOpen + ) { + history.push(routes.login); + } + + function openConnectionDialog() { + loginDetailsContext?.setLoginDetails(undefined); + setChangeConnectionOpen(true); + } + + return ( + <div> + <div> + <Button + variant="outlined" + color="primary" + onClick={() => openConnectionDialog()} + > + Change connection + </Button> + <Dialog + open={changeConnectionOpen} + onClose={() => setChangeConnectionOpen(false)} + > + <Login onConnect={() => setChangeConnectionOpen(false)} /> + </Dialog> + </div> + <RecordingProvider> + <QueryRecorder /> + <MemoryChart /> + </RecordingProvider> + </div> + ); +} diff --git a/app/containers/HomePage.tsx b/app/containers/HomePage.tsx deleted file mode 100644 index 4c7158c6c75d4539740dbb4184d3f3de17bee664..0000000000000000000000000000000000000000 --- a/app/containers/HomePage.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import React from 'react'; -import Home from '../components/Home'; - -export default function HomePage() { - return <Home />; -} diff --git a/app/containers/LoginScreen.tsx b/app/containers/LoginScreen.tsx new file mode 100644 index 0000000000000000000000000000000000000000..5522ff0095ef59407d10e8f33452b4aba351af0f --- /dev/null +++ b/app/containers/LoginScreen.tsx @@ -0,0 +1,57 @@ +import React, { useEffect, useState, useContext } from 'react'; +import { Typography, makeStyles } from '@material-ui/core'; +import Login from '../components/Login'; +import { loadLoginDetails } from '../../backend/utils/fileutils'; +import { LoginDetailsContext } from '../context/LoginDetailsContext'; + +const useStyles = makeStyles((theme) => ({ + wrapper: { + maxWidth: '100vw', + height: '100vh', + }, + content: { + display: 'flex', + flexFlow: 'column nowrap', + alignItems: 'center', + justifyContent: 'space-between', + padding: `${theme.spacing(8)}px ${theme.spacing(16)}px`, + }, + title: { + marginBottom: theme.spacing(8), + textAlign: 'center', + }, +})); + +export default function LoginScreen() { + const [loading, setLoading] = useState(true); + + const loginDetailsContext = useContext(LoginDetailsContext); + + useEffect(() => { + loadLoginDetails() + .then((response) => { + if (response) { + loginDetailsContext?.setLoginDetails(response); + } + setLoading(false); + return true; + }) + .catch((error) => console.error(error)); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + const classes = useStyles(); + + return ( + <div className={classes.wrapper}> + {!loading && ( + <div className={classes.content}> + <Typography className={classes.title} variant="h2"> + Welcome to MySQL query profiler! + </Typography> + <Login /> + </div> + )} + </div> + ); +} diff --git a/app/containers/Root.tsx b/app/containers/Root.tsx deleted file mode 100644 index fef53faa89eeb30c8c6294fd44d1c80eafe96efa..0000000000000000000000000000000000000000 --- a/app/containers/Root.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import React from 'react'; -import { Provider } from 'react-redux'; -import { ConnectedRouter } from 'connected-react-router'; -import { hot } from 'react-hot-loader/root'; -import { History } from 'history'; -import { Store } from '../store'; -import Routes from '../Routes'; - -type Props = { - store: Store; - history: History; -}; - -const Root = ({ store, history }: Props) => ( - <Provider store={store}> - <ConnectedRouter history={history}> - <Routes /> - </ConnectedRouter> - </Provider> -); - -export default hot(Root); diff --git a/app/context/LoadingContext.tsx b/app/context/LoadingContext.tsx new file mode 100644 index 0000000000000000000000000000000000000000..6b08e087f83b7ad0e92c071eae4a821082e14e3d --- /dev/null +++ b/app/context/LoadingContext.tsx @@ -0,0 +1,37 @@ +import React, { useState, createContext } from 'react'; + +type ChildrenType = { + children: React.ReactNode; +}; + +type ContextType = { + connecting: boolean; + setConnecting: (value: boolean) => void; + connected: boolean; + setConnected: (value: boolean) => void; + querying: boolean; + setQuerying: (value: boolean) => void; +}; + +export const LoadingContext = createContext<ContextType | undefined>(undefined); + +export const LoadingProvider = ({ children }: ChildrenType) => { + const [connecting, setConnecting] = useState(false); + const [connected, setConnected] = useState(false); + const [querying, setQuerying] = useState(false); + + return ( + <LoadingContext.Provider + value={{ + connecting, + setConnecting, + connected, + setConnected, + querying, + setQuerying, + }} + > + {children} + </LoadingContext.Provider> + ); +}; diff --git a/app/context/LoginDetailsContext.tsx b/app/context/LoginDetailsContext.tsx new file mode 100644 index 0000000000000000000000000000000000000000..b54b0c6d67337d25bd87b3da88a487dc3e891589 --- /dev/null +++ b/app/context/LoginDetailsContext.tsx @@ -0,0 +1,30 @@ +import React, { useState, createContext } from 'react'; +import { LoginDetails } from '../../backend/utils/LoginDetails'; + +type ChildrenType = { + children: React.ReactNode; +}; + +type ContextType = { + loginDetails?: LoginDetails; + setLoginDetails: (value: LoginDetails | undefined) => void; +}; + +export const LoginDetailsContext = createContext<ContextType | undefined>( + undefined +); + +export const LoginDetailsProvider = ({ children }: ChildrenType) => { + const [loginDetails, setLoginDetails] = useState<LoginDetails>(); + + return ( + <LoginDetailsContext.Provider + value={{ + loginDetails, + setLoginDetails, + }} + > + {children} + </LoginDetailsContext.Provider> + ); +}; diff --git a/app/context/RecordingContext.tsx b/app/context/RecordingContext.tsx new file mode 100644 index 0000000000000000000000000000000000000000..66ec853091b5f8790aa4bb6a0ec9ce25146774d0 --- /dev/null +++ b/app/context/RecordingContext.tsx @@ -0,0 +1,28 @@ +import React, { useState, createContext } from 'react'; +import { MemoryPerformance } from '../../backend/data-processor/DataProcessor'; + +type ChildrenType = { + children: React.ReactNode; +}; + +type ContextType = { + memoryPerformance: MemoryPerformance | undefined; + setMemoryPerformance: (value: MemoryPerformance | undefined) => void; +}; + +export const RecordingContext = createContext<ContextType | undefined>( + undefined +); + +export const RecordingProvider = ({ children }: ChildrenType) => { + const [memoryPerformance, setMemoryPerformance] = useState< + MemoryPerformance | undefined + >(undefined); + return ( + <RecordingContext.Provider + value={{ memoryPerformance, setMemoryPerformance }} + > + {children} + </RecordingContext.Provider> + ); +}; diff --git a/app/context/SqlManagerContext.tsx b/app/context/SqlManagerContext.tsx new file mode 100644 index 0000000000000000000000000000000000000000..486baf6cf1c89ac445f237b479942b4cc7019d8d --- /dev/null +++ b/app/context/SqlManagerContext.tsx @@ -0,0 +1,26 @@ +import React, { useState, createContext } from 'react'; +import SqlManager from '../../backend/recorder/SqlManager'; + +type ChildrenType = { + children: React.ReactNode; +}; + +type ContextType = { + sqlManager: SqlManager | undefined; + setSqlManager: (value: SqlManager | undefined) => void; +}; + +export const SqlManagerContext = createContext<ContextType | undefined>( + undefined +); + +export const SqlManagerProvider = ({ children }: ChildrenType) => { + const [sqlManager, setSqlManager] = useState<SqlManager | undefined>( + undefined + ); + return ( + <SqlManagerContext.Provider value={{ sqlManager, setSqlManager }}> + {children} + </SqlManagerContext.Provider> + ); +}; diff --git a/app/features/counter/Counter.css b/app/features/counter/Counter.css deleted file mode 100644 index 31588ba71a2a75aa18cdd1112839189e43b4fe40..0000000000000000000000000000000000000000 --- a/app/features/counter/Counter.css +++ /dev/null @@ -1,37 +0,0 @@ -.backButton { - position: absolute; -} - -.counter { - position: absolute; - top: 30%; - left: 45%; - font-size: 10rem; - font-weight: bold; - letter-spacing: -0.025em; -} - -.btnGroup { - position: relative; - top: 500px; - width: 480px; - margin: 0 auto; -} - -.btn { - font-size: 1.6rem; - font-weight: bold; - background-color: #fff; - border-radius: 50%; - margin: 10px; - width: 100px; - height: 100px; - opacity: 0.7; - cursor: pointer; - font-family: Arial, Helvetica, Helvetica Neue, sans-serif; -} - -.btn:hover { - color: white; - background-color: rgba(0, 0, 0, 0.5); -} diff --git a/app/features/counter/Counter.tsx b/app/features/counter/Counter.tsx deleted file mode 100644 index 1f39fce037418842ff0fb91d82bb8e5cb8716d9a..0000000000000000000000000000000000000000 --- a/app/features/counter/Counter.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import React from 'react'; -import { useSelector, useDispatch } from 'react-redux'; -import { Link } from 'react-router-dom'; -import styles from './Counter.css'; -import routes from '../../constants/routes.json'; -import { - increment, - decrement, - incrementIfOdd, - incrementAsync, - selectCount, -} from './counterSlice'; - -export default function Counter() { - const dispatch = useDispatch(); - const value = useSelector(selectCount); - return ( - <div> - <div className={styles.backButton} data-tid="backButton"> - <Link to={routes.HOME}> - <i className="fa fa-arrow-left fa-3x" /> - </Link> - </div> - <div className={`counter ${styles.counter}`} data-tid="counter"> - {value} - </div> - <div className={styles.btnGroup}> - <button - className={styles.btn} - onClick={() => { - dispatch(increment()); - }} - data-tclass="btn" - type="button" - > - <i className="fa fa-plus" /> - </button> - <button - className={styles.btn} - onClick={() => { - dispatch(decrement()); - }} - data-tclass="btn" - type="button" - > - <i className="fa fa-minus" /> - </button> - <button - className={styles.btn} - onClick={() => { - dispatch(incrementIfOdd()); - }} - data-tclass="btn" - type="button" - > - odd - </button> - <button - className={styles.btn} - onClick={() => { - dispatch(incrementAsync()); - }} - data-tclass="btn" - type="button" - > - async - </button> - </div> - </div> - ); -} diff --git a/app/features/counter/counterSlice.ts b/app/features/counter/counterSlice.ts deleted file mode 100644 index 385d1807dc6b7dfda1f2a101fe102446d7af065b..0000000000000000000000000000000000000000 --- a/app/features/counter/counterSlice.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { createSlice } from '@reduxjs/toolkit'; -// eslint-disable-next-line import/no-cycle -import { AppThunk, RootState } from '../../store'; - -const counterSlice = createSlice({ - name: 'counter', - initialState: { value: 0 }, - reducers: { - increment: (state) => { - state.value += 1; - }, - decrement: (state) => { - state.value -= 1; - }, - }, -}); - -export const { increment, decrement } = counterSlice.actions; - -export const incrementIfOdd = (): AppThunk => { - return (dispatch, getState) => { - const state = getState(); - if (state.counter.value % 2 === 0) { - return; - } - dispatch(increment()); - }; -}; - -export const incrementAsync = (delay = 1000): AppThunk => (dispatch) => { - setTimeout(() => { - dispatch(increment()); - }, delay); -}; - -export default counterSlice.reducer; - -export const selectCount = (state: RootState) => state.counter.value; diff --git a/app/index.tsx b/app/index.tsx index 4e5c01ef073c70aec9da0f87697a4f6b203e3661..b48c13c5ad4dfe755891a57cee31e9429a79fda0 100644 --- a/app/index.tsx +++ b/app/index.tsx @@ -1,20 +1,8 @@ -import React, { Fragment } from 'react'; +import React from 'react'; import { render } from 'react-dom'; -import { AppContainer as ReactHotAppContainer } from 'react-hot-loader'; -import { history, configuredStore } from './store'; import './app.global.css'; - -const store = configuredStore(); - -const AppContainer = process.env.PLAIN_HMR ? Fragment : ReactHotAppContainer; +import App from './App'; document.addEventListener('DOMContentLoaded', () => { - // eslint-disable-next-line global-require - const Root = require('./containers/Root').default; - render( - <AppContainer> - <Root store={store} history={history} /> - </AppContainer>, - document.getElementById('root') - ); + render(<App />, document.getElementById('root')); }); diff --git a/app/main.dev.ts b/app/main.dev.ts index e900f91c2c5677c89039997e0ce721dbace1cebe..63b86d851e970aedee61d230ac45d5a5c4eb1944 100644 --- a/app/main.dev.ts +++ b/app/main.dev.ts @@ -58,8 +58,8 @@ const createWindow = async () => { mainWindow = new BrowserWindow({ show: false, - width: 1024, - height: 728, + width: 1080, + height: 900, webPreferences: (process.env.NODE_ENV === 'development' || process.env.E2E_BUILD === 'true') && diff --git a/app/menu.ts b/app/menu.ts index 353e5a065c717cc125d20016226827d601fb1735..ecdf1a15fb5164b91d65681652226e57c5e6f8a6 100644 --- a/app/menu.ts +++ b/app/menu.ts @@ -6,6 +6,9 @@ import { MenuItemConstructorOptions, } from 'electron'; +// TODO: This defines the menu at the top of the app window. +// Remove the options we don't need. + interface DarwinMenuItemConstructorOptions extends MenuItemConstructorOptions { selector?: string; submenu?: DarwinMenuItemConstructorOptions[] | Menu; diff --git a/app/package-lock.json b/app/package-lock.json new file mode 100644 index 0000000000000000000000000000000000000000..78cc4ea30ec55191f825a4189c79bb9c8f12b74d --- /dev/null +++ b/app/package-lock.json @@ -0,0 +1,74 @@ +{ + "name": "electron-react-boilerplate", + "version": "1.3.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@mysql/xdevapi": { + "version": "8.0.21", + "resolved": "https://registry.npmjs.org/@mysql/xdevapi/-/xdevapi-8.0.21.tgz", + "integrity": "sha512-fUfPNsMK9MBBiisBadUfUYqrBlniPrSNJnstnveJpfhHksPt6rVfVk6gEPjEd32lwe5MmihcB5LBInSnNdCxbQ==", + "requires": { + "google-protobuf": "3.11.4", + "parsimmon": "1.6.2" + } + }, + "@types/d3-path": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-1.0.8.tgz", + "integrity": "sha512-AZGHWslq/oApTAHu9+yH/Bnk63y9oFOMROtqPAtxl5uB6qm1x2lueWdVEjsjjV3Qc2+QfuzKIwIR5MvVBakfzA==", + "dev": true + }, + "@types/d3-shape": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-1.3.2.tgz", + "integrity": "sha512-LtD8EaNYCaBRzHzaAiIPrfcL3DdIysc81dkGlQvv7WQP3+YXV7b0JJTtR1U3bzeRieS603KF4wUo+ZkJVenh8w==", + "dev": true, + "requires": { + "@types/d3-path": "*" + } + }, + "@types/prop-types": { + "version": "15.7.3", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", + "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==", + "dev": true + }, + "@types/react": { + "version": "16.9.49", + "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.49.tgz", + "integrity": "sha512-DtLFjSj0OYAdVLBbyjhuV9CdGVHCkHn2R+xr3XkBvK2rS1Y1tkc14XSGjYgm5Fjjr90AxH9tiSzc1pCFMGO06g==", + "dev": true, + "requires": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "@types/recharts": { + "version": "1.8.15", + "resolved": "https://registry.npmjs.org/@types/recharts/-/recharts-1.8.15.tgz", + "integrity": "sha512-ApCfDT/R8RCbZTcl0iqLiKsxVdzE3GzoawTgJUHuQOz6ZXhsPVfp7CNSKI2s3zFwrRRsgmpv2AEcbcZceNHg4w==", + "dev": true, + "requires": { + "@types/d3-shape": "*", + "@types/react": "*" + } + }, + "csstype": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.3.tgz", + "integrity": "sha512-jPl+wbWPOWJ7SXsWyqGRk3lGecbar0Cb0OvZF/r/ZU011R4YqiRehgkQ9p4eQfo9DSDLqLL3wHwfxeJiuIsNag==", + "dev": true + }, + "google-protobuf": { + "version": "3.11.4", + "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.11.4.tgz", + "integrity": "sha512-lL6b04rDirurUBOgsY2+LalI6Evq8eH5TcNzi7TYQ3BsIWelT0KSOQSBsXuavEkNf+odQU6c0lgz3UsZXeNX9Q==" + }, + "parsimmon": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/parsimmon/-/parsimmon-1.6.2.tgz", + "integrity": "sha512-bJNB0ZQhHyM5KqO2Z5ttQAVn/PZ2pccxaOnMcZ0Su7HA1Iv4GQTfUmzSZ6N3jcsCn9F68PZcypAvF3nDRRL+3g==" + } + } +} diff --git a/app/package.json b/app/package.json index 2d4b81a6d2db5a65e976f265e64ef1bef04bb134..17220ff8b282f84950f1d73af38738bf5a68f0c5 100644 --- a/app/package.json +++ b/app/package.json @@ -14,5 +14,10 @@ "postinstall": "yarn electron-rebuild" }, "license": "MIT", - "dependencies": {} + "dependencies": { + "@mysql/xdevapi": "8.0.21" + }, + "devDependencies": { + "@types/recharts": "^1.8.15" + } } diff --git a/app/rootReducer.ts b/app/rootReducer.ts deleted file mode 100644 index 35ea3e64d62d0ec84ead4c3d50c9f6c44e01775f..0000000000000000000000000000000000000000 --- a/app/rootReducer.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { combineReducers } from 'redux'; -import { connectRouter } from 'connected-react-router'; -import { History } from 'history'; -// eslint-disable-next-line import/no-cycle -import counterReducer from './features/counter/counterSlice'; - -export default function createRootReducer(history: History) { - return combineReducers({ - router: connectRouter(history), - counter: counterReducer, - }); -} diff --git a/app/routes.ts b/app/routes.ts new file mode 100644 index 0000000000000000000000000000000000000000..717ed155ed7aae867216cf835dc98db28a787c75 --- /dev/null +++ b/app/routes.ts @@ -0,0 +1,6 @@ +const routes = { + login: '/', + dashboard: '/dashboard', +}; + +export default routes; diff --git a/app/store.ts b/app/store.ts deleted file mode 100644 index b218d501daa644e21443795dd156cd4ae627654f..0000000000000000000000000000000000000000 --- a/app/store.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { configureStore, getDefaultMiddleware, Action } from '@reduxjs/toolkit'; -import { createHashHistory } from 'history'; -import { routerMiddleware } from 'connected-react-router'; -import { createLogger } from 'redux-logger'; -import { ThunkAction } from 'redux-thunk'; -// eslint-disable-next-line import/no-cycle -import createRootReducer from './rootReducer'; - -export const history = createHashHistory(); -const rootReducer = createRootReducer(history); -export type RootState = ReturnType<typeof rootReducer>; - -const router = routerMiddleware(history); -const middleware = [...getDefaultMiddleware(), router]; - -const excludeLoggerEnvs = ['test', 'production']; -const shouldIncludeLogger = !excludeLoggerEnvs.includes( - process.env.NODE_ENV || '' -); - -if (shouldIncludeLogger) { - const logger = createLogger({ - level: 'info', - collapsed: true, - }); - middleware.push(logger); -} - -export const configuredStore = (initialState?: RootState) => { - // Create Store - const store = configureStore({ - reducer: rootReducer, - middleware, - preloadedState: initialState, - }); - - if (process.env.NODE_ENV === 'development' && module.hot) { - module.hot.accept( - './rootReducer', - // eslint-disable-next-line global-require - () => store.replaceReducer(require('./rootReducer').default) - ); - } - return store; -}; -export type Store = ReturnType<typeof configuredStore>; -export type AppThunk = ThunkAction<void, RootState, unknown, Action<string>>; diff --git a/app/utils/.gitkeep b/app/utils/.gitkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/app/utils/ssh.ts b/app/utils/ssh.ts deleted file mode 100644 index 64be5c7086accccccef6aa854e0d3f6a7adc50aa..0000000000000000000000000000000000000000 --- a/app/utils/ssh.ts +++ /dev/null @@ -1,56 +0,0 @@ -const { Client } = require('ssh2'); - -const conn = new Client(); -conn - .on('ready', function () { - console.log('Client :: ready'); - conn - .exec('uptime', function (err: any, stream: any) { - if (err) throw err; - stream.on('close', function (code: any, signal: any) { - console.log(`Stream :: close :: code: ${code}, signal: ${signal}`); - }); - conn.end(); - }) - .on('data', function (data: any) { - console.log(`STDOUT: ${data}`); - }) - .stderr.on('data', function (data: any) { - console.log(`STDERR: ${data}`); - }); - }) - .connect({ - host: 'erlenyd@login.stud.ntnu.no', - username: 'erlenyd', - // eslint-disable-next-line global-require - password: 'FAKE', - }); -// import SSH2Promise from 'ssh2-promise'; -// // https://github.com/sanketbajoria/ssh2-promise - -// interface SSH2Config { -// host: string; // Default: 'localhost' -// username: string; // Default: none -// password?: string; // Default: none -// port?: string; // Default: 22 -// privateKey?: string; // Default: none -// } - -// export default async function runSSH(config?: SSH2Config) { -// const sshconfig: SSH2Config = { -// host: 'erlenyd@login.stud.ntnu.no', -// username: 'erlenyd', -// password: 'R!t{[;JKL9nt6.pj%&K,', -// }; - -// const ssh = new SSH2Promise(sshconfig); -// try { -// await ssh.connect(); -// console.log('Connection established'); -// // Example of execution of command. Can be anything -// const response = await ssh.exec('whoami'); -// console.log(response); -// } catch (error) { -// console.error(error); -// } -// } diff --git a/app/yarn.lock b/app/yarn.lock index fb57ccd13afbd082ad82051c2ffebef4840661ec..c40d949732ab5673d1926c2217aa2d056fa43755 100644 --- a/app/yarn.lock +++ b/app/yarn.lock @@ -2,3 +2,58 @@ # yarn lockfile v1 +"@mysql/xdevapi@8.0.21": + version "8.0.21" + resolved "https://registry.yarnpkg.com/@mysql/xdevapi/-/xdevapi-8.0.21.tgz#95056178b655fc14cd96b9c2558dfd85a2ce18dd" + integrity sha512-fUfPNsMK9MBBiisBadUfUYqrBlniPrSNJnstnveJpfhHksPt6rVfVk6gEPjEd32lwe5MmihcB5LBInSnNdCxbQ== + dependencies: + google-protobuf "3.11.4" + parsimmon "1.6.2" + +"@types/d3-path@*": + version "1.0.8" + resolved "https://registry.yarnpkg.com/@types/d3-path/-/d3-path-1.0.8.tgz#48e6945a8ff43ee0a1ce85c8cfa2337de85c7c79" + integrity sha512-AZGHWslq/oApTAHu9+yH/Bnk63y9oFOMROtqPAtxl5uB6qm1x2lueWdVEjsjjV3Qc2+QfuzKIwIR5MvVBakfzA== + +"@types/d3-shape@*": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@types/d3-shape/-/d3-shape-1.3.2.tgz#a41d9d6b10d02e221696b240caf0b5d0f5a588ec" + integrity sha512-LtD8EaNYCaBRzHzaAiIPrfcL3DdIysc81dkGlQvv7WQP3+YXV7b0JJTtR1U3bzeRieS603KF4wUo+ZkJVenh8w== + dependencies: + "@types/d3-path" "*" + +"@types/prop-types@*": + version "15.7.3" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7" + integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw== + +"@types/react@*": + version "16.9.49" + resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.49.tgz#09db021cf8089aba0cdb12a49f8021a69cce4872" + integrity sha512-DtLFjSj0OYAdVLBbyjhuV9CdGVHCkHn2R+xr3XkBvK2rS1Y1tkc14XSGjYgm5Fjjr90AxH9tiSzc1pCFMGO06g== + dependencies: + "@types/prop-types" "*" + csstype "^3.0.2" + +"@types/recharts@^1.8.15": + version "1.8.15" + resolved "https://registry.yarnpkg.com/@types/recharts/-/recharts-1.8.15.tgz#02bc06085c9a31a58c00194d15377b45cf506bbf" + integrity sha512-ApCfDT/R8RCbZTcl0iqLiKsxVdzE3GzoawTgJUHuQOz6ZXhsPVfp7CNSKI2s3zFwrRRsgmpv2AEcbcZceNHg4w== + dependencies: + "@types/d3-shape" "*" + "@types/react" "*" + +csstype@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.3.tgz#2b410bbeba38ba9633353aff34b05d9755d065f8" + integrity sha512-jPl+wbWPOWJ7SXsWyqGRk3lGecbar0Cb0OvZF/r/ZU011R4YqiRehgkQ9p4eQfo9DSDLqLL3wHwfxeJiuIsNag== + +google-protobuf@3.11.4: + version "3.11.4" + resolved "https://registry.yarnpkg.com/google-protobuf/-/google-protobuf-3.11.4.tgz#598ca405a3cfa917a2132994d008b5932ef42014" + integrity sha512-lL6b04rDirurUBOgsY2+LalI6Evq8eH5TcNzi7TYQ3BsIWelT0KSOQSBsXuavEkNf+odQU6c0lgz3UsZXeNX9Q== + +parsimmon@1.6.2: + version "1.6.2" + resolved "https://registry.yarnpkg.com/parsimmon/-/parsimmon-1.6.2.tgz#28fbe6b8caa38ab89f52b77f8c7743563c7d9aff" + integrity sha512-bJNB0ZQhHyM5KqO2Z5ttQAVn/PZ2pccxaOnMcZ0Su7HA1Iv4GQTfUmzSZ6N3jcsCn9F68PZcypAvF3nDRRL+3g== diff --git a/backend/data-processor/DataProcessor.ts b/backend/data-processor/DataProcessor.ts new file mode 100644 index 0000000000000000000000000000000000000000..6b25ce1e159a5ad68e62ecd0104632c9415ae089 --- /dev/null +++ b/backend/data-processor/DataProcessor.ts @@ -0,0 +1,30 @@ +import { ResultValue } from '@mysql/xdevapi'; +import { Result } from '../utils/mysql'; + +export interface MemoryPerformanceDataPoint { + relative_time: number; + geom: number; + total: number; +} + +export type MemoryPerformance = Array<MemoryPerformanceDataPoint>; + +export class DataProcessor { + static processMemoryPerformance(performanceData?: Result) { + // TODO: Add more restrictive tests + if (!performanceData || performanceData.labels.indexOf('geom') !== -1) { + // TODO: Figure out why we need to skip the first element + return <MemoryPerformance>( + performanceData?.values.slice(1).map((value: Array<ResultValue>) => { + const dataPoint: MemoryPerformanceDataPoint = { + relative_time: Number(value[1]), + geom: Number(value[2]), + total: Number(value[3]), + }; + return dataPoint; + }) + ); + } + throw new Error('DataProcessor: performanceData had wrong format'); + } +} diff --git a/backend/recorder/SqlManager.ts b/backend/recorder/SqlManager.ts new file mode 100644 index 0000000000000000000000000000000000000000..3c7d0530a57f1e788281afb8f51c629802427ae7 --- /dev/null +++ b/backend/recorder/SqlManager.ts @@ -0,0 +1,65 @@ +import { URI, Client } from '@mysql/xdevapi'; +import { saveLoginDetails } from '../utils/fileutils'; +import SqlRunner from './SqlRunner'; +import SqlMonitor from './SqlMonitor'; +import { closeConnection } from '../utils/mysql'; + +class SqlManager { + connection?: URI; + + database?: string; + + client?: Client; + + runner?: SqlRunner; + + monitor?: SqlMonitor; + + async connect(connection?: URI, database?: string) { + this.connection = connection; + this.database = database; + this.runner = new SqlRunner(); + this.client = await this.runner.connect(connection, database); + this.monitor = new SqlMonitor(); + await this.monitor.connect(this.client, database); + await saveLoginDetails({ ...connection, database }); + } + + async record(query: string, explainAnalyze?: boolean) { + if (!this.runner || !this.monitor || !this.runner.connectionID) + throw new Error( + 'SqlManager: Runner, monitor or connectionID was missing. Maybe you forgot to call connect()?' + ); + this.monitor.monitorConnection(this.runner.connectionID); + const result = await this.runner.executeQuery(query, explainAnalyze); + if (result?.error) { + await this.reset(); + return { result, memoryPerformance: undefined, error: result.error }; + } + const memoryPerformance = await this.monitor.dumpData(); + return { + result, + memoryPerformance: memoryPerformance.results, + error: undefined, + }; + } + + async reset() { + await closeConnection(this.client); + await this.connect(this.connection, this.database); + } + + async executeQuery(query: string) { + if (!this.runner) return []; + const result = await this.runner.executeQuery(query); + return result; + } + + async executeWithTrace(query: string) { + if (!this.runner) return { result: [], trace: [] }; + const result = await this.runner.executeTraceQuery(query); + return result; + } +} + +export default SqlManager; diff --git a/backend/recorder/SqlMonitor.ts b/backend/recorder/SqlMonitor.ts new file mode 100644 index 0000000000000000000000000000000000000000..5c27ae4d40795152fcab1545ba252500f22ae0a8 --- /dev/null +++ b/backend/recorder/SqlMonitor.ts @@ -0,0 +1,145 @@ +import { Session, Client } from '@mysql/xdevapi'; +import { createSession, runQuery } from '../utils/mysql'; + +export default class SqlMonitor { + session?: Session; + + connectionID?: string; + + fileCount = 0; + + async connect(client?: Client, database?: string) { + this.session = await createSession(client, database); + const resultSet = await runQuery(`SELECT connection_id();`, this.session); + this.connectionID = String(resultSet?.results?.values[0]); + console.log(`SqlMonitor: ConnectionID: ${this.connectionID}`); + } + + async monitorConnection(connectionID: string) { + await runQuery( + 'DROP PROCEDURE IF EXISTS monitor_connection;', + this.session + ); + await runQuery( + `CREATE PROCEDURE monitor_connection( + IN conn_id BIGINT UNSIGNED + ) + BEGIN + DECLARE thd_id BIGINT UNSIGNED; + DECLARE state VARCHAR(16); + DECLARE stage_ignore_before BIGINT UNSIGNED; + DECLARE stage_min_ts BIGINT UNSIGNED; + + SET thd_id = (SELECT THREAD_ID FROM performance_schema.threads WHERE PROCESSLIST_ID=conn_id); + + SET stage_ignore_before = (SELECT MAX(TIMER_END) FROM performance_schema.events_stages_history_long WHERE THREAD_ID=thd_id); + DROP TABLE IF EXISTS monitoring_data; + CREATE TABLE monitoring_data AS + SELECT + NOW(6) AS 'TS', + THREAD_ID, + EVENT_NAME, + COUNT_ALLOC, + COUNT_FREE, + SUM_NUMBER_OF_BYTES_ALLOC, + SUM_NUMBER_OF_BYTES_FREE, + LOW_COUNT_USED, + CURRENT_COUNT_USED, + HIGH_COUNT_USED, + LOW_NUMBER_OF_BYTES_USED, + CURRENT_NUMBER_OF_BYTES_USED, + HIGH_NUMBER_OF_BYTES_USED + FROM performance_schema.memory_summary_by_thread_by_event_name + WHERE THREAD_ID = thd_id; + + SELECT CONCAT ('Waiting for connection ', conn_id, ' (thread ', thd_id, ') to start executing a query') AS 'Status'; + + REPEAT + SET state = (SELECT PROCESSLIST_COMMAND FROM performance_schema.threads WHERE THREAD_ID=thd_id); + UNTIL state = 'Query' END REPEAT; + + SELECT 'Connection monitoring starting' AS 'Status'; + + REPEAT + SET state = (SELECT PROCESSLIST_COMMAND FROM performance_schema.threads WHERE THREAD_ID=thd_id); + INSERT INTO monitoring_data + SELECT + NOW(6) AS 'TS', + THREAD_ID, + EVENT_NAME, + COUNT_ALLOC, + COUNT_FREE, + SUM_NUMBER_OF_BYTES_ALLOC, + SUM_NUMBER_OF_BYTES_FREE, + LOW_COUNT_USED, + CURRENT_COUNT_USED, + HIGH_COUNT_USED, + LOW_NUMBER_OF_BYTES_USED, + CURRENT_NUMBER_OF_BYTES_USED, + HIGH_NUMBER_OF_BYTES_USED + FROM performance_schema.memory_summary_by_thread_by_event_name + WHERE THREAD_ID = thd_id; + DO SLEEP(0.1); + UNTIL state = 'Sleep' END REPEAT; + + SELECT 'Connection monitoring ended' AS 'Status'; + + SET stage_min_ts = (SELECT MIN(timer_start) FROM performance_schema.events_stages_history_long WHERE THREAD_ID=thd_id AND timer_start > stage_ignore_before); + SELECT stage_min_ts AS 'stage_min_ts', stage_ignore_before AS 'stage_ignore_before'; + SELECT + EVENT_NAME, + SOURCE, + timer_start, + timer_end, + (timer_start - stage_min_ts) / 1000000000000 AS start, + (timer_end - stage_min_ts) / 1000000000000 AS end + FROM performance_schema.events_stages_history_long + WHERE THREAD_ID = thd_id AND timer_start > stage_ignore_before ORDER BY timer_start; + + DROP TABLE IF EXISTS monitoring_stages; + CREATE TABLE monitoring_stages AS + SELECT + EVENT_NAME, + SOURCE, + timer_start, + timer_end, + (timer_start - stage_min_ts) / 1000000000000 AS start, + (timer_end - stage_min_ts) / 1000000000000 AS end + FROM performance_schema.events_stages_history_long + WHERE THREAD_ID = thd_id AND timer_start > stage_ignore_before ORDER BY timer_start; + END`, + this.session + ); + await runQuery(`call monitor_connection(${connectionID});`, this.session); + } + + async dumpData() { + await runQuery( + 'SET @min_ts = (SELECT UNIX_TIMESTAMP(MIN(TS)) FROM monitoring_data);', + this.session + ); + + const data = await runQuery( + `(SELECT + 'wall_clock', + 'relative time', + 'geom', + CAST('total' AS CHAR)) + UNION + (SELECT + TS AS the_ts, + UNIX_TIMESTAMP(TS) - @min_ts, + (SELECT + CURRENT_NUMBER_OF_BYTES_USED + FROM monitoring_data + WHERE EVENT_NAME='memory/sql/Geometry::ptr_and_wkb_data' AND TS=the_ts), + SUM(CURRENT_NUMBER_OF_BYTES_USED) + FROM monitoring_data + GROUP BY the_ts + );`, + this.session + ); + console.log(data); + return data; + } +} diff --git a/backend/recorder/SqlRunner.ts b/backend/recorder/SqlRunner.ts new file mode 100644 index 0000000000000000000000000000000000000000..10168c8e78f9ecbe51f3a57758e03670b0b61add --- /dev/null +++ b/backend/recorder/SqlRunner.ts @@ -0,0 +1,36 @@ +import { URI, Session } from '@mysql/xdevapi'; +import { + createConnection, + createSession, + runQuery, + getOptimizerTrace, +} from '../utils/mysql'; + +export default class SqlRunner { + session?: Session; + + connectionID?: string; + + async connect(connection?: URI, database?: string) { + const client = await createConnection(connection, { + pooling: { enabled: true, maxSize: 2 }, + }); + this.session = await createSession(client, database); + const resultSet = await runQuery(`SELECT connection_id();`, this.session); + this.connectionID = String(resultSet?.results?.values[0]); + console.log(`SqlRunner: ConnectionID: ${this.connectionID}`); + return client; + } + + async executeQuery(query: string, explainAnalyze?: boolean) { + if (explainAnalyze) { + const newQuery = `EXPLAIN ANALYZE ${query}`; + return runQuery(newQuery, this.session); + } + return runQuery(query, this.session); + } + + async executeTraceQuery(query: string) { + return getOptimizerTrace(query, this.session); + } +} diff --git a/backend/utils/LoginDetails.ts b/backend/utils/LoginDetails.ts new file mode 100644 index 0000000000000000000000000000000000000000..9b33d6393321662ee9ca08fc1e4013657dcff80e --- /dev/null +++ b/backend/utils/LoginDetails.ts @@ -0,0 +1,5 @@ +import { URI } from '@mysql/xdevapi'; + +export interface LoginDetails extends URI { + database?: string; +} diff --git a/backend/utils/dump_data.sql b/backend/utils/dump_data.sql new file mode 100644 index 0000000000000000000000000000000000000000..a1b923c1c619f2c849b178e520c92d4c2ce95838 --- /dev/null +++ b/backend/utils/dump_data.sql @@ -0,0 +1,16 @@ +# As mentioned in video and repo (as well as seen here) this will dump geometry memory usage as well as total memory usage. We could also divide other total memory by looking at different events. +SET @min_ts = (SELECT UNIX_TIMESTAMP(MIN(TS)) FROM monitoring_data); +(SELECT 'wall_clock', 'relative time', 'geom', CAST('total' AS CHAR)) +UNION +(SELECT + TS AS the_ts, + UNIX_TIMESTAMP(TS) - @min_ts, + (SELECT CURRENT_NUMBER_OF_BYTES_USED FROM monitoring_data WHERE EVENT_NAME='memory/sql/Geometry::ptr_and_wkb_data' AND TS=the_ts), + SUM(CURRENT_NUMBER_OF_BYTES_USED) + FROM monitoring_data + GROUP BY the_ts +) +INTO OUTFILE '/tmp/memory.csv' + FIELDS TERMINATED BY ',' + OPTIONALLY ENCLOSED BY '"' +; diff --git a/backend/utils/fileutils.ts b/backend/utils/fileutils.ts new file mode 100644 index 0000000000000000000000000000000000000000..c847e51e62b739e34e5153abb69f1597de7f5c05 --- /dev/null +++ b/backend/utils/fileutils.ts @@ -0,0 +1,24 @@ +import * as fs from 'fs'; +import { LoginDetails } from './LoginDetails'; + +// fs docs: https://nodejs.org/api/fs.html#fs_file_paths + +export async function saveLoginDetails(loginDetails: LoginDetails) { + const loginDetailsLocation = 'loginDetails.json'; + await fs.promises.writeFile( + loginDetailsLocation, + JSON.stringify(loginDetails, undefined, 2) + ); +} + +export async function loadLoginDetails(): Promise<LoginDetails | undefined> { + const loginDetailsLocation = 'loginDetails.json'; + if (fs.existsSync(loginDetailsLocation)) { + const data = await fs.promises.readFile(loginDetailsLocation); + return JSON.parse(String(data)); + } + console.log(`${loginDetailsLocation} does not exist!`); + return undefined; +} + +// export default { saveLoginDetails, loadLoginDetails }; diff --git a/backend/utils/monitor_connection.sql b/backend/utils/monitor_connection.sql new file mode 100644 index 0000000000000000000000000000000000000000..6a26d77bcf4e09c42d227cf3a719f1a5922087d9 --- /dev/null +++ b/backend/utils/monitor_connection.sql @@ -0,0 +1,103 @@ +DROP PROCEDURE IF EXISTS monitor_connection; +DELIMITER $$ +CREATE PROCEDURE monitor_connection( + IN conn_id BIGINT UNSIGNED +) +BEGIN + DECLARE thd_id BIGINT UNSIGNED; + DECLARE state VARCHAR(16); + DECLARE stage_ignore_before BIGINT UNSIGNED; + DECLARE stage_min_ts BIGINT UNSIGNED; + + # Thread ID is used as key in P_S, so get the thread ID of the suppliced connection ID. + SET thd_id = (SELECT THREAD_ID FROM performance_schema.threads WHERE PROCESSLIST_ID=conn_id); + + # Get the current maximum stage history table timestamp. All new events will have a more recent timestamp. + SET stage_ignore_before = (SELECT MAX(TIMER_END) FROM performance_schema.events_stages_history_long WHERE THREAD_ID=thd_id); + + # Create a table to log memory uage data. + DROP TABLE IF EXISTS monitoring_data; + CREATE TABLE monitoring_data AS + SELECT + NOW(6) AS 'TS', + THREAD_ID, + EVENT_NAME, + COUNT_ALLOC, + COUNT_FREE, + SUM_NUMBER_OF_BYTES_ALLOC, + SUM_NUMBER_OF_BYTES_FREE, + LOW_COUNT_USED, + CURRENT_COUNT_USED, + HIGH_COUNT_USED, + LOW_NUMBER_OF_BYTES_USED, + CURRENT_NUMBER_OF_BYTES_USED, + HIGH_NUMBER_OF_BYTES_USED + FROM performance_schema.memory_summary_by_thread_by_event_name + WHERE THREAD_ID = thd_id; + + SELECT CONCAT ('Waiting for connection ', conn_id, ' (thread ', thd_id, ') to start executing a query') AS 'Status'; + + # Wait for query to start. + REPEAT + SET state = (SELECT PROCESSLIST_COMMAND FROM performance_schema.threads WHERE THREAD_ID=thd_id); + UNTIL state = 'Query' END REPEAT; + + SELECT 'Connection monitoring starting' AS 'Status'; + + # Repeat until query finishes. + REPEAT + SET state = (SELECT PROCESSLIST_COMMAND FROM performance_schema.threads WHERE THREAD_ID=thd_id); + INSERT INTO monitoring_data + SELECT + NOW(6) AS 'TS', + THREAD_ID, + EVENT_NAME, + COUNT_ALLOC, + COUNT_FREE, + SUM_NUMBER_OF_BYTES_ALLOC, + SUM_NUMBER_OF_BYTES_FREE, + LOW_COUNT_USED, + CURRENT_COUNT_USED, + HIGH_COUNT_USED, + LOW_NUMBER_OF_BYTES_USED, + CURRENT_NUMBER_OF_BYTES_USED, + HIGH_NUMBER_OF_BYTES_USED + FROM performance_schema.memory_summary_by_thread_by_event_name + WHERE THREAD_ID = thd_id + ; + DO SLEEP(0.1); + UNTIL state = 'Sleep' END REPEAT; + + SELECT 'Connection monitoring ended' AS 'Status'; + + # Get the minimum timestamp in query stage history. + + SET stage_min_ts = (SELECT MIN(timer_start) FROM performance_schema.events_stages_history_long WHERE THREAD_ID=thd_id AND timer_start > stage_ignore_before); + SELECT stage_min_ts AS 'stage_min_ts', stage_ignore_before AS 'stage_ignore_before'; + # Timestamps in picoseconds, divide by 10^12. + SELECT + EVENT_NAME, + SOURCE, + timer_start, + timer_end, + (timer_start - stage_min_ts) / 1000000000000 AS start, + (timer_end - stage_min_ts) / 1000000000000 AS end + FROM performance_schema.events_stages_history_long + WHERE THREAD_ID = thd_id AND timer_start > stage_ignore_before + ORDER BY timer_start; + # Dump it to a table, too: + DROP TABLE IF EXISTS monitoring_stages; + CREATE TABLE monitoring_stages AS + SELECT + EVENT_NAME, + SOURCE, + timer_start, + timer_end, + (timer_start - stage_min_ts) / 1000000000000 AS start, + (timer_end - stage_min_ts) / 1000000000000 AS end + FROM performance_schema.events_stages_history_long + WHERE THREAD_ID = thd_id AND timer_start > stage_ignore_before + ORDER BY timer_start + ; +END $$ +DELIMITER ; diff --git a/backend/utils/mysql.ts b/backend/utils/mysql.ts new file mode 100644 index 0000000000000000000000000000000000000000..d346c156fe78ffbad0f293800f72f716756e0e9a --- /dev/null +++ b/backend/utils/mysql.ts @@ -0,0 +1,153 @@ +import mysqlx, { + URI, + Session, + PoolingOptions, + Client, + ResultValue, + Column, +} from '@mysql/xdevapi'; + +// mysqlx docs: https://dev.mysql.com/doc/dev/connector-nodejs/8.0/tutorial-Connecting_to_a_Server.html + +export interface Result { + labels: Array<string>; + values: Array<Array<ResultValue>>; +} + +/** + * Run a query using a Session + * @param session (Session) + * @param query (string) + * @returns Array<ResultSet> + */ +export async function runQuery(query: string, session?: Session) { + try { + if (!session) throw new Error('No session was passed to runQuery'); + const result = await session.sql(query).execute(); + const columns = result.getColumns(); + const fetchedResultSets = result.fetchAll(); + const results: Result = { + labels: columns.map((value: Column) => { + return value.getColumnLabel(); + }), + values: fetchedResultSets.map((valueArray: Array<ResultValue>) => { + return valueArray.map((value: ResultValue) => value); + }), + }; + console.log( + `Result from query '${query}':\n${JSON.stringify(results, undefined, 2)}` + ); + return { results, error: undefined }; + } catch (error) { + // TODO: Do more to handle errors + console.error(`Error while running query: ${error}`); + return { results: undefined, error }; + } +} + +export async function startTransaction(session?: Session) { + try { + if (!session) throw new Error('No session was passed to startTransaction'); + await session.startTransaction(); + } catch (error) { + // TODO: Do more to handle errors + console.error(`Error while running startTransaction: ${error}`); + throw error; + } +} + +export async function commit(session?: Session) { + try { + if (!session) throw new Error('No session was passed to commit'); + await session.commit(); + } catch (error) { + // TODO: Do more to handle errors + console.error(`Error while running commit: ${error}`); + throw error; + } +} + +/** + * Run a query using a Session and return its optimizer trace + * @param session (Session) + * @param query (string) + * @returns Object: Optimizer trace steps + */ +export async function getOptimizerTrace(query: string, session?: Session) { + await runQuery('SET optimizer_trace="enabled=on";', session); + const result = await runQuery(query, session); + const optimizerTrace = await runQuery( + 'SELECT * FROM INFORMATION_SCHEMA.OPTIMIZER_TRACE;', + session + ); + await runQuery('SET optimizer_trace="enabled=on";', session); + return { result, trace: optimizerTrace }; +} + +/** + * Create a connection to a MySQL server and return a Client + * @param connection (URI | string): Connection config or a connectionstring + * @param poolingOptions (PoolingOptions): Options for pooling connections, allowing one connection per thread + * @returns Client: A client which handles the connection pool + */ +export async function createConnection( + connection?: URI | string, + poolingOptions: PoolingOptions = { + pooling: { enabled: true, maxSize: 3 }, + } +) { + try { + if (!connection) + throw new Error( + 'No connection information was passed to createConnection' + ); + // Since this will only run locally, it's ok that the password is printed + console.log( + `Connecting with connectionstring: ${JSON.stringify( + connection, + undefined, + 2 + )}` + ); + return await mysqlx.getClient(connection, poolingOptions); + } catch (error) { + // TODO: Do more to handle errors + console.error(`Error while creating connection: ${error}`); + throw error; + } +} + +/** + * Create a session from a client + * @param client (Client): A client which is returned from createConnection() + * @param database (string?): Optionally specify database to connect to + * @returns Session + */ +export async function createSession(client?: Client, database?: string) { + try { + if (!client) throw new Error('No client was passed to createSession'); + const session = await client.getSession(); + if (database) runQuery(`USE ${database};`, session); + console.log(session.inspect()); + return session; + } catch (error) { + // TODO: Do more to handle errors + console.error(`Error while creating session: ${error}`); + throw error; + } +} + +/** + * Close the connection, destroying the pool, closing and cleaning up all connections in the pool + * @param client + */ +export async function closeConnection(client?: Client) { + try { + if (!client) throw new Error('No client passed'); + await client.close(); + } catch (error) { + // TODO: Do more to handle errors + console.error(`Error while disconnecting: ${error}`); + throw error; + } +} diff --git a/backend/utils/mysql_mem_tut.md b/backend/utils/mysql_mem_tut.md new file mode 100644 index 0000000000000000000000000000000000000000..2161068403c65448ec2bf951e045b771545f373a --- /dev/null +++ b/backend/utils/mysql_mem_tut.md @@ -0,0 +1,38 @@ +# Memory profiling + +Watch https://www.youtube.com/watch?v=hKGWdqlEt98 for reference. + +Goal: 1. Execute query 2. Use other connection to monitor executing query 3. Use this other connection to pull information out of performance schema to analyse memory usage of executing query + +## How to + +During the following explination **a** refers to the connection beeing monitored and **b** to the monitoring connection. + +### Understand whats going on + +- You need the `connection_id` of **a** + - `select connection_id()` ; +- From **b** save this id to a variable + - `set @pid = $number$`; + - _Sidenote: id's for all connections can be found by using "`show processlist;`"_ +- Performance schema works with thread id's not connection/process id's, so we need to find the corresponding thread id + - `select * from performance_schema.threads where processlist_id=@pid\G` +- Assign thread id to variable + - `set @tid = (select thread_id from performance_schema.threads where processlist_id=@pid\G)` +- To see memory usage we will look at the field `current_number_of_bytes_used` found in the table `performance_schema.memory_summary_by_thread_by_event_name` +- The following query will list `current_number_of_bytes_used `for all events the thread with id `@tid` is involved with + - `select event_name, current_number_of_bytes_used from performance_schema.memory_summary_by_thread_by_event_name where thread_id=@tid` + +### How to do monitoring and data dump + +- Time for monitoring (from **b**) + - use content of `monitor_connection.sql` + - `call monitor_connection(@pid)` +- example query to monitor creates lots of geometry memory (executed by **a**) + - `select sleep(2), st_buffer(point(0,0), 1000, st_buffer_strategy('point_circle', i)) from (values row(1), row(10), row(1000), row(10000), row(50000)) as ints(i);` +- Followed by content of `dump_data.sql` which will create a csv file + +- `monitor_connection.sql` will also create a table named `monitoring_stages` which can be looked into to see the stages of execution + - `select * from monitoring_stages` + +NB: In some cases memory can be freed by another thread than the one who allocated it which can result in weird numbers in the dataset. This should not be the case for geometry memory according to Norvald. diff --git a/backend/utils/mysqlx.d.ts b/backend/utils/mysqlx.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..8f16fecc90f25ab72c4d918af4a2a98e003362cb --- /dev/null +++ b/backend/utils/mysqlx.d.ts @@ -0,0 +1,248 @@ +/** + * These typings are incomplete, and are added to when more parts of the module are used + */ +declare module '@mysql/xdevapi' { + /** + * Create a legacy X DevAPI connection. + * @param connection (string | URI): connection specification + */ + function getSession(connection: string | URI): Promise<Session>; + + /** + * Create a new X DevAPI connection pool. + * @param connection (string | URI): connection specification + * @param options (PoolingOptions): pooling options + */ + function getClient( + connection: string | URI, + options: PoolingOptions + ): Promise<Client>; + + /** + * Retrieve the connector version number (from package.json). + */ + function getVersion(): string; + + /** + * Client connection pool factory. + * @method close() => Promise<any>: Pool destructor (closes and cleans up all the connections in the pool). + * @method getSession() => Promise<Session>: Retrieve a connection from the pool if one is available. + */ + interface Client { + /** + * Pool destructor (closes and cleans up all the connections in the pool). + */ + close: () => Promise<never>; + /** + * Retrieve a connection from the pool if one is available. + * @returns The active session instance. + */ + getSession: () => Promise<Session>; + } + + /** + * Might be wrong, docs were unclear here + */ + interface PoolingOptions { + pooling: { enabled: boolean; maxSize: number }; + enforceJSON?: boolean; + allowUndefined?: boolean; + } + + /** + * X plugin Session + * @method commit(): Commit a transaction. This will commit a transaction on the server. On success the returned Promise will resolve to true, else the Promise will be rejected with an Error. + * @method sql(sql: string): Execute a raw SQL statement. + * @method inspect(): Get information about the session + */ + interface Session { + Session: (properties: URI) => Session; + /** + * Commit a transaction. + * This will commit a transaction on the server. On success the returned Promise will resolve to true, else the Promise will be rejected with an Error. + */ + commit: () => Promise<boolean>; + // createSchema: (schema: string) => Promise<Schema>; + // dropSchema + // getDefaultSchema + // getSchema + // getSchemas + // releaseSavepoint + // rollback + // rollbackTo + // setSavepoint + /** + * Start a transaction This will start a transaction on the server. On success the returned Promise will resolve to true, else the Promise will be rejected with an Error. + */ + startTransaction: () => Promise<boolean>; + /** + * Execute a raw SQL statement. + * @returns SqlExecute + */ + sql: (sql: string) => SqlExecute; + inspect: () => string; + } + + /** + * SqlExecute factory. + */ + interface SqlExecute { + /** + * Bind values to ordinal query placeholders. + * + * Example: + * + * // values as arguments + * + * const query = session.sql('SELECT FROM person WHERE name = ? AND age = ?').bind('foo', 23) + * + * // values as a single array argument + * + * const query = session.sql('SELECT FROM person WHERE name = ? AND age = ?').bind(['foo', 23]) + */ + bind: (values: string | Array<string>) => SqlExecute; + /** + * Execute a raw SQL query. + */ + execute: () => SqlResult; + } + + type ResultValue = string | number | boolean | Array<ResultValue>; + + /** + * API for raw SQL queries. + * @method getAutoIncrementValue() => number: Retrieve the first AUTO INCREMENT value generated by the operation. + * @method hasData() => boolean: Checks if the result-set contains additional data. + * @method toArray() => Array<ResultValue>: Returns the entire result-set (without flushing) as a JavaScript Arrray. + * @method getResults() => Array<Array<Array<ResultValue>>>: Returns the entire result-set (without flushing) as a JavaScript Arrray. + * @method fetchAll() => Array<Array<ResultValue>>: Consume the current result-set from memory (and flush it). Returns a list of rows. + * @method fetchOne() => Array<ResultValue>: Consume a single result-set row from memory (and flush it). Returns a row + * @method getColumns() => Array<Column>: Retrieve the list of columns that are part of the result-set. + */ + interface SqlResult { + /** + * Consume the current result-set from memory (and flush it). + * @returns A list of rows. + */ + fetchAll: () => Array<Array<ResultValue>>; + /** + * Consume a single result-set row from memory (and flush it). + * @returns A row. + */ + fetchOne: () => Array<ResultValue>; + /** + * Retrieve the first AUTO INCREMENT value generated by the operation. + */ + getAutoIncrementValue: () => number; + /** + * Retrieve the list of columns that are part of the result-set. + * @returns A list of columns. + */ + getColumns: () => Array<Column>; + /** + * Retrieve the entire result-set (without flushing). + * @returns The result set + */ + getResults: () => Array<Array<Array<ResultValue>>>; + /** + * Checks if the result-set contains additional data. + * @returns true if there is additional data + */ + hasData: () => boolean; + /** + * Move to the next available result-set. + * @returns true if there is a next result set + */ + nextResult: () => boolean; + /** + * Returns the entire result-set (without flushing) as a JavaScript Array. + * @returns the entire result-set (without flushing) as a JavaScript Array + */ + toArray: () => Array<ResultValue>; + } + + interface Column { + /** + * Retrieve the name of the charset being used. + */ + getCharacterSetName: () => string; + /** + * Retrieve the name of the collation being used. + */ + getCollationName: () => string; + /** + * Retrieve the alias of the column. + */ + getColumnLabel: () => string; + /** + * Retrieve the actual name of the column. + */ + getColumnName: () => string; + /** + * Retrieve the number of fractional digits allowed for the column (DECIMAL or similar types). + */ + getFractionalDigits: () => number; + /** + * Retrieve the allowed size of the column. + */ + getLength: () => number; + /** + * Retrieve the name of the schema where the table belongs to. + */ + getSchemaName: () => string; + /** + * Retrieve the alias of the table where the column belongs to. + */ + getTableLabel: () => string; + /** + * Retrieve the actual name of the table where the column belongs to. + */ + getTableName: () => string; + /** + * Retrieve the SQL type of the column. + */ + getType: () => number; + /** + * Check if the column value is signed or not (for INT or similar types). + */ + isNumberSigned: () => boolean; + /** + * Check if the column value is being padded. + */ + isPadded: () => string; + } + + /** + * @property host (string): MySQL server hostname or IP address + * @property port (number): X Protocol port number + * @property user (string): Username + * @property password (string): Password + * @property auth (string): Client authentication method (default: PLAIN) + * @property ssl (boolean): Enable SSL/TLS (default: true) + * @property tls (TLSOptions): TLS-related options + * @property connectTimeout (number): Server initial connection timeout + * @property connectionAttributes (Object): Connection attributes + */ + interface URI { + host?: string; + port?: number; + user?: string; + password?: string; + auth?: string; + ssl?: boolean; + tls?: TLSOptions; + connectTimeout?: number; + connectionAttributes?: unknown; + } + + /** + * @property ca (string): path to a certificate authority PEM file + * @property crl (string): path to a certificate revocation list PEM file + * @property versions (Array<string>): list of TLS versions to allow, 'TLSv1', 'TLSv1.1', 'TLSv1.2' and 'TLSv1.3' (Node.js >= 12.0.0 only). + */ + interface TLSOptions { + ca?: string; + crl?: string; + versions: Array<string>; + } +} diff --git a/internals/img/erb-banner.png b/internals/img/erb-banner.png deleted file mode 100644 index cbc19d1216958db5952ad1b2b20e6f2d881269fc..0000000000000000000000000000000000000000 Binary files a/internals/img/erb-banner.png and /dev/null differ diff --git a/internals/img/erb-logo.png b/internals/img/erb-logo.png deleted file mode 100644 index 97a5661aeb0b6f20fde68d9e6dd1e899d8fda63f..0000000000000000000000000000000000000000 Binary files a/internals/img/erb-logo.png and /dev/null differ diff --git a/internals/img/eslint-padded-90.png b/internals/img/eslint-padded-90.png deleted file mode 100644 index d3c59cbaf94e8e2c9a0ae974d91d820c7278f2e9..0000000000000000000000000000000000000000 Binary files a/internals/img/eslint-padded-90.png and /dev/null differ diff --git a/internals/img/eslint-padded.png b/internals/img/eslint-padded.png deleted file mode 100644 index d6f9a1af3af2e64fe8b8a4f3f488efbad1ae2f4c..0000000000000000000000000000000000000000 Binary files a/internals/img/eslint-padded.png and /dev/null differ diff --git a/internals/img/eslint.png b/internals/img/eslint.png deleted file mode 100755 index 7fc0e98015e8ab9146b3bc926d19c630d18be080..0000000000000000000000000000000000000000 Binary files a/internals/img/eslint.png and /dev/null differ diff --git a/internals/img/jest-padded-90.png b/internals/img/jest-padded-90.png deleted file mode 100644 index 1f942a87a6de8fb3f8d5083d80c059907ad02d1c..0000000000000000000000000000000000000000 Binary files a/internals/img/jest-padded-90.png and /dev/null differ diff --git a/internals/img/jest-padded.png b/internals/img/jest-padded.png deleted file mode 100644 index ff62dd02a4c1494345c04758ad9738d0ac16b1a4..0000000000000000000000000000000000000000 Binary files a/internals/img/jest-padded.png and /dev/null differ diff --git a/internals/img/jest.png b/internals/img/jest.png deleted file mode 100644 index 3923b0bc4102bf8b642427fe70e27922c564be67..0000000000000000000000000000000000000000 Binary files a/internals/img/jest.png and /dev/null differ diff --git a/internals/img/js-padded.png b/internals/img/js-padded.png deleted file mode 100644 index d1f0ba6019b9e65fe0405086c78cd0bdc05552fd..0000000000000000000000000000000000000000 Binary files a/internals/img/js-padded.png and /dev/null differ diff --git a/internals/img/js.png b/internals/img/js.png deleted file mode 100755 index 7f49239001778c1ff98d5ec7b0d7931c61df529f..0000000000000000000000000000000000000000 Binary files a/internals/img/js.png and /dev/null differ diff --git a/internals/img/npm.png b/internals/img/npm.png deleted file mode 100755 index 06c649a8634ace436a637290956bb671a91d1482..0000000000000000000000000000000000000000 Binary files a/internals/img/npm.png and /dev/null differ diff --git a/internals/img/react-padded-90.png b/internals/img/react-padded-90.png deleted file mode 100644 index d10d9f2883b40f3687737476a4cabe3fefb6f6aa..0000000000000000000000000000000000000000 Binary files a/internals/img/react-padded-90.png and /dev/null differ diff --git a/internals/img/react-padded.png b/internals/img/react-padded.png deleted file mode 100644 index a5f2429333101ecf3529d6dd40630dc1a1e3c608..0000000000000000000000000000000000000000 Binary files a/internals/img/react-padded.png and /dev/null differ diff --git a/internals/img/react-router-padded-90.png b/internals/img/react-router-padded-90.png deleted file mode 100644 index 711d7412797e9982e4510ee29fad3318cf0a24ef..0000000000000000000000000000000000000000 Binary files a/internals/img/react-router-padded-90.png and /dev/null differ diff --git a/internals/img/react-router-padded.png b/internals/img/react-router-padded.png deleted file mode 100644 index a0c4ac955c57f1cd4b8845d0c8e3de28cdc633a0..0000000000000000000000000000000000000000 Binary files a/internals/img/react-router-padded.png and /dev/null differ diff --git a/internals/img/react-router.png b/internals/img/react-router.png deleted file mode 100644 index a926678b6fb59163442f2df3fe8115aac61df271..0000000000000000000000000000000000000000 Binary files a/internals/img/react-router.png and /dev/null differ diff --git a/internals/img/react.png b/internals/img/react.png deleted file mode 100755 index b82579d5bac865535def0779fc658439d21ab7e7..0000000000000000000000000000000000000000 Binary files a/internals/img/react.png and /dev/null differ diff --git a/internals/img/redux-padded-90.png b/internals/img/redux-padded-90.png deleted file mode 100644 index 7d96a6a6ebf9ed8f59ddee0c289590a64ed253e8..0000000000000000000000000000000000000000 Binary files a/internals/img/redux-padded-90.png and /dev/null differ diff --git a/internals/img/redux-padded.png b/internals/img/redux-padded.png deleted file mode 100644 index fd23430b6981889d7210773f99e4fe804e084266..0000000000000000000000000000000000000000 Binary files a/internals/img/redux-padded.png and /dev/null differ diff --git a/internals/img/redux.png b/internals/img/redux.png deleted file mode 100755 index cda220decf6cfebb54731d433ca2606bf8ddc5bc..0000000000000000000000000000000000000000 Binary files a/internals/img/redux.png and /dev/null differ diff --git a/internals/img/webpack-padded-90.png b/internals/img/webpack-padded-90.png deleted file mode 100644 index 4076d5bd439b3ca5014775cc05885bdb59a97342..0000000000000000000000000000000000000000 Binary files a/internals/img/webpack-padded-90.png and /dev/null differ diff --git a/internals/img/webpack-padded.png b/internals/img/webpack-padded.png deleted file mode 100644 index cc1d1a22ad8c7747333f1f3e9d52561e2a28c281..0000000000000000000000000000000000000000 Binary files a/internals/img/webpack-padded.png and /dev/null differ diff --git a/internals/img/webpack.png b/internals/img/webpack.png deleted file mode 100755 index bc437f0bf89897e96bdc940e80063acbb0a335b9..0000000000000000000000000000000000000000 Binary files a/internals/img/webpack.png and /dev/null differ diff --git a/internals/img/yarn-padded-90.png b/internals/img/yarn-padded-90.png deleted file mode 100644 index a08b53ba4a3b38f4b23bff997790e5c0c5182c93..0000000000000000000000000000000000000000 Binary files a/internals/img/yarn-padded-90.png and /dev/null differ diff --git a/internals/img/yarn-padded.png b/internals/img/yarn-padded.png deleted file mode 100644 index ca85ee2f3801a9ddff5d7319f9c987ef773c1ba7..0000000000000000000000000000000000000000 Binary files a/internals/img/yarn-padded.png and /dev/null differ diff --git a/internals/img/yarn.png b/internals/img/yarn.png deleted file mode 100644 index f406a4718b1d7a12b8b379fcc3d3224cddd5e16f..0000000000000000000000000000000000000000 Binary files a/internals/img/yarn.png and /dev/null differ diff --git a/package.json b/package.json index 50b478d3dc14c6c4e5d9117b2db2976fb2174d5f..317db12e48c2922baf99085e55216f71a0439d05 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { - "name": "electron-react-boilerplate", - "productName": "ElectronReact", - "version": "1.3.0", - "description": "Electron application boilerplate based on React, React Router, Webpack, React Hot Loader for rapid application development", + "name": "mysql-query-profiler", + "productName": "MySQL Query Profiler", + "version": "0.0.1", + "description": "A profiler for MySQL queries using Electron and React", "scripts": { "build": "concurrently \"yarn build-main\" \"yarn build-renderer\"", "build-dll": "cross-env NODE_ENV=development webpack --config ./configs/webpack.config.renderer.dev.dll.babel.js --colors", @@ -30,16 +30,15 @@ "start-main-debug": "yarn start-main-dev --inspect=5858 --remote-debugging-port=9223", "start-main-dev": "cross-env START_HOT=1 NODE_ENV=development electron -r ./internals/scripts/BabelRegister ./app/main.dev.ts", "start-renderer-dev": "cross-env NODE_ENV=development webpack-dev-server --config configs/webpack.config.renderer.dev.babel.js", - "test": "cross-env BABEL_DISABLE_CACHE=1 jest", - "test-all": "yarn lint && yarn tsc && yarn build && yarn test", - "test-e2e": "node -r @babel/register ./internals/scripts/CheckBuildsExist.js && cross-env NODE_ENV=test testcafe electron:./app ./test/e2e/HomePage.e2e.ts", - "test-e2e-live": "node -r @babel/register ./internals/scripts/CheckBuildsExist.js && cross-env NODE_ENV=test testcafe --live electron:./app ./test/e2e/HomePage.e2e.ts", - "test-watch": "yarn test --watch" + "test": "node ./node_modules/jest/bin/jest.js" }, "lint-staged": { "*.{js,jsx,ts,tsx}": [ "cross-env NODE_ENV=development eslint --cache" ], + "*.{js,ts,tsx}": [ + "eslint --fix" + ], "{*.json,.{babelrc,eslintrc,prettierrc,stylelintrc}}": [ "prettier --ignore-path .eslintignore --parser json --write" ], @@ -95,55 +94,13 @@ "output": "release" }, "publish": { - "provider": "github", - "owner": "electron-react-boilerplate", - "repo": "electron-react-boilerplate", + "provider": "gitlab", + "owner": "mysql-query-profiler", + "repo": "mysql-query-profiler", "private": false } }, - "repository": { - "type": "git", - "url": "git+https://github.com/electron-react-boilerplate/electron-react-boilerplate.git" - }, - "author": { - "name": "Electron React Boilerplate Maintainers", - "email": "electronreactboilerplate@gmail.com", - "url": "https://electron-react-boilerplate.js.org" - }, - "contributors": [ - { - "name": "Vikram Rangaraj", - "email": "vikr01@icloud.com", - "url": "https://github.com/vikr01" - }, - { - "name": "Amila Welihinda", - "email": "amilajack@gmail.com", - "url": "https://github.com/amilajack" - }, - { - "name": "John Tran", - "email": "jptran318@gmail.com", - "url": "https://github.com/jooohhn" - } - ], "license": "MIT", - "bugs": { - "url": "https://github.com/electron-react-boilerplate/electron-react-boilerplate/issues" - }, - "keywords": [ - "electron", - "boilerplate", - "react", - "redux", - "typescript", - "ts", - "sass", - "webpack", - "hot", - "reload" - ], - "homepage": "https://github.com/electron-react-boilerplate/electron-react-boilerplate#readme", "jest": { "testURL": "http://localhost/", "moduleNameMapper": { @@ -194,18 +151,21 @@ "@types/enzyme-adapter-react-16": "^1.0.6", "@types/history": "^4.7.6", "@types/jest": "^26.0.10", + "@types/mysql": "^2.15.15", "@types/node": "12", - "@types/react": "^16.9.44", + "@types/react": "^16.9.49", "@types/react-dom": "^16.9.8", "@types/react-redux": "^7.1.9", "@types/react-router": "^5.1.8", "@types/react-router-dom": "^5.1.5", "@types/react-test-renderer": "^16.9.2", + "@types/recharts": "^1.8.15", "@types/redux-logger": "^3.0.8", "@types/webpack": "^4.41.21", "@types/webpack-env": "^1.15.2", - "@typescript-eslint/eslint-plugin": "^3.6.1", - "@typescript-eslint/parser": "^3.6.1", + "@typescript-eslint/eslint-plugin": "^4.0.1", + "@typescript-eslint/parser": "^4.0.1", + "add": "^2.0.6", "babel-eslint": "^10.1.0", "babel-jest": "^26.1.0", "babel-loader": "^8.1.0", @@ -225,7 +185,7 @@ "enzyme": "^3.11.0", "enzyme-adapter-react-16": "^1.15.2", "enzyme-to-json": "^3.5.0", - "eslint": "^7.5.0", + "eslint": "^7.8.1", "eslint-config-airbnb": "^18.2.0", "eslint-config-airbnb-typescript": "^9.0.0", "eslint-config-erb": "^1.0.0", @@ -248,7 +208,7 @@ "mini-css-extract-plugin": "^0.9.0", "opencollective-postinstall": "^2.0.3", "optimize-css-assets-webpack-plugin": "^5.0.3", - "prettier": "^2.0.5", + "prettier": "^2.1.1", "react-test-renderer": "^16.12.0", "redux-logger": "^3.0.6", "rimraf": "^3.0.0", @@ -261,6 +221,7 @@ "testcafe": "^1.8.8", "testcafe-browser-provider-electron": "^0.0.15", "testcafe-react-selectors": "^4.0.0", + "ts-node": "^9.0.0", "typescript": "^3.9.7", "typings-for-css-modules-loader": "^1.7.0", "url-loader": "^4.1.0", @@ -268,28 +229,34 @@ "webpack-bundle-analyzer": "^3.8.0", "webpack-cli": "^3.3.12", "webpack-dev-server": "^3.11.0", - "webpack-merge": "^5.0.9" + "webpack-merge": "^5.0.9", + "yarn": "^1.22.5" }, "dependencies": { "@fortawesome/fontawesome-free": "^5.14.0", "@hot-loader/react-dom": "^16.13.0", + "@material-ui/core": "^4.11.0", + "@mysql/xdevapi": "8.0.21", "@reduxjs/toolkit": "^1.4.0", "connected-react-router": "^6.6.1", "electron-debug": "^3.1.0", "electron-log": "^4.2.2", + "electron-react-devtools": "^0.5.3", "electron-updater": "^4.3.1", "history": "^4.7.2", + "lodash": "^4.17.20", "react": "^16.13.1", "react-dom": "^16.12.0", "react-hot-loader": "^4.12.21", "react-redux": "^7.2.0", "react-router-dom": "^5.2.0", + "react-textarea-autosize": "^8.2.0", + "recharts": "^1.8.5", "redux": "^4.0.5", "redux-thunk": "^2.3.0", "regenerator-runtime": "^0.13.5", "source-map-support": "^0.5.19", - "ssh2": "^0.8.9", - "ssh2-promise": "^0.1.7" + "use-deep-compare-effect": "^1.4.0" }, "devEngines": { "node": ">=7.x", diff --git a/resources/icon.icns b/resources/icon.icns deleted file mode 100644 index c2213ce890a3d807acf6eaebc03b783ea246ecfa..0000000000000000000000000000000000000000 Binary files a/resources/icon.icns and /dev/null differ diff --git a/resources/icon.ico b/resources/icon.ico deleted file mode 100644 index 98948ea682c47f298f588b92fd54bfbe30a653e5..0000000000000000000000000000000000000000 Binary files a/resources/icon.ico and /dev/null differ diff --git a/resources/icon.png b/resources/icon.png deleted file mode 100755 index 755a6e51d53e9252f90c19cdb4a373bebb80af98..0000000000000000000000000000000000000000 Binary files a/resources/icon.png and /dev/null differ diff --git a/resources/icons/1024x1024.png b/resources/icons/1024x1024.png deleted file mode 100755 index 5940b65a3d42d62f38317f14270b28fd8dd568a5..0000000000000000000000000000000000000000 Binary files a/resources/icons/1024x1024.png and /dev/null differ diff --git a/resources/icons/128x128.png b/resources/icons/128x128.png deleted file mode 100755 index 14e578d247d87b16e2f2a91795e50135319c5090..0000000000000000000000000000000000000000 Binary files a/resources/icons/128x128.png and /dev/null differ diff --git a/resources/icons/16x16.png b/resources/icons/16x16.png deleted file mode 100755 index 260a46cb0b4d59377e02f40f299fb8874f409f36..0000000000000000000000000000000000000000 Binary files a/resources/icons/16x16.png and /dev/null differ diff --git a/resources/icons/24x24.png b/resources/icons/24x24.png deleted file mode 100755 index 56172416c79d29acaca9177b942f6b7fb2c94db9..0000000000000000000000000000000000000000 Binary files a/resources/icons/24x24.png and /dev/null differ diff --git a/resources/icons/256x256.png b/resources/icons/256x256.png deleted file mode 100755 index 755a6e51d53e9252f90c19cdb4a373bebb80af98..0000000000000000000000000000000000000000 Binary files a/resources/icons/256x256.png and /dev/null differ diff --git a/resources/icons/32x32.png b/resources/icons/32x32.png deleted file mode 100755 index 63423dfecb8a433cae122101224a277286584a8f..0000000000000000000000000000000000000000 Binary files a/resources/icons/32x32.png and /dev/null differ diff --git a/resources/icons/48x48.png b/resources/icons/48x48.png deleted file mode 100755 index 74d87a0cf105db95f543775d6be1cda0b3038d3e..0000000000000000000000000000000000000000 Binary files a/resources/icons/48x48.png and /dev/null differ diff --git a/resources/icons/512x512.png b/resources/icons/512x512.png deleted file mode 100755 index 313cd499d7958b0564a376120511e496aeeae330..0000000000000000000000000000000000000000 Binary files a/resources/icons/512x512.png and /dev/null differ diff --git a/resources/icons/64x64.png b/resources/icons/64x64.png deleted file mode 100755 index 6de0ec0e03268c0551cbdb8e9c5e2f27f253ec7d..0000000000000000000000000000000000000000 Binary files a/resources/icons/64x64.png and /dev/null differ diff --git a/resources/icons/96x96.png b/resources/icons/96x96.png deleted file mode 100755 index 8255ab58cea944436e3e6853aa5da9a8a88a1082..0000000000000000000000000000000000000000 Binary files a/resources/icons/96x96.png and /dev/null differ diff --git a/test/.eslintrc.json b/test/.eslintrc.json deleted file mode 100644 index 9adaad74c6ca54e84e0f2e5d9e6da1a45482896e..0000000000000000000000000000000000000000 --- a/test/.eslintrc.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "extends": "plugin:testcafe/recommended", - "env": { - "jest/globals": true - }, - "plugins": ["jest", "testcafe"], - "rules": { - "jest/no-disabled-tests": "warn", - "jest/no-focused-tests": "error", - "jest/no-identical-title": "error", - "no-console": "off" - } -} diff --git a/test/e2e/HomePage.e2e.ts b/test/e2e/HomePage.e2e.ts deleted file mode 100644 index 9a862f6b1f91194832b053dfe25af8c217c3dcb1..0000000000000000000000000000000000000000 --- a/test/e2e/HomePage.e2e.ts +++ /dev/null @@ -1,82 +0,0 @@ -/* eslint jest/expect-expect: off, jest/no-test-callback: off */ -import { ClientFunction, Selector } from 'testcafe'; - -const getPageUrl = ClientFunction(() => window.location.href); -const getPageTitle = ClientFunction(() => document.title); -const counterSelector = Selector('[data-tid="counter"]'); -const buttonsSelector = Selector('[data-tclass="btn"]'); -const clickToCounterLink = (t) => - t.click(Selector('a').withExactText('to Counter')); -const incrementButton = buttonsSelector.nth(0); -const decrementButton = buttonsSelector.nth(1); -const oddButton = buttonsSelector.nth(2); -const asyncButton = buttonsSelector.nth(3); -const getCounterText = () => counterSelector().innerText; -const assertNoConsoleErrors = async (t) => { - const { error } = await t.getBrowserConsoleMessages(); - await t.expect(error).eql([]); -}; - -fixture`Home Page`.page('../../app/app.html').afterEach(assertNoConsoleErrors); - -test('e2e', async (t) => { - await t.expect(getPageTitle()).eql('Hello Electron React!'); -}); - -test('should open window and contain expected page title', async (t) => { - await t.expect(getPageTitle()).eql('Hello Electron React!'); -}); - -test( - 'should not have any logs in console of main window', - assertNoConsoleErrors -); - -test('should navigate to Counter with click on the "to Counter" link', async (t) => { - await t.click('[data-tid=container] > a').expect(getCounterText()).eql('0'); -}); - -test('should navigate to /counter', async (t) => { - await t.click('a').expect(getPageUrl()).contains('/counter'); -}); - -fixture`Counter Tests` - .page('../../app/app.html') - .beforeEach(clickToCounterLink) - .afterEach(assertNoConsoleErrors); - -test('should display updated count after the increment button click', async (t) => { - await t.click(incrementButton).expect(getCounterText()).eql('1'); -}); - -test('should display updated count after the descrement button click', async (t) => { - await t.click(decrementButton).expect(getCounterText()).eql('-1'); -}); - -test('should not change even counter if odd button clicked', async (t) => { - await t.click(oddButton).expect(getCounterText()).eql('0'); -}); - -test('should change odd counter if odd button clicked', async (t) => { - await t - .click(incrementButton) - .click(oddButton) - .expect(getCounterText()) - .eql('2'); -}); - -test('should change if async button clicked and a second later', async (t) => { - await t - .click(asyncButton) - .expect(getCounterText()) - .eql('0') - .expect(getCounterText()) - .eql('1'); -}); - -test('should back to home if back button clicked', async (t) => { - await t - .click('[data-tid="backButton"] > a') - .expect(Selector('[data-tid="container"]').visible) - .ok(); -}); diff --git a/test/features/counter/Counter.spec.tsx b/test/features/counter/Counter.spec.tsx deleted file mode 100644 index 498ecae1762af87b6bcab4ad8104e9c466c3296d..0000000000000000000000000000000000000000 --- a/test/features/counter/Counter.spec.tsx +++ /dev/null @@ -1,139 +0,0 @@ -/* eslint react/jsx-props-no-spreading: off, @typescript-eslint/ban-ts-comment: off */ -import React from 'react'; -import Enzyme, { mount } from 'enzyme'; -import Adapter from 'enzyme-adapter-react-16'; -import { BrowserRouter as Router } from 'react-router-dom'; -import renderer from 'react-test-renderer'; -import { Provider } from 'react-redux'; -import { configureStore } from '@reduxjs/toolkit'; -import Counter from '../../../app/features/counter/Counter'; -import * as counterSlice from '../../../app/features/counter/counterSlice'; - -Enzyme.configure({ adapter: new Adapter() }); -jest.useFakeTimers(); - -function setup( - preloadedState: { counter: { value: number } } = { counter: { value: 1 } } -) { - const store = configureStore({ - reducer: { counter: counterSlice.default }, - preloadedState, - }); - - const getWrapper = () => - mount( - <Provider store={store}> - <Router> - <Counter /> - </Router> - </Provider> - ); - const component = getWrapper(); - return { - store, - component, - buttons: component.find('button'), - p: component.find('.counter'), - }; -} - -describe('Counter component', () => { - it('should should display count', () => { - const { p } = setup(); - expect(p.text()).toMatch(/^1$/); - }); - - it('should first button should call increment', () => { - const { buttons } = setup(); - const incrementSpy = jest.spyOn(counterSlice, 'increment'); - - buttons.at(0).simulate('click'); - expect(incrementSpy).toBeCalled(); - incrementSpy.mockRestore(); - }); - - it('should match exact snapshot', () => { - const { store } = setup(); - const tree = renderer - .create( - <Provider store={store}> - <Router> - <Counter /> - </Router> - </Provider> - ) - .toJSON(); - - expect(tree).toMatchSnapshot(); - }); - - it('should second button should call decrement', () => { - const { buttons } = setup(); - const decrementSyp = jest.spyOn(counterSlice, 'decrement'); - buttons.at(1).simulate('click'); - expect(decrementSyp).toBeCalled(); - decrementSyp.mockRestore(); - }); - - it('should third button should call incrementIfOdd', () => { - const { buttons } = setup(); - const incrementIfOdd = jest.spyOn(counterSlice, 'incrementIfOdd'); - buttons.at(2).simulate('click'); - expect(incrementIfOdd).toBeCalled(); - incrementIfOdd.mockRestore(); - }); - - it('should fourth button should call incrementAsync', () => { - const { buttons } = setup(); - const incrementAsync = jest.spyOn(counterSlice, 'incrementAsync'); - buttons.at(3).simulate('click'); - expect(incrementAsync).toBeCalled(); - incrementAsync.mockRestore(); - }); - - it('should display updated count after increment button click', () => { - const { buttons, p } = setup(); - buttons.at(0).simulate('click'); - expect(p.text()).toMatch(/^2$/); - }); - - it('should display updated count after decrement button click', () => { - const { buttons, p } = setup(); - buttons.at(1).simulate('click'); - expect(p.text()).toMatch(/^0$/); - }); - - it('shouldnt change if even and if odd button clicked', () => { - const { buttons, p } = setup({ counter: { value: 2 } }); - buttons.at(2).simulate('click'); - expect(p.text()).toMatch(/^2$/); - }); - - it('should change if odd and if odd button clicked', () => { - const { buttons, p } = setup({ counter: { value: 1 } }); - buttons.at(2).simulate('click'); - expect(p.text()).toMatch(/^2$/); - }); -}); - -describe('Test counter actions', () => { - it('should not call incrementAsync before timer', () => { - const fn = counterSlice.incrementAsync(1000); - expect(fn).toBeInstanceOf(Function); - const dispatch = jest.fn(); - // @ts-ignore - fn(dispatch); - jest.advanceTimersByTime(500); - expect(dispatch).not.toBeCalled(); - }); - - it('should call incrementAsync after timer', () => { - const fn = counterSlice.incrementAsync(1000); - expect(fn).toBeInstanceOf(Function); - const dispatch = jest.fn(); - // @ts-ignore - fn(dispatch); - jest.advanceTimersByTime(1001); - expect(dispatch).toBeCalled(); - }); -}); diff --git a/test/features/counter/__snapshots__/Counter.spec.tsx.snap b/test/features/counter/__snapshots__/Counter.spec.tsx.snap deleted file mode 100644 index e6d3b922dc64bd7c132f6ba8ad0b46ac80085da4..0000000000000000000000000000000000000000 --- a/test/features/counter/__snapshots__/Counter.spec.tsx.snap +++ /dev/null @@ -1,65 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Counter component should match exact snapshot 1`] = ` -<div> - <div - className="backButton" - data-tid="backButton" - > - <a - href="/" - onClick={[Function]} - > - <i - className="fa fa-arrow-left fa-3x" - /> - </a> - </div> - <div - className="counter counter" - data-tid="counter" - > - 1 - </div> - <div - className="btnGroup" - > - <button - className="btn" - data-tclass="btn" - onClick={[Function]} - type="button" - > - <i - className="fa fa-plus" - /> - </button> - <button - className="btn" - data-tclass="btn" - onClick={[Function]} - type="button" - > - <i - className="fa fa-minus" - /> - </button> - <button - className="btn" - data-tclass="btn" - onClick={[Function]} - type="button" - > - odd - </button> - <button - className="btn" - data-tclass="btn" - onClick={[Function]} - type="button" - > - async - </button> - </div> -</div> -`; diff --git a/test/reducers/__snapshots__/counter.spec.ts.snap b/test/reducers/__snapshots__/counter.spec.ts.snap deleted file mode 100644 index bb6dd2cf377463c4f2a9bfb11dfe934de83ca1c0..0000000000000000000000000000000000000000 --- a/test/reducers/__snapshots__/counter.spec.ts.snap +++ /dev/null @@ -1,25 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`reducers counter should handle DECREMENT_COUNTER 1`] = ` -Object { - "value": 0, -} -`; - -exports[`reducers counter should handle INCREMENT_COUNTER 1`] = ` -Object { - "value": 2, -} -`; - -exports[`reducers counter should handle initial state 1`] = ` -Object { - "value": 0, -} -`; - -exports[`reducers counter should handle unknown action type 1`] = ` -Object { - "value": 1, -} -`; diff --git a/test/reducers/counter.spec.ts b/test/reducers/counter.spec.ts deleted file mode 100644 index 3360905df848a3b32ca36dead9ecd4fa66c8af2d..0000000000000000000000000000000000000000 --- a/test/reducers/counter.spec.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { AnyAction } from 'redux'; -import counterReducer, { - increment, - decrement, -} from '../../app/features/counter/counterSlice'; - -describe('reducers', () => { - describe('counter', () => { - it('should handle initial state', () => { - expect(counterReducer(undefined, {} as AnyAction)).toMatchSnapshot(); - }); - - it('should handle INCREMENT_COUNTER', () => { - expect( - counterReducer({ value: 1 }, { type: increment }) - ).toMatchSnapshot(); - }); - - it('should handle DECREMENT_COUNTER', () => { - expect( - counterReducer({ value: 1 }, { type: decrement }) - ).toMatchSnapshot(); - }); - - it('should handle unknown action type', () => { - expect( - counterReducer({ value: 1 }, { type: 'unknown' }) - ).toMatchSnapshot(); - }); - }); -}); diff --git a/tsconfig.json b/tsconfig.json index 57317533a595f9ea333f0f0eba39c4762a2553ef..1b323178fa7b91ea8768c206f0225bb1fcd09e4d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -20,7 +20,8 @@ "esModuleInterop": true, "allowSyntheticDefaultImports": true, "resolveJsonModule": true, - "allowJs": true + "allowJs": true, + "typeRoots": ["backend/recorder/utils/types", "node_modules/@types"] }, "exclude": [ "test", diff --git a/yarn.lock b/yarn.lock index 9be4c82b51ecb785d93dd7b09146824dff19cec8..2ca3862b99b97a2ac4c7a1ba253388f032ef5218 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1083,7 +1083,7 @@ core-js-pure "^3.0.0" regenerator-runtime "^0.13.4" -"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.4": +"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7": version "7.11.2" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.11.2.tgz#f549c13c754cc40b87644b9fa9f09a6a95fe0736" integrity sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw== @@ -1160,18 +1160,32 @@ global-agent "^2.0.2" global-tunnel-ng "^2.7.1" +"@emotion/hash@^0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413" + integrity sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow== + +"@eslint/eslintrc@^0.1.3": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.1.3.tgz#7d1a2b2358552cc04834c0979bd4275362e37085" + integrity sha512-4YVwPkANLeNtRjMekzux1ci8hIaH5eGKktGqR0d3LWsKNn5B2X/1Z6Trxy7jQXl9EBGE6Yj02O+t09FMeRllaA== + dependencies: + ajv "^6.12.4" + debug "^4.1.1" + espree "^7.3.0" + globals "^12.1.0" + ignore "^4.0.6" + import-fresh "^3.2.1" + js-yaml "^3.13.1" + lodash "^4.17.19" + minimatch "^3.0.4" + strip-json-comments "^3.1.1" + "@fortawesome/fontawesome-free@^5.14.0": version "5.14.0" resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-free/-/fontawesome-free-5.14.0.tgz#a371e91029ebf265015e64f81bfbf7d228c9681f" integrity sha512-OfdMsF+ZQgdKHP9jUbmDcRrP0eX90XXrsXIdyjLbkmSBzmMXPABB8eobUJtivaupucYaByz6WNe1PI1JuYm3qA== -"@heroku/socksv5@^0.0.9": - version "0.0.9" - resolved "https://registry.yarnpkg.com/@heroku/socksv5/-/socksv5-0.0.9.tgz#7a3905921136b2666979a0f86bb4f062f657f793" - integrity sha1-ejkFkhE2smZpeaD4a7TwYvZX95M= - dependencies: - ip-address "^5.8.8" - "@hot-loader/react-dom@^16.13.0": version "16.13.0" resolved "https://registry.yarnpkg.com/@hot-loader/react-dom/-/react-dom-16.13.0.tgz#de245b42358110baf80aaf47a0592153d4047997" @@ -1379,6 +1393,70 @@ "@types/yargs" "^15.0.0" chalk "^4.0.0" +"@material-ui/core@^4.11.0": + version "4.11.0" + resolved "https://registry.yarnpkg.com/@material-ui/core/-/core-4.11.0.tgz#b69b26e4553c9e53f2bfaf1053e216a0af9be15a" + integrity sha512-bYo9uIub8wGhZySHqLQ833zi4ZML+XCBE1XwJ8EuUVSpTWWG57Pm+YugQToJNFsEyiKFhPh8DPD0bgupz8n01g== + dependencies: + "@babel/runtime" "^7.4.4" + "@material-ui/styles" "^4.10.0" + "@material-ui/system" "^4.9.14" + "@material-ui/types" "^5.1.0" + "@material-ui/utils" "^4.10.2" + "@types/react-transition-group" "^4.2.0" + clsx "^1.0.4" + hoist-non-react-statics "^3.3.2" + popper.js "1.16.1-lts" + prop-types "^15.7.2" + react-is "^16.8.0" + react-transition-group "^4.4.0" + +"@material-ui/styles@^4.10.0": + version "4.10.0" + resolved "https://registry.yarnpkg.com/@material-ui/styles/-/styles-4.10.0.tgz#2406dc23aa358217aa8cc772e6237bd7f0544071" + integrity sha512-XPwiVTpd3rlnbfrgtEJ1eJJdFCXZkHxy8TrdieaTvwxNYj42VnnCyFzxYeNW9Lhj4V1oD8YtQ6S5Gie7bZDf7Q== + dependencies: + "@babel/runtime" "^7.4.4" + "@emotion/hash" "^0.8.0" + "@material-ui/types" "^5.1.0" + "@material-ui/utils" "^4.9.6" + clsx "^1.0.4" + csstype "^2.5.2" + hoist-non-react-statics "^3.3.2" + jss "^10.0.3" + jss-plugin-camel-case "^10.0.3" + jss-plugin-default-unit "^10.0.3" + jss-plugin-global "^10.0.3" + jss-plugin-nested "^10.0.3" + jss-plugin-props-sort "^10.0.3" + jss-plugin-rule-value-function "^10.0.3" + jss-plugin-vendor-prefixer "^10.0.3" + prop-types "^15.7.2" + +"@material-ui/system@^4.9.14": + version "4.9.14" + resolved "https://registry.yarnpkg.com/@material-ui/system/-/system-4.9.14.tgz#4b00c48b569340cefb2036d0596b93ac6c587a5f" + integrity sha512-oQbaqfSnNlEkXEziDcJDDIy8pbvwUmZXWNqlmIwDqr/ZdCK8FuV3f4nxikUh7hvClKV2gnQ9djh5CZFTHkZj3w== + dependencies: + "@babel/runtime" "^7.4.4" + "@material-ui/utils" "^4.9.6" + csstype "^2.5.2" + prop-types "^15.7.2" + +"@material-ui/types@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@material-ui/types/-/types-5.1.0.tgz#efa1c7a0b0eaa4c7c87ac0390445f0f88b0d88f2" + integrity sha512-7cqRjrY50b8QzRSYyhSpx4WRw2YuO0KKIGQEVk5J8uoz2BanawykgZGoWEqKm7pVIbzFDN0SpPcVV4IhOFkl8A== + +"@material-ui/utils@^4.10.2", "@material-ui/utils@^4.9.6": + version "4.10.2" + resolved "https://registry.yarnpkg.com/@material-ui/utils/-/utils-4.10.2.tgz#3fd5470ca61b7341f1e0468ac8f29a70bf6df321" + integrity sha512-eg29v74P7W5r6a4tWWDAAfZldXIzfyO1am2fIsC39hdUUHm/33k6pGOKPbgDjg/U/4ifmgAePy/1OjkKN6rFRw== + dependencies: + "@babel/runtime" "^7.4.4" + prop-types "^15.7.2" + react-is "^16.8.0" + "@mrmlnc/readdir-enhanced@^2.2.1": version "2.2.1" resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" @@ -1387,6 +1465,14 @@ call-me-maybe "^1.0.1" glob-to-regexp "^0.3.0" +"@mysql/xdevapi@8.0.21": + version "8.0.21" + resolved "https://registry.yarnpkg.com/@mysql/xdevapi/-/xdevapi-8.0.21.tgz#95056178b655fc14cd96b9c2558dfd85a2ce18dd" + integrity sha512-fUfPNsMK9MBBiisBadUfUYqrBlniPrSNJnstnveJpfhHksPt6rVfVk6gEPjEd32lwe5MmihcB5LBInSnNdCxbQ== + dependencies: + google-protobuf "3.11.4" + parsimmon "1.6.2" + "@nodelib/fs.scandir@2.1.3": version "2.1.3" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz#3a582bdb53804c6ba6d146579c46e52130cf4a3b" @@ -1521,6 +1607,18 @@ resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== +"@types/d3-path@*": + version "1.0.8" + resolved "https://registry.yarnpkg.com/@types/d3-path/-/d3-path-1.0.8.tgz#48e6945a8ff43ee0a1ce85c8cfa2337de85c7c79" + integrity sha512-AZGHWslq/oApTAHu9+yH/Bnk63y9oFOMROtqPAtxl5uB6qm1x2lueWdVEjsjjV3Qc2+QfuzKIwIR5MvVBakfzA== + +"@types/d3-shape@*": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@types/d3-shape/-/d3-shape-1.3.2.tgz#a41d9d6b10d02e221696b240caf0b5d0f5a588ec" + integrity sha512-LtD8EaNYCaBRzHzaAiIPrfcL3DdIysc81dkGlQvv7WQP3+YXV7b0JJTtR1U3bzeRieS603KF4wUo+ZkJVenh8w== + dependencies: + "@types/d3-path" "*" + "@types/debug@^4.1.5": version "4.1.5" resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.5.tgz#b14efa8852b7768d898906613c23f688713e02cd" @@ -1651,6 +1749,13 @@ resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.0.tgz#69a23a3ad29caf0097f06eda59b361ee2f0639f6" integrity sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY= +"@types/mysql@^2.15.15": + version "2.15.15" + resolved "https://registry.yarnpkg.com/@types/mysql/-/mysql-2.15.15.tgz#af2223d2841091a5a819eabee6dff19567f1cf1f" + integrity sha512-1GJnq7RwuFPRicMHdT53vza5v39nep9OKIbozxNUpFXP04CydcdWrqpZQ+MlVdlLFCisWnnt09xughajjWpFsw== + dependencies: + "@types/node" "*" + "@types/node@*": version "14.6.0" resolved "https://registry.yarnpkg.com/@types/node/-/node-14.6.0.tgz#7d4411bf5157339337d7cff864d9ff45f177b499" @@ -1732,7 +1837,14 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@^16.9.44": +"@types/react-transition-group@^4.2.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.0.tgz#882839db465df1320e4753e6e9f70ca7e9b4d46d" + integrity sha512-/QfLHGpu+2fQOqQaXh8MG9q03bFENooTb/it4jr5kKaZlDQfWvjqWZg48AwzPVMBHlRuTRAY7hRHCEOXz5kV6w== + dependencies: + "@types/react" "*" + +"@types/react@*": version "16.9.47" resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.47.tgz#fb092936f0b56425f874d0ff1b08051fdf70c1ba" integrity sha512-dAJO4VbrjYqTUwFiQqAKjLyHHl4RSTNnRyPdX3p16MPbDKvow51wxATUPxoe2QsiXNMEYrOjc2S6s92VjG+1VQ== @@ -1740,6 +1852,22 @@ "@types/prop-types" "*" csstype "^3.0.2" +"@types/react@^16.9.49": + version "16.9.49" + resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.49.tgz#09db021cf8089aba0cdb12a49f8021a69cce4872" + integrity sha512-DtLFjSj0OYAdVLBbyjhuV9CdGVHCkHn2R+xr3XkBvK2rS1Y1tkc14XSGjYgm5Fjjr90AxH9tiSzc1pCFMGO06g== + dependencies: + "@types/prop-types" "*" + csstype "^3.0.2" + +"@types/recharts@^1.8.15": + version "1.8.15" + resolved "https://registry.yarnpkg.com/@types/recharts/-/recharts-1.8.15.tgz#02bc06085c9a31a58c00194d15377b45cf506bbf" + integrity sha512-ApCfDT/R8RCbZTcl0iqLiKsxVdzE3GzoawTgJUHuQOz6ZXhsPVfp7CNSKI2s3zFwrRRsgmpv2AEcbcZceNHg4w== + dependencies: + "@types/d3-shape" "*" + "@types/react" "*" + "@types/redux-logger@^3.0.8": version "3.0.8" resolved "https://registry.yarnpkg.com/@types/redux-logger/-/redux-logger-3.0.8.tgz#1fb6d26917bb198792bb1cf57feb31cae1532c5d" @@ -1779,6 +1907,13 @@ resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.3.tgz#9c088679876f374eb5983f150d4787aa6fb32d7e" integrity sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ== +"@types/use-deep-compare-effect@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@types/use-deep-compare-effect/-/use-deep-compare-effect-1.2.0.tgz#d55d9bda6fea5ff7c93038c53052db1b3145f5d9" + integrity sha512-2uNqaSobMvUTGR7G72tUHDX+Kx341q25OuM0m2B6VID7eljIvYuDaFTKfmDnbvej67yEhCc35zA6dmIYriwOXA== + dependencies: + "@types/react" "*" + "@types/webpack-env@^1.15.2": version "1.15.2" resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.15.2.tgz#927997342bb9f4a5185a86e6579a0a18afc33b0a" @@ -1817,12 +1952,13 @@ dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@^3.6.1": - version "3.10.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.10.1.tgz#7e061338a1383f59edc204c605899f93dc2e2c8f" - integrity sha512-PQg0emRtzZFWq6PxBcdxRH3QIQiyFO3WCVpRL3fgj5oQS3CDs3AeAKfv4DxNhzn8ITdNJGJ4D3Qw8eAJf3lXeQ== +"@typescript-eslint/eslint-plugin@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.0.1.tgz#88bde9239e29d688315718552cf80a3490491017" + integrity sha512-pQZtXupCn11O4AwpYVUX4PDFfmIJl90ZgrEBg0CEcqlwvPiG0uY81fimr1oMFblZnpKAq6prrT9a59pj1x58rw== dependencies: - "@typescript-eslint/experimental-utils" "3.10.1" + "@typescript-eslint/experimental-utils" "4.0.1" + "@typescript-eslint/scope-manager" "4.0.1" debug "^4.1.1" functional-red-black-tree "^1.0.1" regexpp "^3.0.0" @@ -1840,6 +1976,18 @@ eslint-scope "^5.0.0" eslint-utils "^2.0.0" +"@typescript-eslint/experimental-utils@4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.0.1.tgz#7d9a3ab6821ad5274dad2186c1aa0d93afd696eb" + integrity sha512-gAqOjLiHoED79iYTt3F4uSHrYmg/GPz/zGezdB0jAdr6S6gwNiR/j7cTZ8nREKVzMVKLd9G3xbg1sV9GClW3sw== + dependencies: + "@types/json-schema" "^7.0.3" + "@typescript-eslint/scope-manager" "4.0.1" + "@typescript-eslint/types" "4.0.1" + "@typescript-eslint/typescript-estree" "4.0.1" + eslint-scope "^5.0.0" + eslint-utils "^2.0.0" + "@typescript-eslint/experimental-utils@^2.5.0": version "2.34.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.34.0.tgz#d3524b644cdb40eebceca67f8cf3e4cc9c8f980f" @@ -1861,11 +2009,34 @@ "@typescript-eslint/typescript-estree" "3.10.1" eslint-visitor-keys "^1.1.0" +"@typescript-eslint/parser@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.0.1.tgz#73772080db7a7a4534a35d719e006f503e664dc3" + integrity sha512-1+qLmXHNAWSQ7RB6fdSQszAiA7JTwzakj5cNYjBTUmpH2cqilxMZEIV+DRKjVZs8NzP3ALmKexB0w/ExjcK9Iw== + dependencies: + "@typescript-eslint/scope-manager" "4.0.1" + "@typescript-eslint/types" "4.0.1" + "@typescript-eslint/typescript-estree" "4.0.1" + debug "^4.1.1" + +"@typescript-eslint/scope-manager@4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.0.1.tgz#24d93c3000bdfcc5a157dc4d32b742405a8631b5" + integrity sha512-u3YEXVJ8jsj7QCJk3om0Y457fy2euEOkkzxIB/LKU3MdyI+FJ2gI0M4aKEaXzwCSfNDiZ13a3lDo5DVozc+XLQ== + dependencies: + "@typescript-eslint/types" "4.0.1" + "@typescript-eslint/visitor-keys" "4.0.1" + "@typescript-eslint/types@3.10.1": version "3.10.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-3.10.1.tgz#1d7463fa7c32d8a23ab508a803ca2fe26e758727" integrity sha512-+3+FCUJIahE9q0lDi1WleYzjCwJs5hIsbugIgnbB+dSCYUxl8L6PwmsyOPFZde2hc1DlTo/xnkOgiTLSyAbHiQ== +"@typescript-eslint/types@4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.0.1.tgz#1cf72582f764931f085cb8230ff215980fe467b2" + integrity sha512-S+gD3fgbkZYW2rnbjugNMqibm9HpEjqZBZkTiI3PwbbNGWmAcxolWIUwZ0SKeG4Dy2ktpKKaI/6+HGYVH8Qrlg== + "@typescript-eslint/typescript-estree@2.34.0": version "2.34.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.34.0.tgz#14aeb6353b39ef0732cc7f1b8285294937cf37d5" @@ -1893,6 +2064,20 @@ semver "^7.3.2" tsutils "^3.17.1" +"@typescript-eslint/typescript-estree@4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.0.1.tgz#29a43c7060641ec51c902d9f50ac7c5866ec479f" + integrity sha512-zGzleORFXrRWRJAMLTB2iJD1IZbCPkg4hsI8mGdpYlKaqzvKYSEWVAYh14eauaR+qIoZVWrXgYSXqLtTlxotiw== + dependencies: + "@typescript-eslint/types" "4.0.1" + "@typescript-eslint/visitor-keys" "4.0.1" + debug "^4.1.1" + globby "^11.0.1" + is-glob "^4.0.1" + lodash "^4.17.15" + semver "^7.3.2" + tsutils "^3.17.1" + "@typescript-eslint/visitor-keys@3.10.1": version "3.10.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-3.10.1.tgz#cd4274773e3eb63b2e870ac602274487ecd1e931" @@ -1900,6 +2085,14 @@ dependencies: eslint-visitor-keys "^1.1.0" +"@typescript-eslint/visitor-keys@4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.0.1.tgz#d4e8de62775f2a6db71c7e8539633680039fdd6c" + integrity sha512-yBSqd6FjnTzbg5RUy9J+9kJEyQjTI34JdGMJz+9ttlJzLCnGkBikxw+N5n2VDcc3CesbIEJ0MnZc5uRYnrEnCw== + dependencies: + "@typescript-eslint/types" "4.0.1" + eslint-visitor-keys "^2.0.0" + "@webassemblyjs/ast@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964" @@ -2108,6 +2301,11 @@ acorn@^7.1.1, acorn@^7.4.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.0.tgz#e1ad486e6c54501634c6c397c5c121daa383607c" integrity sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w== +add@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/add/-/add-2.0.6.tgz#248f0a9f6e5a528ef2295dbeec30532130ae2235" + integrity sha1-JI8Kn25aUo7yKV2+7DBTITCuIjU= + address@^1.0.1: version "1.1.2" resolved "https://registry.yarnpkg.com/address/-/address-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6" @@ -2146,7 +2344,7 @@ ajv-keywords@^3.1.0, ajv-keywords@^3.4.1: resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== -ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.0, ajv@^6.12.2, ajv@^6.12.3: +ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.0, ajv@^6.12.2, ajv@^6.12.3, ajv@^6.12.4: version "6.12.4" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.4.tgz#0614facc4522127fa713445c6bfd3ebd376e2234" integrity sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ== @@ -2302,6 +2500,11 @@ are-we-there-yet@~1.1.2: delegates "^1.0.0" readable-stream "^2.0.6" +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -2438,7 +2641,7 @@ asn1.js@^5.2.0: minimalistic-assert "^1.0.0" safer-buffer "^2.1.0" -asn1@~0.2.0, asn1@~0.2.3: +asn1@~0.2.3: version "0.2.4" resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== @@ -3389,6 +3592,11 @@ bail@^1.0.0: resolved "https://registry.yarnpkg.com/bail/-/bail-1.0.5.tgz#b6fa133404a392cbc1f8c4bf63f5953351e7a776" integrity sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ== +balanced-match@^0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" + integrity sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg= + balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" @@ -3417,7 +3625,7 @@ batch@0.6.1: resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" integrity sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY= -bcrypt-pbkdf@^1.0.0, bcrypt-pbkdf@^1.0.2: +bcrypt-pbkdf@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= @@ -4132,6 +4340,11 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" +classnames@^2.2.5: + version "2.2.6" + resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce" + integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q== + clean-stack@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" @@ -4215,6 +4428,11 @@ clone@^1.0.2: resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= +clsx@^1.0.4: + version "1.1.1" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188" + integrity sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA== + co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" @@ -4520,7 +4738,7 @@ core-js@^1.0.0: resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" integrity sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY= -core-js@^2.4.0, core-js@^2.5.0: +core-js@^2.4.0, core-js@^2.5.0, core-js@^2.6.10: version "2.6.11" resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.11.tgz#38831469f9922bded8ee21c9dc46985e0399308c" integrity sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg== @@ -4730,6 +4948,14 @@ css-tree@1.0.0-alpha.39: mdn-data "2.0.6" source-map "^0.6.1" +css-vendor@^2.0.8: + version "2.0.8" + resolved "https://registry.yarnpkg.com/css-vendor/-/css-vendor-2.0.8.tgz#e47f91d3bd3117d49180a3c935e62e3d9f7f449d" + integrity sha512-x9Aq0XTInxrkuFeHKbYC7zWY8ai7qJ04Kxd9MnvbC1uO5DagxoHQjm4JvG+vCdXOoFtCjbL2XSZfxmoYa9uQVQ== + dependencies: + "@babel/runtime" "^7.8.3" + is-in-browser "^1.0.2" + css-what@2.1: version "2.1.3" resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2" @@ -4847,6 +5073,11 @@ cssstyle@^2.2.0: dependencies: cssom "~0.3.6" +csstype@^2.5.2: + version "2.6.13" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.13.tgz#a6893015b90e84dd6e85d0e3b442a1e84f2dbe0f" + integrity sha512-ul26pfSQTZW8dcOnD2iiJssfXw0gdNVX9IJDH/X3K5DGPfj+fUYe3kB+swUY6BF3oZDxaID3AJt+9/ojSAE05A== + csstype@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.3.tgz#2b410bbeba38ba9633353aff34b05d9755d065f8" @@ -4862,6 +5093,69 @@ cyclist@^1.0.1: resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk= +d3-array@^1.2.0: + version "1.2.4" + resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-1.2.4.tgz#635ce4d5eea759f6f605863dbcfc30edc737f71f" + integrity sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw== + +d3-collection@1: + version "1.0.7" + resolved "https://registry.yarnpkg.com/d3-collection/-/d3-collection-1.0.7.tgz#349bd2aa9977db071091c13144d5e4f16b5b310e" + integrity sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A== + +d3-color@1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-1.4.1.tgz#c52002bf8846ada4424d55d97982fef26eb3bc8a" + integrity sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q== + +d3-format@1: + version "1.4.5" + resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-1.4.5.tgz#374f2ba1320e3717eb74a9356c67daee17a7edb4" + integrity sha512-J0piedu6Z8iB6TbIGfZgDzfXxUFN3qQRMofy2oPdXzQibYGqPB/9iMcxr/TGalU+2RsyDO+U4f33id8tbnSRMQ== + +d3-interpolate@1, d3-interpolate@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-1.4.0.tgz#526e79e2d80daa383f9e0c1c1c7dcc0f0583e987" + integrity sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA== + dependencies: + d3-color "1" + +d3-path@1: + version "1.0.9" + resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-1.0.9.tgz#48c050bb1fe8c262493a8caf5524e3e9591701cf" + integrity sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg== + +d3-scale@^2.1.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-2.2.2.tgz#4e880e0b2745acaaddd3ede26a9e908a9e17b81f" + integrity sha512-LbeEvGgIb8UMcAa0EATLNX0lelKWGYDQiPdHj+gLblGVhGLyNbaCn3EvrJf0A3Y/uOOU5aD6MTh5ZFCdEwGiCw== + dependencies: + d3-array "^1.2.0" + d3-collection "1" + d3-format "1" + d3-interpolate "1" + d3-time "1" + d3-time-format "2" + +d3-shape@^1.2.0: + version "1.3.7" + resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-1.3.7.tgz#df63801be07bc986bc54f63789b4fe502992b5d7" + integrity sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw== + dependencies: + d3-path "1" + +d3-time-format@2: + version "2.3.0" + resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-2.3.0.tgz#107bdc028667788a8924ba040faf1fbccd5a7850" + integrity sha512-guv6b2H37s2Uq/GefleCDtbe0XZAuy7Wa49VGkPVPMfLL9qObgBST3lEHJBMUp8S7NdLQAGIvr2KXk8Hc98iKQ== + dependencies: + d3-time "1" + +d3-time@1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-1.1.0.tgz#b1e19d307dae9c900b7e5b25ffc5dcc249a8a0f1" + integrity sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA== + damerau-levenshtein@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.6.tgz#143c1641cb3d85c60c32329e26899adea8701791" @@ -4922,6 +5216,11 @@ decamelize@^1.1.0, decamelize@^1.2.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= +decimal.js-light@^2.4.1: + version "2.5.0" + resolved "https://registry.yarnpkg.com/decimal.js-light/-/decimal.js-light-2.5.0.tgz#ca7faf504c799326df94b0ab920424fdfc125348" + integrity sha512-b3VJCbd2hwUpeRGG3Toob+CRo8W22xplipNhP3tN7TSVB/cyMX71P1vM2Xjc9H74uV6dS2hDDmo/rHq8L87Upg== + decimal.js@^10.2.0: version "10.2.0" resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.2.0.tgz#39466113a9e036111d02f82489b5fd6b0b5ed231" @@ -5096,6 +5395,11 @@ depd@~1.1.2: resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= +dequal@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.2.tgz#85ca22025e3a87e65ef75a7a437b35284a7e319d" + integrity sha512-q9K8BlJVxK7hQYqa6XISGmBZbtQQWVXSrRrWreHC94rMt1QL/Impruc+7p2CYSYuVIUr+YCt6hjrs1kkdJRTug== + des.js@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843" @@ -5159,7 +5463,7 @@ diff-sequences@^26.3.0: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.3.0.tgz#62a59b1b29ab7fd27cef2a33ae52abe73042d0a2" integrity sha512-5j5vdRcw3CNctePNYN0Wy2e/JbWT6cAYnXv5OuqPhDpyCGc0uLu2TK0zOCJWNB9kOIfYMSpIulRaDgIi4HJ6Ig== -diff@^4.0.2: +diff@^4.0.1, diff@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== @@ -5246,6 +5550,21 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" +dom-helpers@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.4.0.tgz#e9b369700f959f62ecde5a6babde4bccd9169af8" + integrity sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA== + dependencies: + "@babel/runtime" "^7.1.2" + +dom-helpers@^5.0.1: + version "5.2.0" + resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.0.tgz#57fd054c5f8f34c52a3eeffdb7e7e93cd357d95b" + integrity sha512-Ru5o9+V8CpunKnz5LGgWXkmrH/20cGKwcHwS4m73zIvs54CN9epEmT/HLqFJW3kXpakAFkEdzgy1hzlJe3E4OQ== + dependencies: + "@babel/runtime" "^7.8.7" + csstype "^3.0.2" + dom-serializer@0: version "0.2.2" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51" @@ -5456,6 +5775,11 @@ electron-publish@22.8.0: lazy-val "^1.0.4" mime "^2.4.6" +electron-react-devtools@^0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/electron-react-devtools/-/electron-react-devtools-0.5.3.tgz#c74edb1245dc1cfe1380b93016cd4eb588ed00b7" + integrity sha1-x07bEkXcHP4TgLkwFs1OtYjtALc= + electron-rebuild@^1.10.0: version "1.11.0" resolved "https://registry.yarnpkg.com/electron-rebuild/-/electron-rebuild-1.11.0.tgz#e384773a9ad30fe0a6a5bbb326b779d51f668b6a" @@ -5991,12 +6315,18 @@ eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3 resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== -eslint@^7.5.0: - version "7.7.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.7.0.tgz#18beba51411927c4b64da0a8ceadefe4030d6073" - integrity sha512-1KUxLzos0ZVsyL81PnRN335nDtQ8/vZUD6uMtWbF+5zDtjKcsklIi78XoE0MVL93QvWTu+E5y44VyyCsOMBrIg== +eslint-visitor-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8" + integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ== + +eslint@^7.8.1: + version "7.8.1" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.8.1.tgz#e59de3573fb6a5be8ff526c791571646d124a8fa" + integrity sha512-/2rX2pfhyUG0y+A123d0ccXtMm7DV7sH1m3lk9nk2DZ2LReq39FXHueR9xZwshE5MdfSf0xunSaMWRqyIA6M1w== dependencies: "@babel/code-frame" "^7.0.0" + "@eslint/eslintrc" "^0.1.3" ajv "^6.10.0" chalk "^4.0.0" cross-spawn "^7.0.2" @@ -6006,7 +6336,7 @@ eslint@^7.5.0: eslint-scope "^5.1.0" eslint-utils "^2.1.0" eslint-visitor-keys "^1.3.0" - espree "^7.2.0" + espree "^7.3.0" esquery "^1.2.0" esutils "^2.0.2" file-entry-cache "^5.0.1" @@ -6040,7 +6370,7 @@ esotope-hammerhead@0.5.5: dependencies: "@types/estree" "^0.0.39" -espree@^7.2.0: +espree@^7.3.0: version "7.3.0" resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.0.tgz#dc30437cf67947cf576121ebd780f15eeac72348" integrity sha512-dksIWsvKCixn1yrEXO8UosNSxaDoSYpq9reEjZSbHLpT5hpaCAKTLBwq0RHtLrIr+c0ByiYzWT8KTMRzoRCNlw== @@ -6953,6 +7283,11 @@ gonzales-pe@^4.3.0: dependencies: minimist "^1.2.5" +google-protobuf@3.11.4: + version "3.11.4" + resolved "https://registry.yarnpkg.com/google-protobuf/-/google-protobuf-3.11.4.tgz#598ca405a3cfa917a2132994d008b5932ef42014" + integrity sha512-lL6b04rDirurUBOgsY2+LalI6Evq8eH5TcNzi7TYQ3BsIWelT0KSOQSBsXuavEkNf+odQU6c0lgz3UsZXeNX9Q== + got@^9.6.0: version "9.6.0" resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" @@ -7150,7 +7485,7 @@ hmac-drbg@^1.0.0: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" -hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0: +hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== @@ -7356,6 +7691,11 @@ husky@^4.2.5: slash "^3.0.0" which-pm-runs "^1.0.0" +hyphenate-style-name@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz#691879af8e220aea5750e8827db4ef62a54e361d" + integrity sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ== + iconv-lite@0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -7549,15 +7889,6 @@ invariant@^2.2.2, invariant@^2.2.4: dependencies: loose-envify "^1.0.0" -ip-address@^5.8.8: - version "5.9.4" - resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-5.9.4.tgz#4660ac261ad61bd397a860a007f7e98e4eaee386" - integrity sha512-dHkI3/YNJq4b/qQaz+c8LuarD3pY24JqZWfjB8aZx1gtpc2MDILu9L9jpZe1sHpzo/yWFweQVn+U//FhazUxmw== - dependencies: - jsbn "1.1.0" - lodash "^4.17.15" - sprintf-js "1.1.2" - ip-regex@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" @@ -7822,6 +8153,11 @@ is-hexadecimal@^1.0.0: resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz#cc35c97588da4bd49a8eedd6bc4082d44dcb23a7" integrity sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw== +is-in-browser@^1.0.2, is-in-browser@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/is-in-browser/-/is-in-browser-1.1.3.tgz#56ff4db683a078c6082eb95dad7dc62e1d04f835" + integrity sha1-Vv9NtoOgeMYILrldrX3GLh0E+DU= + is-installed-globally@^0.3.1: version "0.3.2" resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.3.2.tgz#fd3efa79ee670d1187233182d5b0a1dd00313141" @@ -8543,11 +8879,6 @@ js-yaml@^3.13.1, js-yaml@^3.14.0: argparse "^1.0.7" esprima "^4.0.0" -jsbn@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-1.1.0.tgz#b01307cb29b618a1ed26ec79e911f803c4da0040" - integrity sha1-sBMHyym2GKHtJux56RH4A8TaAEA= - jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" @@ -8685,6 +9016,76 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.10.0" +jss-plugin-camel-case@^10.0.3: + version "10.4.0" + resolved "https://registry.yarnpkg.com/jss-plugin-camel-case/-/jss-plugin-camel-case-10.4.0.tgz#46c75ff7fd61c304984c21af5817823f0f501ceb" + integrity sha512-9oDjsQ/AgdBbMyRjc06Kl3P8lDCSEts2vYZiPZfGAxbGCegqE4RnMob3mDaBby5H9vL9gWmyyImhLRWqIkRUCw== + dependencies: + "@babel/runtime" "^7.3.1" + hyphenate-style-name "^1.0.3" + jss "10.4.0" + +jss-plugin-default-unit@^10.0.3: + version "10.4.0" + resolved "https://registry.yarnpkg.com/jss-plugin-default-unit/-/jss-plugin-default-unit-10.4.0.tgz#2b10f01269eaea7f36f0f5fd1cfbfcc76ed42854" + integrity sha512-BYJ+Y3RUYiMEgmlcYMLqwbA49DcSWsGgHpVmEEllTC8MK5iJ7++pT9TnKkKBnNZZxTV75ycyFCR5xeLSOzVm4A== + dependencies: + "@babel/runtime" "^7.3.1" + jss "10.4.0" + +jss-plugin-global@^10.0.3: + version "10.4.0" + resolved "https://registry.yarnpkg.com/jss-plugin-global/-/jss-plugin-global-10.4.0.tgz#19449425a94e4e74e113139b629fd44d3577f97d" + integrity sha512-b8IHMJUmv29cidt3nI4bUI1+Mo5RZE37kqthaFpmxf5K7r2aAegGliAw4hXvA70ca6ckAoXMUl4SN/zxiRcRag== + dependencies: + "@babel/runtime" "^7.3.1" + jss "10.4.0" + +jss-plugin-nested@^10.0.3: + version "10.4.0" + resolved "https://registry.yarnpkg.com/jss-plugin-nested/-/jss-plugin-nested-10.4.0.tgz#017d0c02c0b6b454fd9d7d3fc33470a15eea9fd1" + integrity sha512-cKgpeHIxAP0ygeWh+drpLbrxFiak6zzJ2toVRi/NmHbpkNaLjTLgePmOz5+67ln3qzJiPdXXJB1tbOyYKAP4Pw== + dependencies: + "@babel/runtime" "^7.3.1" + jss "10.4.0" + tiny-warning "^1.0.2" + +jss-plugin-props-sort@^10.0.3: + version "10.4.0" + resolved "https://registry.yarnpkg.com/jss-plugin-props-sort/-/jss-plugin-props-sort-10.4.0.tgz#7110bf0b6049cc2080b220b506532bf0b70c0e07" + integrity sha512-j/t0R40/2fp+Nzt6GgHeUFnHVY2kPGF5drUVlgkcwYoHCgtBDOhTTsOfdaQFW6sHWfoQYgnGV4CXdjlPiRrzwA== + dependencies: + "@babel/runtime" "^7.3.1" + jss "10.4.0" + +jss-plugin-rule-value-function@^10.0.3: + version "10.4.0" + resolved "https://registry.yarnpkg.com/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.4.0.tgz#7cff4a91e84973536fa49b6ebbdbf7f339b01c82" + integrity sha512-w8504Cdfu66+0SJoLkr6GUQlEb8keHg8ymtJXdVHWh0YvFxDG2l/nS93SI5Gfx0fV29dO6yUugXnKzDFJxrdFQ== + dependencies: + "@babel/runtime" "^7.3.1" + jss "10.4.0" + tiny-warning "^1.0.2" + +jss-plugin-vendor-prefixer@^10.0.3: + version "10.4.0" + resolved "https://registry.yarnpkg.com/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.4.0.tgz#2a78f3c5d57d1e024fe7ad7c41de34d04e72ecc0" + integrity sha512-DpF+/a+GU8hMh/948sBGnKSNfKkoHg2p9aRFUmyoyxgKjOeH9n74Ht3Yt8lOgdZsuWNJbPrvaa3U4PXKwxVpTQ== + dependencies: + "@babel/runtime" "^7.3.1" + css-vendor "^2.0.8" + jss "10.4.0" + +jss@10.4.0, jss@^10.0.3: + version "10.4.0" + resolved "https://registry.yarnpkg.com/jss/-/jss-10.4.0.tgz#473a6fbe42e85441020a07e9519dac1e8a2e79ca" + integrity sha512-l7EwdwhsDishXzqTc3lbsbyZ83tlUl5L/Hb16pHCvZliA9lRDdNBZmHzeJHP0sxqD0t1mrMmMR8XroR12JBYzw== + dependencies: + "@babel/runtime" "^7.3.1" + csstype "^3.0.2" + is-in-browser "^1.1.3" + tiny-warning "^1.0.2" + jsx-ast-utils@^2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.4.1.tgz#1114a4c1209481db06c690c2b4f488cc665f657e" @@ -8932,6 +9333,11 @@ lodash.assign@^4.2.0: resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7" integrity sha1-DZnzzNem0mHRm9rrkkUAXShYCOc= +lodash.debounce@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" + integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= + lodash.escape@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-4.0.1.tgz#c9044690c21e04294beaa517712fded1fa88de98" @@ -8957,12 +9363,17 @@ lodash.sortby@^4.7.0: resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= +lodash.throttle@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4" + integrity sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ= + lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= -"lodash@4.6.1 || ^4.16.1", lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.4, lodash@^4.17.5: +"lodash@4.6.1 || ^4.16.1", lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.4, lodash@^4.17.5, lodash@~4.17.4: version "4.17.20" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== @@ -9062,6 +9473,11 @@ make-dir@^3.0.0, make-dir@^3.0.2: dependencies: semver "^6.0.0" +make-error@^1.1.1: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + makeerror@1.0.x: version "1.0.11" resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c" @@ -9122,6 +9538,11 @@ matcher@^3.0.0: dependencies: escape-string-regexp "^4.0.0" +math-expression-evaluator@^1.2.14: + version "1.2.22" + resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.22.tgz#c14dcb3d8b4d150e5dcea9c68c8dad80309b0d5e" + integrity sha512-L0j0tFVZBQQLeEjmWOvDLoRciIY8gQGWahvkztXUal8jH8R5Rlqo9GCvgqvXcy9LQhEWdQCVvzqAbxgYNt4blQ== + mathml-tag-names@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz#4ddadd67308e780cf16a47685878ee27b736a0a3" @@ -10247,6 +10668,11 @@ parseurl@~1.3.2, parseurl@~1.3.3: resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== +parsimmon@1.6.2: + version "1.6.2" + resolved "https://registry.yarnpkg.com/parsimmon/-/parsimmon-1.6.2.tgz#28fbe6b8caa38ab89f52b77f8c7743563c7d9aff" + integrity sha512-bJNB0ZQhHyM5KqO2Z5ttQAVn/PZ2pccxaOnMcZ0Su7HA1Iv4GQTfUmzSZ6N3jcsCn9F68PZcypAvF3nDRRL+3g== + pascalcase@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" @@ -10438,6 +10864,11 @@ pngjs@^3.3.1: resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.4.0.tgz#99ca7d725965fb655814eaf65f38f12bbdbf555f" integrity sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w== +popper.js@1.16.1-lts: + version "1.16.1-lts" + resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.16.1-lts.tgz#cf6847b807da3799d80ee3d6d2f90df8a3f50b05" + integrity sha512-Kjw8nKRl1m+VrSFCoVGPph93W/qrSO7ZkqPpTf7F4bk/sqcfWK019dWBUpE/fBOsOQY1dks/Bmcbfn1heM/IsA== + portfinder@^1.0.26: version "1.0.28" resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.28.tgz#67c4622852bd5374dd1dd900f779f53462fac778" @@ -10857,7 +11288,7 @@ prettier-linter-helpers@^1.0.0: dependencies: fast-diff "^1.1.2" -prettier@^2.0.5: +prettier@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.1.1.tgz#d9485dd5e499daa6cb547023b87a6cf51bee37d6" integrity sha512-9bY+5ZWCfqj3ghYBLxApy2zf6m+NJo5GzmLTpr9FsApsfjriNnS2dahWReHMi7qNPhhHl9SYHJs2cHZLgexNIw== @@ -10931,7 +11362,7 @@ prop-types-exact@^1.2.0: object.assign "^4.1.0" reflect.ownkeys "^0.2.0" -prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2: +prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2: version "15.7.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== @@ -11079,7 +11510,7 @@ quick-lru@^4.0.1: resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== -raf@^3.4.1: +raf@^3.4.0, raf@^3.4.1: version "3.4.1" resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39" integrity sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA== @@ -11163,7 +11594,7 @@ react-hot-loader@^4.12.21: shallowequal "^1.1.0" source-map "^0.7.3" -react-is@^16.12.0, react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.6, react-is@^16.9.0: +react-is@^16.12.0, react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.0, react-is@^16.8.1, react-is@^16.8.6, react-is@^16.9.0: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== @@ -11184,6 +11615,16 @@ react-redux@^7.2.0: prop-types "^15.7.2" react-is "^16.9.0" +react-resize-detector@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/react-resize-detector/-/react-resize-detector-2.3.0.tgz#57bad1ae26a28a62a2ddb678ba6ffdf8fa2b599c" + integrity sha512-oCAddEWWeFWYH5FAcHdBYcZjAw9fMzRUK9sWSx6WvSSOPVRxcHd5zTIGy/mOus+AhN/u6T4TMiWxvq79PywnJQ== + dependencies: + lodash.debounce "^4.0.8" + lodash.throttle "^4.1.1" + prop-types "^15.6.0" + resize-observer-polyfill "^1.5.0" + react-router-dom@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.2.0.tgz#9e65a4d0c45e13289e66c7b17c7e175d0ea15662" @@ -11213,6 +11654,16 @@ react-router@5.2.0: tiny-invariant "^1.0.2" tiny-warning "^1.0.0" +react-smooth@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/react-smooth/-/react-smooth-1.0.5.tgz#94ae161d7951cdd893ccb7099d031d342cb762ad" + integrity sha512-eW057HT0lFgCKh8ilr0y2JaH2YbNcuEdFpxyg7Gf/qDKk9hqGMyXryZJ8iMGJEuKH0+wxS0ccSsBBB3W8yCn8w== + dependencies: + lodash "~4.17.4" + prop-types "^15.6.0" + raf "^3.4.0" + react-transition-group "^2.5.0" + react-test-renderer@^16.0.0-0, react-test-renderer@^16.12.0: version "16.13.1" resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.13.1.tgz#de25ea358d9012606de51e012d9742e7f0deabc1" @@ -11223,6 +11674,35 @@ react-test-renderer@^16.0.0-0, react-test-renderer@^16.12.0: react-is "^16.8.6" scheduler "^0.19.1" +react-textarea-autosize@^8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/react-textarea-autosize/-/react-textarea-autosize-8.2.0.tgz#fae38653f5ec172a855fd5fffb39e466d56aebdb" + integrity sha512-grajUlVbkx6VdtSxCgzloUIphIZF5bKr21OYMceWPKkniy7H0mRAT/AXPrRtObAe+zUePnNlBwUc4ivVjUGIjw== + dependencies: + "@babel/runtime" "^7.10.2" + use-composed-ref "^1.0.0" + use-latest "^1.0.0" + +react-transition-group@^2.5.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.9.0.tgz#df9cdb025796211151a436c69a8f3b97b5b07c8d" + integrity sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg== + dependencies: + dom-helpers "^3.4.0" + loose-envify "^1.4.0" + prop-types "^15.6.2" + react-lifecycles-compat "^3.0.4" + +react-transition-group@^4.4.0: + version "4.4.1" + resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.1.tgz#63868f9325a38ea5ee9535d828327f85773345c9" + integrity sha512-Djqr7OQ2aPUiYurhPalTrVy9ddmFCCzwhqQmtN+J3+3DzLO209Fdr70QrN8Z3DsglWql6iY1lDWAfpFiBtuKGw== + dependencies: + "@babel/runtime" "^7.5.5" + dom-helpers "^5.0.1" + loose-envify "^1.4.0" + prop-types "^15.6.2" + react@^16.13.1: version "16.13.1" resolved "https://registry.yarnpkg.com/react/-/react-16.13.1.tgz#2e818822f1a9743122c063d6410d85c1e3afe48e" @@ -11333,6 +11813,30 @@ readdirp@~3.4.0: dependencies: picomatch "^2.2.1" +recharts-scale@^0.4.2: + version "0.4.3" + resolved "https://registry.yarnpkg.com/recharts-scale/-/recharts-scale-0.4.3.tgz#040b4f638ed687a530357292ecac880578384b59" + integrity sha512-t8p5sccG9Blm7c1JQK/ak9O8o95WGhNXD7TXg/BW5bYbVlr6eCeRBNpgyigD4p6pSSMehC5nSvBUPj6F68rbFA== + dependencies: + decimal.js-light "^2.4.1" + +recharts@^1.8.5: + version "1.8.5" + resolved "https://registry.yarnpkg.com/recharts/-/recharts-1.8.5.tgz#ca94a3395550946334a802e35004ceb2583fdb12" + integrity sha512-tM9mprJbXVEBxjM7zHsIy6Cc41oO/pVYqyAsOHLxlJrbNBuLs0PHB3iys2M+RqCF0//k8nJtZF6X6swSkWY3tg== + dependencies: + classnames "^2.2.5" + core-js "^2.6.10" + d3-interpolate "^1.3.0" + d3-scale "^2.1.0" + d3-shape "^1.2.0" + lodash "^4.17.5" + prop-types "^15.6.0" + react-resize-detector "^2.3.0" + react-smooth "^1.0.5" + recharts-scale "^0.4.2" + reduce-css-calc "^1.3.0" + redent@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" @@ -11341,6 +11845,22 @@ redent@^3.0.0: indent-string "^4.0.0" strip-indent "^3.0.0" +reduce-css-calc@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz#747c914e049614a4c9cfbba629871ad1d2927716" + integrity sha1-dHyRTgSWFKTJz7umKYca0dKSdxY= + dependencies: + balanced-match "^0.4.2" + math-expression-evaluator "^1.2.14" + reduce-function-call "^1.0.1" + +reduce-function-call@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/reduce-function-call/-/reduce-function-call-1.0.3.tgz#60350f7fb252c0a67eb10fd4694d16909971300f" + integrity sha512-Hl/tuV2VDgWgCSEeWMLwxLZqX7OK59eU1guxXsRKTAyeYimivsKdtcV4fu3r710tpG5GmDKDhQ0HSZLExnNmyQ== + dependencies: + balanced-match "^1.0.0" + redux-logger@^3.0.6: version "3.0.6" resolved "https://registry.yarnpkg.com/redux-logger/-/redux-logger-3.0.6.tgz#f7555966f3098f3c88604c449cf0baf5778274bf" @@ -11636,6 +12156,11 @@ reselect@^4.0.0: resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.0.0.tgz#f2529830e5d3d0e021408b246a206ef4ea4437f7" integrity sha512-qUgANli03jjAyGlnbYVAV5vvnOmJnODyABz51RdBN7M4WaVu8mecZWgyQNkG8Yqe3KRGRt0l4K4B3XVEULC4CA== +resize-observer-polyfill@^1.5.0: + version "1.5.1" + resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" + integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== + resolve-cwd@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-1.0.0.tgz#4eaeea41ed040d1702457df64a42b2b07d246f9f" @@ -12258,7 +12783,7 @@ source-map-support@^0.4.15: dependencies: source-map "^0.5.6" -source-map-support@^0.5.16, source-map-support@^0.5.19, source-map-support@^0.5.6, source-map-support@~0.5.12: +source-map-support@^0.5.16, source-map-support@^0.5.17, source-map-support@^0.5.19, source-map-support@^0.5.6, source-map-support@~0.5.12: version "0.5.19" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== @@ -12368,7 +12893,7 @@ split-string@^3.0.1, split-string@^3.0.2: dependencies: extend-shallow "^3.0.0" -sprintf-js@1.1.2, sprintf-js@^1.1.2: +sprintf-js@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.2.tgz#da1765262bf8c0f571749f2ad6c26300207ae673" integrity sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug== @@ -12378,30 +12903,6 @@ sprintf-js@~1.0.2: resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= -ssh2-promise@^0.1.7: - version "0.1.7" - resolved "https://registry.yarnpkg.com/ssh2-promise/-/ssh2-promise-0.1.7.tgz#b5a73870a683aae1d596a75d12c48486714f97a0" - integrity sha512-7iaFJQmTKekBzM/nbWZGaC472mqmgqSkz5qAlU8Hj4QIpTHgml/HCMKUX0mc7tOrqNlgwrginURoe+OG+PxnEg== - dependencies: - "@heroku/socksv5" "^0.0.9" - ssh2 "^0.8.9" - -ssh2-streams@~0.4.10: - version "0.4.10" - resolved "https://registry.yarnpkg.com/ssh2-streams/-/ssh2-streams-0.4.10.tgz#48ef7e8a0e39d8f2921c30521d56dacb31d23a34" - integrity sha512-8pnlMjvnIZJvmTzUIIA5nT4jr2ZWNNVHwyXfMGdRJbug9TpI3kd99ffglgfSWqujVv/0gxwMsDn9j9RVst8yhQ== - dependencies: - asn1 "~0.2.0" - bcrypt-pbkdf "^1.0.2" - streamsearch "~0.1.2" - -ssh2@^0.8.9: - version "0.8.9" - resolved "https://registry.yarnpkg.com/ssh2/-/ssh2-0.8.9.tgz#54da3a6c4ba3daf0d8477a538a481326091815f3" - integrity sha512-GmoNPxWDMkVpMFa9LVVzQZHF6EW3WKmBwL+4/GeILf2hFmix5Isxm7Amamo8o7bHiU0tC+wXsGcUXOxp8ChPaw== - dependencies: - ssh2-streams "~0.4.10" - sshpk@^1.7.0: version "1.16.1" resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" @@ -12508,11 +13009,6 @@ stream-shift@^1.0.0: resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== -streamsearch@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a" - integrity sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo= - strict-uri-encode@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" @@ -12699,7 +13195,7 @@ strip-indent@^3.0.0: dependencies: min-indent "^1.0.0" -strip-json-comments@^3.1.0: +strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== @@ -13248,7 +13744,7 @@ tiny-invariant@^1.0.2: resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.1.0.tgz#634c5f8efdc27714b7f386c35e6760991d230875" integrity sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw== -tiny-warning@^1.0.0, tiny-warning@^1.0.3: +tiny-warning@^1.0.0, tiny-warning@^1.0.2, tiny-warning@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== @@ -13410,6 +13906,22 @@ tryer@^1.0.1: resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8" integrity sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA== +ts-essentials@^2.0.3: + version "2.0.12" + resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-2.0.12.tgz#c9303f3d74f75fa7528c3d49b80e089ab09d8745" + integrity sha512-3IVX4nI6B5cc31/GFFE+i8ey/N2eA0CZDbo6n0yrz0zDX8ZJ8djmU1p+XRz7G3is0F3bB3pu2pAroFdAWQKU3w== + +ts-node@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-9.0.0.tgz#e7699d2a110cc8c0d3b831715e417688683460b3" + integrity sha512-/TqB4SnererCDR/vb4S/QvSZvzQMJN8daAslg7MeaiHvD8rDZsSfXmNeNumyZZzMned72Xoq/isQljYSt8Ynfg== + dependencies: + arg "^4.1.0" + diff "^4.0.1" + make-error "^1.1.1" + source-map-support "^0.5.17" + yn "3.1.1" + tsconfig-paths@^3.9.0: version "3.9.0" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz#098547a6c4448807e8fcb8eae081064ee9a3c90b" @@ -13755,6 +14267,34 @@ url@^0.11.0: punycode "1.3.2" querystring "0.2.0" +use-composed-ref@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/use-composed-ref/-/use-composed-ref-1.0.0.tgz#bb13e8f4a0b873632cde4940abeb88b92d03023a" + integrity sha512-RVqY3NFNjZa0xrmK3bIMWNmQ01QjKPDc7DeWR3xa/N8aliVppuutOE5bZzPkQfvL+5NRWMMp0DJ99Trd974FIw== + dependencies: + ts-essentials "^2.0.3" + +use-deep-compare-effect@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/use-deep-compare-effect/-/use-deep-compare-effect-1.4.0.tgz#3f40e3fa5b0b8d79e4126e83be384ed602472405" + integrity sha512-46rF7ULcRnxI4+1Zoul95+KB48hpn1MUH1aXEBMyU+Sh1KJDqrTAkwhnxQL6ydBAqu3gLebYylcr0zpVBzbxxQ== + dependencies: + "@babel/runtime" "^7.11.2" + "@types/use-deep-compare-effect" "^1.2.0" + dequal "^2.0.2" + +use-isomorphic-layout-effect@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.0.0.tgz#f56b4ed633e1c21cd9fc76fe249002a1c28989fb" + integrity sha512-JMwJ7Vd86NwAt1jH7q+OIozZSIxA4ND0fx6AsOe2q1H8ooBUp5aN6DvVCqZiIaYU6JaMRJGyR0FO7EBCIsb/Rg== + +use-latest@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/use-latest/-/use-latest-1.1.0.tgz#7bf9684555869c3f5f37e10d0884c8accf4d3aa6" + integrity sha512-gF04d0ZMV3AMB8Q7HtfkAWe+oq1tFXP6dZKwBHQF5nVXtGsh2oAYeeqma5ZzxtlpOcW8Ro/tLcfmEodjDeqtuw== + dependencies: + use-isomorphic-layout-effect "^1.0.0" + use@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" @@ -14380,6 +14920,11 @@ yargs@^15.3.1: y18n "^4.0.0" yargs-parser "^18.1.2" +yarn@^1.22.5: + version "1.22.5" + resolved "https://registry.yarnpkg.com/yarn/-/yarn-1.22.5.tgz#1933b7635429ca00847222dd9d38f05646e2df23" + integrity sha512-5uzKXwdMc++mYktXqkfpNYT9tY8ViWegU58Hgbo+KXzrzzhEyP1Ip+BTtXloLrXNcNlxFJbLiFKGaS9vK9ym6Q== + yauzl@^2.10.0: version "2.10.0" resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" @@ -14387,3 +14932,8 @@ yauzl@^2.10.0: dependencies: buffer-crc32 "~0.2.3" fd-slicer "~1.1.0" + +yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==