diff options
Diffstat (limited to '')
-rw-r--r-- | .eslintrc.json | 57 | ||||
-rw-r--r-- | .github/dependabot.yml | 12 | ||||
-rw-r--r-- | .github/workflows/codeql-analysis.yml | 58 | ||||
-rw-r--r-- | .github/workflows/node.yml | 6 | ||||
-rw-r--r-- | .prettierrc.json | 3 | ||||
-rw-r--r-- | .vscode/launch.json | 32 | ||||
-rw-r--r-- | .vscode/settings.json | 20 | ||||
-rw-r--r-- | README.md | 3 | ||||
-rw-r--r-- | jest.config.js | 6 | ||||
-rw-r--r-- | package.json | 113 | ||||
-rw-r--r-- | src/index.ts | 17 | ||||
-rw-r--r-- | src/public/css/style.css | 127 | ||||
-rw-r--r-- | src/public/js/form.js | 128 | ||||
-rw-r--r-- | src/routes/api.ts | 287 | ||||
-rw-r--r-- | tsconfig.json | 186 | ||||
-rw-r--r-- | yarn.lock | 29 |
16 files changed, 555 insertions, 529 deletions
diff --git a/.eslintrc.json b/.eslintrc.json index ebb8e6d..ae3fde3 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,37 +1,24 @@ { - "env": { - "browser": true, - "es2021": true, - "node": true - }, - "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/recommended" - ], - "parser": "@typescript-eslint/parser", - "parserOptions": { - "ecmaVersion": "latest", - "sourceType": "module" - }, - "plugins": [ - "@typescript-eslint" - ], - "rules": { - "indent": [ - "error", - 4 - ], - "linebreak-style": [ - "error", - "unix" - ], - "quotes": [ - "error", - "single" - ], - "semi": [ - "error", - "always" - ] - } + "env": { + "browser": true, + "es2021": true, + "node": true + }, + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": "latest", + "sourceType": "module" + }, + "plugins": ["@typescript-eslint", "prettier"], + "rules": { + "linebreak-style": ["error", "unix"], + "quotes": ["error", "single"], + "semi": ["error", "always"], + "prettier/prettier": ["error", { "singleQuote": true }] + }, + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:prettier/recommended" + ] } diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 4df7644..a124ccb 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,12 +5,12 @@ version: 2 updates: - - package-ecosystem: "npm" # See documentation for possible values - directory: "/" # Location of package manifests + - package-ecosystem: 'npm' # See documentation for possible values + directory: '/' # Location of package manifests schedule: - interval: "daily" - - package-ecosystem: "github-actions" - directory: "/" + interval: 'daily' + - package-ecosystem: 'github-actions' + directory: '/' schedule: # Check for updates to GitHub Actions every week - interval: "weekly" + interval: 'weekly' diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 2b40e64..b8c71b2 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -9,14 +9,14 @@ # the `language` matrix defined below to confirm you have the correct set of # supported CodeQL languages. # -name: "CodeQL" +name: 'CodeQL' on: push: - branches: [ master ] + branches: [master] pull_request: # The branches below must be a subset of the branches above - branches: [ master ] + branches: [master] schedule: - cron: '23 20 * * 2' @@ -32,39 +32,39 @@ jobs: strategy: fail-fast: false matrix: - language: [ 'javascript' ] + language: ['javascript'] # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] # Learn more about CodeQL language support at https://git.io/codeql-language-support steps: - - name: Checkout repository - uses: actions/checkout@v3 + - name: Checkout repository + uses: actions/checkout@v3 - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v1 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - # queries: ./path/to/local/query, your-org/your-repo/queries@main + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v1 + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v1 - # âšī¸ Command-line programs to run using the OS shell. - # đ https://git.io/JvXDl + # âšī¸ Command-line programs to run using the OS shell. + # đ https://git.io/JvXDl - # âī¸ If the Autobuild fails above, remove it and uncomment the following three lines - # and modify them (or add more) to build your code if your project - # uses a compiled language + # âī¸ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language - #- run: | - # make bootstrap - # make release + #- run: | + # make bootstrap + # make release - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 diff --git a/.github/workflows/node.yml b/.github/workflows/node.yml index 4c48897..366fe3b 100644 --- a/.github/workflows/node.yml +++ b/.github/workflows/node.yml @@ -14,8 +14,8 @@ jobs: - name: Use Node.js uses: actions/setup-node@v3 with: - node-version: "16.x" - cache: "yarn" + node-version: '16.x' + cache: 'yarn' - name: Install dependencies run: yarn --frozen-lockfile - name: Lint @@ -32,4 +32,4 @@ jobs: steps: - uses: fastify/github-action-merge-dependabot@v3.0.2 with: - github-token: ${{ secrets.GITHUB_TOKEN }}
\ No newline at end of file + github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..544138b --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,3 @@ +{ + "singleQuote": true +} diff --git a/.vscode/launch.json b/.vscode/launch.json index 3220b3d..3ae875c 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,18 +1,16 @@ { - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "type": "node", - "request": "launch", - "name": "Launch Program", - "program": "${workspaceFolder}/src/index.ts", - "preLaunchTask": "npm: build", - "outFiles": [ - "${workspaceFolder}/dist/**/*.js" - ] - } - ] -}
\ No newline at end of file + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Launch Program", + "program": "${workspaceFolder}/src/index.ts", + "preLaunchTask": "npm: build", + "outFiles": ["${workspaceFolder}/dist/**/*.js"] + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json index 3f638b5..82ef2a7 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,13 +1,11 @@ { - "files.exclude": { - "**/*.js": { - "when": "$(basename).ts" - }, - "**/**.js": { - "when": "$(basename).tsx" - } + "files.exclude": { + "**/*.js": { + "when": "$(basename).ts" }, - "cSpell.words": [ - "fileupload" - ] -}
\ No newline at end of file + "**/**.js": { + "when": "$(basename).tsx" + } + }, + "cSpell.words": ["fileupload"] +} @@ -1,7 +1,7 @@ # Lamperski Inverted Pendulum - Web [![GitHub Actions](https://github.com/RosstheRoss/4951w-pendulum/actions/workflows/node.yml/badge.svg)](https://github.com/RosstheRoss/4951w-pendulum/actions/workflows/node.yml) -[![Known Vulnerabilities](<https://snyk.io/test/github/UMN-EE4951W-Lamperski/pendulum-web/badge.svg>)](<https://snyk.io/test/github/UMN-EE4951W-Lamperski/pendulum-web>) +[![Known Vulnerabilities](https://snyk.io/test/github/UMN-EE4951W-Lamperski/pendulum-web/badge.svg)](https://snyk.io/test/github/UMN-EE4951W-Lamperski/pendulum-web) An complete rewrite of the web application for Professor Andrew Lamperski's Remotely Accessible Inverted Pendulum, in TypeScript. @@ -16,6 +16,7 @@ An complete rewrite of the web application for Professor Andrew Lamperski's Remo ## Structure The structure of the repository is as follows: + ``` âââ src (folder) â âââ public (folder) diff --git a/jest.config.js b/jest.config.js index 6193f26..e86e13b 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,5 +1,5 @@ /** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ module.exports = { - preset: 'ts-jest', - testEnvironment: 'node', -};
\ No newline at end of file + preset: 'ts-jest', + testEnvironment: 'node', +}; diff --git a/package.json b/package.json index 74b1207..0c3368b 100644 --- a/package.json +++ b/package.json @@ -1,57 +1,60 @@ { - "dependencies": { - "cookie-parser": "^1.4.6", - "csurf": "^1.11.0", - "ejs": "^3.1.6", - "express": "^4.17.3", - "express-fileupload": "^1.3.1", - "express-rate-limit": "^6.3.0", - "helmet": "^5.0.2", - "shell-quote": "^1.7.3" - }, - "devDependencies": { - "@types/cookie-parser": "^1.4.2", - "@types/csurf": "^1.11.2", - "@types/express": "^4.17.13", - "@types/express-fileupload": "^1.2.2", - "@types/node": "^17.0.21", - "@types/shell-quote": "^1.7.1", - "@typescript-eslint/eslint-plugin": "^5.14.0", - "@typescript-eslint/parser": "^5.14.0", - "eslint": "^8.10.0", - "eslint-config-airbnb-base": "^15.0.0", - "eslint-plugin-import": "^2.25.4", - "install": "^0.13.0", - "nodemon": "^2.0.15", - "npm-run-all": "^4.1.5", - "pkg": "^5.5.2", - "typescript": "^4.6.2" - }, - "scripts": { - "build": "npm-run-all clean tsc copy-views", - "build:ci": "npm-run-all clean lint tsc", - "build:pack": "npm-run-all build pack", - "copy-views": "cp -r ./src/views ./dist/views && cp -r ./src/public ./dist/public", - "clean": "rm -rf dist", - "dev": "nodemon --watch ./src -e ts,ejs,css,js --exec yarn dev:start", - "dev:start": "npm-run-all build start", - "lint": "eslint --ext .ts,.js ./src --fix", - "lint:ci": "eslint --ext .ts,.js ./src", - "pack": "pkg . -t node16-linux -o pend-server", - "start": "node dist/index.js", - "tsc": "tsc --project ./tsconfig.json" - }, - "name": "4951w-pendulum-webapp", - "version": "0.1.0", - "description": "The webapp for Professor Lamperski's Pendulum", - "main": "dist/index.js", - "bin": "dist/index.js", - "author": "Matt Strapp <matt@mattstrapp.net>", - "repository": "https: //github.com/RosstheRoss/4951w-pendulum", - "license": "MIT", - "private": true, - "pkg": { - "scripts": "dist/**/*.js", - "assets": "dist/**/*" - } + "dependencies": { + "cookie-parser": "^1.4.6", + "csurf": "^1.11.0", + "ejs": "^3.1.6", + "express": "^4.17.3", + "express-fileupload": "^1.3.1", + "express-rate-limit": "^6.3.0", + "helmet": "^5.0.2", + "shell-quote": "^1.7.3" + }, + "devDependencies": { + "@types/cookie-parser": "^1.4.2", + "@types/csurf": "^1.11.2", + "@types/express": "^4.17.13", + "@types/express-fileupload": "^1.2.2", + "@types/node": "^17.0.21", + "@types/shell-quote": "^1.7.1", + "@typescript-eslint/eslint-plugin": "^5.14.0", + "@typescript-eslint/parser": "^5.14.0", + "eslint": "^8.10.0", + "eslint-config-airbnb-base": "^15.0.0", + "eslint-config-prettier": "^8.5.0", + "eslint-plugin-import": "^2.25.4", + "eslint-plugin-prettier": "^4.0.0", + "install": "^0.13.0", + "nodemon": "^2.0.15", + "npm-run-all": "^4.1.5", + "pkg": "^5.5.2", + "prettier": "^2.5.1", + "typescript": "^4.6.2" + }, + "scripts": { + "build": "npm-run-all clean tsc copy-views", + "build:ci": "npm-run-all clean tsc", + "build:pack": "npm-run-all build pack", + "copy-views": "cp -r ./src/views ./dist/views && cp -r ./src/public ./dist/public", + "clean": "rm -rf dist", + "dev": "nodemon --watch ./src -e ts,ejs,css,js --exec yarn dev:start", + "dev:start": "npm-run-all build start", + "lint": "eslint --ext .ts,.js ./src --fix && prettier --write ./src", + "lint:ci": "eslint --ext .ts,.js ./src && prettier ./src --check", + "pack": "pkg . -t node16-linux -o pend-server", + "start": "node dist/index.js", + "tsc": "tsc --project ./tsconfig.json" + }, + "name": "4951w-pendulum-webapp", + "version": "0.1.0", + "description": "The webapp for Professor Lamperski's Pendulum", + "main": "dist/index.js", + "bin": "dist/index.js", + "author": "Matt Strapp <matt@mattstrapp.net>", + "repository": "https: //github.com/RosstheRoss/4951w-pendulum", + "license": "MIT", + "private": true, + "pkg": { + "scripts": "dist/**/*.js", + "assets": "dist/**/*" + } } diff --git a/src/index.ts b/src/index.ts index 63c803c..def7aff 100644 --- a/src/index.ts +++ b/src/index.ts @@ -20,14 +20,13 @@ const csrf = csurf({ cookie: true }); // Rate limiting const rateLimiter = rateLimit({ - windowMs: 1 * 60 * 1000, // 1 minute - max: 40, // Limit each IP to 40 requests per `window` (here, per 1 minutes) - standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers - legacyHeaders: false, // Disable the `X-RateLimit-*` headers + windowMs: 1 * 60 * 1000, // 1 minute + max: 40, // Limit each IP to 40 requests per `window` (here, per 1 minutes) + standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers + legacyHeaders: false, // Disable the `X-RateLimit-*` headers }); app.use(rateLimiter); - // The API app.use('/api/v1/', api); @@ -40,14 +39,14 @@ app.use('/public', express.static(path.join(__dirname, 'public'))); // Set stati /* ROUTING */ app.get('/', csrf, (req: Request, res: Response) => { - res.render('index', { csrfToken: req.csrfToken() }); + res.render('index', { csrfToken: req.csrfToken() }); }); app.get('/about', csrf, (req: Request, res: Response) => { - res.render('about'); + res.render('about'); }); // Start the server const port = env.PORT || 2000; app.listen(port, () => { - console.log(`Server is listening on port ${port}`); -});
\ No newline at end of file + console.log(`Server is listening on port ${port}`); +}); diff --git a/src/public/css/style.css b/src/public/css/style.css index a106926..3165d06 100644 --- a/src/public/css/style.css +++ b/src/public/css/style.css @@ -1,126 +1,117 @@ body { - background-color: black; - color: white; - text-align: center; - vertical-align: middle; - position: relative; + background-color: black; + color: white; + text-align: center; + vertical-align: middle; + position: relative; } .header { - margin: 5px; - font-family: "Times New Roman", Times, serif; + margin: 5px; + font-family: 'Times New Roman', Times, serif; } - /* Customize HTML paragraph element margins */ p { - display: block; - margin-top: 1em; - margin-bottom: 1em; - margin-left: 15%; - margin-right: 15%; + display: block; + margin-top: 1em; + margin-bottom: 1em; + margin-left: 15%; + margin-right: 15%; } - /*-------------------------Buttons-----------------------------------------------------*/ - /* Used for id=button1 */ #actuate-but { - background-color: #0a3f73; - border: solid; - border-color: #d5d6d2; - color: white; - padding: 10px 20px; - text-align: center; - text-decoration: none; - font-family: "Constantia", sans-serif; - border-radius: 10px; - display: inline-block; - font-size: 20px; + background-color: #0a3f73; + border: solid; + border-color: #d5d6d2; + color: white; + padding: 10px 20px; + text-align: center; + text-decoration: none; + font-family: 'Constantia', sans-serif; + border-radius: 10px; + display: inline-block; + font-size: 20px; } - /* Used for id=button1 - What the button looks like when a user puts one's cursor on it */ #actuate-but:hover { - background-color: white; - color: maroon; - cursor: pointer; + background-color: white; + color: maroon; + cursor: pointer; } - a { - color: blue; - text-decoration: none; + color: blue; + text-decoration: none; } - /* Used for the about page*/ .about { - color: white; - text-align: center; - margin: 5px; - font-size: large; + color: white; + text-align: center; + margin: 5px; + font-size: large; } - #Background { - color: #ffbb00; + color: #ffbb00; } #Pendulum_info { - /* #4294cf is similar to a light blue color */ - color: #4294cf; + /* #4294cf is similar to a light blue color */ + color: #4294cf; } - /* Used for id=Start */ #Start { - font-size: 25px; - font-weight: bold; - color: #ffbb00; + font-size: 25px; + font-weight: bold; + color: #ffbb00; } - .navbar { - top: 0; - width: 100%; - list-style-type: none; - margin: 0; - padding: 0; - overflow: hidden; - background-color: #7a0019; + top: 0; + width: 100%; + list-style-type: none; + margin: 0; + padding: 0; + overflow: hidden; + background-color: #7a0019; } .navbar li { - float: left; + float: left; } .navbar li a { - display: block; - color: white; - text-align: center; - padding: 14px 16px; - font-family: "Raleway", sans-serif; - text-decoration: none; + display: block; + color: white; + text-align: center; + padding: 14px 16px; + font-family: 'Raleway', sans-serif; + text-decoration: none; } .navbar li a:hover { - background-color: #ffcc33; - color: #000000; - /* Remove underlines from links */ - text-decoration: none; + background-color: #ffcc33; + color: #000000; + /* Remove underlines from links */ + text-decoration: none; } h2 { - font-size: larger; + font-size: larger; } .error { - color: red; -}
\ No newline at end of file + color: red; +} diff --git a/src/public/js/form.js b/src/public/js/form.js index 9e926fa..1e9eaca 100644 --- a/src/public/js/form.js +++ b/src/public/js/form.js @@ -1,84 +1,80 @@ // This is split into comments so it doesn't look as gross with the closure tabs window.onload = function () { - document.getElementById('nojs').hidden = true; - document.getElementById('block').hidden = false; + document.getElementById('nojs').hidden = true; + document.getElementById('block').hidden = false; }; // File submit AJAX request // After successful upload, actuate the file by calling actuate() on the successfully uploaded file document.getElementById('upload').onsubmit = function () { - // Reset error message and success message - document.getElementById('upload-err').innerText = ''; - document.getElementById('actuate-err').innerText = ''; - document.getElementById('upload-response').innerText = ''; - document.getElementById('download-link').innerText = ''; - // Make AJAX request - let xhr = new XMLHttpRequest(); - xhr.open('POST', '/api/v1/upload'); - let formData = new FormData(this); - xhr.send(formData); - xhr.onreadystatechange = function () { - if (xhr.readyState === 4) { - let response = JSON.parse(xhr.responseText); - if (xhr.status === 201) { - // Display upload success message to the user - document.getElementById('upload-response').innerText = response.msg; - actuate(response); - } else { - // Display upload error message to the user - document.getElementById('upload-err').innerText = response.error; - // DEBUG: Print full error if unknown error occurs - if (xhr.status === 500) - console.error(response.error_msg); - } - } - }; - document.getElementById('upload').reset(); - return false; + // Reset error message and success message + document.getElementById('upload-err').innerText = ''; + document.getElementById('actuate-err').innerText = ''; + document.getElementById('upload-response').innerText = ''; + document.getElementById('download-link').innerText = ''; + // Make AJAX request + let xhr = new XMLHttpRequest(); + xhr.open('POST', '/api/v1/upload'); + let formData = new FormData(this); + xhr.send(formData); + xhr.onreadystatechange = function () { + if (xhr.readyState === 4) { + let response = JSON.parse(xhr.responseText); + if (xhr.status === 201) { + // Display upload success message to the user + document.getElementById('upload-response').innerText = response.msg; + actuate(response); + } else { + // Display upload error message to the user + document.getElementById('upload-err').innerText = response.error; + // DEBUG: Print full error if unknown error occurs + if (xhr.status === 500) console.error(response.error_msg); + } + } + }; + document.getElementById('upload').reset(); + return false; }; // Actuate button AJAX request // Should always be called after upload // Implies that upload has been successful since it relies on the upload response -// +// function actuate(file) { - let xhr = new XMLHttpRequest(); - xhr.open('POST', '/api/v1/actuate'); - let data = { - file: file.file - }; - xhr.setRequestHeader('Content-Type', 'application/json; charset=utf-8'); - xhr.setRequestHeader('X-CSRF-TOKEN', file.csrf); - xhr.send(JSON.stringify(data)); - xhr.onreadystatechange = function () { - if (xhr.readyState === 4) { - let response = JSON.parse(xhr.responseText); - if (xhr.status === 200 || xhr.status === 500) { - createDownload(response.file); - } - if (xhr.status === 500) { - document.getElementById('actuate-err').innerText = response.error; - // DEBUG: Print full error if unknown error occurs - console.error(response.error_msg); - } - - } - return; - }; + let xhr = new XMLHttpRequest(); + xhr.open('POST', '/api/v1/actuate'); + let data = { + file: file.file, + }; + xhr.setRequestHeader('Content-Type', 'application/json; charset=utf-8'); + xhr.setRequestHeader('X-CSRF-TOKEN', file.csrf); + xhr.send(JSON.stringify(data)); + xhr.onreadystatechange = function () { + if (xhr.readyState === 4) { + let response = JSON.parse(xhr.responseText); + if (xhr.status === 200 || xhr.status === 500) { + createDownload(response.file); + } + if (xhr.status === 500) { + document.getElementById('actuate-err').innerText = response.error; + // DEBUG: Print full error if unknown error occurs + console.error(response.error_msg); + } + } + return; + }; } - // Creates the download element function createDownload(response) { - if (!response) - return; - const tempName = response.filename; - const downloadName = response.name.split('.')[ 0 ]; - const downloadLink = document.createElement('a'); - downloadLink.setAttribute('href', `/api/v1/download/?filename=${tempName}`); - downloadLink.setAttribute('download', `${downloadName}.csv`); - downloadLink.innerText = 'Download CSV of results here.'; - document.getElementById('download-link').appendChild(downloadLink); - return; -}
\ No newline at end of file + if (!response) return; + const tempName = response.filename; + const downloadName = response.name.split('.')[0]; + const downloadLink = document.createElement('a'); + downloadLink.setAttribute('href', `/api/v1/download/?filename=${tempName}`); + downloadLink.setAttribute('download', `${downloadName}.csv`); + downloadLink.innerText = 'Download CSV of results here.'; + document.getElementById('download-link').appendChild(downloadLink); + return; +} diff --git a/src/routes/api.ts b/src/routes/api.ts index ee80ea5..22edd79 100644 --- a/src/routes/api.ts +++ b/src/routes/api.ts @@ -15,15 +15,16 @@ api.use(cookieParser()); const csrf = csurf({ cookie: true }); // For file uploads -api.use(fileUpload({ +api.use( + fileUpload({ preserveExtension: true, // Preserve file extension on upload safeFileNames: true, // Only allow alphanumeric characters in file names limits: { fileSize: 1 * 1024 * 1024 }, // Limit file size to 1MB useTempFiles: true, // Store files in temp instead of memory tempFileDir: '/tmp/', // Store files in /tmp/ debug: false, // Log debug information -})); - + }) +); /* Upload a file to the server @@ -44,34 +45,44 @@ api.use(fileUpload({ 415 when the file's MIME type is not text/x-python or text/plain (WINDOWS WHY) 500 for any other errors */ -api.route('/upload') - .post(csrf, (req: Request, res: Response) => { - try { - // Check if anything was actually uploaded - if (!req.files || Object.keys(req.files).length === 0) - return res.status(400).json({ error: 'No file uploaded.' }); - - const file: UploadedFile = req.files.file as UploadedFile; // Kludge to prevent a compiler error, only one file gets uploaded so this should be fine - - // Check if the file is too large (see fileUpload.limits for the limit) - if (file.truncated) - return res.status(413).json({ error: 'File uploaded was too large.' }); - - // Check if the file is a python file - if (file.mimetype !== 'text/x-python' && file.mimetype !== 'text/plain') - return res.status(415).json({ error: 'File uploaded was not a Python file.' }); - - res.status(201).json({ file: { file: file.name, path: file.tempFilePath }, msg: 'File uploaded successfully.', csrf: req.csrfToken() }); - } catch (err) { - // Generic error handler - res.status(500).json({ error: 'An unknown error occurred while uploading the file.', error_msg: err }); - } - }) - // Fallback - .all(csrf, (req: Request, res: Response) => { - res.set('Allow', 'POST'); - res.status(405).json({ error: 'Method not allowed.' }); - }); +api + .route('/upload') + .post(csrf, (req: Request, res: Response) => { + try { + // Check if anything was actually uploaded + if (!req.files || Object.keys(req.files).length === 0) + return res.status(400).json({ error: 'No file uploaded.' }); + + const file: UploadedFile = req.files.file as UploadedFile; // Kludge to prevent a compiler error, only one file gets uploaded so this should be fine + + // Check if the file is too large (see fileUpload.limits for the limit) + if (file.truncated) + return res.status(413).json({ error: 'File uploaded was too large.' }); + + // Check if the file is a python file + if (file.mimetype !== 'text/x-python' && file.mimetype !== 'text/plain') + return res + .status(415) + .json({ error: 'File uploaded was not a Python file.' }); + + res.status(201).json({ + file: { file: file.name, path: file.tempFilePath }, + msg: 'File uploaded successfully.', + csrf: req.csrfToken(), + }); + } catch (err) { + // Generic error handler + res.status(500).json({ + error: 'An unknown error occurred while uploading the file.', + error_msg: err, + }); + } + }) + // Fallback + .all(csrf, (req: Request, res: Response) => { + res.set('Allow', 'POST'); + res.status(405).json({ error: 'Method not allowed.' }); + }); /* Actuate the pendulum @@ -102,20 +113,20 @@ api.route('/upload') Minimizing PE vectors like running this as an extremely low privilege user is a must. */ -api.route('/actuate') - // Snyk error mitigation, should be fine since the rate limiting is already in place - // file deepcode ignore NoRateLimitingForExpensiveWebOperation: This is already rate limited by the website, so we don't need to do it again - .post(csrf, async (req: Request, res: Response) => { - try { - const path: string = req.body.file.path; - // Verify that the file exists and is a regular file - // Return if not since the res will be sent by the verifyFile function - if (await verifyFile(path, res) !== true) - return; - - const escaped = quote([ path ]); - // Run the code - /* +api + .route('/actuate') + // Snyk error mitigation, should be fine since the rate limiting is already in place + // file deepcode ignore NoRateLimitingForExpensiveWebOperation: This is already rate limited by the website, so we don't need to do it again + .post(csrf, async (req: Request, res: Response) => { + try { + const path: string = req.body.file.path; + // Verify that the file exists and is a regular file + // Return if not since the res will be sent by the verifyFile function + if ((await verifyFile(path, res)) !== true) return; + + const escaped = quote([path]); + // Run the code + /* TODO: - Potentially add the limiter to one-per-person here - Add a timeout @@ -123,37 +134,48 @@ api.route('/actuate') - Make this more secure - HOW? */ - // let output = ''; - let stderr = ''; - const actuation = spawn('python', escaped.split(' ')); - // actuation.stdout.on('data', (data: Buffer) => { - // output += data.toString(); - // }); - actuation.stderr.on('data', (data: Buffer) => { - stderr += `STDERR: ${data.toString()}`; - }); - actuation.on('close', (code: number) => { - const filename: string = (req.body.file.path as string).split('/').pop() as string; - // Make sure the program exited with a code of 0 (success) - if (code !== 0) - return res.status(500).json({ error: `Program exited with exit code ${code}`, error_msg: stderr, file: { name: req.body.file.file, filename: filename } }); - return res.status(200).json({ file: { name: req.body.file.file, filename: filename } }); - }); - // Kill the process if it takes too long - // Default timeout is 120 seconds (2 minutes) - setTimeout(() => { - actuation.kill(); - }, 120000); - } catch (err) { - // Generic error handler - return res.status(500).json({ error: 'An unknown error occurred while running the file.', error_msg: err }); - } - }) - // Fallback - .all(csrf, (req: Request, res: Response) => { - res.set('Allow', 'POST'); - return res.status(405).json({ error: 'Method not allowed.' }); - }); + // let output = ''; + let stderr = ''; + const actuation = spawn('python', escaped.split(' ')); + // actuation.stdout.on('data', (data: Buffer) => { + // output += data.toString(); + // }); + actuation.stderr.on('data', (data: Buffer) => { + stderr += `STDERR: ${data.toString()}`; + }); + actuation.on('close', (code: number) => { + const filename: string = (req.body.file.path as string) + .split('/') + .pop() as string; + // Make sure the program exited with a code of 0 (success) + if (code !== 0) + return res.status(500).json({ + error: `Program exited with exit code ${code}`, + error_msg: stderr, + file: { name: req.body.file.file, filename: filename }, + }); + return res + .status(200) + .json({ file: { name: req.body.file.file, filename: filename } }); + }); + // Kill the process if it takes too long + // Default timeout is 120 seconds (2 minutes) + setTimeout(() => { + actuation.kill(); + }, 120000); + } catch (err) { + // Generic error handler + return res.status(500).json({ + error: 'An unknown error occurred while running the file.', + error_msg: err, + }); + } + }) + // Fallback + .all(csrf, (req: Request, res: Response) => { + res.set('Allow', 'POST'); + return res.status(405).json({ error: 'Method not allowed.' }); + }); /* Download the CSV file after running the pendulum @@ -166,35 +188,34 @@ api.route('/actuate') 404 when the file is not accessible or does not exist 500 for any other errors */ -api.route('/download') - .get(csrf, async (req: Request, res: Response) => { - const filename: string = req.query.filename as string; - if (!filename) - return res.status(400).json({ error: 'No filename specified.' }); - // Make sure no path traversal is attempted - // This regex matches all alphanumeric characters, underscores, and dashes. - // MAKE SURE THIS DOES NOT ALLOW PATH TRAVERSAL - if (!/^[\w-]+$/.test(filename)) - return res.status(403).json({ error: 'No.' }); - - const path = `/tmp/${filename}.csv`; - - // Verify that the file exists and is a regular file - // Return if not since the res will be sent by the verifyFile function - if (await verifyFile(path, res) !== true) - return; - // Read the file and send it to the client - res.type('text/csv'); - // Snyk error mitigation, should be fine since tmp is private and the simple regex above should prevent path traversal - // deepcode ignore PT: This is probably mitigated by the regex - return res.sendFile(path); - }) - // Fallback - .all(csrf, (req: Request, res: Response) => { - res.set('Allow', 'GET'); - return res.status(405).json({ error: 'Method not allowed.' }); - }); +api + .route('/download') + .get(csrf, async (req: Request, res: Response) => { + const filename: string = req.query.filename as string; + if (!filename) + return res.status(400).json({ error: 'No filename specified.' }); + // Make sure no path traversal is attempted + // This regex matches all alphanumeric characters, underscores, and dashes. + // MAKE SURE THIS DOES NOT ALLOW PATH TRAVERSAL + if (!/^[\w-]+$/.test(filename)) + return res.status(403).json({ error: 'No.' }); + + const path = `/tmp/${filename}.csv`; + // Verify that the file exists and is a regular file + // Return if not since the res will be sent by the verifyFile function + if ((await verifyFile(path, res)) !== true) return; + // Read the file and send it to the client + res.type('text/csv'); + // Snyk error mitigation, should be fine since tmp is private and the simple regex above should prevent path traversal + // deepcode ignore PT: This is probably mitigated by the regex + return res.sendFile(path); + }) + // Fallback + .all(csrf, (req: Request, res: Response) => { + res.set('Allow', 'GET'); + return res.status(405).json({ error: 'Method not allowed.' }); + }); /* Verify that the file exists and is a regular file @@ -207,36 +228,38 @@ api.route('/download') ** AFTER THIS POINT, THE API HAS ALREADY SENT A RESPONSE, SO THE FUNCTION THAT CALLED IT SHOULD NOT RETURN ANOTHER RESPONSE ** */ async function verifyFile(file: string, res: Response) { - // Make sure the file being requested to run exists - try { - await access(file); - } catch (err) { - res.status(404).json({ error: 'File is not accessible or does not exist.' }); - return false; - } - // This is a try catch because otherwise type checking will fail and get all messed up - // Handle your promise rejections, kids - try { - const stats = await stat(file); - // Make sure the file being requested to run is a regular file - if (!stats.isFile()) { - res.status(400).json({ error: 'File is not a regular file.' }); - return false; - } - // Make sure the file being requested to run is not a directory - else if (stats.isDirectory()) { - res.status(400).json({ error: 'File is a directory.' }); - return false; - } - - - // File does exist and is a regular file, so it is good to go - return true; + // Make sure the file being requested to run exists + try { + await access(file); + } catch (err) { + res + .status(404) + .json({ error: 'File is not accessible or does not exist.' }); + return false; + } + // This is a try catch because otherwise type checking will fail and get all messed up + // Handle your promise rejections, kids + try { + const stats = await stat(file); + // Make sure the file being requested to run is a regular file + if (!stats.isFile()) { + res.status(400).json({ error: 'File is not a regular file.' }); + return false; } - catch (err) { - res.status(404).json({ error: 'File is not accessible or does not exist.' }); - return false; + // Make sure the file being requested to run is not a directory + else if (stats.isDirectory()) { + res.status(400).json({ error: 'File is a directory.' }); + return false; } + + // File does exist and is a regular file, so it is good to go + return true; + } catch (err) { + res + .status(404) + .json({ error: 'File is not accessible or does not exist.' }); + return false; + } } export default api; diff --git a/tsconfig.json b/tsconfig.json index 4ee1d0f..006f26d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,96 +1,94 @@ { - "compilerOptions": { - /* Visit https://aka.ms/tsconfig.json to read more about this file */ - /* Projects */ - // "incremental": true, /* Enable incremental compilation */ - // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ - // "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */ - // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */ - // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ - // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ - /* Language and Environment */ - "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ - // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ - // "jsx": "preserve", /* Specify what JSX code is generated. */ - // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ - // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ - // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */ - // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ - // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */ - // "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */ - // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ - // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ - /* Modules */ - "module": "commonjs", /* Specify what module code is generated. */ - // "rootDir": "./", /* Specify the root folder within your source files. */ - "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ - // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ - // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ - // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ - // "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */ - // "types": [], /* Specify type package names to be included without being referenced in a source file. */ - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - // "resolveJsonModule": true, /* Enable importing .json files */ - // "noResolve": true, /* Disallow `import`s, `require`s or `<reference>`s from expanding the number of files TypeScript should add to a project. */ - /* JavaScript Support */ - // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */ - // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ - // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */ - /* Emit */ - "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ - "declarationMap": true, /* Create sourcemaps for d.ts files. */ - // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ - "sourceMap": true, /* Create source map files for emitted JavaScript files. */ - // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */ - "outDir": "./dist", /* Specify an output folder for all emitted files. */ - "removeComments": true, /* Disable emitting comments. */ - // "noEmit": true, /* Disable emitting files from a compilation. */ - "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ - // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */ - // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ - // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ - // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ - // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ - // "newLine": "crlf", /* Set the newline character for emitting files. */ - // "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */ - // "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */ - // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ - // "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */ - // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ - // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ - /* Interop Constraints */ - // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ - // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ - "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */ - // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ - "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ - /* Type Checking */ - "strict": true, /* Enable all strict type-checking options. */ - "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */ - // "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */ - // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ - // "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */ - // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ - // "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */ - // "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */ - // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ - // "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */ - // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */ - // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ - // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ - // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ - // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ - // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ - // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */ - // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ - // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ - /* Completeness */ - // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ - "skipLibCheck": true /* Skip type checking all .d.ts files. */ - }, - "include": [ - "src/**/*" - ] + "compilerOptions": { + /* Visit https://aka.ms/tsconfig.json to read more about this file */ + /* Projects */ + // "incremental": true, /* Enable incremental compilation */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + /* Language and Environment */ + "target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */ + // "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + /* Modules */ + "module": "commonjs" /* Specify what module code is generated. */, + // "rootDir": "./", /* Specify the root folder within your source files. */ + "moduleResolution": "node" /* Specify how TypeScript looks up a file from a given module specifier. */, + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "resolveJsonModule": true, /* Enable importing .json files */ + // "noResolve": true, /* Disallow `import`s, `require`s or `<reference>`s from expanding the number of files TypeScript should add to a project. */ + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */ + /* Emit */ + "declaration": true /* Generate .d.ts files from TypeScript and JavaScript files in your project. */, + "declarationMap": true /* Create sourcemaps for d.ts files. */, + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + "sourceMap": true /* Create source map files for emitted JavaScript files. */, + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */ + "outDir": "./dist" /* Specify an output folder for all emitted files. */, + "removeComments": true /* Disable emitting comments. */, + // "noEmit": true, /* Disable emitting files from a compilation. */ + "importHelpers": true /* Allow importing helper functions from tslib once per project, instead of including them per-file. */, + // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */, + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, + /* Type Checking */ + "strict": true /* Enable all strict type-checking options. */, + "noImplicitAny": true /* Enable error reporting for expressions and declarations with an implied `any` type.. */, + // "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */ + // "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + }, + "include": ["src/**/*"] } @@ -968,6 +968,11 @@ eslint-config-airbnb-base@^15.0.0: object.entries "^1.1.5" semver "^6.3.0" +eslint-config-prettier@^8.5.0: + version "8.5.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz#5a81680ec934beca02c7b1a61cf8ca34b66feab1" + integrity sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q== + eslint-import-resolver-node@^0.3.6: version "0.3.6" resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz#4048b958395da89668252001dbd9eca6b83bacbd" @@ -1003,6 +1008,13 @@ eslint-plugin-import@^2.25.4: resolve "^1.20.0" tsconfig-paths "^3.12.0" +eslint-plugin-prettier@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-4.0.0.tgz#8b99d1e4b8b24a762472b4567992023619cb98e0" + integrity sha512-98MqmCJ7vJodoQK359bqQWaxOE0CS8paAz/GgjaZLyex4TTk3g9HugoO89EqWCrFiOqn9EVvcoo7gZzONCWVwQ== + dependencies: + prettier-linter-helpers "^1.0.0" + eslint-scope@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" @@ -1183,6 +1195,11 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== +fast-diff@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" + integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== + fast-glob@^3.2.9: version "3.2.11" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9" @@ -2405,6 +2422,18 @@ prepend-http@^2.0.0: resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= +prettier-linter-helpers@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" + integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== + dependencies: + fast-diff "^1.1.2" + +prettier@^2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.5.1.tgz#fff75fa9d519c54cf0fce328c1017d94546bc56a" + integrity sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg== + process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" |