diff --git a/frontend/.eslintrc.js b/frontend/.eslintrc.js
index a11a034..c2b5ba1 100644
--- a/frontend/.eslintrc.js
+++ b/frontend/.eslintrc.js
@@ -5,7 +5,8 @@ module.exports = {
},
"extends": [
"plugin:react/recommended",
- "airbnb"
+ "airbnb",
+ "airbnb-typescript",
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
@@ -13,7 +14,8 @@ module.exports = {
"jsx": true
},
"ecmaVersion": "latest",
- "sourceType": "module"
+ "sourceType": "module",
+ "project": "tsconfig.json"
},
"plugins": [
"react",
diff --git a/frontend/package.json b/frontend/package.json
index 2fd24c1..c64943f 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -4,16 +4,19 @@
"source": "src/index.html",
"browserslist": "> 0.5%, last 2 versions, not dead",
"scripts": {
+ "check": "tsc --noEmit && eslint ./src",
"start": "parcel",
"build": "parcel build"
},
"devDependencies": {
+ "@types/node": "^18.15.11",
"@types/react": "^18.0.26",
"@types/react-dom": "^18.0.10",
"@typescript-eslint/eslint-plugin": "^5.48.1",
"@typescript-eslint/parser": "^5.48.1",
"eslint": "^8.31.0",
"eslint-config-airbnb": "^19.0.4",
+ "eslint-config-airbnb-typescript": "^17.0.0",
"eslint-plugin-import": "^2.27.4",
"eslint-plugin-jsx-a11y": "^6.7.1",
"eslint-plugin-react": "^7.32.0",
diff --git a/frontend/src/app.tsx b/frontend/src/app.tsx
index 62d93ac..7f51b1f 100644
--- a/frontend/src/app.tsx
+++ b/frontend/src/app.tsx
@@ -6,8 +6,8 @@ import '@fontsource/roboto/400.css';
import '@fontsource/roboto/500.css';
import '@fontsource/roboto/700.css';
-import App from './components/app.tsx';
+import App from './components/app';
const container = document.getElementById('app');
-const root = createRoot(container);
+const root = createRoot(container!);
root.render();
diff --git a/frontend/src/components/app.tsx b/frontend/src/components/app.tsx
index e9206a5..b312717 100644
--- a/frontend/src/components/app.tsx
+++ b/frontend/src/components/app.tsx
@@ -19,9 +19,11 @@ import {
TextField,
} from '@mui/material';
-import GraphG6 from './graph-g6.tsx';
+import LoadingContext from './loading-context';
+import GraphG6, { GraphProps } from './graph-g6';
+import Footer from './footer';
-const { useState, useCallback } = React;
+const { useState, useCallback, useMemo } = React;
const darkTheme = createTheme({
palette: {
@@ -57,7 +59,7 @@ function App() {
const [loading, setLoading] = useState(false);
const [code, setCode] = useState(placeholder);
const [parsing, setParsing] = useState(Parsing.Naive);
- const [graphs, setGraphs] = useState();
+ const [graphs, setGraphs] = useState();
const [graphIndex, setGraphIndex] = useState(0);
const submitHandler = useCallback(
@@ -82,93 +84,98 @@ function App() {
[code, parsing],
);
+ const loadingContext = useMemo(() => ({ loading, setLoading }), [loading, setLoading]);
+
return (
-
-
-
- Solve your ADF Problem with OBDDs!
-
+
+
+
+
+ Solve your ADF Problem with OBDDs!
+
-
-
+
+ For more info on the syntax, have a
+ look
+ {' '}
+ here
+ .
+ >
+ )}
+ multiline
+ fullWidth
+ variant="filled"
+ value={code}
+ onChange={(event) => { setCode(event.target.value); }}
+ />
+
+
+
+ Parsing Strategy
+ setParsing(((e.target as HTMLInputElement).value) as Parsing)}
+ >
+ } label="Naive" />
+ } label="Hybrid" />
+
+
+
+
+
+ {' '}
+
+ {' '}
+
+ {' '}
+
+ {' '}
+
+ {' '}
+
+ {' '}
+
+
+
+ {graphs
+ && (
+
+ {graphs.length > 1
+ && (
<>
- For more info on the syntax, have a
- look
- {' '}
- here
- .
+ Models:
+
+ setGraphIndex(value - 1)} />
>
- )}
- multiline
- fullWidth
- variant="filled"
- value={code}
- onChange={(event) => { setCode(event.target.value); }}
- />
-
-
-
- Parsing Strategy
- setParsing((e.target as HTMLInputElement).value)}
- >
- } label="Naive" />
- } label="Hybrid" />
-
-
-
-
-
- {' '}
-
- {' '}
-
- {' '}
-
- {' '}
-
- {' '}
-
- {' '}
-
-
+ )}
+ {graphs.length > 0
+ && (
+
+
+
+ )}
+ {graphs.length === 0
+ && <>No models!>}
+
+ )}
+
+
- {graphs
- && (
-
- {graphs.length > 1
- && (
- <>
- Models:
-
- setGraphIndex(value - 1)} />
- >
- )}
- {graphs.length > 0
- && (
-
-
-
- )}
- {graphs.length === 0
- && <>No models!>}
-
- )}
-
-
-
-
-
+
+
+
+
);
}
diff --git a/frontend/src/components/footer.tsx b/frontend/src/components/footer.tsx
new file mode 100644
index 0000000..ad0a30c
--- /dev/null
+++ b/frontend/src/components/footer.tsx
@@ -0,0 +1,220 @@
+import React, {
+ useState, useCallback, useContext, useEffect,
+} from 'react';
+
+import {
+ Alert,
+ AppBar,
+ Button,
+ Dialog,
+ DialogActions,
+ DialogContent,
+ DialogTitle,
+ Snackbar,
+ TextField,
+ Toolbar,
+} from '@mui/material';
+
+import LoadingContext from './loading-context';
+
+enum UserFormType {
+ Login = 'Login',
+ Register = 'Register',
+ Update = 'Update',
+}
+
+interface UserFormProps {
+ formType: UserFormType | null;
+ close: (message?: string) => void;
+ username?: string;
+}
+
+function UserForm({ username: propUsername, formType, close }: UserFormProps) {
+ const { setLoading } = useContext(LoadingContext);
+ const [username, setUsername] = useState(propUsername || '');
+ const [password, setPassword] = useState('');
+ const [errorOccurred, setError] = useState(false);
+
+ const submitHandler = useCallback(
+ (del: boolean) => {
+ setLoading(true);
+ setError(false);
+
+ let method; let
+ endpoint;
+ if (del) {
+ method = 'DELETE';
+ endpoint = '/users/delete';
+ } else {
+ switch (formType) {
+ case UserFormType.Login:
+ method = 'POST';
+ endpoint = '/users/login';
+ break;
+ case UserFormType.Register:
+ method = 'POST';
+ endpoint = '/users/register';
+ break;
+ case UserFormType.Update:
+ method = 'PUT';
+ endpoint = '/users/update';
+ break;
+ default:
+ // NOTE: the value is not null when the dialog is open
+ break;
+ }
+ }
+
+ fetch(`${process.env.NODE_ENV === 'development' ? '//localhost:8080' : ''}${endpoint}`, {
+ method,
+ credentials: process.env.NODE_ENV === 'development' ? 'include' : 'same-origin',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: !del ? JSON.stringify({ username, password }) : undefined,
+ })
+ .then((res) => {
+ switch (res.status) {
+ case 200:
+ close(`Action '${formType}' successful!`);
+ break;
+ default:
+ setError(true);
+ break;
+ }
+ })
+ .finally(() => setLoading(false));
+ },
+ [username, password, formType],
+ );
+
+ return (
+ <>
+ {formType}
+
+ { setUsername(event.target.value); }} />
+
+ { setPassword(event.target.value); }} />
+ {errorOccurred
+ && Check your inputs!}
+
+
+
+
+ {formType === UserFormType.Update
+ // TODO: add another confirm dialog here
+ && (
+
+ )}
+
+ >
+ );
+}
+
+UserForm.defaultProps = { username: undefined };
+
+function Footer() {
+ const [username, setUsername] = useState();
+ const [tempUser, setTempUser] = useState();
+ const [dialogTypeOpen, setDialogTypeOpen] = useState(null);
+ const [snackbarMessage, setSnackbarMessage] = useState();
+
+ const logout = useCallback(() => {
+ fetch(`${process.env.NODE_ENV === 'development' ? '//localhost:8080' : ''}/users/logout`, {
+ method: 'DELETE',
+ credentials: process.env.NODE_ENV === 'development' ? 'include' : 'same-origin',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ })
+ .then((res) => {
+ switch (res.status) {
+ case 200:
+ setSnackbarMessage('Logout successful!');
+ setUsername(undefined);
+ break;
+ default:
+ setSnackbarMessage('An error occurred while trying to log out.');
+ break;
+ }
+ });
+ }, [setSnackbarMessage]);
+
+ useEffect(() => {
+ // Intuition: If the dialog was just closed (or on first render).
+ if (!dialogTypeOpen) {
+ fetch(`${process.env.NODE_ENV === 'development' ? '//localhost:8080' : ''}/users/info`, {
+ method: 'GET',
+ credentials: process.env.NODE_ENV === 'development' ? 'include' : 'same-origin',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ })
+ .then((res) => {
+ switch (res.status) {
+ case 200:
+ res.json().then(({ username: user, temp }) => {
+ setUsername(user);
+ setTempUser(temp);
+ });
+ break;
+ default:
+ setUsername(undefined);
+ break;
+ }
+ });
+ }
+ }, [dialogTypeOpen]);
+
+ return (
+ <>
+
+
+ {username ? (
+ <>
+
+ Logged in as:
+ {' '}
+ {username}
+ {' '}
+ {tempUser ? '(Temporary User. Edit to set a password!)' : undefined}
+
+
+ {!tempUser && }
+ >
+ ) : (
+ <>
+
+
+ >
+ )}
+
+
+
+ setSnackbarMessage(undefined)}
+ />
+ >
+ );
+}
+
+export default Footer;
diff --git a/frontend/src/components/graph-g6.tsx b/frontend/src/components/graph-g6.tsx
index ce004a0..889974e 100644
--- a/frontend/src/components/graph-g6.tsx
+++ b/frontend/src/components/graph-g6.tsx
@@ -1,13 +1,13 @@
import React, { useEffect, useRef } from 'react';
-import G6 from '@antv/g6';
+import G6, { Graph } from '@antv/g6';
G6.registerNode('nodeWithFlag', {
draw(cfg, group) {
- const mainWidth = Math.max(30, 5 * cfg.mainLabel.length + 10);
+ const mainWidth = Math.max(30, 5 * (cfg!.mainLabel as string).length + 10);
const mainHeight = 30;
- const keyShape = group.addShape('rect', {
+ const keyShape = group!.addShape('rect', {
attrs: {
width: mainWidth,
height: mainHeight,
@@ -20,13 +20,13 @@ G6.registerNode('nodeWithFlag', {
draggable: true,
});
- group.addShape('text', {
+ group!.addShape('text', {
attrs: {
x: mainWidth / 2,
y: mainHeight / 2,
textAlign: 'center',
textBaseline: 'middle',
- text: cfg.mainLabel,
+ text: cfg!.mainLabel,
fill: '#212121',
fontFamily: 'Roboto',
cursor: 'pointer',
@@ -37,14 +37,14 @@ G6.registerNode('nodeWithFlag', {
draggable: true,
});
- if (cfg.subLabel) {
- const subWidth = 5 * cfg.subLabel.length + 4;
+ if (cfg!.subLabel) {
+ const subWidth = 5 * (cfg!.subLabel as string).length + 4;
const subHeight = 20;
const subRectX = mainWidth - 4;
const subRectY = -subHeight + 4;
- group.addShape('rect', {
+ group!.addShape('rect', {
attrs: {
x: subRectX,
y: subRectY,
@@ -59,13 +59,13 @@ G6.registerNode('nodeWithFlag', {
draggable: true,
});
- group.addShape('text', {
+ group!.addShape('text', {
attrs: {
x: subRectX + subWidth / 2,
y: subRectY + subHeight / 2,
textAlign: 'center',
textBaseline: 'middle',
- text: cfg.subLabel,
+ text: cfg!.subLabel,
fill: '#212121',
fontFamily: 'Roboto',
fontSize: 10,
@@ -95,6 +95,7 @@ G6.registerNode('nodeWithFlag', {
// },
// },
setState(name, value, item) {
+ if (!item) { return; }
const group = item.getContainer();
const mainShape = group.get('children')[0]; // Find the first graphics shape of the node. It is determined by the order of being added
const subShape = group.get('children')[2];
@@ -131,11 +132,11 @@ G6.registerNode('nodeWithFlag', {
},
});
-interface GraphProps {
- lo_edges: [number, number][],
- hi_edges: [number, number][],
- node_labels: { [key: number]: string },
- tree_root_labels: { [key: number]: string[] },
+export interface GraphProps {
+ lo_edges: [string, string][],
+ hi_edges: [string, string][],
+ node_labels: { [key: string]: string },
+ tree_root_labels: { [key: string]: string[] },
}
function nodesAndEdgesFromGraphProps(graphProps: GraphProps) {
@@ -174,24 +175,26 @@ function GraphG6(props: Props) {
const ref = useRef(null);
- const graphRef = useRef();
+ const graphRef = useRef();
useEffect(
() => {
if (!graphRef.current) {
- graphRef.current = new G6.Graph({
- container: ref.current,
+ graphRef.current = new Graph({
+ container: ref.current!,
width: 1200,
height: 600,
fitView: true,
- rankdir: 'TB',
- align: 'DR',
- nodesep: 100,
- ranksep: 100,
modes: {
default: ['drag-canvas', 'zoom-canvas', 'drag-node'],
},
- layout: { type: 'dagre' },
+ layout: {
+ type: 'dagre',
+ rankdir: 'TB',
+ align: 'DR',
+ nodesep: 100,
+ ranksep: 100,
+ },
// defaultNode: {
// anchorPoints: [[0.5, 0], [0, 0.5], [1, 0.5], [0.5, 1]],
// type: 'rect',
@@ -239,13 +242,13 @@ function GraphG6(props: Props) {
// Mouse enter a node
graph.on('node:mouseenter', (e) => {
- const nodeItem = e.item; // Get the target item
+ const nodeItem = e.item!; // Get the target item
graph.setItemState(nodeItem, 'hover', true); // Set the state 'hover' of the item to be true
});
// Mouse leave a node
graph.on('node:mouseleave', (e) => {
- const nodeItem = e.item; // Get the target item
+ const nodeItem = e.item!; // Get the target item
graph.setItemState(nodeItem, 'hover', false); // Set the state 'hover' of the item to be false
});
},
@@ -254,11 +257,11 @@ function GraphG6(props: Props) {
useEffect(
() => {
- const graph = graphRef.current;
+ const graph = graphRef.current!;
// Click a node
graph.on('node:click', (e) => {
- const nodeItem = e.item; // et the clicked item
+ const nodeItem = e.item!; // et the clicked item
let onlyRemoveStates = false;
if (nodeItem.hasState('highlight')) {
@@ -290,12 +293,12 @@ function GraphG6(props: Props) {
graph.setItemState(edge, 'lowlight', true);
});
- const relevantNodeIds = [];
- const relevantLoEdges = [];
- const relevantHiEdges = [];
- let newNodeIds = [nodeItem.getModel().id];
- let newLoEdges = [];
- let newHiEdges = [];
+ const relevantNodeIds: string[] = [];
+ const relevantLoEdges: [string, string][] = [];
+ const relevantHiEdges: [string, string][] = [];
+ let newNodeIds: string[] = [nodeItem.getModel().id!];
+ let newLoEdges: [string, string][] = [];
+ let newHiEdges: [string, string][] = [];
while (newNodeIds.length > 0 || newLoEdges.length > 0 || newHiEdges.length > 0) {
relevantNodeIds.push(...newNodeIds);
@@ -347,7 +350,7 @@ function GraphG6(props: Props) {
useEffect(
() => {
- const graph = graphRef.current;
+ const graph = graphRef.current!;
const { nodes, edges } = nodesAndEdgesFromGraphProps(graphProps);
diff --git a/frontend/src/components/loading-context.ts b/frontend/src/components/loading-context.ts
new file mode 100644
index 0000000..05ad059
--- /dev/null
+++ b/frontend/src/components/loading-context.ts
@@ -0,0 +1,13 @@
+import { createContext } from 'react';
+
+interface ILoadingContext {
+ loading: boolean;
+ setLoading: (loading: boolean) => void;
+}
+
+const LoadingContext = createContext({
+ loading: false,
+ setLoading: () => {},
+});
+
+export default LoadingContext;
diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json
new file mode 100644
index 0000000..b9dae99
--- /dev/null
+++ b/frontend/tsconfig.json
@@ -0,0 +1,103 @@
+{
+ "compilerOptions": {
+ /* Visit https://aka.ms/tsconfig to read more about this file */
+
+ /* Projects */
+ // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
+ // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
+ // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
+ // "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. */
+ // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
+
+ /* 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. */
+ // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
+ // "resolveJsonModule": true, /* Enable importing .json files. */
+ // "noResolve": true, /* Disallow 'import's, 'require's or ''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": "./", /* 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, /* Default catch clause variables as 'unknown' instead of 'any'. */
+ // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
+ // "noUnusedLocals": true, /* Enable error reporting when 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, /* Add 'undefined' to a type when accessed using an index. */
+ // "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. */
+ }
+}
diff --git a/frontend/yarn.lock b/frontend/yarn.lock
index ad7b388..73409ad 100644
--- a/frontend/yarn.lock
+++ b/frontend/yarn.lock
@@ -1323,6 +1323,11 @@
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==
+"@types/node@^18.15.11":
+ version "18.15.11"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.11.tgz#b3b790f09cb1696cffcec605de025b088fa4225f"
+ integrity sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==
+
"@types/parse-json@^4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
@@ -2204,6 +2209,13 @@ eslint-config-airbnb-base@^15.0.0:
object.entries "^1.1.5"
semver "^6.3.0"
+eslint-config-airbnb-typescript@^17.0.0:
+ version "17.0.0"
+ resolved "https://registry.yarnpkg.com/eslint-config-airbnb-typescript/-/eslint-config-airbnb-typescript-17.0.0.tgz#360dbcf810b26bbcf2ff716198465775f1c49a07"
+ integrity sha512-elNiuzD0kPAPTXjFWg+lE24nMdHMtuxgYoD30OyMD6yrW1AhFZPAg27VX7d3tzOErw+dgJTNWfRSDqEcXb4V0g==
+ dependencies:
+ eslint-config-airbnb-base "^15.0.0"
+
eslint-config-airbnb@^19.0.4:
version "19.0.4"
resolved "https://registry.yarnpkg.com/eslint-config-airbnb/-/eslint-config-airbnb-19.0.4.tgz#84d4c3490ad70a0ffa571138ebcdea6ab085fdc3"