diff --git a/web/.eslintrc.js b/web/.eslintrc.js
index 622a949e8..152752e5b 100644
--- a/web/.eslintrc.js
+++ b/web/.eslintrc.js
@@ -18,6 +18,7 @@ module.exports = {
     'eslint:recommended',
     'plugin:react/recommended',
   ],
+  'parser': 'babel-eslint',
   'parserOptions': {
     'ecmaVersion': 2018,
     'ecmaFeatures': {
diff --git a/web/package.json b/web/package.json
index b77776569..ec27c5498 100644
--- a/web/package.json
+++ b/web/package.json
@@ -7,12 +7,15 @@
   ],
   "license": "PostgreSQL",
   "devDependencies": {
-    "@babel/core": "~7.6.0",
-    "@babel/preset-env": "~7.6.0",
+    "@babel/core": "^7.10.2",
+    "@babel/plugin-proposal-object-rest-spread": "^7.9.6",
+    "@babel/preset-env": "^7.10.2",
+    "@emotion/core": "^10.0.14",
+    "@emotion/styled": "^10.0.14",
     "autoprefixer": "^9.6.4",
     "axios-mock-adapter": "^1.17.0",
-    "babel-loader": "~8.0.5",
-    "babel-plugin-transform-object-rest-spread": "^7.0.0-beta.3",
+    "babel-eslint": "^10.1.0",
+    "babel-loader": "^8.1.0",
     "copy-webpack-plugin": "^5.1.0",
     "core-js": "^3.2.1",
     "cross-env": "^5.2.0",
@@ -41,7 +44,8 @@
     "popper.js": "^1.14.7",
     "postcss-loader": "^3.0.0",
     "prop-types": "^15.7.2",
-    "raw-loader": "^1.0.0",
+    "raw-loader": "^3.1.0",
+    "resize-observer-polyfill": "^1.5.1",
     "sass": "^1.24.4",
     "sass-loader": "^7.1.0",
     "sass-resources-loader": "^2.0.0",
@@ -50,7 +54,7 @@
     "url-loader": "^1.1.2",
     "webpack": "^4.41.2",
     "webpack-bundle-analyzer": "^3.5.1",
-    "webpack-cli": "^3.2.3",
+    "webpack-cli": "^3.3.11",
     "webpack-require-from": "^1.8.0",
     "yarn-audit-html": "^1.1.0"
   },
@@ -58,12 +62,12 @@
     "@babel/plugin-proposal-class-properties": "^7.10.4",
     "@babel/preset-react": "^7.10.4",
     "@fortawesome/fontawesome-free": "^5.14.0",
+    "@projectstorm/react-diagrams": "^6.2.0",
     "@simonwep/pickr": "^1.5.1",
+    "@tippyjs/react": "^4.2.0",
     "acitree": "git+https://github.com/imsurinder90/jquery-aciTree.git#rc.7",
     "alertifyjs": "git+https://github.com/EnterpriseDB/AlertifyJS/#72c1d794f5b6d4ec13a68d123c08f19021afe263",
     "axios": "^0.18.1",
-    "babel-plugin-transform-es2015-modules-amd": "^6.24.1",
-    "babel-preset-es2015-without-strict": "~0.0.4",
     "babelify": "~10.0.0",
     "backbone": "1.4.0",
     "backform": "^0.2.0",
@@ -75,12 +79,17 @@
     "bootstrap4-toggle": "3.4.0",
     "bowser": "2.1.2",
     "browserify": "~16.2.3",
+    "canvg": "^3.0.7",
     "chart.js": "^2.9.3",
+    "closest": "^0.0.1",
     "codemirror": "^5.54.0",
     "css-loader": "2.1.0",
     "cssnano": "^4.1.10",
+    "dagre": "^0.8.4",
     "dropzone": "^5.5.1",
     "exports-loader": "~0.7.0",
+    "html-to-image": "^0.1.1",
+    "html2canvas": "^1.0.0-rc.7",
     "immutability-helper": "^3.0.0",
     "imports-loader": "^0.8.0",
     "ip-address": "^5.8.9",
@@ -91,11 +100,17 @@
     "json-bignumber": "^1.0.1",
     "karma-coverage": "^2.0.3",
     "leaflet": "^1.5.1",
+    "lodash": "4.*",
+    "ml-matrix": "^6.5.0",
     "moment": "^2.24.0",
     "moment-timezone": "^0.5.23",
     "mousetrap": "^1.6.3",
+    "pathfinding": "^0.4.18",
+    "paths-js": "^0.4.9",
+    "raf": "^3.4.1",
     "react": "^16.13.1",
     "react-dom": "^16.13.1",
+    "react-to-print": "^2.10.3",
     "requirejs": "~2.3.6",
     "select2": "^4.0.6-rc.1",
     "shim-loader": "^1.0.1",
diff --git a/web/pgadmin/browser/__init__.py b/web/pgadmin/browser/__init__.py
index 77120cd59..46417ae96 100644
--- a/web/pgadmin/browser/__init__.py
+++ b/web/pgadmin/browser/__init__.py
@@ -767,6 +767,7 @@ def utils():
             editor_insert_pair_brackets=insert_pair_brackets,
             editor_indent_with_tabs=editor_indent_with_tabs,
             app_name=config.APP_NAME,
+            app_version_int=config.APP_VERSION_INT,
             pg_libpq_version=pg_libpq_version,
             support_ssh_tunnel=config.SUPPORT_SSH_TUNNEL,
             logout_url=_get_logout_url()
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/__init__.py
index 51323a273..a15df20d1 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/__init__.py
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/__init__.py
@@ -21,7 +21,6 @@ from pgadmin.browser.server_groups.servers.utils import parse_priv_to_db
 from pgadmin.utils.ajax import make_json_response, internal_server_error, \
     make_response as ajax_response, gone
 from .utils import BaseTableView
-from pgadmin.utils.preferences import Preferences
 from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry
 from pgadmin.browser.server_groups.servers.databases.schemas.tables.\
     constraints.foreign_key import utils as fkey_utils
@@ -134,8 +133,7 @@ class TableModule(SchemaChildModule):
 blueprint = TableModule(__name__)
 
 
-class TableView(BaseTableView, DataTypeReader, VacuumSettings,
-                SchemaDiffTableCompare):
+class TableView(BaseTableView, DataTypeReader, SchemaDiffTableCompare):
     """
     This class is responsible for generating routes for Table node
 
@@ -589,7 +587,7 @@ class TableView(BaseTableView, DataTypeReader, VacuumSettings,
         Returns:
             JSON of selected table node
         """
-        status, res = self._fetch_properties(did, scid, tid)
+        status, res = self._fetch_table_properties(did, scid, tid)
         if not status:
             return res
         if not res['rows']:
@@ -599,86 +597,6 @@ class TableView(BaseTableView, DataTypeReader, VacuumSettings,
             gid, sid, did, scid, tid, res=res
         )
 
-    @staticmethod
-    def _check_rlspolicy_support(res):
-        """
-        This function is used to check whether 'rlspolicy' in response
-        as it supported for version 9.5 and above
-        :param res:
-        :return:
-        """
-        if 'rlspolicy' in res['rows'][0]:
-            # Set the value of rls policy
-            if res['rows'][0]['rlspolicy'] == "true":
-                res['rows'][0]['rlspolicy'] = True
-
-            # Set the value of force rls policy for table owner
-            if res['rows'][0]['forcerlspolicy'] == "true":
-                res['rows'][0]['forcerlspolicy'] = True
-
-    def _fetch_properties(self, did, scid, tid):
-        """
-        This function is used to fetch the properties of the specified object
-        :param did:
-        :param scid:
-        :param tid:
-        :return:
-        """
-        sql = render_template(
-            "/".join([self.table_template_path, self._PROPERTIES_SQL]),
-            did=did, scid=scid, tid=tid,
-            datlastsysoid=self.datlastsysoid
-        )
-        status, res = self.conn.execute_dict(sql)
-        if not status:
-            return False, internal_server_error(errormsg=res)
-
-        elif len(res['rows']) == 0:
-            return False, gone(
-                gettext(self.not_found_error_msg()))
-
-        # Update autovacuum properties
-        self.update_autovacuum_properties(res['rows'][0])
-
-        # We will check the threshold set by user before executing
-        # the query because that can cause performance issues
-        # with large result set
-        pref = Preferences.module('browser')
-        table_row_count_pref = pref.preference('table_row_count_threshold')
-        table_row_count_threshold = table_row_count_pref.get()
-        estimated_row_count = int(res['rows'][0].get('reltuples', 0))
-
-        # Check whether 'rlspolicy' in response as it supported for
-        # version 9.5 and above
-        TableView._check_rlspolicy_support(res)
-
-        # If estimated rows are greater than threshold then
-        if estimated_row_count and \
-                estimated_row_count > table_row_count_threshold:
-            res['rows'][0]['rows_cnt'] = str(table_row_count_threshold) + '+'
-
-        # If estimated rows is lower than threshold then calculate the count
-        elif estimated_row_count and \
-                table_row_count_threshold >= estimated_row_count:
-            sql = render_template(
-                "/".join(
-                    [self.table_template_path, 'get_table_row_count.sql']
-                ), data=res['rows'][0]
-            )
-
-            status, count = self.conn.execute_scalar(sql)
-
-            if not status:
-                return False, internal_server_error(errormsg=count)
-
-            res['rows'][0]['rows_cnt'] = count
-
-        # If estimated_row_count is zero then set the row count with same
-        elif not estimated_row_count:
-            res['rows'][0]['rows_cnt'] = estimated_row_count
-
-        return True, res
-
     @BaseTableView.check_precondition
     def types(self, gid, sid, did, scid, tid=None, clid=None):
         """
@@ -686,12 +604,8 @@ class TableView(BaseTableView, DataTypeReader, VacuumSettings,
             This function will return list of types available for column node
             for node-ajax-control
         """
-        condition = render_template(
-            "/".join([
-                self.table_template_path, 'get_types_where_condition.sql'
-            ]),
-            show_system_objects=self.blueprint.show_system_objects
-        )
+        condition = self.get_types_condition_sql(
+            self.blueprint.show_system_objects)
 
         status, types = self.get_types(self.conn, condition, True, sid)
 
@@ -1073,7 +987,7 @@ class TableView(BaseTableView, DataTypeReader, VacuumSettings,
                 data[k] = v
 
         try:
-            status, res = self._fetch_properties(did, scid, tid)
+            status, res = self._fetch_table_properties(did, scid, tid)
             if not status:
                 return res
 
@@ -1314,7 +1228,7 @@ class TableView(BaseTableView, DataTypeReader, VacuumSettings,
         res = None
 
         if tid is not None:
-            status, res = self._fetch_properties(did, scid, tid)
+            status, res = self._fetch_table_properties(did, scid, tid)
             if not status:
                 return res
 
@@ -1378,7 +1292,7 @@ class TableView(BaseTableView, DataTypeReader, VacuumSettings,
         """
         main_sql = []
 
-        status, res = self._fetch_properties(did, scid, tid)
+        status, res = self._fetch_table_properties(did, scid, tid)
         if not status:
             return res
 
@@ -1665,57 +1579,12 @@ class TableView(BaseTableView, DataTypeReader, VacuumSettings,
         :return: Table dataset
         """
 
-        if tid:
-            status, data = self._fetch_properties(did, scid, tid)
-
-            if not status:
-                current_app.logger.error(data)
-                return False
-
-            data = super(TableView, self).properties(
-                0, sid, did, scid, tid, res=data, return_ajax_response=False
-            )
-
-            return data
-
-        else:
-            res = dict()
-            sql = render_template("/".join([self.table_template_path,
-                                            self._NODES_SQL]), scid=scid)
-            status, tables = self.conn.execute_2darray(sql)
-            if not status:
-                current_app.logger.error(tables)
-                return False
-
-            for row in tables['rows']:
-                status, data = self._fetch_properties(did, scid, row['oid'])
-
-                if status:
-                    data = super(TableView, self).properties(
-                        0, sid, did, scid, row['oid'], res=data,
-                        return_ajax_response=False
-                    )
-
-                    # Get sub module data of a specified table for object
-                    # comparison
-                    self._get_sub_module_data_for_compare(sid, did, scid, data,
-                                                          row)
-                    res[row['name']] = data
-
-            return res
+        status, res = BaseTableView.fetch_tables(self, sid, did, scid, tid)
+        if not status:
+            current_app.logger.error(res)
+            return False
 
-    def _get_sub_module_data_for_compare(self, sid, did, scid, data, row):
-        # Get sub module data of a specified table for object
-        # comparison
-        for module in self.tables_sub_modules:
-            module_view = SchemaDiffRegistry.get_node_view(module)
-            if module_view.blueprint.server_type is None or \
-                self.manager.server_type in \
-                    module_view.blueprint.server_type:
-                sub_data = module_view.fetch_objects_to_compare(
-                    sid=sid, did=did, scid=scid, tid=row['oid'],
-                    oid=None)
-                data[module] = sub_data
+        return res
 
     def get_submodule_template_path(self, module_name):
         """
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/utils.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/utils.py
index c122a4be6..e072a4391 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/utils.py
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/utils.py
@@ -318,22 +318,23 @@ def _get_sql_for_create_fk_const(data, conn, template_path):
          len(data['columns']) < 1):
         return True, '-- definition incomplete', name, ''
 
-    if data['autoindex'] and \
+    if data.get('autoindex', False) and \
             ('coveringindex' not in data or data['coveringindex'] == ''):
         return True, '-- definition incomplete', name, ''
 
-    # Get the parent schema and table.
-    schema, table = get_parent(conn,
-                               data['columns'][0]['references'])
+    if 'references' in data['columns'][0]:
+        # Get the parent schema and table.
+        schema, table = get_parent(conn,
+                                   data['columns'][0]['references'])
 
-    # Below handling will be used in Schema diff in case
-    # of different database comparison
-    _checks_for_schema_diff(table, schema, data)
+        # Below handling will be used in Schema diff in case
+        # of different database comparison
+        _checks_for_schema_diff(table, schema, data)
 
     sql = render_template("/".join([template_path, 'create.sql']),
                           data=data, conn=conn)
 
-    if data['autoindex']:
+    if data.get('autoindex', False):
         sql += render_template(
             "/".join([template_path, 'create_index.sql']),
             data=data, conn=conn)
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/partitions/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/partitions/__init__.py
index 1df83e99d..ce03cd1be 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/partitions/__init__.py
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/partitions/__init__.py
@@ -175,8 +175,7 @@ class PartitionsModule(CollectionNodeModule):
 blueprint = PartitionsModule(__name__)
 
 
-class PartitionsView(BaseTableView, DataTypeReader, VacuumSettings,
-                     SchemaDiffObjectCompare):
+class PartitionsView(BaseTableView, DataTypeReader, SchemaDiffObjectCompare):
     """
     This class is responsible for generating routes for Partition node
 
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/default/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/default/create.sql
index 22fd79a9c..c8dd96f82 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/default/create.sql
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/foreign_key/sql/default/create.sql
@@ -2,21 +2,21 @@ ALTER TABLE {{ conn|qtIdent(data.schema, data.table) }}
     ADD{% if data.name %} CONSTRAINT {{ conn|qtIdent(data.name) }}{% endif%} FOREIGN KEY ({% for columnobj in data.columns %}{% if loop.index != 1 %}
 , {% endif %}{{ conn|qtIdent(columnobj.local_column)}}{% endfor %})
     REFERENCES {{ conn|qtIdent(data.remote_schema, data.remote_table) }} ({% for columnobj in data.columns %}{% if loop.index != 1 %}
-, {% endif %}{{ conn|qtIdent(columnobj.referenced)}}{% endfor %}) {% if data.confmatchtype %}MATCH FULL{% else %}MATCH SIMPLE{% endif%}
+, {% endif %}{{ conn|qtIdent(columnobj.referenced)}}{% endfor %}){% if data.confmatchtype is defined %} {% if data.confmatchtype %}MATCH FULL{% else %}MATCH SIMPLE{% endif%}{% endif%}{% if data.confupdtype is defined %}
 
     ON UPDATE{% if data.confupdtype  == 'a' %}
  NO ACTION{% elif data.confupdtype  == 'r' %}
  RESTRICT{% elif data.confupdtype  == 'c' %}
  CASCADE{% elif data.confupdtype  == 'n' %}
  SET NULL{% elif data.confupdtype  == 'd' %}
- SET DEFAULT{% endif %}
+ SET DEFAULT{% endif %}{% endif %}{% if data.confdeltype is defined %}
 
     ON DELETE{% if data.confdeltype  == 'a' %}
  NO ACTION{% elif data.confdeltype  == 'r' %}
  RESTRICT{% elif data.confdeltype  == 'c' %}
  CASCADE{% elif data.confdeltype  == 'n' %}
  SET NULL{% elif data.confdeltype  == 'd' %}
- SET DEFAULT{% endif %}
+ SET DEFAULT{% endif %}{% endif %}
 {% if data.condeferrable %}
 
     DEFERRABLE{% if data.condeferred %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/utils.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/utils.py
index 1b90f8c46..16c8eca37 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/utils.py
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/utils.py
@@ -45,9 +45,13 @@ from pgadmin.browser.server_groups.servers.databases.schemas.tables.\
 from pgadmin.browser.server_groups.servers.databases.schemas. \
     tables.row_security_policies import \
     utils as row_security_policies_utils
+from pgadmin.utils.preferences import Preferences
+from pgadmin.browser.server_groups.servers.databases.schemas.utils \
+    import VacuumSettings
+from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry
 
 
-class BaseTableView(PGChildNodeView, BasePartitionTable):
+class BaseTableView(PGChildNodeView, BasePartitionTable, VacuumSettings):
     """
     This class is base class for tables and partitioned tables.
 
@@ -101,7 +105,11 @@ class BaseTableView(PGChildNodeView, BasePartitionTable):
             driver = get_driver(PG_DEFAULT_DRIVER)
             did = kwargs['did']
             self.manager = driver.connection_manager(kwargs['sid'])
-            self.conn = self.manager.connection(did=kwargs['did'])
+            if "conn_id" in kwargs:
+                self.conn = self.manager.connection(
+                    did=kwargs['did'], conn_id=kwargs['conn_id'])
+            else:
+                self.conn = self.manager.connection(did=kwargs['did'])
             self.qtIdent = driver.qtIdent
             self.qtTypeIdent = driver.qtTypeIdent
             # We need datlastsysoid to check if current table is system table
@@ -442,6 +450,161 @@ class BaseTableView(PGChildNodeView, BasePartitionTable):
             status=200
         )
 
+    def get_types_condition_sql(self, show_system_objects):
+        condition = render_template(
+            "/".join([
+                self.table_template_path, 'get_types_where_condition.sql'
+            ]),
+            show_system_objects=show_system_objects
+        )
+
+        return condition
+
+    def fetch_tables(self, sid, did, scid, tid=None):
+        """
+        This function will fetch the list of all the tables
+        and will be used by schema diff.
+
+        :param sid: Server Id
+        :param did: Database Id
+        :param scid: Schema Id
+        :param tid: Table Id
+        :return: Table dataset
+        """
+
+        if tid:
+            status, data = self._fetch_table_properties(did, scid, tid)
+
+            if not status:
+                return False, data
+
+            data = BaseTableView.properties(
+                self, 0, sid, did, scid, tid, res=data,
+                return_ajax_response=False
+            )
+
+            return True, data
+
+        else:
+            res = dict()
+            sql = render_template("/".join([self.table_template_path,
+                                            self._NODES_SQL]), scid=scid)
+            status, tables = self.conn.execute_2darray(sql)
+            if not status:
+                return False, tables
+
+            for row in tables['rows']:
+                status, data = \
+                    self._fetch_table_properties(did, scid, row['oid'])
+
+                if status:
+                    data = BaseTableView.properties(
+                        self, 0, sid, did, scid, row['oid'], res=data,
+                        return_ajax_response=False
+                    )
+
+                    # Get sub module data of a specified table for object
+                    # comparison
+                    BaseTableView._get_sub_module_data_for_compare(
+                        self, sid, did, scid, data, row)
+                    res[row['name']] = data
+                    res[row['name']] = data
+
+            return True, res
+
+    def _get_sub_module_data_for_compare(self, sid, did, scid, data, row):
+        # Get sub module data of a specified table for object
+        # comparison
+        for module in self.tables_sub_modules:
+            module_view = SchemaDiffRegistry.get_node_view(module)
+            if module_view.blueprint.server_type is None or \
+                self.manager.server_type in \
+                    module_view.blueprint.server_type:
+                sub_data = module_view.fetch_objects_to_compare(
+                    sid=sid, did=did, scid=scid, tid=row['oid'],
+                    oid=None)
+                data[module] = sub_data
+
+    @staticmethod
+    def _check_rlspolicy_support(res):
+        """
+        This function is used to check whether 'rlspolicy' in response
+        as it supported for version 9.5 and above
+        :param res:
+        :return:
+        """
+        if 'rlspolicy' in res['rows'][0]:
+            # Set the value of rls policy
+            if res['rows'][0]['rlspolicy'] == "true":
+                res['rows'][0]['rlspolicy'] = True
+
+            # Set the value of force rls policy for table owner
+            if res['rows'][0]['forcerlspolicy'] == "true":
+                res['rows'][0]['forcerlspolicy'] = True
+
+    def _fetch_table_properties(self, did, scid, tid):
+        """
+        This function is used to fetch the properties of the specified object
+        :param did:
+        :param scid:
+        :param tid:
+        :return:
+        """
+        sql = render_template(
+            "/".join([self.table_template_path, self._PROPERTIES_SQL]),
+            did=did, scid=scid, tid=tid,
+            datlastsysoid=self.datlastsysoid
+        )
+        status, res = self.conn.execute_dict(sql)
+        if not status:
+            return False, internal_server_error(errormsg=res)
+
+        elif len(res['rows']) == 0:
+            return False, gone(
+                gettext(self.not_found_error_msg()))
+
+        # Update autovacuum properties
+        self.update_autovacuum_properties(res['rows'][0])
+
+        # We will check the threshold set by user before executing
+        # the query because that can cause performance issues
+        # with large result set
+        pref = Preferences.module('browser')
+        table_row_count_pref = pref.preference('table_row_count_threshold')
+        table_row_count_threshold = table_row_count_pref.get()
+        estimated_row_count = int(res['rows'][0].get('reltuples', 0))
+
+        # Check whether 'rlspolicy' in response as it supported for
+        # version 9.5 and above
+        BaseTableView._check_rlspolicy_support(res)
+
+        # If estimated rows are greater than threshold then
+        if estimated_row_count and \
+                estimated_row_count > table_row_count_threshold:
+            res['rows'][0]['rows_cnt'] = str(table_row_count_threshold) + '+'
+
+        # If estimated rows is lower than threshold then calculate the count
+        elif estimated_row_count and \
+                table_row_count_threshold >= estimated_row_count:
+            sql = render_template(
+                "/".join(
+                    [self.table_template_path, 'get_table_row_count.sql']
+                ), data=res['rows'][0]
+            )
+
+            status, count = self.conn.execute_scalar(sql)
+
+            if not status:
+                return False, internal_server_error(errormsg=count)
+
+            res['rows'][0]['rows_cnt'] = count
+
+        # If estimated_row_count is zero then set the row count with same
+        elif not estimated_row_count:
+            res['rows'][0]['rows_cnt'] = estimated_row_count
+
+        return True, res
+
     def _format_column_list(self, data):
         # Now we have all lis of columns which we need
         # to include in our create definition, Let's format them
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/utils.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/utils.py
index 10fce304f..d00d08833 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/utils.py
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/utils.py
@@ -110,13 +110,15 @@ class DataTypeReader:
         """
         # Check if template path is already set or not
         # if not then we will set the template path here
+        manager = conn.manager if not hasattr(self, 'manager') \
+            else self.manager
         if not hasattr(self, 'data_type_template_path'):
             self.data_type_template_path = 'datatype/sql/' + (
                 '#{0}#{1}#'.format(
-                    self.manager.server_type,
-                    self.manager.version
-                ) if self.manager.server_type == 'gpdb' else
-                '#{0}#'.format(self.manager.version)
+                    manager.server_type,
+                    manager.version
+                ) if manager.server_type == 'gpdb' else
+                '#{0}#'.format(manager.version)
             )
         sql = render_template(
             "/".join([self.data_type_template_path, 'get_types.sql']),
@@ -701,3 +703,23 @@ def get_schema(sid, did, scid):
     )
 
     return status, schema_name
+
+
+def get_schemas(conn, show_system_objects=False):
+    """
+    This function will return the schemas.
+    """
+
+    ver = conn.manager.version
+    server_type = conn.manager.server_type
+
+    SQL = render_template(
+        "/".join(['schemas',
+                  '{0}/#{1}#'.format(server_type, ver),
+                  'sql/nodes.sql']),
+        show_sysobj=show_system_objects,
+        schema_restrictions=None
+    )
+
+    status, rset = conn.execute_2darray(SQL)
+    return status, rset
diff --git a/web/pgadmin/browser/server_groups/servers/databases/static/js/database.js b/web/pgadmin/browser/server_groups/servers/databases/static/js/database.js
index 3605e1d57..eb5c73f1e 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/static/js/database.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/static/js/database.js
@@ -82,6 +82,10 @@ define('pgadmin.node.database', [
           applies: ['object', 'context'], callback: 'disconnect_database',
           category: 'drop', priority: 5, label: gettext('Disconnect Database...'),
           icon: 'fa fa-unlink', enable : 'is_connected',
+        },{
+          name: 'generate_erd', node: 'database', module: this,
+          applies: ['object', 'context'], callback: 'generate_erd',
+          category: 'erd', priority: 5, label: gettext('Generate ERD(Beta)...'),
         }]);
 
         _.bindAll(this, 'connection_lost');
@@ -236,6 +240,15 @@ define('pgadmin.node.database', [
           return false;
         },
 
+        /* Generate the ERD */
+        generate_erd: function(args) {
+          var input = args || {},
+            t = pgBrowser.tree,
+            i = input.item || t.selected(),
+            d = i && i.length == 1 ? t.itemData(i) : undefined;
+          pgBrowser.erd.showErdTool(d, i, true);
+        },
+
         /* Connect the database (if not connected), before opening this node */
         beforeopen: function(item, data) {
           if(!data || data._type != 'database' || data.label == 'template0') {
diff --git a/web/pgadmin/browser/templates/browser/js/utils.js b/web/pgadmin/browser/templates/browser/js/utils.js
index 516b62697..c0a2fc58e 100644
--- a/web/pgadmin/browser/templates/browser/js/utils.js
+++ b/web/pgadmin/browser/templates/browser/js/utils.js
@@ -68,6 +68,7 @@ define('pgadmin.browser.utils',
     braceMatching: '{{ editor_brace_matching }}' == 'True',
     is_indent_with_tabs: '{{ editor_indent_with_tabs }}' == 'True',
     app_name: '{{ app_name }}',
+    app_version_int: '{{ app_version_int}}',
     pg_libpq_version: {{pg_libpq_version|e}},
     support_ssh_tunnel: '{{ support_ssh_tunnel }}' == 'True',
     logout_url: '{{logout_url}}',
diff --git a/web/pgadmin/static/bundle/browser.js b/web/pgadmin/static/bundle/browser.js
index 7ed9b725d..eba8ffafa 100644
--- a/web/pgadmin/static/bundle/browser.js
+++ b/web/pgadmin/static/bundle/browser.js
@@ -10,6 +10,7 @@
 define('bundled_browser',[
   'pgadmin.browser',
   'sources/browser/index',
+  'top/tools/erd/static/js/index',
 ], function(pgBrowser) {
   pgBrowser.init();
 });
diff --git a/web/pgadmin/static/js/backform.pgadmin.js b/web/pgadmin/static/js/backform.pgadmin.js
index b35cedef0..dca935662 100644
--- a/web/pgadmin/static/js/backform.pgadmin.js
+++ b/web/pgadmin/static/js/backform.pgadmin.js
@@ -1465,6 +1465,10 @@ define([
               collection: collection,
               node_info: self.model.node_info,
             });
+
+            if(data.beforeAdd) {
+              m = data.beforeAdd.apply(self, [m]);
+            }
             collection.add(m);
 
             var idx = collection.indexOf(m),
diff --git a/web/pgadmin/static/js/backgrid.pgadmin.js b/web/pgadmin/static/js/backgrid.pgadmin.js
index def9c18f6..5033965f2 100644
--- a/web/pgadmin/static/js/backgrid.pgadmin.js
+++ b/web/pgadmin/static/js/backgrid.pgadmin.js
@@ -10,10 +10,10 @@
 define([
   'sources/gettext', 'underscore', 'jquery', 'backbone', 'backform', 'backgrid', 'alertify',
   'moment', 'bignumber', 'codemirror', 'sources/utils', 'sources/keyboard_shortcuts', 'sources/select2/configure_show_on_scroll',
-  'bootstrap.datetimepicker', 'backgrid.filter', 'bootstrap.toggle',
+  'sources/window', 'bootstrap.datetimepicker', 'backgrid.filter', 'bootstrap.toggle',
 ], function(
   gettext, _, $, Backbone, Backform, Backgrid, Alertify, moment, BigNumber, CodeMirror,
-  commonUtils, keyboardShortcuts, configure_show_on_scroll
+  commonUtils, keyboardShortcuts, configure_show_on_scroll, pgWindow
 ) {
   /*
    * Add mechanism in backgrid to render different types of cells in
@@ -43,7 +43,7 @@ define([
   // bind shortcut in cell edit mode
   _.extend(Backgrid.InputCellEditor.prototype.events, {
     'keydown': function(e) {
-      let preferences = pgBrowser.get_preferences_for_module('browser');
+      let preferences = pgWindow.default.pgAdmin.Browser.get_preferences_for_module('browser');
       if(preferences && keyboardShortcuts.validateShortcutKeys(preferences.add_grid_row,e)) {
         pgBrowser.keyboardNavigation.bindAddGridRow();
       } else {
@@ -323,7 +323,7 @@ define([
         },
         events: {
           'keydown': function (event) {
-            let preferences = pgBrowser.get_preferences_for_module('browser');
+            let preferences = pgWindow.default.pgAdmin.Browser.get_preferences_for_module('browser');
             if(preferences && keyboardShortcuts.validateShortcutKeys(preferences.add_grid_row,event)) {
               pgBrowser.keyboardNavigation.bindAddGridRow();
             }
@@ -764,7 +764,7 @@ define([
     },
 
     onKeyDown: function(e) {
-      let preferences = pgBrowser.get_preferences_for_module('browser');
+      let preferences = pgWindow.default.pgAdmin.Browser.get_preferences_for_module('browser');
       if(keyboardShortcuts.validateShortcutKeys(preferences.add_grid_row,e)) {
         pgBrowser.keyboardNavigation.bindAddGridRow();
       }
diff --git a/web/pgadmin/static/scss/_bootstrap.overrides.scss b/web/pgadmin/static/scss/_bootstrap.overrides.scss
index 29c4a433c..0d4d68f24 100644
--- a/web/pgadmin/static/scss/_bootstrap.overrides.scss
+++ b/web/pgadmin/static/scss/_bootstrap.overrides.scss
@@ -181,6 +181,15 @@ legend {
     }
 }
 
+.btn-warning {
+  @include button-variant($color-warning, $color-warning-fg);
+  border-color: $color-warning;
+  @include hover() {
+    color: $color-warning-fg !important;
+    border-color: $color-warning !important;
+  }
+}
+
 
 .form-group fieldset {
 	background-color: $color-gray-lighter;
@@ -370,6 +379,11 @@ td.switch-cell > div.toggle {
   line-height: 0.7rem;
 }
 
+.btn-xs {
+  @extend .btn-sm;
+  padding: 0.125rem 0.25rem !important;
+}
+
 .btn-toolbar {
   min-width: 100%;
 }
diff --git a/web/pgadmin/static/scss/_pgadmin.style.scss b/web/pgadmin/static/scss/_pgadmin.style.scss
index fadd71b7b..f8f2bcf99 100644
--- a/web/pgadmin/static/scss/_pgadmin.style.scss
+++ b/web/pgadmin/static/scss/_pgadmin.style.scss
@@ -886,6 +886,12 @@ table.table-noouter-border {
       border-bottom: $table-border-width solid transparent;
     }
   }
+
+  &.table-noheader {
+    & > tbody tr:first-of-type td {
+      border-top: none !important;
+    }
+  }
 }
 
 table.table-bottom-border {
diff --git a/web/pgadmin/static/scss/_tippy.overrides.scss b/web/pgadmin/static/scss/_tippy.overrides.scss
new file mode 100644
index 000000000..9750434b7
--- /dev/null
+++ b/web/pgadmin/static/scss/_tippy.overrides.scss
@@ -0,0 +1,9 @@
+@import "~node_modules/tippy.js/dist/tippy.css";
+
+.tippy-box {
+  background-color: $popover-bg;
+  color: $popover-body-color;
+  .tippy-arrow {
+    color: $popover-bg;
+  }
+}
diff --git a/web/pgadmin/static/scss/pgadmin.scss b/web/pgadmin/static/scss/pgadmin.scss
index 19e79adf2..cc751b77e 100644
--- a/web/pgadmin/static/scss/pgadmin.scss
+++ b/web/pgadmin/static/scss/pgadmin.scss
@@ -13,7 +13,6 @@ $theme-colors: (
 }
 
 @import "node_modules/bootstrap/scss/bootstrap";
-
 @import 'webcabin.pgadmin';
 @import 'bootstrap.overrides';
 @import 'backgrid.overrides';
@@ -27,3 +26,4 @@ $theme-colors: (
 @import 'pgadmin.style';
 @import 'bootstrap4-toggle.overrides';
 @import 'pickr.overrides';
+@import 'tippy.overrides';
diff --git a/web/pgadmin/static/scss/resources/_default.variables.scss b/web/pgadmin/static/scss/resources/_default.variables.scss
index 9d913788d..b71c86a4c 100644
--- a/web/pgadmin/static/scss/resources/_default.variables.scss
+++ b/web/pgadmin/static/scss/resources/_default.variables.scss
@@ -111,8 +111,8 @@ $dropdown-spacer: .125rem; //no-change
 $dropdown-link-disabled-color: $text-muted;
 $nav-divider-margin-y: .25rem;
 
-$popover-bg: $color-gray-dark !default;
-$popover-body-color: $white !default;
+$popover-bg: $color-fg !default;
+$popover-body-color: $color-bg !default;
 $popover-border-color: $dropdown-border-color;
 $popover-box-shadow: $dropdown-box-shadow;
 
@@ -349,3 +349,16 @@ $grid-hover-fg-color: $color-fg !default;
 
 $btn-copied-color-fg: $active-color !default;
 
+/** ERD **/
+$erd-row-padding: 0.25rem;
+$erd-node-border-color: $border-color !default;
+$erd-canvas-bg: $color-bg !default;
+$erd-canvas-grid: $color-gray !default;
+$erd-link-color: $color-fg !default;
+$erd-link-selected-color: $color-fg !default;
+
+
+@function url-friendly-colour($colour) {
+  @return '%23' + str-slice('#{$colour}', 2, -1)
+}
+$erd-bg-grid: url("data:image/svg+xml, %3Csvg width='100%25' viewBox='0 0 45 45' style='background-color:#{url-friendly-colour($erd-canvas-bg)}' height='100%25' xmlns='http://www.w3.org/2000/svg'%3E%3Cdefs%3E%3Cpattern id='smallGrid' width='15' height='15' patternUnits='userSpaceOnUse'%3E%3Cpath d='M 15 0 L 0 0 0 15' fill='none' stroke='#{url-friendly-colour($erd-canvas-grid)}' stroke-width='0.5'/%3E%3C/pattern%3E%3Cpattern id='grid' width='45' height='45' patternUnits='userSpaceOnUse'%3E%3Crect width='100' height='100' fill='url(%23smallGrid)'/%3E%3Cpath d='M 100 0 L 0 0 0 100' fill='none' stroke='#{url-friendly-colour($erd-canvas-grid)}' stroke-width='1'/%3E%3C/pattern%3E%3C/defs%3E%3Crect width='100%25' height='100%25' fill='url(%23grid)' /%3E%3C/svg%3E%0A");
diff --git a/web/pgadmin/static/scss/resources/dark/_theme.variables.scss b/web/pgadmin/static/scss/resources/dark/_theme.variables.scss
index 129b9a214..d8ca65b50 100644
--- a/web/pgadmin/static/scss/resources/dark/_theme.variables.scss
+++ b/web/pgadmin/static/scss/resources/dark/_theme.variables.scss
@@ -121,3 +121,11 @@ $color-success-hover-fg: $color-fg;
 $datagrid-selected-color: $color-primary-fg;
 
 $select2-placeholder: #999;
+
+/* ERD */
+$erd-row-padding: 0.25rem;
+$erd-node-border-color: $border-color;
+$erd-canvas-bg: $color-gray-light;
+$erd-canvas-grid: #444952;
+$erd-link-color: $color-fg;
+$erd-link-selected-color: $color-fg;
diff --git a/web/pgadmin/tools/datagrid/static/js/datagrid_panel_title.js b/web/pgadmin/tools/datagrid/static/js/datagrid_panel_title.js
index bae5e36d6..53197cd83 100644
--- a/web/pgadmin/tools/datagrid/static/js/datagrid_panel_title.js
+++ b/web/pgadmin/tools/datagrid/static/js/datagrid_panel_title.js
@@ -20,16 +20,17 @@ function isServerInformationAvailable(parentData) {
   return parentData.server === undefined;
 }
 
-export function getPanelTitle(pgBrowser, selected_item=null, custom_title=null) {
+export function getPanelTitle(pgBrowser, selected_item=null, custom_title=null, parentData=null) {
   var preferences = pgBrowser.get_preferences_for_module('browser');
-  if(selected_item == null) {
+  if(selected_item == null && parentData == null) {
     selected_item = pgBrowser.treeMenu.selected();
   }
 
-  const parentData = getTreeNodeHierarchyFromIdentifier
-    .call(pgBrowser, selected_item);
-  if (isServerInformationAvailable(parentData)) {
-    return;
+  if(parentData == null) {
+    parentData = getTreeNodeHierarchyFromIdentifier.call(pgBrowser, selected_item);
+    if (isServerInformationAvailable(parentData)) {
+      return;
+    }
   }
 
   const db_label = getDatabaseLabel(parentData);
diff --git a/web/pgadmin/tools/datagrid/static/js/show_query_tool.js b/web/pgadmin/tools/datagrid/static/js/show_query_tool.js
index a28091268..36abadac3 100644
--- a/web/pgadmin/tools/datagrid/static/js/show_query_tool.js
+++ b/web/pgadmin/tools/datagrid/static/js/show_query_tool.js
@@ -18,7 +18,7 @@ function hasDatabaseInformation(parentData) {
   return parentData.database;
 }
 
-function generateUrl(trans_id, title, parentData) {
+function generateUrl(trans_id, title, parentData, sqlId) {
   let url_endpoint = url_for('datagrid.panel', {
     'trans_id': trans_id,
   });
@@ -32,6 +32,10 @@ function generateUrl(trans_id, title, parentData) {
     url_endpoint += `&did=${parentData.database._id}`;
   }
 
+  if(sqlId) {
+    url_endpoint += `&sql_id=${sqlId}`;
+  }
+
   return url_endpoint;
 }
 
@@ -85,6 +89,25 @@ export function generateScript(parentData, datagrid, alertify) {
   launchDataGrid(datagrid, transId, url_endpoint, queryToolTitle, '', alertify);
 }
 
+export function showERDSqlTool(parentData, erdSqlId, queryToolTitle, datagrid, alertify) {
+  const transId = getRandomInt(1, 9999999);
+  parentData = {
+    server_group: {
+      _id: parentData.sgid,
+    },
+    server: {
+      _id: parentData.sid,
+      server_type: parentData.stype,
+    },
+    database: {
+      _id: parentData.did,
+    },
+  };
+
+  const gridUrl = generateUrl(transId, queryToolTitle, parentData, erdSqlId);
+  launchDataGrid(datagrid, transId, gridUrl, queryToolTitle, '', alertify);
+}
+
 export function launchDataGrid(datagrid, transId, gridUrl, queryToolTitle, sURL, alertify) {
   let retVal = datagrid.launch_grid(transId, gridUrl, true, queryToolTitle, sURL);
 
diff --git a/web/pgadmin/tools/erd/__init__.py b/web/pgadmin/tools/erd/__init__.py
new file mode 100644
index 000000000..1cff397f2
--- /dev/null
+++ b/web/pgadmin/tools/erd/__init__.py
@@ -0,0 +1,587 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+"""A blueprint module implementing the schema_diff frame."""
+import simplejson as json
+
+from flask import url_for, request
+from flask import render_template, current_app as app
+from flask_security import login_required
+from flask_babelex import gettext
+from werkzeug.useragents import UserAgent
+from pgadmin.utils import PgAdminModule, \
+    SHORTCUT_FIELDS as shortcut_fields
+from pgadmin.utils.ajax import make_json_response, bad_request, \
+    internal_server_error
+from pgadmin.model import Server
+from config import PG_DEFAULT_DRIVER
+from pgadmin.utils.driver import get_driver
+from pgadmin.browser.utils import underscore_unescape
+from pgadmin.utils.exception import ConnectionLost, SSHTunnelConnectionLost
+from pgadmin.browser.server_groups.servers.databases.schemas.utils \
+    import DataTypeReader
+from pgadmin.browser.server_groups.servers.databases.schemas.utils \
+    import get_schemas
+from pgadmin.browser.server_groups.servers.databases.schemas.tables. \
+    constraints.foreign_key import utils as fkey_utils
+from pgadmin.utils.constants import PREF_LABEL_DISPLAY, \
+    PREF_LABEL_KEYBOARD_SHORTCUTS
+from .utils import ERDHelper
+
+MODULE_NAME = 'erd'
+
+
+class ERDModule(PgAdminModule):
+    """
+    class SchemaDiffModule(PgAdminModule)
+
+        A module class for ERD derived from PgAdminModule.
+    """
+
+    LABEL = gettext("ERD tool")
+
+    def get_own_menuitems(self):
+        return {}
+
+    def get_own_javascripts(self):
+        return [{
+            'name': 'pgadmin.erd',
+            'path': url_for('erd.index') + "erd",
+            'when': None
+        }]
+
+    def get_panels(self):
+        return []
+
+    def get_exposed_url_endpoints(self):
+        """
+        Returns:
+            list: URL endpoints for Schema Diff module
+        """
+        return [
+            'erd.panel',
+            'erd.initialize',
+            'erd.prequisite',
+            'erd.sql',
+            'erd.tables',
+            'erd.close'
+        ]
+
+    def register_preferences(self):
+        self.preference.register(
+            'display', 'erd_new_browser_tab',
+            gettext("Open in new browser tab"), 'boolean', False,
+            category_label=gettext('Display'),
+            help_str=gettext('If set to True, the ERD tool '
+                             'will be opened in a new browser tab.')
+        )
+
+        self.preference.register(
+            'keyboard_shortcuts',
+            'open_project',
+            gettext('Open project'),
+            'keyboardshortcut',
+            {
+                'alt': False,
+                'shift': False,
+                'control': True,
+                'key': {
+                    'key_code': 79,
+                    'char': 'o'
+                }
+            },
+            category_label=PREF_LABEL_KEYBOARD_SHORTCUTS,
+            fields=shortcut_fields
+        )
+
+        self.preference.register(
+            'keyboard_shortcuts',
+            'save_project',
+            gettext('Save project'),
+            'keyboardshortcut',
+            {
+                'alt': False,
+                'shift': False,
+                'control': True,
+                'key': {
+                    'key_code': 83,
+                    'char': 's'
+                }
+            },
+            category_label=PREF_LABEL_KEYBOARD_SHORTCUTS,
+            fields=shortcut_fields
+        )
+
+        self.preference.register(
+            'keyboard_shortcuts',
+            'save_project_as',
+            gettext('Save project as'),
+            'keyboardshortcut',
+            {
+                'alt': False,
+                'shift': True,
+                'control': True,
+                'key': {
+                    'key_code': 83,
+                    'char': 's'
+                }
+            },
+            category_label=PREF_LABEL_KEYBOARD_SHORTCUTS,
+            fields=shortcut_fields
+        )
+
+        self.preference.register(
+            'keyboard_shortcuts',
+            'generate_sql',
+            gettext('Generate SQL'),
+            'keyboardshortcut',
+            {
+                'alt': True,
+                'shift': False,
+                'control': True,
+                'key': {
+                    'key_code': 83,
+                    'char': 's'
+                }
+            },
+            category_label=PREF_LABEL_KEYBOARD_SHORTCUTS,
+            fields=shortcut_fields
+        )
+
+        self.preference.register(
+            'keyboard_shortcuts',
+            'download_image',
+            gettext('Download image'),
+            'keyboardshortcut',
+            {
+                'alt': True,
+                'shift': False,
+                'control': True,
+                'key': {
+                    'key_code': 73,
+                    'char': 'i'
+                }
+            },
+            category_label=PREF_LABEL_KEYBOARD_SHORTCUTS,
+            fields=shortcut_fields
+        )
+
+        self.preference.register(
+            'keyboard_shortcuts',
+            'add_table',
+            gettext('Add table'),
+            'keyboardshortcut',
+            {
+                'alt': True,
+                'shift': False,
+                'control': True,
+                'key': {
+                    'key_code': 65,
+                    'char': 'a'
+                }
+            },
+            category_label=PREF_LABEL_KEYBOARD_SHORTCUTS,
+            fields=shortcut_fields
+        )
+
+        self.preference.register(
+            'keyboard_shortcuts',
+            'edit_table',
+            gettext('Edit table'),
+            'keyboardshortcut',
+            {
+                'alt': True,
+                'shift': False,
+                'control': True,
+                'key': {
+                    'key_code': 69,
+                    'char': 'e'
+                }
+            },
+            category_label=PREF_LABEL_KEYBOARD_SHORTCUTS,
+            fields=shortcut_fields
+        )
+
+        self.preference.register(
+            'keyboard_shortcuts',
+            'clone_table',
+            gettext('Clone table'),
+            'keyboardshortcut',
+            {
+                'alt': True,
+                'shift': False,
+                'control': True,
+                'key': {
+                    'key_code': 67,
+                    'char': 'c'
+                }
+            },
+            category_label=PREF_LABEL_KEYBOARD_SHORTCUTS,
+            fields=shortcut_fields
+        )
+
+        self.preference.register(
+            'keyboard_shortcuts',
+            'drop_table',
+            gettext('Drop table'),
+            'keyboardshortcut',
+            {
+                'alt': True,
+                'shift': False,
+                'control': True,
+                'key': {
+                    'key_code': 68,
+                    'char': 'd'
+                }
+            },
+            category_label=PREF_LABEL_KEYBOARD_SHORTCUTS,
+            fields=shortcut_fields
+        )
+
+        self.preference.register(
+            'keyboard_shortcuts',
+            'add_edit_note',
+            gettext('Add/Edit note'),
+            'keyboardshortcut',
+            {
+                'alt': True,
+                'shift': False,
+                'control': True,
+                'key': {
+                    'key_code': 78,
+                    'char': 'n'
+                }
+            },
+            category_label=PREF_LABEL_KEYBOARD_SHORTCUTS,
+            fields=shortcut_fields
+        )
+
+        self.preference.register(
+            'keyboard_shortcuts',
+            'one_to_many',
+            gettext('One to many link'),
+            'keyboardshortcut',
+            {
+                'alt': True,
+                'shift': False,
+                'control': True,
+                'key': {
+                    'key_code': 79,
+                    'char': 'o'
+                }
+            },
+            category_label=PREF_LABEL_KEYBOARD_SHORTCUTS,
+            fields=shortcut_fields
+        )
+
+        self.preference.register(
+            'keyboard_shortcuts',
+            'many_to_many',
+            gettext('Many to many link'),
+            'keyboardshortcut',
+            {
+                'alt': True,
+                'shift': False,
+                'control': True,
+                'key': {
+                    'key_code': 77,
+                    'char': 'm'
+                }
+            },
+            category_label=PREF_LABEL_KEYBOARD_SHORTCUTS,
+            fields=shortcut_fields
+        )
+
+        self.preference.register(
+            'keyboard_shortcuts',
+            'auto_align',
+            gettext('Auto align'),
+            'keyboardshortcut',
+            {
+                'alt': True,
+                'shift': False,
+                'control': True,
+                'key': {
+                    'key_code': 76,
+                    'char': 'l'
+                }
+            },
+            category_label=PREF_LABEL_KEYBOARD_SHORTCUTS,
+            fields=shortcut_fields
+        )
+
+        self.preference.register(
+            'keyboard_shortcuts',
+            'zoom_to_fit',
+            gettext('Zoom to fit'),
+            'keyboardshortcut',
+            {
+                'alt': True,
+                'shift': True,
+                'control': False,
+                'key': {
+                    'key_code': 70,
+                    'char': 'f'
+                }
+            },
+            category_label=PREF_LABEL_KEYBOARD_SHORTCUTS,
+            fields=shortcut_fields
+        )
+
+        self.preference.register(
+            'keyboard_shortcuts',
+            'zoom_in',
+            gettext('Zoom in'),
+            'keyboardshortcut',
+            {
+                'alt': True,
+                'shift': True,
+                'control': False,
+                'key': {
+                    'key_code': 187,
+                    'char': '+'
+                }
+            },
+            category_label=PREF_LABEL_KEYBOARD_SHORTCUTS,
+            fields=shortcut_fields
+        )
+
+        self.preference.register(
+            'keyboard_shortcuts',
+            'zoom_out',
+            gettext('Zoom out'),
+            'keyboardshortcut',
+            {
+                'alt': True,
+                'shift': True,
+                'control': False,
+                'key': {
+                    'key_code': 189,
+                    'char': '-'
+                }
+            },
+            category_label=PREF_LABEL_KEYBOARD_SHORTCUTS,
+            fields=shortcut_fields
+        )
+
+
+blueprint = ERDModule(MODULE_NAME, __name__, static_url_path='/static')
+
+
+@blueprint.route(
+    '/panel/<int:trans_id>',
+    methods=["POST"],
+    endpoint='panel'
+)
+@login_required
+def panel(trans_id):
+    """
+    This method calls index.html to render the schema diff.
+
+    Args:
+        panel_title: Title of the panel
+    """
+
+    params = {
+        'trans_id': trans_id,
+        'title': request.form['title']
+    }
+    if request.args:
+        params.update({k: v for k, v in request.args.items()})
+
+    if 'gen' in params:
+        params['gen'] = True if params['gen'] == 'true' else False
+
+    close_url = request.form['close_url']
+
+    # We need client OS information to render correct Keyboard shortcuts
+    user_agent = UserAgent(request.headers.get('User-Agent'))
+
+    """
+    Animations and transitions are not automatically GPU accelerated and by
+    default use browser's slow rendering engine. We need to set 'translate3d'
+    value of '-webkit-transform' property in order to use GPU. After applying
+    this property under linux, Webkit calculates wrong position of the
+    elements so panel contents are not visible. To make it work, we need to
+    explicitly set '-webkit-transform' property to 'none' for .ajs-notifier,
+    .ajs-message, .ajs-modal classes.
+
+    This issue is only with linux runtime application and observed in Query
+    tool and debugger. When we open 'Open File' dialog then whole Query tool
+    panel content is not visible though it contains HTML element in back end.
+
+    The port number should have already been set by the runtime if we're
+    running in desktop mode.
+    """
+    is_linux_platform = False
+
+    from sys import platform as _platform
+    if "linux" in _platform:
+        is_linux_platform = True
+
+    s = Server.query.filter_by(id=params['sid']).first()
+
+    params.update({
+        'bgcolor': s.bgcolor,
+        'fgcolor': s.fgcolor,
+        'client_platform': user_agent.platform,
+        'is_desktop_mode': app.PGADMIN_RUNTIME,
+        'is_linux': is_linux_platform
+    })
+
+    return render_template(
+        "erd/index.html",
+        title=underscore_unescape(params['title']),
+        close_url=close_url,
+        params=json.dumps(params),
+    )
+
+
+@blueprint.route(
+    '/initialize/<int:trans_id>/<int:sgid>/<int:sid>/<int:did>',
+    methods=["POST"], endpoint='initialize'
+)
+@login_required
+def initialize_erd(trans_id, sgid, sid, did):
+    """
+    This method is responsible for instantiating and initializing
+    the erd tool object. It will also create a unique
+    transaction id and store the information into session variable.
+
+    Args:
+        sgid: Server group Id
+        sid: Server Id
+        did: Database Id
+    """
+    connect = True
+    # Read the data if present. Skipping read may cause connection
+    # reset error if data is sent from the client
+    if request.data:
+        _ = request.data
+
+    # req_args = request.args
+    # if ('recreate' in req_args and
+    #     req_args['recreate'] == '1'):
+    #     connect = False
+
+    conn = _get_connection(sid, did, trans_id)
+
+    return make_json_response(
+        data={
+            'connId': str(trans_id),
+            'serverVersion': conn.manager.version,
+        }
+    )
+
+
+def _get_connection(sid, did, trans_id):
+    """
+    Get the connection object of ERD.
+    :param sid:
+    :param did:
+    :param trans_id:
+    :return:
+    """
+    manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
+    try:
+        conn = manager.connection(did=did, conn_id=trans_id,
+                                  auto_reconnect=True,
+                                  use_binary_placeholder=True)
+        status, msg = conn.connect()
+        if not status:
+            app.logger.error(msg)
+            return internal_server_error(errormsg=str(msg))
+
+        return conn
+    except Exception as e:
+        app.logger.error(e)
+        raise
+
+
+@blueprint.route('/prequisite/<int:trans_id>/<int:sgid>/<int:sid>/<int:did>',
+                 methods=["GET"],
+                 endpoint='prequisite')
+@login_required
+def prequisite(trans_id, sgid, sid, did):
+    conn = _get_connection(sid, did, trans_id)
+    helper = ERDHelper(trans_id, sid, did)
+    status, col_types = helper.get_types()
+
+    if not status:
+        return internal_server_error(errormsg=col_types)
+
+    status, schemas = get_schemas(conn, show_system_objects=False)
+
+    if not status:
+        return internal_server_error(errormsg=schemas)
+
+    return make_json_response(
+        data={
+            'col_types': col_types,
+            'schemas': schemas['rows']
+        },
+        status=200
+    )
+
+
+@blueprint.route('/sql/<int:trans_id>/<int:sgid>/<int:sid>/<int:did>',
+                 methods=["POST"],
+                 endpoint='sql')
+@login_required
+def sql(trans_id, sgid, sid, did):
+    data = json.loads(request.data, encoding='utf-8')
+    helper = ERDHelper(trans_id, sid, did)
+    conn = _get_connection(sid, did, trans_id)
+
+    sql = ''
+    for tab_key, tab_data in data.get('nodes', {}).items():
+        sql += '\n\n' + helper.get_table_sql(tab_data)
+
+    for link_key, link_data in data.get('links', {}).items():
+        link_sql, name = fkey_utils.get_sql(conn, link_data, None)
+        sql += '\n\n' + link_sql
+
+    return make_json_response(
+        data=sql,
+        status=200
+    )
+
+
+@blueprint.route('/tables/<int:trans_id>/<int:sgid>/<int:sid>/<int:did>',
+                 methods=["GET"],
+                 endpoint='tables')
+@login_required
+def tables(trans_id, sgid, sid, did):
+    helper = ERDHelper(trans_id, sid, did)
+    status, tables = helper.get_all_tables()
+
+    if not status:
+        return internal_server_error(errormsg=tables)
+
+    return make_json_response(
+        data=tables,
+        status=200
+    )
+
+
+@blueprint.route('/close/<int:trans_id>/<int:sgid>/<int:sid>/<int:did>',
+                 methods=["DELETE"],
+                 endpoint='close')
+@login_required
+def close(trans_id, sgid, sid, did):
+    manager = get_driver(
+        PG_DEFAULT_DRIVER).connection_manager(sid)
+    if manager is not None:
+        conn = manager.connection(did=did, conn_id=trans_id)
+
+        # Release the connection
+        if conn.connected():
+            conn.cancel_transaction(trans_id, did=did)
+            manager.release(did=did, conn_id=trans_id)
+    return make_json_response(data={'status': True})
diff --git a/web/pgadmin/tools/erd/static/js/erd_module.js b/web/pgadmin/tools/erd/static/js/erd_module.js
new file mode 100644
index 000000000..1291833de
--- /dev/null
+++ b/web/pgadmin/tools/erd/static/js/erd_module.js
@@ -0,0 +1,215 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2020, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import Alertify from 'pgadmin.alertifyjs';
+import {getTreeNodeHierarchyFromIdentifier} from 'sources/tree/pgadmin_tree_node';
+import {getPanelTitle} from 'tools/datagrid/static/js/datagrid_panel_title';
+import {getRandomInt} from 'sources/utils';
+
+
+export function setPanelTitle(erdToolPanel, panelTitle) {
+  erdToolPanel.title('<span title="'+panelTitle+'">'+panelTitle+'</span>');
+}
+
+export function initialize(gettext, url_for, $, _, pgAdmin, csrfToken, pgBrowser, wcDocker) {
+  /* Return back, this has been called more than once */
+  if (pgBrowser.erd)
+    return pgBrowser.erd;
+
+  pgBrowser.erd = {
+    init: function() {
+      if (this.initialized)
+        return;
+
+      this.initialized = true;
+      csrfToken.setPGCSRFToken(pgAdmin.csrf_token_header, pgAdmin.csrf_token);
+
+
+      // Define the nodes on which the menus to be appear
+      var menus = [{
+        name: 'erd',
+        module: this,
+        applies: ['tools'],
+        callback: 'showErdTool',
+        priority: 1,
+        label: gettext('New ERD project(Beta)...'),
+        enable: this.erdToolEnabled,
+      }];
+
+      pgBrowser.add_menus(menus);
+
+      // Creating a new pgBrowser frame to show the data.
+      var erdFrameType = new pgBrowser.Frame({
+        name: 'frm_erdtool',
+        showTitle: true,
+        isCloseable: true,
+        isPrivate: true,
+        url: 'about:blank',
+      });
+
+      let self = this;
+      /* Cache may take time to load for the first time
+       * Keep trying till available
+       */
+      let cacheIntervalId = setInterval(function() {
+        if(pgBrowser.preference_version() > 0) {
+          self.preferences = pgBrowser.get_preferences_for_module('erd');
+          clearInterval(cacheIntervalId);
+        }
+      },0);
+
+      pgBrowser.onPreferencesChange('erd', function() {
+        self.preferences = pgBrowser.get_preferences_for_module('erd');
+      });
+
+      // Load the newly created frame
+      erdFrameType.load(pgBrowser.docker);
+      return this;
+    },
+
+    erdToolEnabled: function(obj) {
+      /* Same as query tool */
+      var isEnabled = (() => {
+        if (!_.isUndefined(obj) && !_.isNull(obj)) {
+          if (_.indexOf(pgAdmin.unsupported_nodes, obj._type) == -1) {
+            if (obj._type == 'database' && obj.allowConn) {
+              return true;
+            } else if (obj._type != 'database') {
+              return true;
+            } else {
+              return false;
+            }
+          } else {
+            return false;
+          }
+        } else {
+          return false;
+        }
+      })();
+      return isEnabled;
+    },
+
+    // Callback to draw schema diff for objects
+    showErdTool: function(data, aciTreeIdentifier, gen=false) {
+      const node = pgBrowser.treeMenu.findNodeByDomElement(aciTreeIdentifier);
+      if (node === undefined || !node.getData()) {
+        Alertify.alert(
+          gettext('ERD Error'),
+          gettext('No object selected.')
+        );
+        return;
+      }
+
+      const parentData = getTreeNodeHierarchyFromIdentifier.call(
+        pgBrowser,
+        aciTreeIdentifier
+      );
+
+      if(_.isUndefined(parentData.database)) {
+        Alertify.alert(
+          gettext('ERD Error'),
+          gettext('Please select a database/database object.')
+        );
+        return;
+      }
+
+      const transId = getRandomInt(1, 9999999);
+      const panelTitle = getPanelTitle(pgBrowser, aciTreeIdentifier);
+      const [panelUrl, panelCloseUrl] = this.getPanelUrls(transId, panelTitle, parentData, gen);
+
+      let self = this;
+      let erdToolForm = `
+        <form id="erdToolForm" action="${panelUrl}" method="post">
+          <input id="title" name="title" hidden />
+          <input name="close_url" value="${panelCloseUrl}" hidden />
+        </form>
+        <script>
+          document.getElementById("title").value = "${_.escape(panelTitle)}";
+          document.getElementById("erdToolForm").submit();
+        </script>
+      `;
+
+      if (self.preferences.new_browser_tab) {
+        var newWin = window.open('', '_blank');
+        newWin.document.write(erdToolForm);
+        newWin.document.title = panelTitle;
+      } else {
+        /* On successfully initialization find the dashboard panel,
+         * create new panel and add it to the dashboard panel.
+         */
+        var propertiesPanel = pgBrowser.docker.findPanels('properties');
+        var erdToolPanel = pgBrowser.docker.addPanel('frm_erdtool', wcDocker.DOCK.STACKED, propertiesPanel[0]);
+
+        // Set panel title and icon
+        setPanelTitle(erdToolPanel, 'Untitled');
+        erdToolPanel.icon('fa fa-sitemap');
+        erdToolPanel.focus();
+
+        // Listen on the panel closed event.
+        erdToolPanel.on(wcDocker.EVENT.CLOSED, function() {
+          $.ajax({
+            url: panelCloseUrl,
+            method: 'DELETE',
+          });
+        });
+
+        var openErdToolURL = function(j) {
+          // add spinner element
+          let $spinner_el =
+            $(`<div class="pg-sp-container">
+                  <div class="pg-sp-content">
+                      <div class="row">
+                          <div class="col-12 pg-sp-icon"></div>
+                      </div>
+                  </div>
+              </div>`).appendTo($(j).data('embeddedFrame').$container);
+
+          let init_poller_id = setInterval(function() {
+            var frameInitialized = $(j).data('frameInitialized');
+            if (frameInitialized) {
+              clearInterval(init_poller_id);
+              var frame = $(j).data('embeddedFrame');
+              if (frame) {
+                frame.onLoaded(()=>{
+                  $spinner_el.remove();
+                });
+                frame.openHTML(erdToolForm);
+              }
+            }
+          }, 100);
+        };
+
+        openErdToolURL(erdToolPanel);
+      }
+    },
+
+    getPanelUrls: function(transId, panelTitle, parentData, gen) {
+      let openUrl = url_for('erd.panel', {
+        trans_id: transId,
+      });
+
+      openUrl += `?sgid=${parentData.server_group._id}`
+        +`&sid=${parentData.server._id}`
+        +`&server_type=${parentData.server.server_type}`
+        +`&did=${parentData.database._id}`
+        +`&gen=${gen}`;
+
+      let closeUrl = url_for('erd.close', {
+        trans_id: transId,
+        sgid: parentData.server_group._id,
+        sid: parentData.server._id,
+        did: parentData.database._id,
+      });
+
+      return [openUrl, closeUrl];
+    },
+  };
+
+  return pgBrowser.erd;
+}
diff --git a/web/pgadmin/tools/erd/static/js/erd_tool/ERDCore.js b/web/pgadmin/tools/erd/static/js/erd_tool/ERDCore.js
new file mode 100644
index 000000000..f64a84f5e
--- /dev/null
+++ b/web/pgadmin/tools/erd/static/js/erd_tool/ERDCore.js
@@ -0,0 +1,395 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2020, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+/*
+ * The ERDCore is the middleware between the canvas engine and the UI DOM.
+ */
+import createEngine from '@projectstorm/react-diagrams';
+import {DagreEngine, PathFindingLinkFactory, PortModelAlignment} from '@projectstorm/react-diagrams';
+import { ZoomCanvasAction } from '@projectstorm/react-canvas-core';
+
+import {TableNodeFactory, TableNodeModel } from './nodes/TableNode';
+import {OneToManyLinkFactory, OneToManyLinkModel } from './links/OneToManyLink';
+import { OneToManyPortFactory } from './ports/OneToManyPort';
+import ERDModel from './ERDModel';
+
+export default class ERDCore {
+  constructor() {
+    this._cache = {};
+    this.table_counter = 1;
+    this.node_position_updating = false;
+    this.link_position_updating = false;
+    this.initializeEngine();
+    this.initializeModel();
+    this.computeTableCounter();
+  }
+
+  initializeEngine() {
+    this.engine = createEngine({
+      registerDefaultDeleteItemsAction: false,
+      registerDefaultZoomCanvasAction: false,
+    });
+    this.dagre_engine = new DagreEngine({
+      graph: {
+        marginx: 5,
+        marginy: 5,
+      },
+      includeLinks: true,
+    });
+
+    this.engine.getNodeFactories().registerFactory(new TableNodeFactory());
+    this.engine.getLinkFactories().registerFactory(new OneToManyLinkFactory());
+    this.engine.getPortFactories().registerFactory(new OneToManyPortFactory());
+    this.registerKeyAction(new ZoomCanvasAction({inverseZoom: true}));
+  }
+
+  initializeModel(data, callback=()=>{}) {
+    let model = new ERDModel();
+    if(data) {
+      model.deserializeModel(data, this.engine);
+    }
+
+    const registerNodeEvents = (node) => {
+      node.registerListener({
+        eventDidFire: (e) => {
+          if(e.function === 'selectionChanged') {
+            this.fireEvent({}, 'nodesSelectionChanged', true);
+          }
+          else if(e.function === 'showNote') {
+            this.fireEvent({node: e.entity}, 'showNote', true);
+          }
+          else if(e.function === 'editNode') {
+            this.fireEvent({node: e.entity}, 'editNode', true);
+          }
+          else if(e.function === 'nodeUpdated') {
+            this.fireEvent({}, 'nodesUpdated', true);
+          }
+          else if(e.function === 'positionChanged') {
+            /* Eat up the excessive positionChanged events if node is dragged continuosly */
+            if(!this.node_position_updating) {
+              this.node_position_updating = true;
+              this.fireEvent({}, 'nodesUpdated', true);
+              setTimeout(()=>{
+                this.node_position_updating = false;
+              }, 500);
+            }
+          }
+        },
+      });
+    };
+
+    const registerLinkEvents = (link) => {
+      link.registerListener({
+        eventDidFire: (e) => {
+          if(e.function === 'selectionChanged') {
+            this.fireEvent({}, 'linksSelectionChanged', true);
+          }
+          else if(e.function === 'positionChanged') {
+            /* positionChanged is triggered manually in Link */
+            /* Eat up the excessive positionChanged events if link is dragged continuosly */
+            if(!this.link_position_updating) {
+              this.link_position_updating = true;
+              this.fireEvent({}, 'linksUpdated', true);
+              setTimeout(()=>{
+                this.link_position_updating = false;
+              }, 500);
+            }
+          }
+        },
+      });
+    };
+
+    /* Register events for deserialized data */
+    model.getNodes().forEach(node => {
+      registerNodeEvents(node);
+    });
+    model.getLinks().forEach(link => {
+      registerLinkEvents(link);
+    });
+
+    /* Listen and register events for new data */
+    model.registerListener({
+      'nodesUpdated': (e)=>{
+        if(e.isCreated) {
+          registerNodeEvents(e.node);
+        }
+      },
+      'linksUpdated': (e)=>{
+        if(e.isCreated) {
+          registerLinkEvents(e.link);
+        }
+      },
+    });
+
+    model.setGridSize(15);
+    this.engine.setModel(model);
+    callback();
+  }
+
+  computeTableCounter() {
+    /* Some inteligence can be added to set the counter */
+    this.table_counter = 1;
+  }
+
+  setCache(data, value) {
+    if(typeof(data) == 'string') {
+      this._cache[data] = value;
+    } else {
+      this._cache = {
+        ...this._cache,
+        ...data,
+      };
+    }
+  }
+
+  getCache(key) {
+    return key ? this._cache[key]: this._cache;
+  }
+
+  registerModelEvent(eventName, callback) {
+    this.getModel().registerListener({
+      [eventName]: callback,
+    });
+  }
+
+  getNextTableName() {
+    let newTableName = `newtable${this.table_counter}`;
+    this.table_counter++;
+    return newTableName;
+  }
+
+  getEngine() {return this.engine;}
+
+  getModel() {return this.getEngine().getModel();}
+
+  getNewNode(initData) {
+    return this.getEngine().getNodeFactories().getFactory('table').generateModel({
+      initialConfig: {
+        otherInfo: {
+          data:initData,
+        },
+      },
+    });
+  }
+
+  getNewLink(type, initData) {
+    return this.getEngine().getLinkFactories().getFactory(type).generateModel({
+      initialConfig: {
+        data:initData,
+      },
+    });
+  }
+
+  getNewPort(type, initData, initOptions) {
+    return this.getEngine().getPortFactories().getFactory(type).generateModel({
+      initialConfig: {
+        data:initData,
+        options:initOptions,
+      },
+    });
+  }
+
+  addNode(data, position=[50, 50]) {
+    let newNode = this.getNewNode(data);
+    this.clearSelection();
+    newNode.setPosition(position[0], position[1]);
+    this.getModel().addNode(newNode);
+    return newNode;
+  }
+
+  addLink(data, type) {
+    let tableNodesDict = this.getModel().getNodesDict();
+    let sourceNode = tableNodesDict[data.referenced_table_uid];
+    let targetNode = tableNodesDict[data.local_table_uid];
+
+    let portName = sourceNode.getPortName(data.referenced_column_attnum);
+    let sourcePort = sourceNode.getPort(portName);
+    /* Create the port if not there */
+    if(!sourcePort) {
+      sourcePort = sourceNode.addPort(this.getNewPort(type, null, {name:portName, alignment:PortModelAlignment.RIGHT}));
+    }
+
+    portName = targetNode.getPortName(data.local_column_attnum);
+    let targetPort = targetNode.getPort(portName);
+    /* Create the port if not there */
+    if(!targetPort) {
+      targetPort = targetNode.addPort(this.getNewPort(type, null, {name:portName, alignment:PortModelAlignment.RIGHT}));
+    }
+
+    /* Link the ports */
+    let newLink = this.getNewLink(type, data);
+    newLink.setSourcePort(sourcePort);
+    newLink.setTargetPort(targetPort);
+    this.getModel().addLink(newLink);
+    return newLink;
+  }
+
+  serialize(version) {
+    return {
+      version: version||0,
+      data: this.getModel().serialize(),
+    };
+  }
+
+  deserialize(json_data) {
+    if(json_data.version) {
+      this.initializeModel(json_data.data);
+    }
+  }
+
+  serializeData() {
+    let nodes = {}, links = {};
+    let nodesDict = this.getModel().getNodesDict();
+
+    Object.keys(nodesDict).map((id)=>{
+      nodes[id] = nodesDict[id].serializeData();
+    });
+
+    this.getModel().getLinks().map((link)=>{
+      links[link.getID()] = link.serializeData(nodesDict);
+    });
+
+    /* Separate the links from nodes so that we don't have any dependancy issues */
+    return {
+      'nodes': nodes,
+      'links': links,
+    };
+  }
+
+  deserializeData(data){
+    let oidUidMap = {};
+    let uidFks = [];
+    data.forEach((node)=>{
+      let newData = {
+        name: node.name,
+        schema: node.schema,
+        description: node.description,
+        columns: node.columns,
+        primary_key: node.primary_key,
+      };
+      let newNode = this.addNode(newData);
+      oidUidMap[node.oid] = newNode.getID();
+      if(node.foreign_key) {
+        node.foreign_key.forEach((a_fk)=>{
+          uidFks.push({
+            uid: newNode.getID(),
+            data: a_fk.columns[0],
+          });
+        });
+      }
+    });
+
+    /* Lets use the oidUidMap for creating the links */
+    uidFks.forEach((fkData)=>{
+      let tableNodesDict = this.getModel().getNodesDict();
+      let newData = {
+        local_table_uid: fkData.uid,
+        local_column_attnum: undefined,
+        referenced_table_uid: oidUidMap[fkData.data.references],
+        referenced_column_attnum: undefined,
+      };
+
+      let sourceNode = tableNodesDict[newData.referenced_table_uid];
+      let targetNode = tableNodesDict[newData.local_table_uid];
+
+      newData.local_column_attnum = _.find(targetNode.getColumns(), (col)=>col.name==fkData.data.local_column).attnum;
+      newData.referenced_column_attnum = _.find(sourceNode.getColumns(), (col)=>col.name==fkData.data.referenced).attnum;
+
+      this.addLink(newData, 'onetomany');
+    });
+    setTimeout(this.dagreDistributeNodes.bind(this), 0);
+  }
+
+  repaint() {
+    this.getEngine().repaintCanvas();
+  }
+
+  clearSelection() {
+    this.getEngine()
+      .getModel()
+      .clearSelection();
+  }
+
+  getNodesData() {
+    return this.getEngine().getModel().getNodes().map((node)=>{
+      return node.getData();
+    });
+  }
+
+  getSelectedNodes() {
+    return this.getEngine()
+      .getModel()
+      .getSelectedEntities()
+      .filter(entity => entity instanceof TableNodeModel);
+  }
+
+  getSelectedLinks() {
+    return this.getEngine()
+      .getModel()
+      .getSelectedEntities()
+      .filter(entity => entity instanceof OneToManyLinkModel);
+  }
+
+  dagreDistributeNodes() {
+    this.dagre_engine.redistribute(this.getModel());
+    this.getEngine()
+      .getLinkFactories()
+      .getFactory(PathFindingLinkFactory.NAME)
+      .calculateRoutingMatrix();
+    this.repaint();
+  }
+
+  zoomIn() {
+    let model = this.getEngine().getModel();
+    if(model){
+      model.setZoomLevel(model.getZoomLevel() + 25);
+      this.repaint();
+    }
+  }
+
+  zoomOut() {
+    let model = this.getEngine().getModel();
+    if(model) {
+      model.setZoomLevel(model.getZoomLevel() - 25);
+      this.repaint();
+    }
+  }
+
+  zoomToFit() {
+    this.getEngine().zoomToFit();
+  }
+
+  // Sample call: this.fireAction({ type: 'keydown', ctrlKey: true, code: 'KeyN' });
+  fireAction(event) {
+    this.getEngine().getActionEventBus().fireAction({
+      event: {
+        ...event,
+        key: '',
+        preventDefault: () => {},
+        stopPropagation: () => {},
+      },
+    });
+  }
+
+  fireEvent(data, eventName, model=false) {
+    if(model) {
+      this.getEngine().getModel().fireEvent(data, eventName);
+    } else {
+      this.getEngine().fireEvent(data, eventName);
+    }
+  }
+
+  registerKeyAction(action) {
+    this.getEngine().getActionEventBus().registerAction(action);
+  }
+
+  deregisterKeyAction(action) {
+    this.getEngine().getActionEventBus().deregisterAction(action);
+  }
+}
diff --git a/web/pgadmin/tools/erd/static/js/erd_tool/ERDModel.js b/web/pgadmin/tools/erd/static/js/erd_tool/ERDModel.js
new file mode 100644
index 000000000..ae73db8ac
--- /dev/null
+++ b/web/pgadmin/tools/erd/static/js/erd_tool/ERDModel.js
@@ -0,0 +1,21 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2020, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import { DiagramModel } from '@projectstorm/react-diagrams';
+import _ from 'lodash';
+
+export default class ERDModel extends DiagramModel {
+  constructor(options) {
+    super(options);
+  }
+
+  getNodesDict() {
+    return _.fromPairs(this.getNodes().map(node => [node.getID(), node]));
+  }
+}
diff --git a/web/pgadmin/tools/erd/static/js/erd_tool/dialogs/DialogWrapper.js b/web/pgadmin/tools/erd/static/js/erd_tool/dialogs/DialogWrapper.js
new file mode 100644
index 000000000..9b6e54fa9
--- /dev/null
+++ b/web/pgadmin/tools/erd/static/js/erd_tool/dialogs/DialogWrapper.js
@@ -0,0 +1,158 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2020, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import gettext from 'sources/gettext';
+import * as commonUtils from 'sources/utils';
+
+export default class DialogWrapper {
+  constructor(dialogContainerSelector, dialogTitle, typeOfDialog,
+    jquery, pgBrowser, alertify, backform, backgrid) {
+
+    this.dialogContainerSelector = dialogContainerSelector;
+    this.dialogTitle = dialogTitle;
+    this.jquery = jquery;
+    this.pgBrowser = pgBrowser;
+    this.alertify = alertify;
+    this.backform = backform;
+    this.backgrid = backgrid;
+    this.typeOfDialog = typeOfDialog;
+  }
+
+  main(title, dialogModel, okCallback) {
+    this.set('title', title);
+    this.dialogModel = dialogModel;
+    this.okCallback = okCallback;
+  }
+
+  build() {
+    this.alertify.pgDialogBuild.apply(this);
+  }
+
+  disableOKButton() {
+    this.__internal.buttons[1].element.disabled = true;
+  }
+
+  enableOKButton() {
+    this.__internal.buttons[1].element.disabled = false;
+  }
+
+  focusOnDialog(alertifyDialog) {
+    let backform_tab = this.jquery(alertifyDialog.elements.body).find('.backform-tab');
+    backform_tab.attr('tabindex', -1);
+    this.pgBrowser.keyboardNavigation.getDialogTabNavigator(this.jquery(alertifyDialog.elements.dialog));
+    let container = backform_tab.find('.tab-content:first > .tab-pane.active:first');
+
+    if(container.length === 0 && alertifyDialog.elements.content.innerHTML) {
+      container = this.jquery(alertifyDialog.elements.content);
+    }
+    commonUtils.findAndSetFocus(container);
+  }
+
+  setup() {
+    return {
+      buttons: [{
+        text: gettext('Cancel'),
+        key: 27,
+        className: 'btn btn-secondary fa fa-lg fa-times pg-alertify-button',
+        'data-btn-name': 'cancel',
+      }, {
+        text: gettext('OK'),
+        key: 13,
+        className: 'btn btn-primary fa fa-lg fa-save pg-alertify-button',
+        'data-btn-name': 'ok',
+      }],
+      // Set options for dialog
+      options: {
+        title: this.dialogTitle,
+        //disable both padding and overflow control.
+        padding: !1,
+        overflow: !1,
+        model: 0,
+        resizable: true,
+        maximizable: true,
+        pinnable: false,
+        closableByDimmer: false,
+        modal: false,
+      },
+    };
+  }
+
+  prepare() {
+    const $container = this.jquery(this.dialogContainerSelector);
+    const dialog = this.createDialog($container);
+    dialog.render();
+    this.elements.content.innerHTML = '';
+    this.elements.content.appendChild($container.get(0));
+    this.jquery(this.elements.body.childNodes[0]).addClass(
+      'alertify_tools_dialog_properties obj_properties'
+    );
+    const statusBar = this.jquery(
+      '<div class=\'pg-prop-status-bar pg-prop-status-bar-absolute pg-el-xs-12 d-none\'>' +
+      '  <div class="error-in-footer"> ' +
+      '    <div class="d-flex px-2 py-1"> ' +
+      '      <div class="pr-2"> ' +
+      '        <i class="fa fa-exclamation-triangle text-danger" aria-hidden="true"></i> ' +
+      '      </div> ' +
+      '      <div class="alert-text" role="alert"></div> ' +
+      '       <div class="ml-auto close-error-bar"> ' +
+      '          <a aria-label="' + gettext('Close error bar') + '" class="close-error fa fa-times text-danger"></a> ' +
+      '        </div> ' +
+      '    </div> ' +
+      '  </div> ' +
+      '</div>').appendTo($container);
+
+    statusBar.find('.close-error').on('click', ()=>{
+      statusBar.addClass('d-none');
+    });
+
+    var onSessionInvalid = (msg) => {
+      statusBar.find('.alert-text').text(msg);
+      statusBar.removeClass('d-none');
+      this.disableOKButton();
+      return true;
+    };
+
+    var onSessionValidated = () => {
+      statusBar.find('.alert-text').text('');
+      statusBar.addClass('d-none');
+      this.enableOKButton();
+      return true;
+    };
+
+    this.dialogModel.on('pgadmin-session:valid', onSessionValidated);
+    this.dialogModel.on('pgadmin-session:invalid', onSessionInvalid);
+    this.dialogModel.startNewSession();
+    this.disableOKButton();
+    this.focusOnDialog(this);
+  }
+
+  callback(event) {
+    if (this.wasOkButtonPressed(event)) {
+      this.okCallback(this.view.model.toJSON(true));
+    }
+  }
+
+  createDialog($container) {
+    let fields = this.backform.generateViewSchema(
+      null, this.dialogModel, 'create', null, null, true, null
+    );
+
+    this.view = new this.backform.Dialog({
+      el: $container,
+      model: this.dialogModel,
+      schema: fields,
+    });
+
+    return this.view;
+  }
+
+  wasOkButtonPressed(event) {
+    return event.button['data-btn-name'] === 'ok';
+  }
+}
diff --git a/web/pgadmin/tools/erd/static/js/erd_tool/dialogs/ManyToManyDialog.js b/web/pgadmin/tools/erd/static/js/erd_tool/dialogs/ManyToManyDialog.js
new file mode 100644
index 000000000..abec79f5e
--- /dev/null
+++ b/web/pgadmin/tools/erd/static/js/erd_tool/dialogs/ManyToManyDialog.js
@@ -0,0 +1,140 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2020, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import gettext from 'sources/gettext';
+import Backform from 'sources/backform.pgadmin';
+import Alertify from 'pgadmin.alertifyjs';
+import $ from 'jquery';
+
+import DialogWrapper from './DialogWrapper';
+import _ from 'lodash';
+
+export default class ManyToManyDialog {
+  constructor(pgBrowser) {
+    this.pgBrowser = pgBrowser;
+  }
+
+  dialogName() {
+    return 'manytomany_dialog';
+  }
+
+  getDataModel(attributes, tableNodesDict) {
+    const parseColumns = (columns)=>{
+      return columns.map((col)=>{
+        return {
+          value: col.attnum, label: col.name,
+        };
+      });
+    };
+
+    let dialogModel = this.pgBrowser.DataModel.extend({
+      defaults: {
+        left_table_uid: undefined,
+        left_table_column_attnum: undefined,
+        right_table_uid: undefined,
+        right_table_column_attnum: undefined,
+      },
+      schema: [{
+        id: 'left_table_uid', label: gettext('Left Table'),
+        type: 'select2', readonly: true,
+        options: ()=>{
+          let retOpts = [];
+          _.forEach(tableNodesDict, (node, uid)=>{
+            let [schema, name] = node.getSchemaTableName();
+            retOpts.push({value: uid, label: `(${schema}) ${name}`});
+          });
+          return retOpts;
+        },
+      }, {
+        id: 'left_table_column_attnum', label: gettext('Left table Column'),
+        type: 'select2', disabled: false, first_empty: false,
+        editable: true, options: (view)=>{
+          return parseColumns(tableNodesDict[view.model.get('left_table_uid')].getColumns());
+        },
+      },{
+        id: 'right_table_uid', label: gettext('Right Table'),
+        type: 'select2', disabled: false,
+        editable: true, options: (view)=>{
+          let retOpts = [];
+          _.forEach(tableNodesDict, (node, uid)=>{
+            if(uid === view.model.get('left_table_uid')) {
+              return;
+            }
+            let [schema, name] = node.getSchemaTableName();
+            retOpts.push({value: uid, label: `(${schema}) ${name}`});
+          });
+          return retOpts;
+        },
+      },{
+        id: 'right_table_column_attnum', label: gettext('Right table Column'),
+        type: 'select2', disabled: false, deps: ['right_table_uid'],
+        editable: true, options: (view)=>{
+          if(view.model.get('right_table_uid')) {
+            return parseColumns(tableNodesDict[view.model.get('right_table_uid')].getColumns());
+          }
+          return [];
+        },
+      }],
+      validate: function(keys) {
+        var msg = undefined;
+
+        // Nothing to validate
+        if (keys && keys.length == 0) {
+          this.errorModel.clear();
+          return null;
+        } else {
+          this.errorModel.clear();
+        }
+
+        if (_.isUndefined(this.get('left_table_column_attnum')) || this.get('left_table_column_attnum') == '') {
+          msg = gettext('Select the left table column.');
+          this.errorModel.set('left_table_column_attnum', msg);
+          return msg;
+        }
+        if (_.isUndefined(this.get('right_table_uid')) || this.get('right_table_uid') == '') {
+          msg = gettext('Select the right table.');
+          this.errorModel.set('right_table_uid', msg);
+          return msg;
+        }
+        if (_.isUndefined(this.get('right_table_column_attnum')) || this.get('right_table_column_attnum') == '') {
+          msg = gettext('Select the right table column.');
+          this.errorModel.set('right_table_column_attnum', msg);
+          return msg;
+        }
+      },
+    });
+
+    return new dialogModel(attributes);
+  }
+
+  createOrGetDialog(title) {
+    const dialogName = this.dialogName();
+
+    if (!Alertify[dialogName]) {
+      Alertify.dialog(dialogName, () => {
+        return new DialogWrapper(
+          `<div class="${dialogName}"></div>`,
+          title,
+          null,
+          $,
+          this.pgBrowser,
+          Alertify,
+          Backform
+        );
+      });
+    }
+    return Alertify[dialogName];
+  }
+
+  show(title, attributes, tablesData, sVersion, callback) {
+    let dialogTitle = title || gettext('Unknown');
+    const dialog = this.createOrGetDialog('manytomany_dialog');
+    dialog(dialogTitle, this.getDataModel(attributes, tablesData), callback).resizeTo(this.pgBrowser.stdW.sm, this.pgBrowser.stdH.md);
+  }
+}
diff --git a/web/pgadmin/tools/erd/static/js/erd_tool/dialogs/OneToManyDialog.js b/web/pgadmin/tools/erd/static/js/erd_tool/dialogs/OneToManyDialog.js
new file mode 100644
index 000000000..0f4a9ce3e
--- /dev/null
+++ b/web/pgadmin/tools/erd/static/js/erd_tool/dialogs/OneToManyDialog.js
@@ -0,0 +1,140 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2020, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import gettext from 'sources/gettext';
+import Backform from 'sources/backform.pgadmin';
+import Alertify from 'pgadmin.alertifyjs';
+import $ from 'jquery';
+
+import DialogWrapper from './DialogWrapper';
+import _ from 'lodash';
+
+export default class OneToManyDialog {
+  constructor(pgBrowser) {
+    this.pgBrowser = pgBrowser;
+  }
+
+  dialogName() {
+    return 'onetomany_dialog';
+  }
+
+  getDataModel(attributes, tableNodesDict) {
+    const parseColumns = (columns)=>{
+      return columns.map((col)=>{
+        return {
+          value: col.attnum, label: col.name,
+        };
+      });
+    };
+
+    let dialogModel = this.pgBrowser.DataModel.extend({
+      defaults: {
+        local_table_uid: undefined,
+        local_column_attnum: undefined,
+        referenced_table_uid: undefined,
+        referenced_column_attnum: undefined,
+      },
+      schema: [{
+        id: 'local_table_uid', label: gettext('Local Table'),
+        type: 'select2', readonly: true,
+        options: ()=>{
+          let retOpts = [];
+          _.forEach(tableNodesDict, (node, uid)=>{
+            let [schema, name] = node.getSchemaTableName();
+            retOpts.push({value: uid, label: `(${schema}) ${name}`});
+          });
+          return retOpts;
+        },
+      }, {
+        id: 'local_column_attnum', label: gettext('Local Column'),
+        type: 'select2', disabled: false, first_empty: false,
+        editable: true, options: (view)=>{
+          return parseColumns(tableNodesDict[view.model.get('local_table_uid')].getColumns());
+        },
+      },{
+        id: 'referenced_table_uid', label: gettext('Referenced Table'),
+        type: 'select2', disabled: false,
+        editable: true, options: (view)=>{
+          let retOpts = [];
+          _.forEach(tableNodesDict, (node, uid)=>{
+            if(uid === view.model.get('local_table_uid')) {
+              return;
+            }
+            let [schema, name] = node.getSchemaTableName();
+            retOpts.push({value: uid, label: `(${schema}) ${name}`});
+          });
+          return retOpts;
+        },
+      },{
+        id: 'referenced_column_attnum', label: gettext('Referenced Column'),
+        type: 'select2', disabled: false, deps: ['referenced_table_uid'],
+        editable: true, options: (view)=>{
+          if(view.model.get('referenced_table_uid')) {
+            return parseColumns(tableNodesDict[view.model.get('referenced_table_uid')].getColumns());
+          }
+          return [];
+        },
+      }],
+      validate: function(keys) {
+        var msg = undefined;
+
+        // Nothing to validate
+        if (keys && keys.length == 0) {
+          this.errorModel.clear();
+          return null;
+        } else {
+          this.errorModel.clear();
+        }
+
+        if (_.isUndefined(this.get('local_column_attnum')) || this.get('local_column_attnum') == '') {
+          msg = gettext('Select the local column.');
+          this.errorModel.set('local_column_attnum', msg);
+          return msg;
+        }
+        if (_.isUndefined(this.get('referenced_table_uid')) || this.get('referenced_table_uid') == '') {
+          msg = gettext('Select the referenced table.');
+          this.errorModel.set('referenced_table_uid', msg);
+          return msg;
+        }
+        if (_.isUndefined(this.get('referenced_column_attnum')) || this.get('referenced_column_attnum') == '') {
+          msg = gettext('Select the referenced table column.');
+          this.errorModel.set('referenced_column_attnum', msg);
+          return msg;
+        }
+      },
+    });
+
+    return new dialogModel(attributes);
+  }
+
+  createOrGetDialog(title) {
+    const dialogName = this.dialogName();
+
+    if (!Alertify[dialogName]) {
+      Alertify.dialog(dialogName, () => {
+        return new DialogWrapper(
+          `<div class="${dialogName}"></div>`,
+          title,
+          null,
+          $,
+          this.pgBrowser,
+          Alertify,
+          Backform
+        );
+      });
+    }
+    return Alertify[dialogName];
+  }
+
+  show(title, attributes, tablesData, sVersion, callback) {
+    let dialogTitle = title || gettext('Unknown');
+    const dialog = this.createOrGetDialog('onetomany_dialog');
+    dialog(dialogTitle, this.getDataModel(attributes, tablesData), callback).resizeTo(this.pgBrowser.stdW.sm, this.pgBrowser.stdH.md);
+  }
+}
diff --git a/web/pgadmin/tools/erd/static/js/erd_tool/dialogs/TableDialog.js b/web/pgadmin/tools/erd/static/js/erd_tool/dialogs/TableDialog.js
new file mode 100644
index 000000000..e5d38f71e
--- /dev/null
+++ b/web/pgadmin/tools/erd/static/js/erd_tool/dialogs/TableDialog.js
@@ -0,0 +1,731 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2020, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import gettext from 'sources/gettext';
+import Backgrid from 'sources/backgrid.pgadmin';
+import Backform from 'sources/backform.pgadmin';
+import Alertify from 'pgadmin.alertifyjs';
+import $ from 'jquery';
+import _ from 'lodash';
+
+import DialogWrapper from './DialogWrapper';
+
+export function transformToSupported(data) {
+  /* Table fields */
+  data = _.pick(data, ['oid', 'name', 'schema', 'description', 'columns', 'primary_key', 'foreign_key']);
+
+  /* Columns */
+  data['columns'] = data['columns'].map((column)=>{
+    return _.pick(column,[
+      'name','description','attowner','attnum','cltype','min_val_attlen','min_val_attprecision','max_val_attlen',
+      'max_val_attprecision', 'is_primary_key','attnotnull','attlen','attprecision','attidentity','colconstype',
+      'seqincrement','seqstart','seqmin','seqmax','seqcache','seqcycle',
+    ]);
+  });
+
+  /* Primary key */
+  data['primary_key'] = data['primary_key'].map((primary_key)=>{
+    primary_key = _.pick(primary_key, ['columns']);
+    primary_key['columns'] = primary_key['columns'].map((column)=>{
+      return _.pick(column, ['column']);
+    });
+    return primary_key;
+  });
+
+  return data;
+}
+
+export default class TableDialog {
+  constructor(pgBrowser) {
+    this.pgBrowser = pgBrowser;
+  }
+
+  dialogName() {
+    return 'entity_dialog';
+  }
+
+  getDataModel(attributes, colTypes, schemas, sVersion) {
+    let self = this;
+    let columnsModel = this.pgBrowser.DataModel.extend({
+      idAttribute: 'attnum',
+      defaults: {
+        name: undefined,
+        description: undefined,
+        attowner: undefined,
+        attnum: undefined,
+        cltype: undefined,
+        min_val_attlen: undefined,
+        min_val_attprecision: undefined,
+        max_val_attlen: undefined,
+        max_val_attprecision: undefined,
+        is_primary_key: false,
+        attnotnull: false,
+        attlen: null,
+        attprecision: null,
+        attidentity: 'a',
+        colconstype: 'n',
+        seqincrement: undefined,
+        seqstart: undefined,
+        seqmin: undefined,
+        seqmax: undefined,
+        seqcache: undefined,
+        seqcycle: undefined,
+      },
+      initialize: function(attrs) {
+        if (_.size(attrs) !== 0) {
+          this.set({
+            'old_attidentity': this.get('attidentity'),
+          }, {silent: true});
+        }
+        self.pgBrowser.DataModel.prototype.initialize.apply(this, arguments);
+
+        if(!this.get('cltype') && colTypes.length > 0) {
+          this.set({
+            'cltype': colTypes[0]['value'],
+          }, {silent: true});
+        }
+      },
+      schema: [{
+        id: 'name', label: gettext('Name'), cell: 'string',
+        type: 'text', disabled: false,
+        cellHeaderClasses: 'width_percent_30',
+        editable: true,
+      }, {
+        // Need to show this field only when creating new table
+        // [in SubNode control]
+        id: 'is_primary_key', label: gettext('Primary key?'),
+        cell: Backgrid.Extension.TableChildSwitchCell, type: 'switch',
+        deps: ['name'], cellHeaderClasses: 'width_percent_5',
+        options: {
+          onText: gettext('Yes'), offText: gettext('No'),
+          onColor: 'success', offColor: 'ternary',
+        },
+        visible: function () {
+          return true;
+        },
+        disabled: false,
+        editable: true,
+      }, {
+        id: 'description', label: gettext('Comment'), cell: 'string', type: 'multiline',
+      }, {
+        id: 'cltype', label: gettext('Data type'),
+        cell: 'select2',
+        type: 'select2', disabled: false,
+        control: 'select2',
+        cellHeaderClasses: 'width_percent_30',
+        select2: { allowClear: false, first_empty: false }, group: gettext('Definition'),
+        options: function () {
+          return colTypes;
+        },
+      }, {
+        id: 'attlen', label: gettext('Length/Precision'), cell: Backgrid.Extension.IntegerDepCell,
+        deps: ['cltype'], type: 'int', group: gettext('Definition'), cellHeaderClasses: 'width_percent_20',
+        disabled: function (m) {
+          var of_type = m.get('cltype'),
+            flag = true;
+          _.each(colTypes, function (o) {
+            if (of_type == o.value) {
+              if (o.length) {
+                m.set('min_val_attlen', o.min_val, { silent: true });
+                m.set('max_val_attlen', o.max_val, { silent: true });
+                flag = false;
+              }
+            }
+          });
+
+          flag && setTimeout(function () {
+            if (m.get('attlen')) {
+              m.set('attlen', null);
+            }
+          }, 10);
+
+          return flag;
+        },
+        editable: function (m) {
+          var of_type = m.get('cltype'),
+            flag = false;
+          _.each(colTypes, function (o) {
+            if (of_type == o.value) {
+              if (o.length) {
+                m.set('min_val_attlen', o.min_val, { silent: true });
+                m.set('max_val_attlen', o.max_val, { silent: true });
+                flag = true;
+              }
+            }
+          });
+
+          !flag && setTimeout(function () {
+            if (m.get('attlen')) {
+              m.set('attlen', null);
+            }
+          }, 10);
+
+          return flag;
+        },
+      }, {
+        id: 'attprecision', label: gettext('Scale'), cell: Backgrid.Extension.IntegerDepCell,
+        deps: ['cltype'], type: 'int', group: gettext('Definition'), cellHeaderClasses: 'width_percent_20',
+        disabled: function (m) {
+          var of_type = m.get('cltype'),
+            flag = true;
+          _.each(colTypes, function (o) {
+            if (of_type == o.value) {
+              if (o.precision) {
+                m.set('min_val_attprecision', 0, { silent: true });
+                m.set('max_val_attprecision', o.max_val, { silent: true });
+                flag = false;
+              }
+            }
+          });
+
+          flag && setTimeout(function () {
+            if (m.get('attprecision')) {
+              m.set('attprecision', null);
+            }
+          }, 10);
+          return flag;
+        },
+        editable: function (m) {
+          if (!colTypes) {
+            // datatypes not loaded yet, may be this call is from CallByNeed from backgrid cell initialize.
+            return true;
+          }
+
+          var of_type = m.get('cltype'),
+            flag = false;
+          _.each(colTypes, function (o) {
+            if (of_type == o.value) {
+              if (o.precision) {
+                m.set('min_val_attprecision', 0, { silent: true });
+                m.set('max_val_attprecision', o.max_val, { silent: true });
+                flag = true;
+              }
+            }
+          });
+
+          !flag && setTimeout(function () {
+            if (m.get('attprecision')) {
+              m.set('attprecision', null);
+            }
+          }, 10);
+
+          return flag;
+        },
+      }, {
+        id: 'attnotnull', label: gettext('Not NULL?'), cell: 'switch',
+        type: 'switch', cellHeaderClasses: 'width_percent_20',
+        group: gettext('Constraints'),
+        options: { onText: gettext('Yes'), offText: gettext('No'), onColor: 'success', offColor: 'ternary' },
+        disabled: function(m) {
+          if (m.get('colconstype') == 'i') {
+            setTimeout(function () {
+              m.set('attnotnull', true);
+            }, 10);
+          }
+          return false;
+        },
+      }, {
+        id: 'colconstype',
+        label: gettext('Type'),
+        cell: 'string',
+        type: 'radioModern',
+        controlsClassName: 'pgadmin-controls col-12 col-sm-9',
+        controlLabelClassName: 'control-label col-sm-3 col-12',
+        group: gettext('Constraints'),
+        options: function() {
+          var opt_array = [
+            {'label': gettext('NONE'), 'value': 'n'},
+            {'label': gettext('IDENTITY'), 'value': 'i'},
+          ];
+
+          if (sVersion >= 120000) {
+            opt_array.push({
+              'label': gettext('GENERATED'),
+              'value': 'g',
+            });
+          }
+
+          return opt_array;
+        },
+        disabled: false,
+        visible: function() {
+          if (sVersion >= 100000) {
+            return true;
+          }
+          return false;
+        },
+      }, {
+        id: 'attidentity', label: gettext('Identity'), control: 'select2',
+        cell: 'select2',
+        select2: {placeholder: 'Select identity', allowClear: false, width: '100%'},
+        group: gettext('Constraints'),
+        'options': [
+          {label: gettext('ALWAYS'), value: 'a'},
+          {label: gettext('BY DEFAULT'), value: 'd'},
+        ],
+        deps: ['colconstype'],
+        visible: function(m) {
+          if (sVersion >= 100000 && m.isTypeIdentity(m)) {
+            return true;
+          }
+          return false;
+        },
+        disabled: function() {
+          return false;
+        },
+      }, {
+        id: 'seqincrement', label: gettext('Increment'), type: 'int',
+        mode: ['properties', 'create', 'edit'], group: gettext('Constraints'),
+        min: 1, deps: ['attidentity', 'colconstype'], disabled: 'isIdentityColumn',
+        visible: 'isTypeIdentity',
+      },{
+        id: 'seqstart', label: gettext('Start'), type: 'int',
+        mode: ['properties', 'create', 'edit'], group: gettext('Constraints'),
+        disabled: function(m) {
+          let isIdentity = m.get('attidentity');
+          if(!_.isUndefined(isIdentity) && !_.isNull(isIdentity) && !_.isEmpty(isIdentity))
+            return false;
+          return true;
+        }, deps: ['attidentity', 'colconstype'],
+        visible: 'isTypeIdentity',
+      },{
+        id: 'seqmin', label: gettext('Minimum'), type: 'int',
+        mode: ['properties', 'create', 'edit'], group: gettext('Constraints'),
+        deps: ['attidentity', 'colconstype'], disabled: 'isIdentityColumn',
+        visible: 'isTypeIdentity',
+      },{
+        id: 'seqmax', label: gettext('Maximum'), type: 'int',
+        mode: ['properties', 'create', 'edit'], group: gettext('Constraints'),
+        deps: ['attidentity', 'colconstype'], disabled: 'isIdentityColumn',
+        visible: 'isTypeIdentity',
+      },{
+        id: 'seqcache', label: gettext('Cache'), type: 'int',
+        mode: ['properties', 'create', 'edit'], group: gettext('Constraints'),
+        min: 1, deps: ['attidentity', 'colconstype'], disabled: 'isIdentityColumn',
+        visible: 'isTypeIdentity',
+      },{
+        id: 'seqcycle', label: gettext('Cycled'), type: 'switch',
+        mode: ['properties', 'create', 'edit'], group: gettext('Constraints'),
+        deps: ['attidentity', 'colconstype'], disabled: 'isIdentityColumn',
+        visible: 'isTypeIdentity',
+      }],
+      validate: function(keys) {
+        var msg = undefined;
+
+        // Nothing to validate
+        if (keys && keys.length == 0) {
+          this.errorModel.clear();
+          return null;
+        } else {
+          this.errorModel.clear();
+        }
+
+        if (_.isUndefined(this.get('name'))
+            || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') {
+          msg = gettext('Column name cannot be empty.');
+          this.errorModel.set('name', msg);
+          return msg;
+        }
+
+        if (_.isUndefined(this.get('cltype'))
+            || String(this.get('cltype')).replace(/^\s+|\s+$/g, '') == '') {
+          msg = gettext('Column type cannot be empty.');
+          this.errorModel.set('cltype', msg);
+          return msg;
+        }
+
+        if (!_.isUndefined(this.get('cltype'))
+              && !_.isUndefined(this.get('attlen'))
+              && !_.isNull(this.get('attlen'))
+              && this.get('attlen') !== '') {
+          // Validation for Length field
+          if (this.get('attlen') < this.get('min_val_attlen'))
+            msg = gettext('Length/Precision should not be less than: ') + this.get('min_val_attlen');
+          if (this.get('attlen') > this.get('max_val_attlen'))
+            msg = gettext('Length/Precision should not be greater than: ') + this.get('max_val_attlen');
+          // If we have any error set then throw it to user
+          if(msg) {
+            this.errorModel.set('attlen', msg);
+            return msg;
+          }
+        }
+
+        if (!_.isUndefined(this.get('cltype'))
+              && !_.isUndefined(this.get('attprecision'))
+              && !_.isNull(this.get('attprecision'))
+              && this.get('attprecision') !== '') {
+          // Validation for precision field
+          if (this.get('attprecision') < this.get('min_val_attprecision'))
+            msg = gettext('Scale should not be less than: ') + this.get('min_val_attprecision');
+          if (this.get('attprecision') > this.get('max_val_attprecision'))
+            msg = gettext('Scale should not be greater than: ') + this.get('max_val_attprecision');
+          // If we have any error set then throw it to user
+          if(msg) {
+            this.errorModel.set('attprecision', msg);
+            return msg;
+          }
+        }
+
+        var minimum = this.get('seqmin'),
+          maximum = this.get('seqmax'),
+          start = this.get('seqstart');
+
+        if (!this.isNew() && this.get('colconstype') == 'i' &&
+          (this.get('old_attidentity') == 'a' || this.get('old_attidentity') == 'd') &&
+          (this.get('attidentity') == 'a' || this.get('attidentity') == 'd')) {
+          if (_.isUndefined(this.get('seqincrement'))
+            || String(this.get('seqincrement')).replace(/^\s+|\s+$/g, '') == '') {
+            msg = gettext('Increment value cannot be empty.');
+            this.errorModel.set('seqincrement', msg);
+            return msg;
+          } else {
+            this.errorModel.unset('seqincrement');
+          }
+
+          if (_.isUndefined(this.get('seqmin'))
+            || String(this.get('seqmin')).replace(/^\s+|\s+$/g, '') == '') {
+            msg = gettext('Minimum value cannot be empty.');
+            this.errorModel.set('seqmin', msg);
+            return msg;
+          } else {
+            this.errorModel.unset('seqmin');
+          }
+
+          if (_.isUndefined(this.get('seqmax'))
+            || String(this.get('seqmax')).replace(/^\s+|\s+$/g, '') == '') {
+            msg = gettext('Maximum value cannot be empty.');
+            this.errorModel.set('seqmax', msg);
+            return msg;
+          } else {
+            this.errorModel.unset('seqmax');
+          }
+
+          if (_.isUndefined(this.get('seqcache'))
+            || String(this.get('seqcache')).replace(/^\s+|\s+$/g, '') == '') {
+            msg = gettext('Cache value cannot be empty.');
+            this.errorModel.set('seqcache', msg);
+            return msg;
+          } else {
+            this.errorModel.unset('seqcache');
+          }
+        }
+        var min_lt = gettext('Minimum value must be less than maximum value.'),
+          start_lt = gettext('Start value cannot be less than minimum value.'),
+          start_gt = gettext('Start value cannot be greater than maximum value.');
+
+        if (_.isEmpty(minimum) || _.isEmpty(maximum))
+          return null;
+
+        if ((minimum == 0 && maximum == 0) ||
+            (parseInt(minimum, 10) >= parseInt(maximum, 10))) {
+          this.errorModel.set('seqmin', min_lt);
+          return min_lt;
+        } else {
+          this.errorModel.unset('seqmin');
+        }
+
+        if (start && minimum && parseInt(start) < parseInt(minimum)) {
+          this.errorModel.set('seqstart', start_lt);
+          return start_lt;
+        } else {
+          this.errorModel.unset('seqstart');
+        }
+
+        if (start && maximum && parseInt(start) > parseInt(maximum)) {
+          this.errorModel.set('seqstart', start_gt);
+          return start_gt;
+        } else {
+          this.errorModel.unset('seqstart');
+        }
+
+        return null;
+      },
+      // Check whether the column is identity column or not
+      isIdentityColumn: function(m) {
+        let isIdentity = m.get('attidentity');
+        if(!_.isUndefined(isIdentity) && !_.isNull(isIdentity) && !_.isEmpty(isIdentity))
+          return false;
+        return true;
+      },
+      // Check whether the column is a identity column
+      isTypeIdentity: function(m) {
+        let colconstype = m.get('colconstype');
+        if (!_.isUndefined(colconstype) && !_.isNull(colconstype) && colconstype == 'i') {
+          return true;
+        }
+        return false;
+      },
+      // Check whether the column is a generated column
+      isTypeGenerated: function(m) {
+        let colconstype = m.get('colconstype');
+        if (!_.isUndefined(colconstype) && !_.isNull(colconstype) && colconstype == 'g') {
+          return true;
+        }
+        return false;
+      },
+    });
+
+    const formatSchemaItem = function(opt) {
+      if (!opt.id) {
+        return opt.text;
+      }
+
+      var optimage = $(opt.element).data('image');
+
+      if (!optimage) {
+        return opt.text;
+      } else {
+        return $('<span></span>').append(
+          $('<span></span>', {
+            class: 'wcTabIcon ' + optimage,
+          })
+        ).append($('<span></span>').text(opt.text));
+      }
+    };
+
+    let dialogModel = this.pgBrowser.DataModel.extend({
+      defaults: {
+        name: undefined,
+        schema: undefined,
+        description: undefined,
+        columns: [],
+        primary_key: [],
+      },
+      initialize: function() {
+        self.pgBrowser.DataModel.prototype.initialize.apply(this, arguments);
+
+        if(!this.get('schema') && schemas.length > 0) {
+          this.set({
+            'schema': schemas[0]['name'],
+          }, {silent: true});
+        }
+      },
+      schema: [{
+        id: 'name', label: gettext('Name'), type: 'text', disabled: false,
+      },{
+        id: 'schema', label: gettext('Schema'), type: 'text',
+        control: 'select2', select2: {
+          allowClear: false, first_empty: false,
+          templateResult: formatSchemaItem,
+          templateSelection: formatSchemaItem,
+        },
+        options: function () {
+          return schemas.map((schema)=>{
+            return {
+              'value': schema['name'],
+              'image': 'icon-schema',
+              'label': schema['name'],
+            };
+          });
+        },
+        filter: function(d) {
+          // If schema name start with pg_* then we need to exclude them
+          if(d && d.label.match(/^pg_/))
+          {
+            return false;
+          }
+          return true;
+        },
+      },{
+        id: 'description', label: gettext('Comment'), type: 'multiline',
+      },{
+        id: 'columns', label: gettext('Columns'), type: 'collection', mode: ['create'],
+        group: gettext('Columns'),
+        model: columnsModel,
+        subnode: columnsModel,
+        disabled: false,
+        uniqueCol : ['name'],
+        columns : ['name' , 'cltype', 'attlen', 'attprecision', 'attnotnull', 'is_primary_key'],
+        control: Backform.UniqueColCollectionControl.extend({
+          initialize: function() {
+
+            Backform.UniqueColCollectionControl.prototype.initialize.apply(this, arguments);
+            var self = this,
+              collection = self.model.get(self.field.get('name'));
+
+            if(collection.isEmpty()) {
+              self.last_attnum = -1;
+            } else {
+              var lastCol = collection.max(function(col) {
+                return col.get('attnum');
+              });
+              self.last_attnum = lastCol.get('attnum');
+            }
+
+            collection.on('change:is_primary_key', function(m) {
+              var primary_key_coll = self.model.get('primary_key'),
+                column_name = m.get('name'),
+                primary_key, primary_key_column_coll;
+
+              if(m.get('is_primary_key')) {
+                // Add column to primary key.
+                if (primary_key_coll.length < 1) {
+                  primary_key = new (primary_key_coll.model)({}, {
+                    top: self.model,
+                    collection: primary_key_coll,
+                    handler: primary_key_coll,
+                  });
+                  primary_key_coll.add(primary_key);
+                } else {
+                  primary_key = primary_key_coll.first();
+                }
+
+                primary_key_column_coll = primary_key.get('columns');
+                var primary_key_column_exist = primary_key_column_coll.where({column:column_name});
+
+                if (primary_key_column_exist.length == 0) {
+                  var primary_key_column = new (
+                    primary_key_column_coll.model
+                  )({column: column_name}, {
+                    silent: true,
+                    top: self.model,
+                    collection: primary_key_coll,
+                    handler: primary_key_coll,
+                  });
+
+                  primary_key_column_coll.add(primary_key_column);
+                }
+
+                primary_key_column_coll.trigger(
+                  'pgadmin:multicolumn:updated', primary_key_column_coll
+                );
+              } else {
+                // remove column from primary key.
+                if (primary_key_coll.length > 0) {
+                  primary_key = primary_key_coll.first();
+                  // Do not alter existing primary key columns.
+                  if (!_.isUndefined(primary_key.get('oid'))) {
+                    return;
+                  }
+
+                  primary_key_column_coll = primary_key.get('columns');
+                  var removedCols = primary_key_column_coll.where({column:column_name});
+                  if (removedCols.length > 0) {
+                    primary_key_column_coll.remove(removedCols);
+                    _.each(removedCols, function(local_model) {
+                      local_model.destroy();
+                    });
+                    if (primary_key_column_coll.length == 0) {
+                      /* Ideally above line of code should be "primary_key_coll.reset()".
+                       * But our custom DataCollection (extended from Backbone collection in datamodel.js)
+                       * does not respond to reset event, it only supports add, remove, change events.
+                       * And hence no custom event listeners/validators get called for reset event.
+                       */
+                      primary_key_coll.remove(primary_key_coll.first());
+                    }
+                  }
+                  primary_key_column_coll.trigger('pgadmin:multicolumn:updated', primary_key_column_coll);
+                }
+              }
+            });
+
+            collection.on('change:name', function(m) {
+              let primary_key = self.model.get('primary_key').first(),
+                updatedCols = primary_key.get('columns').where(
+                  {column: m.previous('name')}
+                );
+              if (updatedCols.length > 0) {
+                /*
+                 * Table column name has changed so update
+                 * column name in primary key as well.
+                 */
+                updatedCols[0].set(
+                  {'column': m.get('name')},
+                  {silent: true});
+              }
+            });
+
+            collection.on('remove', function(m) {
+              let primary_key = self.model.get('primary_key').first(),
+                removedCols = primary_key.get('columns').where(
+                  {column: m.get('name')}
+                );
+
+              primary_key.get('columns').remove(removedCols);
+            });
+          },
+        }),
+        canAdd: true,
+        canEdit: true, canDelete: true,
+        // For each row edit/delete button enable/disable
+        canEditRow: true,
+        canDeleteRow: true,
+        allowMultipleEmptyRow: false,
+        beforeAdd: function(newModel) {
+          this.last_attnum++;
+          newModel.set('attnum', this.last_attnum);
+          return newModel;
+        },
+      },{
+        // Here we will create tab control for constraints
+        // We will hide the tab for ERD
+        type: 'nested', control: 'tab', group: gettext('Constraints'), mode: ['properties'],
+        schema: [{
+          id: 'primary_key', label: '',
+          model: this.pgBrowser.Nodes['primary_key'].model,
+          subnode: this.pgBrowser.Nodes['primary_key'].model,
+          editable: false, type: 'collection',
+        },
+        ],
+      }],
+      validate: function() {
+        var msg,
+          name = this.get('name'),
+          schema = this.get('schema');
+
+        if (
+          _.isUndefined(name) || _.isNull(name) ||
+            String(name).replace(/^\s+|\s+$/g, '') == ''
+        ) {
+          msg = gettext('Table name cannot be empty.');
+          this.errorModel.set('name', msg);
+          return msg;
+        }
+        this.errorModel.unset('name');
+        if (
+          _.isUndefined(schema) || _.isNull(schema) ||
+            String(schema).replace(/^\s+|\s+$/g, '') == ''
+        ) {
+          msg = gettext('Table schema cannot be empty.');
+          this.errorModel.set('schema', msg);
+          return msg;
+        }
+        this.errorModel.unset('schema');
+        return null;
+      },
+    });
+
+    return new dialogModel(attributes);
+  }
+
+  createOrGetDialog(type) {
+    const dialogName = this.dialogName();
+
+    if (!Alertify[dialogName]) {
+      Alertify.dialog(dialogName, () => {
+        return new DialogWrapper(
+          `<div class="${dialogName}"></div>`,
+          null,
+          type,
+          $,
+          this.pgBrowser,
+          Alertify,
+          Backform
+        );
+      });
+    }
+    return Alertify[dialogName];
+  }
+
+  show(title, attributes, colTypes, schemas, sVersion, callback) {
+    let dialogTitle = title || gettext('Unknown');
+    const dialog = this.createOrGetDialog('table_dialog');
+    dialog(dialogTitle, this.getDataModel(attributes, colTypes, schemas, sVersion), callback).resizeTo(this.pgBrowser.stdW.md, this.pgBrowser.stdH.md);
+  }
+}
diff --git a/web/pgadmin/tools/erd/static/js/erd_tool/dialogs/index.js b/web/pgadmin/tools/erd/static/js/erd_tool/dialogs/index.js
new file mode 100644
index 000000000..eb0ab1336
--- /dev/null
+++ b/web/pgadmin/tools/erd/static/js/erd_tool/dialogs/index.js
@@ -0,0 +1,32 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2020, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import TableDialog, {transformToSupported as transformToSupportedTable} from './TableDialog';
+import OneToManyDialog from './OneToManyDialog';
+import ManyToManyDialog from './ManyToManyDialog';
+import pgBrowser from 'top/browser/static/js/browser';
+import 'sources/backgrid.pgadmin';
+import 'sources/backform.pgadmin';
+
+export default function getDialog(dialogName) {
+  if(dialogName === 'entity_dialog') {
+    return new TableDialog(pgBrowser);
+  } else if(dialogName === 'onetomany_dialog') {
+    return new OneToManyDialog(pgBrowser);
+  } else if(dialogName === 'manytomany_dialog') {
+    return new ManyToManyDialog(pgBrowser);
+  }
+}
+
+export function transformToSupported(type, data) {
+  if(type == 'table') {
+    return transformToSupportedTable(data);
+  }
+  return data;
+}
diff --git a/web/pgadmin/tools/erd/static/js/erd_tool/index.js b/web/pgadmin/tools/erd/static/js/erd_tool/index.js
new file mode 100644
index 000000000..1bd4c3bc6
--- /dev/null
+++ b/web/pgadmin/tools/erd/static/js/erd_tool/index.js
@@ -0,0 +1,30 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2020, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import React from 'react';
+import ReactDOM from 'react-dom';
+import BodyWidget from './ui_components/BodyWidget';
+import getDialog, {transformToSupported} from './dialogs';
+import Alertify from 'pgadmin.alertifyjs';
+import pgWindow from 'sources/window';
+
+export default class ERDTool {
+  constructor(container, params) {
+    this.container = document.querySelector(container);
+    this.params = params;
+  }
+
+  render() {
+    /* Mount the React ERD tool to the container */
+    ReactDOM.render(
+      <BodyWidget params={this.params} getDialog={getDialog} transformToSupported={transformToSupported} pgAdmin={pgWindow.pgAdmin} alertify={Alertify} />,
+      this.container
+    );
+  }
+}
diff --git a/web/pgadmin/tools/erd/static/js/erd_tool/links/OneToManyLink.jsx b/web/pgadmin/tools/erd/static/js/erd_tool/links/OneToManyLink.jsx
new file mode 100644
index 000000000..260e3dc57
--- /dev/null
+++ b/web/pgadmin/tools/erd/static/js/erd_tool/links/OneToManyLink.jsx
@@ -0,0 +1,301 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2020, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import React from 'react';
+import {
+    RightAngleLinkModel,
+    RightAngleLinkWidget,
+    DefaultLinkFactory,
+    PortModelAlignment,
+    LinkWidget,
+    PointModel
+} from '@projectstorm/react-diagrams';
+import {Point} from '@projectstorm/geometry';
+import _ from 'lodash';
+
+export const OneToManyModel = {
+  local_table_uid: undefined,
+  local_column_attnum: undefined,
+  referenced_table_uid: undefined,
+  referenced_column_attnum: undefined,
+}
+
+export class OneToManyLinkModel extends RightAngleLinkModel {
+  constructor({data, ...options}) {
+    super({
+      type: 'onetomany',
+      width: 1,
+      class: 'link-onetomany',
+      locked: true,
+      ...options
+    });
+
+    this._data = {
+      ...data,
+    };
+  }
+
+  getData() {
+    return this._data;
+  }
+
+  setData(data) {
+    this._data = data;
+  }
+
+  serializeData(nodesDict) {
+    let data = this.getData();
+    let target = nodesDict[data['local_table_uid']].getData();
+    let source = nodesDict[data['referenced_table_uid']].getData();
+    return {
+      'schema': target.schema,
+      'table': target.name,
+      'remote_schema': source.schema,
+      'remote_table': source.name,
+      'columns': [{
+        'local_column': _.find(target.columns, (col)=>data.local_column_attnum == col.attnum).name,
+        'referenced': _.find(source.columns, (col)=>data.referenced_column_attnum == col.attnum).name,
+      }],
+    }
+  }
+
+  serialize() {
+    return {
+      ...super.serialize(),
+      data: this.getData()
+    };
+  }
+}
+
+const CustomLinkEndWidget = props => {
+  const { point, rotation, tx, ty, type } = props;
+
+  const svgForType = (type) => {
+    if(type == 'many') {
+      return (
+        <>
+          <circle className="svg-link-ele svg-otom-circle" cx="0" cy="16" r={props.width*1.75} strokeWidth={props.width} />
+          <polyline className="svg-link-ele" points="-8,0 0,15 0,0 0,30 0,15 8,0" fill="none" strokeWidth={props.width} />
+        </>
+      )
+    } else if (type == 'one') {
+      return (
+        <polyline className="svg-link-ele" points="-8,15 0,15 0,0 0,30 0,15 8,15" fill="none" strokeWidth={props.width} />
+      )
+    }
+  }
+
+  return (
+    <g transform={'translate(' + point.getPosition().x + ', ' + point.getPosition().y + ')'}>
+      <g transform={'translate('+tx+','+ty+')'}>
+        <g style={{ transform: 'rotate(' + rotation + 'deg)' }}>
+          {svgForType(type)}
+        </g>
+      </g>
+    </g>
+  );
+};
+
+export class OneToManyLinkWidget extends RightAngleLinkWidget {
+  constructor(props) {
+    super(props);
+  }
+
+  endPointTranslation(alignment, offset) {
+    let degree = 0;
+    let tx = 0, ty = 0;
+    switch(alignment) {
+      case PortModelAlignment.BOTTOM:
+        degree = 0;
+        ty = -offset;
+        break;
+      case PortModelAlignment.LEFT:
+        degree = 90;
+        tx = offset
+        break;
+      case PortModelAlignment.TOP:
+        degree = 180;
+        ty = offset;
+        break;
+      case PortModelAlignment.RIGHT:
+        degree = -90;
+        tx = -offset;
+        break;
+    }
+    return [degree, tx, ty];
+  }
+
+  addCustomWidgetPoint(type, endpoint, point) {
+    let offset = 30;
+    const [rotation, tx, ty] = this.endPointTranslation(endpoint.options.alignment, offset);
+    if(!point) {
+      point = this.props.link.point(
+        endpoint.getX()-tx, endpoint.getY()-ty, {'one': 1, 'many': 2}[type]
+      );
+    } else {
+      point.setPosition(endpoint.getX()-tx, endpoint.getY()-ty);
+    }
+
+    return {
+      type: type,
+      point: point,
+      rotation: rotation,
+      tx: tx,
+      ty: ty
+    }
+  }
+
+  generateCustomEndWidget({type, point, rotation, tx, ty}) {
+    return (
+      <CustomLinkEndWidget
+        key={point.getID()}
+        point={point}
+        rotation={rotation}
+        tx={tx}
+        ty={ty}
+        type={type}
+        colorSelected={this.props.link.getOptions().selectedColor}
+        color={this.props.link.getOptions().color}
+        width={this.props.width}
+      />
+    );
+  }
+
+  draggingEvent(event, index) {
+    let points = this.props.link.getPoints();
+    // get moving difference. Index + 1 will work because links indexes has
+    // length = points.lenght - 1
+    let dx = Math.abs(points[index].getX() - points[index + 1].getX());
+    let dy = Math.abs(points[index].getY() - points[index + 1].getY());
+
+    // moving with y direction
+    if (dx === 0) {
+      this.calculatePositions(points, event, index, 'x');
+    } else if (dy === 0) {
+      this.calculatePositions(points, event, index, 'y');
+    }
+    this.props.link.setFirstAndLastPathsDirection();
+  }
+
+  handleMove = function(event) {
+    this.props.link.getTargetPort()
+    this.draggingEvent(event, this.dragging_index);
+    this.props.link.fireEvent({}, 'positionChanged');
+  }.bind(this);
+
+  render() {
+    //ensure id is present for all points on the path
+    let points = this.props.link.getPoints();
+    let paths = [];
+
+    // Get points based on link orientation
+    let pointLeft = points[0];
+    let pointRight = points[points.length - 1];
+    let hadToSwitch = false;
+    if (pointLeft.getX() > pointRight.getX()) {
+      pointLeft = points[points.length - 1];
+      pointRight = points[0];
+      hadToSwitch = true;
+    }
+    let dy = Math.abs(points[0].getY() - points[points.length - 1].getY());
+    let dx = Math.abs(points[0].getX() - points[points.length - 1].getX());
+
+    let onePoint = this.addCustomWidgetPoint('one', this.props.link.getSourcePort(), points[0]);;
+    let manyPoint = this.addCustomWidgetPoint('many', this.props.link.getTargetPort(), points[points.length-1]);
+
+    if (!this.state.canDrag && points.length > 2) {
+      // Those points and its position only will be moved
+      for (let i = 1; i < points.length; i += points.length - 2) {
+        if (i - 1 === 0) {
+          if (this.props.link.getFirstPathXdirection()) {
+            points[i].setPosition(points[i].getX(), points[i - 1].getY());
+          } else {
+            points[i].setPosition(points[i - 1].getX(), points[i].getY());
+          }
+        } else {
+          if (this.props.link.getLastPathXdirection()) {
+            points[i - 1].setPosition(points[i - 1].getX(), points[i].getY());
+          } else {
+            points[i - 1].setPosition(points[i].getX(), points[i - 1].getY());
+          }
+        }
+      }
+    }
+
+    // If there is existing link which has two points add one
+    if (points.length === 2 && !this.state.canDrag) {
+      this.props.link.addPoint(
+        new PointModel({
+          link: this.props.link,
+          position: new Point(onePoint.point.getX(), manyPoint.point.getY())
+        })
+      );
+    }
+
+    paths.push(this.generateCustomEndWidget(onePoint));
+    for (let j = 0; j < points.length - 1; j++) {
+      paths.push(
+        this.generateLink(
+          LinkWidget.generateLinePath(points[j], points[j + 1]),
+          {
+            'data-linkid': this.props.link.getID(),
+            'data-point': j,
+            onMouseDown: (event) => {
+              if (event.button === 0) {
+                this.setState({ canDrag: true });
+                this.dragging_index = j;
+                // Register mouse move event to track mouse position
+                // On mouse up these events are unregistered check "this.handleUp"
+                window.addEventListener('mousemove', this.handleMove);
+                window.addEventListener('mouseup', this.handleUp);
+              }
+            },
+            onMouseEnter: (event) => {
+              this.setState({ selected: true });
+              this.props.link.lastHoverIndexOfPath = j;
+            }
+          },
+          j
+        )
+      );
+    }
+    paths.push(this.generateCustomEndWidget(manyPoint));
+
+
+    this.refPaths = [];
+    return <g data-default-link-test={this.props.link.getOptions().testName}>{paths}</g>;
+  }
+}
+
+export class OneToManyLinkFactory extends DefaultLinkFactory {
+  constructor() {
+    super('onetomany');
+  }
+
+  generateModel(event) {
+    return new OneToManyLinkModel(event.initialConfig);
+  }
+
+  generateReactWidget(event) {
+    return <OneToManyLinkWidget color='#fff' width={1} smooth={true} link={event.model} diagramEngine={this.engine} factory={this} />;
+  }
+
+  generateLinkSegment(model, selected, path) {
+    return (
+      <path
+        className={'svg-link-ele path ' + (selected ? 'selected' : '')}
+        stroke={model.getOptions().color}
+        selected={selected}
+        strokeWidth={model.getOptions().width}
+        d={path}
+      >
+      </path>
+    );
+  }
+}
diff --git a/web/pgadmin/tools/erd/static/js/erd_tool/nodes/TableNode.jsx b/web/pgadmin/tools/erd/static/js/erd_tool/nodes/TableNode.jsx
new file mode 100644
index 000000000..05d368776
--- /dev/null
+++ b/web/pgadmin/tools/erd/static/js/erd_tool/nodes/TableNode.jsx
@@ -0,0 +1,203 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2020, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import React from 'react';
+import { PortModelAlignment, DefaultNodeModel } from '@projectstorm/react-diagrams';
+import { PortWidget } from '@projectstorm/react-diagrams';
+import { AbstractReactFactory } from '@projectstorm/react-canvas-core';
+import _ from 'lodash';
+import { IconButton, DetailsToggleButton } from '../ui_components/ToolBar';
+
+const TYPE = 'table';
+
+export class TableNodeModel extends DefaultNodeModel {
+  constructor({otherInfo, ...options}) {
+    super({
+      ...options,
+      type: TYPE
+    });
+
+    this._note = otherInfo.note || '';
+
+    this._data = {
+      columns: [],
+      ...otherInfo.data,
+    };
+  }
+
+  getPortName(attnum) {
+    return `coll-port-${attnum}`;
+  }
+
+  setNote(note) {
+    this._note = note;
+  }
+
+  getNote() {
+    return this._note;
+  }
+
+  addColumn(col) {
+    this._data.columns.push(col);
+  }
+
+  getColumnAt(attnum) {
+    return _.find(this.getColumns(), (col)=>col.attnum==attnum);
+  }
+
+  getColumns() {
+    return this._data.columns;
+  }
+
+  setName(name) {
+    this._data['name'] = name;
+  }
+
+  cloneData(name) {
+    let newData = {
+      ...this.getData()
+    };
+    if(name) {
+      newData['name'] = name
+    }
+    return newData;
+  }
+
+  setData(data) {
+    let self = this;
+    /* Remove the links if column dropped */
+    _.differenceWith(this._data.columns, data.columns, function(existing, incoming) {
+      return existing.attnum == incoming.attnum;
+    }).forEach((col)=>{
+      let existPort = self.getPort(self.getPortName(col.attnum));
+      if(existPort) {
+        existPort.removeAllLinks();
+        self.removePort(existPort);
+      }
+    });
+    this._data = data;
+    this.fireEvent({}, 'nodeUpdated');
+  }
+
+  getData() {
+    return this._data;
+  }
+
+  getSchemaTableName() {
+    return [this._data.schema, this._data.name];
+  }
+
+  remove() {
+    Object.values(this.getPorts()).forEach((port)=>{
+      port.removeAllLinks();
+    });
+    super.remove();
+  }
+
+  serializeData() {
+    return this.getData();
+  }
+
+  serialize() {
+    return {
+      ...super.serialize(),
+      otherInfo: {
+        data: this.getData(),
+        note: this.getNote(),
+      }
+    };
+  }
+}
+
+export class TableNodeWidget extends React.Component {
+  constructor(props) {
+    super(props);
+
+    this.state = {
+      show_details: true
+    }
+
+    this.props.node.registerListener({
+      toggleDetails: (event) => {
+        this.setState({show_details: event.show_details});
+      },
+    });
+  }
+
+  generateColumn(col) {
+    let port = this.props.node.getPort(this.props.node.getPortName(col.attnum));
+    return (
+      <div className='d-flex col-row' key={col.attnum}>
+        <div className='d-flex col-row-data'>
+          <div><span className={'wcTabIcon ' + (col.is_primary_key?'icon-primary_key':'icon-column')}></span></div>
+          <div>
+            <span className='col-name'>{col.name}</span>&nbsp;
+            {this.state.show_details &&
+            <span className='col-datatype'>{col.cltype}{col.attlen ? ('('+ col.attlen + (col.attprecision ? ','+col.attprecision : '') +')') : ''}</span>}
+          </div>
+        </div>
+        <div className="ml-auto col-row-port">{this.generatePort(port)}</div>
+      </div>
+    )
+  }
+
+  generatePort = port => {
+    if(port) {
+      return (
+        <PortWidget engine={this.props.engine} port={port} key={port.getID()} className={"port-" + port.options.alignment} />
+      );
+    }
+    return <></>;
+  };
+
+  toggleShowDetails = (e) => {
+    e.preventDefault();
+    this.setState((prevState)=>({show_details: !prevState.show_details}));
+  }
+
+  render() {
+    let node_data = this.props.node.getData();
+    return (
+      <div className={"table-node " + (this.props.node.isSelected() ? 'selected': '') } onDoubleClick={()=>{this.props.node.fireEvent({}, 'editNode')}}>
+        <div className="table-toolbar">
+          <DetailsToggleButton className='btn-xs' showDetails={this.state.show_details} onClick={this.toggleShowDetails} onDoubleClick={(e)=>{e.stopPropagation();}} />
+          {this.props.node.getNote() &&
+            <IconButton icon="far fa-sticky-note" className="btn-xs btn-warning ml-auto" onClick={()=>{
+              this.props.node.fireEvent({}, 'showNote')
+            }} title="Check note" />}
+        </div>
+        <div className="table-schema">
+          <span className="wcTabIcon icon-schema"></span>
+          {node_data.schema}
+        </div>
+        <div className="table-name">
+          <span className="wcTabIcon icon-table"></span>
+          {node_data.name}
+        </div>
+        <div className="table-cols">
+          {_.map(node_data.columns, (col)=>this.generateColumn(col))}
+        </div>
+      </div>
+    );
+  }
+}
+
+export class TableNodeFactory extends AbstractReactFactory {
+  constructor() {
+    super(TYPE);
+  }
+
+  generateModel(event) {
+    return new TableNodeModel(event.initialConfig);
+  }
+
+  generateReactWidget(event) {
+    return <TableNodeWidget engine={this.engine} node={event.model} />;
+  }
+}
diff --git a/web/pgadmin/tools/erd/static/js/erd_tool/ports/OneToManyPort.js b/web/pgadmin/tools/erd/static/js/erd_tool/ports/OneToManyPort.js
new file mode 100644
index 000000000..6f6e2c0f2
--- /dev/null
+++ b/web/pgadmin/tools/erd/static/js/erd_tool/ports/OneToManyPort.js
@@ -0,0 +1,43 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2020, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import { PortModel } from '@projectstorm/react-diagrams-core';
+import {OneToManyLinkModel} from '../links/OneToManyLink';
+import { AbstractModelFactory } from '@projectstorm/react-canvas-core';
+
+const TYPE = 'onetomany';
+
+export default class OneToManyPortModel extends PortModel {
+  constructor({options}) {
+    super({
+      ...options,
+      type: TYPE,
+    });
+  }
+
+  removeAllLinks() {
+    Object.values(this.getLinks()).forEach((link)=>{
+      link.remove();
+    });
+  }
+
+  createLinkModel() {
+    return new OneToManyLinkModel({});
+  }
+}
+
+export class OneToManyPortFactory extends AbstractModelFactory {
+  constructor() {
+    super(TYPE);
+  }
+
+  generateModel(event) {
+    return new OneToManyPortModel(event.initialConfig||{});
+  }
+}
diff --git a/web/pgadmin/tools/erd/static/js/erd_tool/ui_components/BodyWidget.jsx b/web/pgadmin/tools/erd/static/js/erd_tool/ui_components/BodyWidget.jsx
new file mode 100644
index 000000000..2846640a3
--- /dev/null
+++ b/web/pgadmin/tools/erd/static/js/erd_tool/ui_components/BodyWidget.jsx
@@ -0,0 +1,684 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2020, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import * as React from 'react';
+import { CanvasWidget } from '@projectstorm/react-canvas-core';
+import axios from 'axios';
+import { Action, InputType } from '@projectstorm/react-canvas-core';
+import html2canvas from 'html2canvas';
+
+import gettext from 'sources/gettext';
+import url_for from 'sources/url_for';
+import {showERDSqlTool} from 'tools/datagrid/static/js/show_query_tool';
+import ERDCore from '../ERDCore';
+import ToolBar, {IconButton, DetailsToggleButton, ButtonGroup} from './ToolBar';
+import ConnectionBar, { STATUS as CONNECT_STATUS } from './ConnectionBar';
+import Loader from './Loader';
+import FloatingNote from './FloatingNote';
+import {setPanelTitle} from '../../erd_module';
+
+export class KeyboardShortcutAction extends Action {
+  constructor(shortcut_handlers=[]) {
+      super({
+          type: InputType.KEY_DOWN,
+          fire: ({ event })=>{
+            this.callHandler(event);
+          }
+      });
+      this.shortcuts = {};
+
+      for(let i=0; i<shortcut_handlers.length; i++){
+        let [key, handler] = shortcut_handlers[i];
+        this.shortcuts[this.shortcutKey(key.alt, key.control, key.shift, false, key.key.key_code)] = handler;
+      }
+  }
+
+  shortcutKey(altKey, ctrlKey, shiftKey, metaKey, keyCode) {
+    return `${altKey}:${ctrlKey}:${shiftKey}:${metaKey}:${keyCode}`;
+  }
+
+  callHandler(event) {
+    let handler = this.shortcuts[this.shortcutKey(event.altKey, event.ctrlKey, event.shiftKey, event.metaKey, event.keyCode)];
+    if(handler) {
+      handler();
+    }
+  }
+}
+
+export default class BodyWidget extends React.Component {
+  constructor() {
+    super();
+    this.state = {
+      conn_status: CONNECT_STATUS.DISCONNECTED,
+      server_version: null,
+      no_node_selected: true,
+      no_link_selected: true,
+      coll_types: [],
+      loading_msg: null,
+      note_open: false,
+      note_node: null,
+      current_file: null,
+      dirty: false,
+      show_details: true,
+      preferences: {},
+    }
+    this.diagram = new ERDCore();
+    this.fileInputRef = React.createRef();
+    this.diagramContainerRef = React.createRef();
+    this.canvasEle = null;
+    this.noteRefEle = null;
+    this.noteNode = null;
+    this.keyboardActionObj = null;
+
+    this.onLoadDiagram = this.onLoadDiagram.bind(this);
+    this.onSaveDiagram = this.onSaveDiagram.bind(this);
+    this.onSaveAsDiagram = this.onSaveAsDiagram.bind(this);
+    this.onSQLClick = this.onSQLClick.bind(this);
+    this.onImageClick = this.onImageClick.bind(this);
+    this.onAddNewNode = this.onAddNewNode.bind(this);
+    this.onEditNode = this.onEditNode.bind(this);
+    this.onCloneNode = this.onCloneNode.bind(this);
+    this.onDeleteNode = this.onDeleteNode.bind(this);
+    this.onNoteClick = this.onNoteClick.bind(this);
+    this.onNoteClose = this.onNoteClose.bind(this);
+    this.onOneToManyClick = this.onOneToManyClick.bind(this);
+    this.onManyToManyClick = this.onManyToManyClick.bind(this);
+    this.onAutoDistribute = this.onAutoDistribute.bind(this);
+    this.onDetailsToggle = this.onDetailsToggle.bind(this);
+    this.onHelpClick = this.onHelpClick.bind(this);
+    this.diagram.zoomToFit = this.diagram.zoomToFit.bind(this.diagram);
+    this.diagram.zoomIn = this.diagram.zoomIn.bind(this.diagram);
+    this.diagram.zoomOut = this.diagram.zoomOut.bind(this.diagram);
+  }
+
+  registerModelEvents() {
+    let diagramEvents = {
+      'offsetUpdated': (event)=>{
+        this.realignGrid({backgroundPosition: `${event.offsetX}px ${event.offsetY}px`});
+        event.stopPropagation();
+      },
+      'zoomUpdated': (event)=>{
+        let { gridSize } = this.diagram.getModel().getOptions();
+        let bgSize = gridSize*event.zoom/100;
+        this.realignGrid({backgroundSize: `${bgSize*3}px ${bgSize*3}px`});
+      },
+      'nodesSelectionChanged': ()=>{
+        let selected = this.diagram.getSelectedNodes();
+        this.setState({no_node_selected: selected.length == 0});
+      },
+      'linksSelectionChanged': ()=>{
+        let selected = this.diagram.getSelectedLinks();
+        this.setState({no_link_selected: selected.length == 0});
+      },
+      'linksUpdated': () => {
+        this.setState({dirty: true});
+      },
+      'nodesUpdated': ()=>{
+        this.setState({dirty: true});
+      },
+      'showNote': (event)=>{
+        this.showNote(event.node);
+      },
+      'editNode': (event) => {
+        this.addEditNode(event.node);
+      }
+    };
+    Object.keys(diagramEvents).forEach(eventName => {
+      this.diagram.registerModelEvent(eventName, diagramEvents[eventName]);
+    });
+  }
+
+  registerKeyboardShortcuts() {
+    /* First deregister to avoid double events */
+    this.keyboardActionObj && this.diagram.deregisterKeyAction(this.keyboardActionObj);
+
+    this.keyboardActionObj = new KeyboardShortcutAction([
+      [this.state.preferences.open_project, this.onLoadDiagram],
+      [this.state.preferences.save_project, this.onSaveDiagram],
+      [this.state.preferences.save_project_as, this.onSaveAsDiagram],
+      [this.state.preferences.generate_sql, this.onSQLClick],
+      [this.state.preferences.download_image, this.onImageClick],
+      [this.state.preferences.add_table, this.onAddNewNode],
+      [this.state.preferences.edit_table, this.onEditNode],
+      [this.state.preferences.clone_table, this.onCloneNode],
+      [this.state.preferences.drop_table, this.onDeleteNode],
+      [this.state.preferences.add_edit_note, this.onNoteClick],
+      [this.state.preferences.one_to_many, this.onOneToManyClick],
+      [this.state.preferences.many_to_many, this.onManyToManyClick],
+      [this.state.preferences.auto_align, this.onAutoDistribute],
+      [this.state.preferences.zoom_to_fit, this.diagram.zoomToFit],
+      [this.state.preferences.zoom_in, this.diagram.zoomIn],
+      [this.state.preferences.zoom_out, this.diagram.zoomOut]
+    ]);
+
+    this.diagram.registerKeyAction(this.keyboardActionObj);
+  }
+
+  handleAxiosCatch(err) {
+    let alert = this.props.alertify.alert().set('title', gettext('Error'));
+    if (err.response) {
+      // client received an error response (5xx, 4xx)
+      alert.set('message', `${err.response.statusText} - ${err.response.data.errormsg}`).show();
+      console.error('response error', err.response);
+    } else if (err.request) {
+      // client never received a response, or request never left
+      alert.set('message', gettext('Client error') + ':' + err).show();
+      console.error('client eror', err);
+    } else {
+      alert.set('message', err.message).show();
+      console.error('other error', err);
+    }
+  }
+
+  async componentDidMount() {
+    this.setLoading('Preparing');
+    this.setTitle(this.state.current_file);
+    this.setState({
+      preferences: this.props.pgAdmin.Browser.get_preferences_for_module('erd')
+    }, this.registerKeyboardShortcuts);
+    this.registerModelEvents();
+    this.realignGrid({
+      backgroundSize: '45px 45px',
+      backgroundPosition: '0px 0px',
+    });
+
+    this.props.pgAdmin.Browser.Events.on('pgadmin-storage:finish_btn:select_file', this.openFile, this);
+    this.props.pgAdmin.Browser.Events.on('pgadmin-storage:finish_btn:create_file', this.saveFile, this);
+    this.props.pgAdmin.Browser.onPreferencesChange('erd', () => {
+      this.setState({
+        preferences: this.props.pgAdmin.Browser.get_preferences_for_module('erd')
+      }, ()=>this.registerKeyboardShortcuts());
+    });
+
+    let done = await this.initConnection();
+    if(!done) return;
+
+    done = await this.loadPrequisiteData();
+    if(!done) return;
+
+    if(this.props.params.gen) {
+      done = await this.loadTablesData();
+    }
+  }
+
+  componentDidUpdate() {
+    if(this.state.dirty) {
+      this.setTitle(this.state.current_file, true);
+    }
+  }
+
+  getDialog(dialogName) {
+    if(dialogName === 'entity_dialog') {
+      return (title, attributes, callback)=>{
+          this.props.getDialog(dialogName).show(
+            title, attributes, this.diagram.getCache('colTypes'), this.diagram.getCache('schemas'), this.state.server_version, callback
+          );
+      };
+    } else if(dialogName === 'onetomany_dialog') {
+      return (title, attributes, callback)=>{
+          this.props.getDialog(dialogName).show(
+              title, attributes, this.diagram.getModel().getNodesDict(), this.state.server_version, callback
+          );
+      };
+    } else if(dialogName === 'manytomany_dialog') {
+      return (title, attributes, callback)=>{
+        this.props.getDialog(dialogName).show(
+              title, attributes, this.diagram.getModel().getNodesDict(), this.state.server_version, callback
+          );
+      };
+    }
+  }
+
+  setLoading(message) {
+    this.setState({loading_msg: message});
+  }
+
+  realignGrid({backgroundSize, backgroundPosition}) {
+    if(backgroundSize) {
+      this.canvasEle.style.backgroundSize = backgroundSize;
+    }
+    if(backgroundPosition) {
+      this.canvasEle.style.backgroundPosition = backgroundPosition;
+    }
+  }
+
+  addEditNode(node) {
+    let dialog = this.getDialog('entity_dialog');
+    if(node) {
+      let [schema, table] = node.getSchemaTableName();
+      dialog(_.escape(`Table: ${table} (${schema})`), node.getData(), (newData)=>{
+        node.setData(newData);
+        this.diagram.repaint();
+      });
+    } else {
+      dialog('New table', {name: this.diagram.getNextTableName()}, (newData)=>{
+        let newNode = this.diagram.addNode(newData);
+        newNode.setSelected(true);
+      });
+    }
+  }
+
+  onEditNode() {
+    const selected = this.diagram.getSelectedNodes();
+    if(selected.length == 1) {
+      this.addEditNode(selected[0]);
+    }
+  }
+
+  onAddNewNode() {
+    this.addEditNode();
+  }
+
+  onCloneNode() {
+    const selected = this.diagram.getSelectedNodes();
+    if(selected.length == 1) {
+      let newData = selected[0].cloneData(this.diagram.getNextTableName());
+      let {x, y} = selected[0].getPosition();
+      let newNode = this.diagram.addNode(newData, [x+20, y+20]);
+      newNode.setSelected(true);
+    }
+  }
+
+  onDeleteNode() {
+    this.diagram.getSelectedNodes().forEach((node)=>{
+      node.setSelected(false);
+      node.remove();
+    });
+    this.diagram.getSelectedLinks().forEach((link)=>{
+      link.getTargetPort().remove();
+      link.getSourcePort().remove();
+      link.setSelected(false);
+      link.remove();
+    });
+    this.diagram.repaint();
+  }
+
+  onAutoDistribute() {
+    this.diagram.dagreDistributeNodes();
+  }
+
+  onDetailsToggle() {
+    this.setState((prevState)=>({
+      show_details: !prevState.show_details
+    }), ()=>{
+      this.diagram.getModel().getNodes().forEach((node)=>{
+        node.fireEvent({show_details: this.state.show_details}, 'toggleDetails');
+      })
+    });
+  }
+
+  onHelpClick() {
+    let url = url_for('help.static', {'filename': 'erd.html'});
+    window.open(url, 'pgadmin_help');
+  }
+
+  onLoadDiagram() {
+    var params = {
+      'supported_types': ['pgerd'], // file types allowed
+      'dialog_type': 'select_file', // open select file dialog
+    };
+    this.props.pgAdmin.FileManager.init();
+    this.props.pgAdmin.FileManager.show_dialog(params);
+  }
+
+  openFile(fileName) {
+    axios.post(url_for('sqleditor.load_file'), {
+      'file_name': decodeURI(fileName)
+    }).then((res)=>{
+      this.setState({
+        current_file: fileName,
+        dirty: false,
+      });
+      this.setTitle(fileName);
+      this.diagram.deserialize(res.data);
+      this.registerModelEvents();
+    }).catch((err)=>{
+      this.handleAxiosCatch(err);
+    });
+  }
+
+  onSaveDiagram(isSaveAs=false) {
+    if(this.state.current_file && !isSaveAs) {
+      this.saveFile(this.state.current_file);
+    } else {
+      var params = {
+        'supported_types': ['pgerd'],
+        'dialog_type': 'create_file',
+        'dialog_title': 'Save File',
+        'btn_primary': 'Save',
+      };
+      this.props.pgAdmin.FileManager.init();
+      this.props.pgAdmin.FileManager.show_dialog(params);
+    }
+  }
+
+  onSaveAsDiagram() {
+    this.onSaveDiagram(true);
+  }
+
+  saveFile(fileName) {
+    axios.post(url_for('sqleditor.save_file'), {
+      'file_name': decodeURI(fileName),
+      'file_content': JSON.stringify(this.diagram.serialize(this.props.pgAdmin.Browser.utils.app_version_int))
+    }).then(()=>{
+      this.props.alertify.success(gettext('Project saved successfully.'));
+      this.setState({
+        current_file: fileName,
+        dirty: false,
+      });
+      this.setTitle(fileName);
+    }).catch((err)=>{
+      this.handleAxiosCatch(err);
+    });
+  }
+
+  getCurrentProjectName(path) {
+    let currPath = path || this.state.current_file || 'Untitled';
+    return currPath.split('\\').pop().split('/').pop();
+  }
+
+  setTitle(title, dirty=false) {
+    if(title === null || title === '') {
+      title = 'Untitled';
+    }
+    title = this.getCurrentProjectName(title) + (dirty ? '*': '');
+    if (this.new_browser_tab) {
+      window.document.title = title;
+    } else {
+      _.each(this.props.pgAdmin.Browser.docker.findPanels('frm_erdtool'), function(p) {
+        if (p.isVisible()) {
+          setPanelTitle(p, title);
+        }
+      });
+    }
+  }
+
+  onSQLClick() {
+    let scriptHeader = gettext('-- This script was generated by a beta version of the ERD tool in pgAdmin 4.\n');
+    scriptHeader += gettext('-- Please log an issue at https://redmine.postgresql.org/projects/pgadmin4/issues/new if you find any bugs, including reproduction steps.\n');
+
+    let url = url_for('erd.sql', {
+      trans_id: this.props.params.trans_id,
+      sgid: this.props.params.sgid,
+      sid: this.props.params.sid,
+      did: this.props.params.did
+    });
+
+    this.setLoading(gettext('Preparing the SQL...'));
+    axios.post(url, this.diagram.serializeData())
+      .then((resp)=>{
+        let sqlScript = resp.data.data;
+        sqlScript = scriptHeader + 'BEGIN;\n' + sqlScript + '\nEND;';
+
+        let parentData = {
+          sgid: this.props.params.sgid,
+          sid: this.props.params.sid,
+          did: this.props.params.did,
+          stype: this.props.params.server_type,
+        }
+
+        let sqlId = `erd${this.props.params.trans_id}`;
+        sessionStorage.setItem(sqlId, sqlScript);
+        showERDSqlTool(parentData, sqlId, this.props.params.title, this.props.pgAdmin.DataGrid, this.props.alertify);
+      })
+      .catch((error)=>{
+        this.handleAxiosCatch(error);
+      })
+      .then(()=>{
+        this.setLoading(null);
+      })
+  }
+
+  onImageClick() {
+    /* Not working completely */
+    return;
+    this.setLoading(gettext('Preparing the image...'));
+    var svgElements = this.canvasEle.querySelectorAll('svg');
+    svgElements.forEach(function(svg) {
+      svg.setAttribute("width", svg.getBoundingClientRect().width);
+      svg.setAttribute("height", svg.getBoundingClientRect().height);
+      svg.style.width = null;
+      svg.style.height= null;
+    });
+    this.canvasEle.style.overflow = 'auto';
+    html2canvas(this.canvasEle, {
+      scrollX: 0,
+      scrollY: 0
+    }).then((canvas)=>{
+      let link = document.createElement('a');
+      link.setAttribute('href', canvas.toDataURL('image/png'));
+      link.setAttribute('download', this.getCurrentProjectName() + '.png');
+      link.click();
+    }).catch((err)=>{
+      console.log(err);
+    }).then(()=>{
+      this.canvasEle.style.overflow = 'hidden';
+      this.setLoading(null);
+    });
+  }
+
+  onOneToManyClick() {
+    let dialog = this.getDialog('onetomany_dialog');
+    let initData = {local_table_uid: this.diagram.getSelectedNodes()[0].getID()};
+    dialog('One to many relation', initData, (newData)=>{
+      let newLink = this.diagram.addLink(newData, 'onetomany');
+      this.diagram.clearSelection();
+      newLink.setSelected(true);
+      this.diagram.repaint();
+    });
+  }
+
+  onManyToManyClick() {
+    let dialog = this.getDialog('manytomany_dialog');
+    let initData = {left_table_uid: this.diagram.getSelectedNodes()[0].getID()};
+    dialog('Many to many relation', initData, (newData)=>{
+      let nodes = this.diagram.getModel().getNodesDict();
+      let left_table = nodes[newData.left_table_uid];
+      let right_table = nodes[newData.right_table_uid];
+      let tableData = {
+        name: `${left_table.getData().name}_${right_table.getData().name}`,
+        schema: left_table.getData().schema,
+        columns: [{
+          ...left_table.getColumnAt(newData.left_table_column_attnum),
+          'name': `${left_table.getData().name}_${left_table.getColumnAt(newData.left_table_column_attnum).name}`,
+          'is_primary_key': false,
+          'attnum': 0,
+        },{
+          ...right_table.getColumnAt(newData.right_table_column_attnum),
+          'name': `${right_table.getData().name}_${right_table.getColumnAt(newData.right_table_column_attnum).name}`,
+          'is_primary_key': false,
+          'attnum': 1,
+        }]
+      }
+      let newNode = this.diagram.addNode(tableData);
+      newNode.setSelected(true);
+
+      let linkData = {
+        local_table_uid: newNode.getID(),
+        local_column_attnum: newNode.getColumns()[0].attnum,
+        referenced_table_uid: newData.left_table_uid,
+        referenced_column_attnum : newData.left_table_column_attnum,
+      }
+      this.diagram.addLink(linkData, 'onetomany');
+
+      linkData = {
+        local_table_uid: newNode.getID(),
+        local_column_attnum: newNode.getColumns()[1].attnum,
+        referenced_table_uid: newData.right_table_uid,
+        referenced_column_attnum : newData.right_table_column_attnum,
+      }
+
+      this.diagram.addLink(linkData, 'onetomany');
+
+      this.diagram.repaint();
+    });
+  }
+
+  showNote(noteNode) {
+    if(noteNode) {
+      this.noteRefEle = this.diagram.getEngine().getNodeElement(noteNode);
+      this.setState({
+        note_node: noteNode,
+        note_open: true
+      });
+    }
+  }
+
+  onNoteClick(e) {
+    let noteNode = this.diagram.getSelectedNodes()[0];
+    this.showNote(noteNode);
+  }
+
+  onNoteClose(updated) {
+    this.setState({note_open: false})
+    updated && this.diagram.fireEvent({}, 'nodesUpdated', true);
+  }
+
+  async initConnection() {
+    this.setLoading(gettext('Initializing connection...'));
+    this.setState({conn_status: CONNECT_STATUS.CONNECTING});
+
+    let initUrl = url_for('erd.initialize', {
+      trans_id: this.props.params.trans_id,
+      sgid: this.props.params.sgid,
+      sid: this.props.params.sid,
+      did: this.props.params.did
+    });
+
+    try {
+      let response = await axios.post(initUrl);
+      this.setState({
+        conn_status: CONNECT_STATUS.CONNECTED,
+        server_version: response.data.data.serverVersion
+      });
+      return true;
+    } catch (error) {
+      this.setState({conn_status: CONNECT_STATUS.FAILED});
+      this.handleAxiosCatch(error);
+      return false;
+    } finally {
+      this.setLoading(null);
+    }
+  }
+
+  /* Get all prequisite in one conn since
+   * we have only one connection
+   */
+  async loadPrequisiteData() {
+    this.setLoading(gettext('Fetching required data...'));
+    let url = url_for('erd.prequisite', {
+      trans_id: this.props.params.trans_id,
+      sgid: this.props.params.sgid,
+      sid: this.props.params.sid,
+      did: this.props.params.did
+    });
+
+    try {
+      let response = await axios.get(url);
+      let data = response.data.data;
+      this.diagram.setCache('colTypes', data['col_types']);
+      this.diagram.setCache('schemas', data['schemas']);
+      return true;
+    } catch (error) {
+      this.handleAxiosCatch(error);
+      return false;
+    } finally {
+      this.setLoading(null);
+    }
+  }
+
+  async loadTablesData() {
+    this.setLoading(gettext('Fetching schema data...'));
+    let url = url_for('erd.tables', {
+      trans_id: this.props.params.trans_id,
+      sgid: this.props.params.sgid,
+      sid: this.props.params.sid,
+      did: this.props.params.did
+    });
+
+    try {
+      let response = await axios.get(url);
+      let tables = response.data.data.map((table)=>{
+        return this.props.transformToSupported('table', table);
+      });
+      this.diagram.deserializeData(tables);
+      return true;
+    } catch (error) {
+      this.handleAxiosCatch(error);
+      return false;
+    } finally {
+      this.setLoading(null);
+    }
+  }
+
+  render() {
+    return (
+      <>
+      <ToolBar id="btn-toolbar">
+        <ButtonGroup>
+          <IconButton id="open-file" icon="fa fa-folder-open" onClick={this.onLoadDiagram} title={gettext('Load from file')}
+            shortcut={this.state.preferences.open_project}/>
+          <IconButton id="save-erd" icon="fa fa-save" onClick={()=>{this.onSaveDiagram()}} title={gettext('Save project')}
+            shortcut={this.state.preferences.save_project} disabled={!this.state.dirty}/>
+          <IconButton id="save-as-erd" icon="fa fa-share-square" onClick={this.onSaveAsDiagram} title={gettext('Save as')}
+            shortcut={this.state.preferences.save_project_as}/>
+        </ButtonGroup>
+        <ButtonGroup>
+          <IconButton id="save-sql" icon="fa fa-file-code" onClick={this.onSQLClick} title={gettext('Generate SQL')}
+            shortcut={this.state.preferences.generate_sql}/>
+        </ButtonGroup>
+        <IconButton id="save-image" icon="far fa-file-image" onClick={this.onImageClick} title={gettext('Download as image')}
+            shortcut={this.state.preferences.download_image} className={'d-none'}/>
+        <ButtonGroup>
+          <IconButton id="add-node" icon="fa fa-plus-square" onClick={this.onAddNewNode} title={gettext('Add table')}
+            shortcut={this.state.preferences.add_table}/>
+          <IconButton id="edit-node" icon="fa fa-pencil-alt" onClick={this.onEditNode} title={gettext('Edit table')}
+            shortcut={this.state.preferences.edit_table} disabled={this.state.no_node_selected}/>
+          <IconButton id="clone-node" icon="fa fa-clone" onClick={this.onCloneNode} title={gettext('Clone table')}
+            shortcut={this.state.preferences.clone_table} disabled={this.state.no_node_selected}/>
+          <IconButton id="delete-node" icon="fa fa-trash-alt" onClick={this.onDeleteNode} title={gettext('Drop table/link')}
+            shortcut={this.state.preferences.drop_table} disabled={this.state.no_node_selected && this.state.no_link_selected}/>
+        </ButtonGroup>
+        <ButtonGroup>
+          <IconButton id="add-note" icon="fa fa-sticky-note" onClick={this.onNoteClick} title={gettext('Add/Edit note')}
+            shortcut={this.state.preferences.add_edit_note} disabled={this.state.no_node_selected}/>
+          <IconButton id="add-onetomany" text="1M" onClick={this.onOneToManyClick} title={gettext('One-to-Many link')}
+            shortcut={this.state.preferences.one_to_many} disabled={this.state.no_node_selected}/>
+          <IconButton id="add-manytomany" text="MM" onClick={this.onManyToManyClick} title={gettext('Many-to-Many link')}
+            shortcut={this.state.preferences.many_to_many} disabled={this.state.no_node_selected}/>
+        </ButtonGroup>
+        <ButtonGroup>
+          <IconButton id="auto-align" icon="fa fa-magic" onClick={this.onAutoDistribute} title={gettext('Auto align')}
+            shortcut={this.state.preferences.auto_align} />
+          <DetailsToggleButton id="more-details" onClick={this.onDetailsToggle} showDetails={this.state.show_details} />
+        </ButtonGroup>
+        <ButtonGroup>
+          <IconButton id="zoom-to-fit" icon="fa fa-compress" onClick={this.diagram.zoomToFit} title={gettext('Zoom to fit')}
+            shortcut={this.state.preferences.zoom_to_fit}/>
+          <IconButton id="zoom-in" icon="fa fa-search-plus" onClick={this.diagram.zoomIn} title={gettext('Zoom in')}
+            shortcut={this.state.preferences.zoom_in}/>
+          <IconButton id="zoom-out" icon="fa fa-search-minus" onClick={this.diagram.zoomOut} title={gettext('Zoom out')}
+            shortcut={this.state.preferences.zoom_out}/>
+        </ButtonGroup>
+        <ButtonGroup>
+          <IconButton id="help" icon="fa fa-question" onClick={this.onHelpClick} title={gettext('Help')} />
+        </ButtonGroup>
+      </ToolBar>
+      <ConnectionBar statusId="btn-conn-status" status={this.state.conn_status} bgcolor={this.props.params.bgcolor}
+        fgcolor={this.props.params.fgcolor} title={this.props.params.title}/>
+      <FloatingNote open={this.state.note_open} onClose={this.onNoteClose}
+        reference={this.noteRefEle} noteNode={this.state.note_node} appendTo={this.diagramContainerRef.current} rows={8}/>
+      <div className="diagram-container" ref={this.diagramContainerRef}>
+        <Loader message={this.state.loading_msg} autoEllipsis={true}/>
+        <CanvasWidget className="diagram-canvas flex-grow-1" ref={(ele)=>{this.canvasEle = ele?.ref?.current}} engine={this.diagram.getEngine()} />
+      </div>
+      </>
+    );
+  }
+}
diff --git a/web/pgadmin/tools/erd/static/js/erd_tool/ui_components/ConnectionBar.jsx b/web/pgadmin/tools/erd/static/js/erd_tool/ui_components/ConnectionBar.jsx
new file mode 100644
index 000000000..cb7e7155f
--- /dev/null
+++ b/web/pgadmin/tools/erd/static/js/erd_tool/ui_components/ConnectionBar.jsx
@@ -0,0 +1,47 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2020, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import React from 'react';
+import gettext from 'sources/gettext';
+
+export const STATUS = {
+  CONNECTED: 1,
+  DISCONNECTED: 2,
+  CONNECTING: 3,
+  FAILED: 4,
+}
+
+export default function ConnectionBar({statusId, status, bgcolor, fgcolor, title}) {
+  return (
+    <div className="connection_status_wrapper d-flex">
+      <div id={statusId}
+        role="status"
+        className="connection_status d-flex justify-content-center align-items-center" data-container="body"
+        data-toggle="popover" data-placement="bottom"
+        data-content=""
+        data-panel-visible="visible"
+        tabIndex="0">
+        <span className={'pg-font-icon d-flex m-auto '
+            + (status == STATUS.CONNECTED ? 'icon-query-tool-connected' : '')
+            + (status == (STATUS.DISCONNECTED || STATUS.FAILED) ? 'icon-query-tool-disconnected ' : '')
+            + (status == STATUS.CONNECTING ? 'obtaining-conn' : '')}
+          aria-hidden="true" title="" role="img">
+        </span>
+      </div>
+      <div className="connection-info btn-group" role="group" aria-label="">
+        <div className="editor-title"
+          style={{backgroundColor: bgcolor, color: fgcolor}}>
+            {status == STATUS.CONNECTING ? '(' + gettext('Obtaining connection...') + ') ' : ''}
+            {status == STATUS.FAILED ? '(' + gettext('Connection failed') + ') ' : ''}
+            {title}
+        </div>
+      </div>
+    </div>
+  )
+}
diff --git a/web/pgadmin/tools/erd/static/js/erd_tool/ui_components/FloatingNote.jsx b/web/pgadmin/tools/erd/static/js/erd_tool/ui_components/FloatingNote.jsx
new file mode 100644
index 000000000..f714e1f5d
--- /dev/null
+++ b/web/pgadmin/tools/erd/static/js/erd_tool/ui_components/FloatingNote.jsx
@@ -0,0 +1,59 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2020, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import React, { useEffect, useState } from 'react';
+import Tippy from '@tippyjs/react';
+import gettext from 'sources/gettext';
+
+export default function FloatingNote({open, onClose, reference, rows, noteNode, ...tippyProps}) {
+  const textRef = React.useRef(null);
+  const [text, setText] = useState('');
+  const [header, setHeader] = useState('');
+  useEffect(()=>{
+    if(noteNode) {
+      setText(noteNode.getNote());
+      let [schema, name] = noteNode.getSchemaTableName();
+      setHeader(`${name} (${schema})`);
+    }
+
+    if(open) {
+      textRef?.current.focus();
+      textRef?.current.dispatchEvent(new KeyboardEvent('keypress'));
+    }
+  }, [noteNode, open]);
+
+  return (
+    <Tippy render={(attrs)=>(
+      <div className="floating-note" {...attrs}>
+        <div className="note-header">{gettext('Note')}:</div>
+        <div className="note-body">
+          <div className="p-1">{header}</div>
+          <textarea ref={textRef} className="pg-textarea" value={text} rows={rows} onChange={(e)=>setText(e.target.value)}></textarea>
+          <div className="pg_buttons">
+            <button className="btn btn-primary long_text_editor pg-alertify-button" data-label="OK"
+              onClick={()=>{
+                let updated = (noteNode.getNote() != text);
+                noteNode.setNote(text);
+                if(onClose) onClose(updated);
+              }}>
+              <span className="fa fa-check pg-alertify-button"></span>&nbsp;{gettext('OK')}
+            </button>
+          </div>
+        </div>
+      </div>
+      )}
+      visible={open}
+      interactive={true}
+      animation={false}
+      reference={reference}
+      placement='auto-end'
+      {...tippyProps}
+    />
+  );
+}
diff --git a/web/pgadmin/tools/erd/static/js/erd_tool/ui_components/Loader.jsx b/web/pgadmin/tools/erd/static/js/erd_tool/ui_components/Loader.jsx
new file mode 100644
index 000000000..96e248162
--- /dev/null
+++ b/web/pgadmin/tools/erd/static/js/erd_tool/ui_components/Loader.jsx
@@ -0,0 +1,27 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2020, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import React from 'react';
+
+export default function Loader({message, autoEllipsis=false}) {
+  if(message || message == '') {
+    return (
+      <div className="pg-sp-container">
+        <div className="pg-sp-content">
+          <div className="row">
+            <div className="col-12 pg-sp-icon"></div>
+          </div>
+          <div className="row"><div className="col-12 pg-sp-text">{message}{autoEllipsis ? '...':''}</div></div>
+        </div>
+      </div>
+    );
+  } else {
+    return null;
+  }
+}
diff --git a/web/pgadmin/tools/erd/static/js/erd_tool/ui_components/ToolBar.jsx b/web/pgadmin/tools/erd/static/js/erd_tool/ui_components/ToolBar.jsx
new file mode 100644
index 000000000..5341ac406
--- /dev/null
+++ b/web/pgadmin/tools/erd/static/js/erd_tool/ui_components/ToolBar.jsx
@@ -0,0 +1,83 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2020, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import React, { forwardRef } from 'react';
+import Tippy from '@tippyjs/react';
+import {isMac} from 'sources/keyboard_shortcuts';
+import gettext from 'sources/gettext';
+
+const BaseIconButton = forwardRef((props, ref)=>{
+  const {icon, text, className, ...otherProps} = props;
+
+  return(
+    <button ref={ref} className={className} {...otherProps}>
+      {icon && <span className={`${icon} sql-icon-lg`} aria-hidden="true" role="img"></span>}
+      {text && <span className="text-icon">{text}</span>}
+    </button>
+  );
+});
+
+export function Shortcut({shortcut}) {
+  let keys = [];
+  shortcut.alt && keys.push((isMac() ? 'Option' : 'Alt'));
+  shortcut.control && keys.push('Ctrl');
+  shortcut.shift && keys.push('Shift');
+  keys.push(shortcut.key.char.toUpperCase());
+  return (
+    <div style={{justifyContent: 'center', marginTop: '0.125rem'}} className="d-flex">
+      {keys.map((key, i)=>{
+        return <div key={i} className="shortcut-key">{key}</div>
+      })}
+    </div>
+  )
+}
+
+export const IconButton = forwardRef((props, ref) => {
+  const {title, shortcut, className, ...otherProps} = props;
+
+  if (title) {
+    return (
+      <Tippy content={
+        <>
+          {title && <div style={{textAlign: 'center'}}>{title}</div>}
+          {shortcut && <Shortcut shortcut={shortcut} />}
+        </>
+      }>
+        <BaseIconButton ref={ref} className={'btn btn-sm btn-primary-icon ' + (className || '')} {...otherProps}/>
+      </Tippy>
+    );
+  } else {
+    return <BaseIconButton ref={ref} className='btn btn-sm btn-primary-icon' {...otherProps}/>
+  }
+});
+
+export function DetailsToggleButton({showDetails, ...props}) {
+  return (
+    <IconButton
+      icon={showDetails ? 'far fa-eye' : 'fas fa-low-vision'}
+      title={showDetails ? gettext('Show fewer details') : gettext("Show more details") }
+      {...props} />
+  );
+}
+
+export function ButtonGroup({className, children}) {
+  return (
+    <div className={'btn-group mr-1 ' + (className ? className : '')} role="group" aria-label="save group">
+      {children}
+    </div>
+  )
+}
+
+export default function ToolBar({id, children}) {
+  return (
+    <div id={id} className="editor-toolbar d-flex" role="toolbar" aria-label="">
+      {children}
+    </div>
+  )
+}
diff --git a/web/pgadmin/tools/erd/static/js/erd_tool_hook.js b/web/pgadmin/tools/erd/static/js/erd_tool_hook.js
new file mode 100644
index 000000000..14a65fbaa
--- /dev/null
+++ b/web/pgadmin/tools/erd/static/js/erd_tool_hook.js
@@ -0,0 +1,36 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2020, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+define([
+  'sources/url_for', 'jquery',
+  'sources/pgadmin', 'pgadmin.tools.erd/erd_tool', 'pgadmin.browser',
+  'pgadmin.browser.server.privilege', 'pgadmin.node.database', 'pgadmin.node.primary_key',
+  'pgadmin.node.foreign_key', 'pgadmin.browser.datamodel', 'pgadmin.file_manager',
+], function(
+  url_for, $, pgAdmin, ERDToolModule
+) {
+  var pgTools = pgAdmin.Tools = pgAdmin.Tools || {};
+  var ERDTool = ERDToolModule.default;
+
+  /* Return back, this has been called more than once */
+  if (pgTools.ERDToolHook)
+    return pgTools.ERDToolHook;
+
+  pgTools.ERDToolHook = {
+    load: function(params) {
+      /* Create the ERD Tool object and render it */
+      let erdObj = new ERDTool('#erd-tool-container', params);
+      erdObj.render();
+    },
+  };
+
+  return pgTools.ERDToolHook;
+});
+
+
diff --git a/web/pgadmin/tools/erd/static/js/index.js b/web/pgadmin/tools/erd/static/js/index.js
new file mode 100644
index 000000000..b968a5223
--- /dev/null
+++ b/web/pgadmin/tools/erd/static/js/index.js
@@ -0,0 +1,23 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2020, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+import gettext from 'sources/gettext';
+import url_for from 'sources/url_for';
+import $ from 'jquery';
+import _ from 'underscore';
+import pgAdmin from 'sources/pgadmin';
+import pgBrowser from 'top/browser/static/js/browser';
+import * as csrfToken from 'sources/csrf';
+import {initialize} from './erd_module';
+var wcDocker = window.wcDocker;
+
+let pgBrowserOut = initialize(gettext, url_for, $, _, pgAdmin, csrfToken, pgBrowser, wcDocker);
+
+module.exports = {
+  pgBrowser: pgBrowserOut,
+};
diff --git a/web/pgadmin/tools/erd/static/scss/_erd.scss b/web/pgadmin/tools/erd/static/scss/_erd.scss
new file mode 100644
index 000000000..c9b7d3185
--- /dev/null
+++ b/web/pgadmin/tools/erd/static/scss/_erd.scss
@@ -0,0 +1,192 @@
+.shortcut-key {
+  padding: 0 0.25rem;
+  border: 1px solid $border-color;
+  margin-right: 0.125rem;
+  border-radius: $btn-border-radius;
+}
+
+#erd-tool-container {
+	width: 100%;
+	height: 100%;
+
+  .file-input-hidden {
+    height: 0;
+    width: 0;
+    visibility: hidden;
+  }
+
+  .text-icon {
+    font-weight: bold;
+  }
+
+  .erd-status-bar {
+    background: $sql-gutters-bg;
+    border-top: $panel-border;
+    padding: 0.25rem 0.5rem;
+  }
+
+  .diagram-container {
+    position: relative;
+    width: 100%;
+    height: 100%;
+  }
+
+  .floating-note {
+    width: 250px;
+    border: $panel-border;
+    border-radius: $panel-border-radius;
+    box-shadow: $dialog-box-shadow;
+    background-color: $alert-dialog-body-bg !important;
+    color: $color-fg !important;
+
+    .note-header {
+      padding: 0.25rem 0.5rem;
+      background-color: $alert-header-bg;
+      font-size: $font-size-base;
+      font-weight: bold;
+      color: $alert-header-fg;
+      overflow: hidden;
+      white-space: nowrap;
+      text-overflow: ellipsis;
+      border-radius: 0rem;
+      border-top-left-radius: $panel-border-radius;
+      border-top-right-radius: $panel-border-radius;
+      border-bottom: none;
+      margin: -$alertify-borderremove-margin; //-24px is default by alertify
+      margin-bottom: 0px;
+    }
+
+    .note-body {
+      & textarea {
+        width: 100%;
+        border: none;
+        border-bottom: $border-width solid $erd-node-border-color;
+        border-top: $border-width solid $erd-node-border-color;
+      }
+
+      & .pg_buttons {
+        padding: 0.25rem;
+      }
+    }
+  }
+
+  .diagram-canvas{
+    width: 100%;
+    height: 100%;
+    color: $color-fg;
+    font-family: sans-serif;
+    background-image: $erd-bg-grid;
+
+    // overflow: auto;
+    // cursor: unset;
+
+    .table-node {
+      background-color: $input-bg;
+      border: $border-width solid $erd-node-border-color;
+      border-radius: $input-border-radius;
+      position: relative;
+      width: 175px;
+      font-size: 0.8em;
+
+      &.selected {
+        border-color: $input-focus-border-color;
+        box-shadow: $input-btn-focus-box-shadow;
+      }
+
+      .table-toolbar {
+        background: $editor-toolbar-bg;
+        border-bottom: $border-width solid $erd-node-border-color;
+        padding: 0.125rem;
+        border-top-left-radius: inherit;
+        border-top-right-radius: inherit;
+        display: flex;
+
+        .btn {
+          &:not(:first-of-type) {
+            margin-left: 0.125rem;
+          }
+        }
+      }
+
+      .table-schema {
+        border-bottom: $border-width solid $erd-node-border-color;
+        padding: $erd-row-padding;
+        font-weight: bold;
+      }
+
+      .table-name {
+        border-bottom: $border-width*2 solid $erd-node-border-color;
+        padding: $erd-row-padding;
+        font-weight: bold;
+      }
+
+      .table-cols {
+        .col-row {
+          border-bottom: $border-width solid $erd-node-border-color;
+          .col-row-data {
+            padding: $erd-row-padding;
+            width: 100%;
+
+            .col-name {
+              word-break: break-all;
+            }
+          }
+          .col-row-port {
+            padding: 0;
+            min-height: 0;
+          }
+        }
+      }
+    }
+
+    .svg-link-ele {
+      stroke: $erd-link-color;
+    }
+
+    .svg-link-ele.path {
+      pointer-events: all;
+    }
+
+    @keyframes svg-link-ele-selected {
+      from { stroke-dashoffset: 24; } to { stroke-dashoffset: 0; }
+    }
+
+    .svg-link-ele.selected {
+      stroke: $erd-link-selected-color;
+      stroke-dasharray: 10, 2;
+      animation: svg-link-ele-selected 1s linear infinite;
+    }
+
+    .svg-link-ele.svg-otom-circle {
+      fill: $erd-link-color;
+    }
+
+    .custom-node-color{
+      position: absolute;
+      top: 50%;
+      left: 50%;
+      width: 20px;
+      height: 20px;
+      transform: translate(-50%, -50%);
+      border-radius: 10px;
+    }
+
+    .circle-port{
+      width: 12px;
+      height: 12px;
+      margin: 2px;
+      border-radius: 4px;
+      background: darkgray;
+      cursor: pointer;
+    }
+
+    .circle-port:hover{
+      background: mediumpurple;
+    }
+
+    .port {
+      display: inline-block;
+      margin: auto;
+    }
+  }
+}
diff --git a/web/pgadmin/tools/erd/templates/erd/index.html b/web/pgadmin/tools/erd/templates/erd/index.html
new file mode 100644
index 000000000..10a0896f5
--- /dev/null
+++ b/web/pgadmin/tools/erd/templates/erd/index.html
@@ -0,0 +1,55 @@
+{% extends "base.html" %}
+{% block title %}{{title}}{% endblock %}
+
+{% block css_link %}
+<link type="text/css" rel="stylesheet" href="{{ url_for('browser.browser_css')}}"/>
+{% endblock %}
+{% block body %}
+<style>
+    body {padding: 0px;}
+    {% if is_desktop_mode and is_linux %}
+    .alertify .ajs-dimmer,.alertify .ajs-modal{-webkit-transform: none;}
+    .alertify-notifier{-webkit-transform: none;}
+    .alertify-notifier .ajs-message{-webkit-transform: none;}
+    .alertify .ajs-dialog.ajs-shake{-webkit-animation-name: none;}
+    .sql-editor-busy-icon.fa-pulse{-webkit-animation: none;}
+    {% endif %}
+</style>
+<div id="erd-tool-container" class="d-flex flex-column">
+</div>
+{% endblock %}
+{% block init_script %}
+    try {
+        require(
+            ['sources/generated/browser_nodes', 'sources/generated/codemirror'],
+            function() {
+                require(['sources/generated/erd_tool'], function(erdToolHook) {
+                var erdToolHook = erdToolHook || pgAdmin.Tools.ERDToolHook;
+                erdToolHook.load({{ params|safe }});
+
+                if(window.opener) {
+                    $(window).on('unload', function(ev) {
+                        $.ajax({
+                            method: 'DELETE',
+                            url: '{{close_url}}'
+                        });
+                    });
+                } else {
+                    $(window).on('beforeunload', function(ev) {
+                        $.ajax({
+                            method: 'DELETE',
+                            url: '{{close_url}}'
+                        });
+                    });
+                }
+            }, function() {
+                console.log(arguments);
+            });
+        },
+        function() {
+            console.log(arguments);
+        });
+    } catch (err) {
+        console.log(err);
+    }
+{% endblock %}
diff --git a/web/pgadmin/tools/erd/tests/__init__.py b/web/pgadmin/tools/erd/tests/__init__.py
new file mode 100644
index 000000000..8c8c486cc
--- /dev/null
+++ b/web/pgadmin/tools/erd/tests/__init__.py
@@ -0,0 +1,15 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+from pgadmin.utils.route import BaseTestGenerator
+
+
+class ERDGenerateTestCase(BaseTestGenerator):
+    def runTest(self):
+        return
diff --git a/web/pgadmin/tools/erd/tests/sql/12_plus/test_sql_output.sql b/web/pgadmin/tools/erd/tests/sql/12_plus/test_sql_output.sql
new file mode 100644
index 000000000..e3d6ab762
--- /dev/null
+++ b/web/pgadmin/tools/erd/tests/sql/12_plus/test_sql_output.sql
@@ -0,0 +1,25 @@
+
+
+CREATE TABLE public.newtable1
+(
+    id integer,
+    col1 character varying(50),
+    PRIMARY KEY (id)
+);
+
+CREATE TABLE public.newtable2
+(
+    table1_id integer,
+    col2 character varying(50),
+    PRIMARY KEY (id)
+);
+
+CREATE TABLE public.newtable3
+(
+)
+;
+
+ALTER TABLE public.newtable2
+    ADD FOREIGN KEY (table1_id)
+    REFERENCES public.newtable1 (id)
+    NOT VALID;
diff --git a/web/pgadmin/tools/erd/tests/sql/default/test_sql_output.sql b/web/pgadmin/tools/erd/tests/sql/default/test_sql_output.sql
new file mode 100644
index 000000000..330e81443
--- /dev/null
+++ b/web/pgadmin/tools/erd/tests/sql/default/test_sql_output.sql
@@ -0,0 +1,34 @@
+
+
+CREATE TABLE public.newtable1
+(
+    id integer,
+    col1 character varying(50),
+    PRIMARY KEY (id)
+)
+WITH (
+    OIDS = FALSE
+);
+
+CREATE TABLE public.newtable2
+(
+    table1_id integer,
+    col2 character varying(50),
+    PRIMARY KEY (id)
+)
+WITH (
+    OIDS = FALSE
+);
+
+CREATE TABLE public.newtable3
+(
+)
+
+WITH (
+    OIDS = FALSE
+);
+
+ALTER TABLE public.newtable2
+    ADD FOREIGN KEY (table1_id)
+    REFERENCES public.newtable1 (id)
+    NOT VALID;
diff --git a/web/pgadmin/tools/erd/tests/test_close.py b/web/pgadmin/tools/erd/tests/test_close.py
new file mode 100644
index 000000000..008ddab39
--- /dev/null
+++ b/web/pgadmin/tools/erd/tests/test_close.py
@@ -0,0 +1,55 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+import json
+
+from pgadmin.utils.route import BaseTestGenerator
+from regression.python_test_utils import test_utils as utils
+from regression import parent_node_dict
+from regression.test_setup import config_data
+from pgadmin.browser.server_groups.servers.databases.tests import utils as \
+    database_utils
+
+
+class ERDClose(BaseTestGenerator):
+
+    def setUp(self):
+        self.db_name = "erdtestdb"
+        self.sid = parent_node_dict["server"][-1]["server_id"]
+        self.did = utils.create_database(self.server, self.db_name)
+        self.sgid = config_data["server_group"]
+
+    def runTest(self):
+        db_con = database_utils.connect_database(self,
+                                                 self.sgid,
+                                                 self.sid,
+                                                 self.did)
+
+        if not db_con["info"] == "Database connected.":
+            raise Exception("Could not connect to database to add the schema.")
+
+        url = '/erd/initialize/{trans_id}/{sgid}/{sid}/{did}'.format(
+            trans_id=123344, sgid=self.sgid, sid=self.sid, did=self.did)
+
+        response = self.tester.post(url)
+        self.assertEqual(response.status_code, 200)
+
+        url = '/erd/close/{trans_id}/{sgid}/{sid}/{did}'.format(
+            trans_id=123344, sgid=self.sgid, sid=self.sid, did=self.did)
+
+        response = self.tester.delete(url)
+        self.assertEqual(response.status_code, 200)
+
+    def tearDown(self):
+        connection = utils.get_db_connection(self.server['db'],
+                                             self.server['username'],
+                                             self.server['db_password'],
+                                             self.server['host'],
+                                             self.server['port'])
+        utils.drop_database(connection, self.db_name)
diff --git a/web/pgadmin/tools/erd/tests/test_initialize.py b/web/pgadmin/tools/erd/tests/test_initialize.py
new file mode 100644
index 000000000..d586bb3b6
--- /dev/null
+++ b/web/pgadmin/tools/erd/tests/test_initialize.py
@@ -0,0 +1,54 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+import json
+
+from pgadmin.utils.route import BaseTestGenerator
+from regression.python_test_utils import test_utils as utils
+from regression import parent_node_dict
+from regression.test_setup import config_data
+from pgadmin.browser.server_groups.servers.databases.tests import utils as \
+    database_utils
+
+
+class ERDInitialize(BaseTestGenerator):
+
+    def setUp(self):
+        self.db_name = "erdtestdb"
+        self.sid = parent_node_dict["server"][-1]["server_id"]
+        self.did = utils.create_database(self.server, self.db_name)
+        self.sgid = config_data["server_group"]
+
+    def runTest(self):
+        db_con = database_utils.connect_database(self,
+                                                 self.sgid,
+                                                 self.sid,
+                                                 self.did)
+
+        if not db_con["info"] == "Database connected.":
+            raise Exception("Could not connect to database to add the schema.")
+
+        url = '/erd/initialize/{trans_id}/{sgid}/{sid}/{did}'.format(
+            trans_id=123344, sgid=self.sgid, sid=self.sid, did=self.did)
+
+        response = self.tester.post(url)
+        self.assertEqual(response.status_code, 200)
+        response_data = json.loads(response.data.decode('utf-8'))
+        self.assertEqual(response_data['data'], {
+            'connId': '123344',
+            'serverVersion': self.server_information['server_version'],
+        })
+
+    def tearDown(self):
+        connection = utils.get_db_connection(self.server['db'],
+                                             self.server['username'],
+                                             self.server['db_password'],
+                                             self.server['host'],
+                                             self.server['port'])
+        utils.drop_database(connection, self.db_name)
diff --git a/web/pgadmin/tools/erd/tests/test_panel.py b/web/pgadmin/tools/erd/tests/test_panel.py
new file mode 100644
index 000000000..ad44d004a
--- /dev/null
+++ b/web/pgadmin/tools/erd/tests/test_panel.py
@@ -0,0 +1,44 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+import json
+
+from pgadmin.utils.route import BaseTestGenerator
+from regression.python_test_utils import test_utils as utils
+from regression import parent_node_dict
+from regression.test_setup import config_data
+from pgadmin.browser.server_groups.servers.databases.tests import utils as \
+    database_utils
+
+
+class ERDPanel(BaseTestGenerator):
+
+    def setUp(self):
+        self.db_name = "erdtestdb"
+        self.sid = parent_node_dict["server"][-1]["server_id"]
+        self.did = utils.create_database(self.server, self.db_name)
+        self.sgid = config_data["server_group"]
+
+    def runTest(self):
+        url = '/erd/panel/{trans_id}?sgid={sgid}&sid={sid}&server_type=pg' \
+              '&did={did}&gen=false'.\
+            format(trans_id=123344, sgid=self.sgid, sid=self.sid, did=self.did)
+
+        response = self.tester.post(
+            url, data={"title": "panel_title", "close_url": "the/close/url"},
+            content_type="application/x-www-form-urlencoded")
+        self.assertEqual(response.status_code, 200)
+
+    def tearDown(self):
+        connection = utils.get_db_connection(self.server['db'],
+                                             self.server['username'],
+                                             self.server['db_password'],
+                                             self.server['host'],
+                                             self.server['port'])
+        utils.drop_database(connection, self.db_name)
diff --git a/web/pgadmin/tools/erd/tests/test_prequisite.py b/web/pgadmin/tools/erd/tests/test_prequisite.py
new file mode 100644
index 000000000..fed6a0652
--- /dev/null
+++ b/web/pgadmin/tools/erd/tests/test_prequisite.py
@@ -0,0 +1,52 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+import json
+
+from pgadmin.utils.route import BaseTestGenerator
+from regression.python_test_utils import test_utils as utils
+from regression import parent_node_dict
+from regression.test_setup import config_data
+from pgadmin.browser.server_groups.servers.databases.tests import utils as \
+    database_utils
+
+
+class ERDPrequisite(BaseTestGenerator):
+
+    def setUp(self):
+        self.db_name = "erdtestdb"
+        self.sid = parent_node_dict["server"][-1]["server_id"]
+        self.did = utils.create_database(self.server, self.db_name)
+        self.sgid = config_data["server_group"]
+
+    def runTest(self):
+        db_con = database_utils.connect_database(self,
+                                                 self.sgid,
+                                                 self.sid,
+                                                 self.did)
+
+        if not db_con["info"] == "Database connected.":
+            raise Exception("Could not connect to database to add the schema.")
+
+        url = '/erd/prequisite/{trans_id}/{sgid}/{sid}/{did}'.format(
+            trans_id=123344, sgid=self.sgid, sid=self.sid, did=self.did)
+
+        response = self.tester.get(url)
+        self.assertEqual(response.status_code, 200)
+        response_data = json.loads(response.data.decode('utf-8'))
+        self.assertIn('col_types', response_data['data'])
+        self.assertIn('schemas', response_data['data'])
+
+    def tearDown(self):
+        connection = utils.get_db_connection(self.server['db'],
+                                             self.server['username'],
+                                             self.server['db_password'],
+                                             self.server['host'],
+                                             self.server['port'])
+        utils.drop_database(connection, self.db_name)
diff --git a/web/pgadmin/tools/erd/tests/test_sql.py b/web/pgadmin/tools/erd/tests/test_sql.py
new file mode 100644
index 000000000..352a1dc83
--- /dev/null
+++ b/web/pgadmin/tools/erd/tests/test_sql.py
@@ -0,0 +1,91 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+import json
+
+from pgadmin.utils.route import BaseTestGenerator
+from regression.python_test_utils import test_utils as utils
+from regression import parent_node_dict
+from regression.test_setup import config_data
+from pgadmin.browser.server_groups.servers.databases.tests import utils as \
+    database_utils
+from pgadmin.utils.versioned_template_loader import \
+    get_version_mapping_directories
+from os import path
+
+
+class ERDSql(BaseTestGenerator):
+
+    def setUp(self):
+        self.db_name = "erdtestdb"
+        self.sid = parent_node_dict["server"][-1]["server_id"]
+        self.did = utils.create_database(self.server, self.db_name)
+        self.sgid = config_data["server_group"]
+        self.maxDiff = None
+
+    def get_expected_sql(self):
+        sql_base_path = path.join(
+            path.dirname(path.realpath(__file__)), 'sql')
+
+        # Iterate the version mapping directories.
+        for version_mapping in \
+                get_version_mapping_directories(self.server['type']):
+            if version_mapping['number'] > \
+                    self.server_information['server_version']:
+                continue
+
+            complete_path = path.join(
+                sql_base_path, version_mapping['name'])
+
+            if not path.exists(complete_path):
+                complete_path = path.join(sql_base_path, 'default')
+            break
+
+        data_sql = ''
+        with open(path.join(complete_path, 'test_sql_output.sql')) as fp:
+            data_sql = fp.read()
+
+        return data_sql
+
+    def runTest(self):
+        pass
+        db_con = database_utils.connect_database(self,
+                                                 self.sgid,
+                                                 self.sid,
+                                                 self.did)
+
+        if not db_con["info"] == "Database connected.":
+            raise Exception("Could not connect to database to add the schema.")
+
+        url = '/erd/sql/{trans_id}/{sgid}/{sid}/{did}'.format(
+            trans_id=123344, sgid=self.sgid, sid=self.sid, did=self.did)
+
+        curr_dir = path.dirname(__file__)
+
+        data_json = None
+        with open(path.join(curr_dir, 'test_sql_input_data.json')) as fp:
+            data_json = fp.read()
+
+        response = self.tester.post(url,
+                                    data=data_json,
+                                    content_type='html/json')
+        self.assertEqual(response.status_code, 200)
+
+        data_sql = self.get_expected_sql()
+
+        resp_sql = json.loads(response.data.decode('utf-8'))['data']
+        self.assertEqual(resp_sql, data_sql)
+
+    def tearDown(self):
+        connection = utils.get_db_connection(self.server['db'],
+                                             self.server['username'],
+                                             self.server['db_password'],
+                                             self.server['host'],
+                                             self.server['port'])
+        utils.drop_database(connection, self.db_name)
diff --git a/web/pgadmin/tools/erd/tests/test_sql_input_data.json b/web/pgadmin/tools/erd/tests/test_sql_input_data.json
new file mode 100644
index 000000000..3c8182e68
--- /dev/null
+++ b/web/pgadmin/tools/erd/tests/test_sql_input_data.json
@@ -0,0 +1,106 @@
+{
+  "nodes": {
+    "1d9dc56e-e4f9-48b9-889b-6084ec6446bf": {
+      "columns": [
+        {
+          "name": "id",
+          "attnum": 0,
+          "cltype": "integer",
+          "is_primary_key": true,
+          "attnotnull": false,
+          "attlen": null,
+          "attprecision": null,
+          "attidentity": "a",
+          "colconstype": "n"
+        },
+        {
+          "name": "col1",
+          "attnum": 1,
+          "cltype": "character varying",
+          "min_val_attlen": 1,
+          "max_val_attlen": 2147483647,
+          "is_primary_key": false,
+          "attnotnull": false,
+          "attlen": 50,
+          "attprecision": null,
+          "attidentity": "a",
+          "colconstype": "n"
+        }
+      ],
+      "name": "newtable1",
+      "schema": "public",
+      "primary_key": [
+        {
+          "columns": [
+            {
+              "column": "id"
+            }
+          ],
+          "include": []
+        }
+      ]
+    },
+    "c4fee4ad-cf32-4fc6-bb87-98b896bcab60": {
+      "name": "newtable2",
+      "schema": "public",
+      "columns": [
+        {
+          "name": "table1_id",
+          "attnum": 0,
+          "cltype": "integer",
+          "is_primary_key": false,
+          "attnotnull": false,
+          "attlen": null,
+          "attprecision": null,
+          "attidentity": "a",
+          "colconstype": "n",
+          "old_attidentity": "a"
+        },
+        {
+          "name": "col2",
+          "attnum": 1,
+          "cltype": "character varying",
+          "min_val_attlen": 1,
+          "max_val_attlen": 2147483647,
+          "is_primary_key": false,
+          "attnotnull": false,
+          "attlen": 50,
+          "attprecision": null,
+          "attidentity": "a",
+          "colconstype": "n",
+          "old_attidentity": "a"
+        }
+      ],
+      "primary_key": [
+        {
+          "columns": [
+            {
+              "column": "id"
+            }
+          ],
+          "include": []
+        }
+      ]
+    },
+    "f001a770-d6fa-4572-b88b-11dd5e38d30c": {
+      "columns": [],
+      "name": "newtable3",
+      "schema": "public",
+      "primary_key": []
+    }
+  },
+  "links": {
+    "998de19a-caa0-431e-9cf7-97827f01022b": {
+      "schema": "public",
+      "table": "newtable2",
+      "remote_schema": "public",
+      "remote_table": "newtable1",
+      "columns": [
+        {
+          "local_column": "table1_id",
+          "referenced": "id"
+        }
+      ]
+    }
+  }
+}
diff --git a/web/pgadmin/tools/erd/tests/test_tables.py b/web/pgadmin/tools/erd/tests/test_tables.py
new file mode 100644
index 000000000..b8de11646
--- /dev/null
+++ b/web/pgadmin/tools/erd/tests/test_tables.py
@@ -0,0 +1,79 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+import json
+import uuid
+
+from pgadmin.utils.route import BaseTestGenerator
+from regression.python_test_utils import test_utils as utils
+from regression import parent_node_dict
+from regression.test_setup import config_data
+from pgadmin.browser.server_groups.servers.databases.tests import utils as \
+    database_utils
+from pgadmin.browser.server_groups.servers.databases.schemas.tables.tests \
+    import utils as tables_utils
+from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
+    utils as schema_utils
+
+
+class ERDTables(BaseTestGenerator):
+
+    def dropDB(self):
+        connection = utils.get_db_connection(self.server['db'],
+                                             self.server['username'],
+                                             self.server['db_password'],
+                                             self.server['host'],
+                                             self.server['port'])
+        utils.drop_database(connection, self.db_name)
+
+    def setUp(self):
+        self.db_name = "erdtestdb"
+        self.sid = parent_node_dict["server"][-1]["server_id"]
+        self.did = utils.create_database(self.server, self.db_name)
+
+        try:
+            self.sgid = config_data["server_group"]
+            self.tables = [
+                ["erd1", "table_1"], ["erd2", "table_2"]
+            ]
+
+            for tab in self.tables:
+                connection = utils.get_db_connection(
+                    self.db_name, self.server['username'],
+                    self.server['db_password'], self.server['host'],
+                    self.server['port'])
+                schema_utils.create_schema(connection, tab[0])
+                tables_utils.create_table(self.server, self.db_name, tab[0],
+                                          tab[1])
+                connection.close()
+        except Exception as _:
+            self.dropDB()
+            raise
+
+    def runTest(self):
+        db_con = database_utils.connect_database(self,
+                                                 self.sgid,
+                                                 self.sid,
+                                                 self.did)
+
+        if not db_con["info"] == "Database connected.":
+            raise Exception("Could not connect to database to add the schema.")
+
+        url = '/erd/tables/{trans_id}/{sgid}/{sid}/{did}'.format(
+            trans_id=123344, sgid=self.sgid, sid=self.sid, did=self.did)
+
+        response = self.tester.get(url)
+        self.assertEqual(response.status_code, 200)
+
+        response = json.loads(response.data.decode('utf-8'))
+        self.assertEqual(self.tables, [[tab['schema'], tab['name']]
+                                       for tab in response['data']])
+
+    def tearDown(self):
+        self.dropDB()
diff --git a/web/pgadmin/tools/erd/utils.py b/web/pgadmin/tools/erd/utils.py
new file mode 100644
index 000000000..1999a71e5
--- /dev/null
+++ b/web/pgadmin/tools/erd/utils.py
@@ -0,0 +1,71 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+from pgadmin.browser.server_groups.servers.databases.schemas.tables.utils \
+    import BaseTableView
+from pgadmin.browser.server_groups.servers.databases.schemas.utils \
+    import get_schemas
+from pgadmin.browser.server_groups.servers.databases.schemas.utils \
+    import DataTypeReader
+
+
+class ERDTableView(BaseTableView, DataTypeReader):
+    def __init__(self):
+        super(BaseTableView, self).__init__(cmd='erd')
+
+    @BaseTableView.check_precondition
+    def sql(self, conn_id=None, did=None, sid=None, data={}):
+        return BaseTableView.get_sql(self, did, None, None, data, None)
+
+    @BaseTableView.check_precondition
+    def get_types(self, conn_id=None, did=None, sid=None):
+        condition = self.get_types_condition_sql(False)
+        return DataTypeReader.get_types(self, self.conn, condition, True)
+
+    @BaseTableView.check_precondition
+    def fetch_all_tables(self, conn_id=None, did=None, sid=None):
+        status, schemas = get_schemas(self.conn, show_system_objects=False)
+        if not status:
+            return status, schemas
+
+        all_tables = []
+        for row in schemas['rows']:
+            status, res = \
+                BaseTableView.fetch_tables(self, sid, did, row['oid'])
+            if not status:
+                return status, res
+
+            all_tables.extend(res.values())
+
+        return True, all_tables
+
+
+class ERDHelper:
+    def __init__(self, conn_id, sid, did):
+        self.conn_id = conn_id
+        self.did = did
+        self.sid = sid
+        self.table_view = ERDTableView()
+        self.link_view = None
+
+    def get_types(self):
+        return self.table_view.get_types(
+            conn_id=self.conn_id, did=self.did, sid=self.sid)
+
+    def get_table_sql(self, data):
+        SQL, name = self.table_view.sql(
+            conn_id=self.conn_id, did=self.did, sid=self.sid,
+            data=data)
+        return SQL
+
+    def get_all_tables(self):
+        status, res = self.table_view.fetch_all_tables(
+            conn_id=self.conn_id, did=self.did, sid=self.sid)
+
+        return status, res
diff --git a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
index abd33a47b..5d74fd546 100644
--- a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
+++ b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
@@ -2651,6 +2651,11 @@ define('tools.querytool', [
                 }
 
               });
+          } else if(url_params.sql_id) {
+            let sqlValue = sessionStorage.getItem(url_params.sql_id);
+            if(sqlValue) {
+              self.gridView.query_tool_obj.setValue(sqlValue);
+            }
           }
         }
         else {
@@ -2668,7 +2673,7 @@ define('tools.querytool', [
       },
 
       set_value_to_editor: function(query) {
-        if (this.gridView && this.gridView.query_tool_obj && !_.isUndefined(query)) {
+        if (this.gridView && this.gridView.query_tool_obj && !_.isUndefined(query) && query != '') {
           this.gridView.query_tool_obj.setValue(query);
         }
       },
diff --git a/web/pgadmin/utils/csrf.py b/web/pgadmin/utils/csrf.py
index 434e653a7..1e6aef7d8 100644
--- a/web/pgadmin/utils/csrf.py
+++ b/web/pgadmin/utils/csrf.py
@@ -36,7 +36,8 @@ class _PGCSRFProtect(CSRFProtect):
             'pgadmin.tools.debugger.direct_new',
             'pgadmin.tools.schema_diff.panel',
             'pgadmin.tools.schema_diff.ddl_compare',
-            'pgadmin.authenticate.login'
+            'pgadmin.authenticate.login',
+            'pgadmin.tools.erd.panel',
         ]
 
         for exempt in exempt_views:
diff --git a/web/regression/javascript/erd/erd_core_spec.js b/web/regression/javascript/erd/erd_core_spec.js
new file mode 100644
index 000000000..c488ffdf2
--- /dev/null
+++ b/web/regression/javascript/erd/erd_core_spec.js
@@ -0,0 +1,382 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2020, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+import ERDCore from 'pgadmin.tools.erd/erd_tool/ERDCore';
+import * as createEngineLib from '@projectstorm/react-diagrams';
+import TEST_TABLES_DATA from './test_tables';
+
+describe('ERDCore', ()=>{
+  let eleFactory = jasmine.createSpyObj('nodeFactories', {
+    'registerFactory': null,
+    'getFactory': jasmine.createSpyObj('getFactory', ['generateModel', 'calculateRoutingMatrix']),
+  });
+  let erdEngine = jasmine.createSpyObj('engine', {
+    'getNodeFactories': eleFactory,
+    'getLinkFactories': eleFactory,
+    'getPortFactories': eleFactory,
+    'getActionEventBus': jasmine.createSpyObj('actionBus', ['fireAction', 'deregisterAction', 'registerAction']),
+    'setModel': null,
+    'getModel': jasmine.createSpyObj('modelObj', {
+      'addNode': null,
+      'clearSelection': null,
+      'getNodesDict': null,
+      'getLinks': null,
+      'serialize': ()=>({
+        'data': 'serialized',
+      }),
+      'addLink': null,
+      'getNodes': null,
+      'setZoomLevel': null,
+      'getZoomLevel': null,
+      'fireEvent': null,
+      'registerListener': null,
+    }),
+    'repaintCanvas': null,
+    'zoomToFit': null,
+    'fireEvent': null,
+  });
+
+  beforeAll(()=>{
+    spyOn(createEngineLib, 'default').and.returnValue(erdEngine);
+  });
+
+  it('initialization', ()=>{
+    spyOn(ERDCore.prototype, 'initializeEngine').and.callThrough();
+    spyOn(ERDCore.prototype, 'initializeModel').and.callThrough();
+    spyOn(ERDCore.prototype, 'computeTableCounter').and.callThrough();
+    let erdCoreObj = new ERDCore();
+    expect(erdCoreObj.initializeEngine).toHaveBeenCalled();
+    expect(erdCoreObj.initializeModel).toHaveBeenCalled();
+    expect(erdCoreObj.computeTableCounter).toHaveBeenCalled();
+  });
+
+  describe('functions', ()=>{
+    let erdCoreObj;
+
+    beforeAll(()=>{
+      erdCoreObj = new ERDCore();
+    });
+
+    describe('cache check', ()=>{
+      it('for single value', ()=>{
+        erdCoreObj.setCache('key1', 'value1');
+        expect(erdCoreObj.getCache('key1')).toEqual('value1');
+      });
+
+      it('for multiple value', ()=>{
+        erdCoreObj.setCache({'key1': 'valuem1', 'key2': 'valuem2'});
+        expect(erdCoreObj.getCache('key1')).toEqual('valuem1');
+        expect(erdCoreObj.getCache('key2')).toEqual('valuem2');
+      });
+    });
+
+    it('registerModelEvent', ()=>{
+      let fn = ()=>{};
+      erdCoreObj.registerModelEvent('someEvent', fn);
+      expect(erdCoreObj.getModel().registerListener).toHaveBeenCalledWith({
+        'someEvent': fn,
+      });
+    });
+
+    it('getNextTableName', ()=>{
+      expect(erdCoreObj.getNextTableName()).toEqual('newtable1');
+      expect(erdCoreObj.getNextTableName()).toEqual('newtable2');
+    });
+
+    it('getEngine', ()=>{
+      expect(erdCoreObj.getEngine()).toBe(erdEngine);
+    });
+
+    it('getNewNode', ()=>{
+      let data = {name: 'table1'};
+      erdCoreObj.getNewNode(data);
+
+      expect(erdEngine.getNodeFactories().getFactory().generateModel).toHaveBeenCalledWith({
+        initialConfig: {
+          otherInfo: {
+            data:data,
+          },
+        },
+      });
+    });
+
+    it('getNewLink', ()=>{
+      let data = {name: 'link1'};
+      erdCoreObj.getNewLink('linktype', data);
+
+      expect(erdEngine.getLinkFactories().getFactory).toHaveBeenCalledWith('linktype');
+      expect(erdEngine.getLinkFactories().getFactory().generateModel).toHaveBeenCalledWith({
+        initialConfig: {
+          data: data,
+        },
+      });
+    });
+
+    it('getNewPort', ()=>{
+      let data = {name: 'link1'};
+      let options = {opt1: 'val1'};
+      erdCoreObj.getNewPort('porttype', data, options);
+
+      expect(erdEngine.getPortFactories().getFactory).toHaveBeenCalledWith('porttype');
+      expect(erdEngine.getPortFactories().getFactory().generateModel).toHaveBeenCalledWith({
+        initialConfig: {
+          data:data,
+          options:options,
+        },
+      });
+    });
+
+    it('addNode', ()=>{
+      let newNode = jasmine.createSpyObj('newNode', ['setPosition']);
+      spyOn(erdCoreObj, 'getNewNode').and.returnValue(newNode);
+      spyOn(erdCoreObj, 'clearSelection');
+
+      let data = {name: 'link1'};
+
+      /* Without position */
+      erdCoreObj.addNode(data);
+      expect(erdCoreObj.getNewNode).toHaveBeenCalledWith(data);
+      expect(erdEngine.getModel().addNode).toHaveBeenCalledWith(newNode);
+      expect(erdCoreObj.clearSelection).toHaveBeenCalled();
+
+      /* With position */
+      erdCoreObj.addNode(data, [108, 108]);
+      expect(erdCoreObj.getNewNode().setPosition).toHaveBeenCalledWith(108, 108);
+    });
+
+
+    it('addLink', ()=>{
+      let nodesDict = {
+        'id1': {
+          serializeData: function(){ return {
+            'name': 'table1',
+          };},
+          getPortName: function(attnum) {
+            return `port-${attnum}`;
+          },
+          getPort: function() {
+            return null;
+          },
+          addPort: jasmine.createSpy('addPort').and.callFake((obj)=>obj),
+        },
+        'id2': {
+          serializeData: function(){ return {
+            'name': 'table2',
+          };},
+          getPortName: function(attnum) {
+            return `port-${attnum}`;
+          },
+          getPort: function() {
+            return null;
+          },
+          addPort: jasmine.createSpy('addPort').and.callFake((obj)=>obj),
+        },
+      };
+      let link = jasmine.createSpyObj('link', ['setSourcePort', 'setTargetPort']);
+      spyOn(erdEngine.getModel(), 'getNodesDict').and.returnValue(nodesDict);
+      spyOn(erdCoreObj, 'getNewLink').and.callFake(function() {
+        return link;
+      });
+      spyOn(erdCoreObj, 'getNewPort').and.callFake(function(type, initData, options) {
+        return {
+          name: options.name,
+        };
+      });
+
+      erdCoreObj.addLink({
+        'referenced_column_attnum': 1,
+        'referenced_table_uid': 'id1',
+        'local_column_attnum': 3,
+        'local_table_uid': 'id2',
+      }, 'onetomany');
+
+      expect(nodesDict['id1'].addPort).toHaveBeenCalledWith({name: 'port-1'});
+      expect(nodesDict['id2'].addPort).toHaveBeenCalledWith({name: 'port-3'});
+      expect(link.setSourcePort).toHaveBeenCalledWith({name: 'port-1'});
+      expect(link.setTargetPort).toHaveBeenCalledWith({name: 'port-3'});
+
+    });
+
+    it('serialize', ()=>{
+      let retVal = erdCoreObj.serialize();
+      expect(retVal.hasOwnProperty('version')).toBeTruthy();
+      expect(retVal.hasOwnProperty('data')).toBeTruthy();
+      expect(erdEngine.getModel().serialize).toHaveBeenCalled();
+    });
+
+    it('deserialize', ()=>{
+      let deserialValue = {
+        'version': 123,
+        'data': {
+          'key': 'serialized',
+        },
+      };
+      spyOn(erdCoreObj, 'initializeModel');
+      erdCoreObj.deserialize(deserialValue);
+      expect(erdCoreObj.initializeModel).toHaveBeenCalledWith(deserialValue.data);
+    });
+
+    it('serializeData', ()=>{
+      spyOn(erdEngine.getModel(), 'getNodesDict').and.returnValue({
+        'id1': {
+          serializeData: function(){ return {
+            'name': 'table1',
+          };},
+        },
+        'id2': {
+          serializeData: function(){ return {
+            'name': 'table2',
+          };},
+        },
+      });
+      spyOn(erdEngine.getModel(), 'getLinks').and.returnValue([
+        {
+          serializeData:  function(){ return {
+            'name': 'link1',
+          };},
+          getID:  function(){ return 'lid1'; },
+        },
+        {
+          serializeData:  function(){ return {
+            'name': 'link2',
+          };},
+          getID:  function(){ return 'lid2'; },
+        },
+      ]);
+      expect(JSON.stringify(erdCoreObj.serializeData())).toEqual(JSON.stringify({
+        nodes: {
+          'id1': {'name': 'table1'},
+          'id2': {'name': 'table2'},
+        },
+        links: {
+          'lid1': {'name': 'link1'},
+          'lid2': {'name': 'link2'},
+        },
+      }));
+    });
+
+    it('deserializeData', (done)=>{
+      let nodesDict = {};
+      TEST_TABLES_DATA.forEach((table)=>{
+        nodesDict[`id-${table.name}`] = {
+          getColumns: function() {
+            return table.columns;
+          },
+          getPortName: function(attnum) {
+            return `port-${attnum}`;
+          },
+          getPort: function(name) {
+            return {'name': name};
+          },
+          addPort: function() {
+
+          },
+        };
+      });
+      spyOn(erdEngine.getModel(), 'getNodesDict').and.returnValue(nodesDict);
+
+      spyOn(erdCoreObj, 'getNewLink').and.callFake(function() {
+        return {
+          setSourcePort: function() {},
+          setTargetPort: function() {},
+        };
+      });
+      spyOn(erdCoreObj, 'getNewPort').and.returnValue({id: 'id'});
+      spyOn(erdCoreObj, 'addNode').and.callFake(function(data) {
+        return {
+          getID: function() {
+            return `id-${data.name}`;
+          },
+        };
+      });
+      spyOn(erdCoreObj, 'addLink');
+      spyOn(erdCoreObj, 'dagreDistributeNodes');
+
+      erdCoreObj.deserializeData(TEST_TABLES_DATA);
+      expect(erdCoreObj.addNode).toHaveBeenCalledTimes(TEST_TABLES_DATA.length);
+      expect(erdCoreObj.addLink).toHaveBeenCalledTimes(1);
+
+      setTimeout(()=>{
+        expect(erdCoreObj.dagreDistributeNodes).toHaveBeenCalled();
+        done();
+      }, 10);
+    });
+
+    it('clearSelection', ()=>{
+      erdCoreObj.clearSelection();
+      expect(erdEngine.getModel().clearSelection).toHaveBeenCalled();
+    });
+
+    it('repaint', ()=>{
+      erdCoreObj.repaint();
+      expect(erdEngine.repaintCanvas).toHaveBeenCalled();
+    });
+
+    it('getNodesData', ()=>{
+      spyOn(erdEngine.getModel(), 'getNodes').and.returnValue([
+        {getData: function () {return {name:'node1'};}},
+        {getData: function () {return {name:'node2'};}},
+      ]);
+      expect(JSON.stringify(erdCoreObj.getNodesData())).toEqual(JSON.stringify([
+        {name:'node1'}, {name:'node2'},
+      ]));
+    });
+
+    it('dagreDistributeNodes', ()=>{
+      spyOn(erdCoreObj.dagre_engine, 'redistribute');
+      erdCoreObj.dagreDistributeNodes();
+      expect(erdEngine.getLinkFactories().getFactory().calculateRoutingMatrix).toHaveBeenCalled();
+      expect(erdCoreObj.dagre_engine.redistribute).toHaveBeenCalledWith(erdEngine.getModel());
+    });
+
+    it('zoomIn', ()=>{
+      spyOn(erdEngine.getModel(), 'getZoomLevel').and.returnValue(100);
+      spyOn(erdCoreObj, 'repaint');
+      erdCoreObj.zoomIn();
+      expect(erdEngine.getModel().setZoomLevel).toHaveBeenCalledWith(125);
+      expect(erdCoreObj.repaint).toHaveBeenCalled();
+    });
+
+    it('zoomOut', ()=>{
+      spyOn(erdEngine.getModel(), 'getZoomLevel').and.returnValue(100);
+      spyOn(erdCoreObj, 'repaint');
+      erdCoreObj.zoomOut();
+      expect(erdEngine.getModel().setZoomLevel).toHaveBeenCalledWith(75);
+      expect(erdCoreObj.repaint).toHaveBeenCalled();
+    });
+
+    it('zoomToFit', ()=>{
+      erdCoreObj.zoomToFit();
+      expect(erdEngine.zoomToFit).toHaveBeenCalled();
+    });
+
+    it('fireAction', ()=>{
+      erdCoreObj.fireAction({key: 'xyz'});
+      expect(erdEngine.getActionEventBus().fireAction).toHaveBeenCalled();
+    });
+
+    it('fireEvent', ()=>{
+      erdCoreObj.fireEvent({key: 'xyz'}, 'someevent', false);
+      expect(erdEngine.fireEvent).toHaveBeenCalledWith({key: 'xyz'}, 'someevent');
+
+      erdCoreObj.fireEvent({key: 'xyz'}, 'someevent', true);
+      expect(erdEngine.getModel().fireEvent).toHaveBeenCalledWith({key: 'xyz'}, 'someevent');
+    });
+
+    it('registerKeyAction', ()=>{
+      erdCoreObj.registerKeyAction({key: 'xyz'});
+      expect(erdEngine.getActionEventBus().registerAction).toHaveBeenCalledWith({key: 'xyz'});
+    });
+
+    it('deregisterKeyAction', ()=>{
+      let action = {key: 'xyz'};
+      erdCoreObj.deregisterKeyAction(action);
+      expect(erdEngine.getActionEventBus().deregisterAction).toHaveBeenCalledWith({key: 'xyz'});
+    });
+  });
+});
diff --git a/web/regression/javascript/erd/erd_model_spec.js b/web/regression/javascript/erd/erd_model_spec.js
new file mode 100644
index 000000000..cfdcc0983
--- /dev/null
+++ b/web/regression/javascript/erd/erd_model_spec.js
@@ -0,0 +1,34 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2020, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+import ERDModel from 'pgadmin.tools.erd/erd_tool/ERDModel';
+
+describe('ERDModel', ()=>{
+  it('getNodesDict', ()=>{
+    let model = new ERDModel();
+
+    spyOn(model, 'getNodes').and.returnValue([
+      {
+        name: 'test1',
+        getID: function() {
+          return 'id1';
+        },
+      },
+      {
+        name: 'test2',
+        getID: function() {
+          return 'id2';
+        },
+      },
+    ]);
+    expect(JSON.stringify(model.getNodesDict())).toBe(JSON.stringify({
+      'id1': {name: 'test1'},
+      'id2': {name: 'test2'},
+    }));
+  });
+});
diff --git a/web/regression/javascript/erd/keyboard_shortcut_action_spec.js b/web/regression/javascript/erd/keyboard_shortcut_action_spec.js
new file mode 100644
index 000000000..3134795ea
--- /dev/null
+++ b/web/regression/javascript/erd/keyboard_shortcut_action_spec.js
@@ -0,0 +1,61 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2020, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+import {KeyboardShortcutAction} from 'pgadmin.tools.erd/erd_tool/ui_components/BodyWidget';
+
+describe('KeyboardShortcutAction', ()=>{
+  let keyAction = null;
+  let key1 = {
+    alt: true,
+    control: true,
+    shift: false,
+    key: {
+      key_code: 65,
+    },
+  };
+  let key2 = {
+    alt: false,
+    control: true,
+    shift: false,
+    key: {
+      key_code: 66,
+    },
+  };
+  let handler1 = jasmine.createSpy('handler1');
+  let handler2 = jasmine.createSpy('handler2');
+
+  beforeAll(()=>{
+    spyOn(KeyboardShortcutAction.prototype, 'shortcutKey').and.callThrough();
+    keyAction = new KeyboardShortcutAction([
+      [key1, handler1],
+      [key2, handler2],
+    ]);
+  });
+
+  it('init', ()=>{
+    expect(Object.keys(keyAction.shortcuts).length).toBe(2);
+  });
+
+  it('shortcutKey', ()=>{
+    expect(keyAction.shortcutKey(true, true, true, true, 65)).toBe('true:true:true:true:65');
+    expect(keyAction.shortcutKey(true, false, true, true, 65)).toBe('true:false:true:true:65');
+    expect(keyAction.shortcutKey(true, true, false, true, 65)).toBe('true:true:false:true:65');
+    expect(keyAction.shortcutKey(true, true, true, false, 65)).toBe('true:true:true:false:65');
+    expect(keyAction.shortcutKey(false, true, true, true, 65)).toBe('false:true:true:true:65');
+  });
+
+  it('callHandler', ()=>{
+    let keyEvent = {altKey: key1.alt, ctrlKey: key1.control, shiftKey: key1.shift, metaKey: false, keyCode:key1.key.key_code};
+    keyAction.callHandler(keyEvent);
+    expect(handler1).toHaveBeenCalled();
+
+    keyEvent = {altKey: key2.alt, ctrlKey: key2.control, shiftKey: key2.shift, metaKey: false, keyCode:key2.key.key_code};
+    keyAction.callHandler(keyEvent);
+    expect(handler2).toHaveBeenCalled();
+  });
+});
diff --git a/web/regression/javascript/erd/onetomany_link_spec.js b/web/regression/javascript/erd/onetomany_link_spec.js
new file mode 100644
index 000000000..a7590db64
--- /dev/null
+++ b/web/regression/javascript/erd/onetomany_link_spec.js
@@ -0,0 +1,133 @@
+import jasmineEnzyme from 'jasmine-enzyme';
+import React from 'react';
+import {mount} from 'enzyme';
+import '../helper/enzyme.helper';
+import {
+  RightAngleLinkModel,
+} from '@projectstorm/react-diagrams';
+
+import OneToManyPortModel from 'pgadmin.tools.erd/erd_tool/ports/OneToManyPort';
+import {OneToManyLinkModel, OneToManyLinkWidget, OneToManyLinkFactory} from 'pgadmin.tools.erd/erd_tool/links/OneToManyLink';
+
+
+describe('ERD OneToManyLinkModel', ()=>{
+  let modelObj = null;
+  beforeAll(()=>{
+    spyOn(RightAngleLinkModel.prototype, 'serialize').and.returnValue({'key': 'value'});
+  });
+  beforeEach(()=>{
+    modelObj = new OneToManyLinkModel({
+      data: {
+        local_table_uid: 'id1',
+        local_column_attnum: 0,
+        referenced_table_uid: 'id2',
+        referenced_column_attnum: 1,
+      },
+    });
+  });
+
+  it('init', ()=>{
+    expect(modelObj.getData()).toEqual({
+      local_table_uid: 'id1',
+      local_column_attnum: 0,
+      referenced_table_uid: 'id2',
+      referenced_column_attnum: 1,
+    });
+  });
+
+  it('setData', ()=>{
+    modelObj.setData({
+      local_column_attnum: 2,
+      referenced_column_attnum: 4,
+    });
+    expect(modelObj.getData()).toEqual({
+      local_column_attnum: 2,
+      referenced_column_attnum: 4,
+    });
+  });
+
+  it('serializeData', ()=>{
+    let nodesDict = {
+      'id1': {
+        getData: function(){ return {
+          'name': 'table1',
+          'schema': 'erd1',
+          'columns': [
+            {'name': 'col11', attnum: 0},
+            {'name': 'col12', attnum: 1},
+          ],
+        };},
+      },
+      'id2': {
+        getData: function(){ return {
+          'name': 'table2',
+          'schema': 'erd2',
+          'columns': [
+            {'name': 'col21', attnum: 0},
+            {'name': 'col22', attnum: 1},
+          ],
+        };},
+      },
+    };
+
+    expect(modelObj.serializeData(nodesDict)).toEqual({
+      'schema': 'erd1',
+      'table': 'table1',
+      'remote_schema': 'erd2',
+      'remote_table': 'table2',
+      'columns': [{
+        'local_column': 'col11',
+        'referenced': 'col22',
+      }],
+    });
+  });
+
+  it('serialize', ()=>{
+    let retVal = modelObj.serialize();
+    expect(RightAngleLinkModel.prototype.serialize).toHaveBeenCalled();
+    expect(retVal).toEqual({
+      key: 'value',
+      data: {
+        local_table_uid: 'id1',
+        local_column_attnum: 0,
+        referenced_table_uid: 'id2',
+        referenced_column_attnum: 1,
+      },
+    });
+  });
+});
+
+describe('ERD OneToManyLinkWidget', ()=>{
+  let linkFactory = new OneToManyLinkFactory();
+  let engine = {
+    getFactoryForLink: ()=>linkFactory,
+  };
+  let link = null;
+
+  beforeEach(()=>{
+    jasmineEnzyme();
+
+    link = new OneToManyLinkModel({
+      color: '#000',
+      data: {
+        local_table_uid: 'id1',
+        local_column_attnum: 0,
+        referenced_table_uid: 'id2',
+        referenced_column_attnum: 1,
+      },
+    });
+    link.setSourcePort(new OneToManyPortModel({options: {}}));
+    link.setTargetPort(new OneToManyPortModel({options: {}}));
+  });
+
+  it('render', ()=>{
+    let linkWidget = mount(
+      <svg><OneToManyLinkWidget link={link} diagramEngine={engine} factory={linkFactory} /></svg>
+    );
+
+    let paths = linkWidget.find('g g');
+    expect(paths.at(0).find('polyline').length).toBe(1);
+    expect(paths.at(paths.length-1).find('polyline').length).toBe(1);
+    expect(paths.at(paths.length-1).find('circle').length).toBe(1);
+  });
+});
diff --git a/web/regression/javascript/erd/onetomany_port_spec.js b/web/regression/javascript/erd/onetomany_port_spec.js
new file mode 100644
index 000000000..0ad4831ef
--- /dev/null
+++ b/web/regression/javascript/erd/onetomany_port_spec.js
@@ -0,0 +1,21 @@
+import { PortModel } from '@projectstorm/react-diagrams-core';
+import OneToManyPortModel from 'pgadmin.tools.erd/erd_tool/ports/OneToManyPort';
+import {OneToManyLinkModel} from 'pgadmin.tools.erd/erd_tool/links/OneToManyLink';
+
+describe('ERD OneToManyPortModel', ()=>{
+  it('removeAllLinks', ()=>{
+    let link1 = jasmine.createSpyObj('link1', ['remove']);
+    let link2 = jasmine.createSpyObj('link2', ['remove']);
+    spyOn(PortModel.prototype, 'getLinks').and.returnValue([link1, link2]);
+
+    let portObj = new OneToManyPortModel({options: {}});
+    portObj.removeAllLinks();
+    expect(link1.remove).toHaveBeenCalled();
+    expect(link2.remove).toHaveBeenCalled();
+  });
+
+  it('createLinkModel', ()=>{
+    let portObj = new OneToManyPortModel({options: {}});
+    expect(portObj.createLinkModel()).toBeInstanceOf(OneToManyLinkModel);
+  });
+});
diff --git a/web/regression/javascript/erd/table_node_spec.js b/web/regression/javascript/erd/table_node_spec.js
new file mode 100644
index 000000000..d8edcdb44
--- /dev/null
+++ b/web/regression/javascript/erd/table_node_spec.js
@@ -0,0 +1,305 @@
+import jasmineEnzyme from 'jasmine-enzyme';
+import React from 'react';
+import {mount} from 'enzyme';
+import '../helper/enzyme.helper';
+import { DefaultNodeModel } from '@projectstorm/react-diagrams';
+
+import {TableNodeModel, TableNodeWidget} from 'pgadmin.tools.erd/erd_tool/nodes/TableNode';
+import { IconButton, DetailsToggleButton } from 'pgadmin.tools.erd/erd_tool/ui_components/ToolBar';
+
+
+describe('ERD TableNodeModel', ()=>{
+  let modelObj = null;
+  beforeAll(()=>{
+    spyOn(DefaultNodeModel.prototype, 'serialize').and.returnValue({'key': 'value'});
+  });
+  beforeEach(()=>{
+    modelObj = new TableNodeModel({
+      color: '#000',
+      otherInfo: {
+        note: 'some note',
+        data: {
+          name: 'table1',
+          schema: 'erd',
+        },
+      },
+    });
+  });
+
+  it('init', ()=>{
+    expect(modelObj.getData()).toEqual({
+      columns: [],
+      name: 'table1',
+      schema: 'erd',
+    });
+    expect(modelObj.getNote()).toBe('some note');
+    expect(modelObj.getColumns()).toEqual([]);
+  });
+
+  it('getPortName', ()=>{
+    expect(modelObj.getPortName(2)).toBe('coll-port-2');
+  });
+
+  it('setNote', ()=>{
+    modelObj.setNote('some note to test');
+    expect(modelObj.getNote()).toBe('some note to test');
+  });
+
+  it('addColumn', ()=>{
+    modelObj.addColumn({name: 'col1', not_null:false, attnum: 0});
+    expect(modelObj.getColumns()).toEqual([{name: 'col1', not_null:false, attnum: 0}]);
+  });
+
+  it('getColumnAt', ()=>{
+    modelObj.addColumn({name: 'col1', not_null:false, attnum: 0});
+    modelObj.addColumn({name: 'col2', not_null:false, attnum: 1});
+    expect(modelObj.getColumnAt(0)).toEqual({name: 'col1', not_null:false, attnum: 0});
+    expect(modelObj.getColumnAt(1)).toEqual({name: 'col2', not_null:false, attnum: 1});
+    expect(modelObj.getColumnAt(2)).toBeUndefined();
+  });
+
+  it('setName', ()=>{
+    modelObj.setName('changedName');
+    expect(modelObj.getData().name).toBe('changedName');
+  });
+
+  it('cloneData', ()=>{
+    modelObj.addColumn({name: 'col1', not_null:false, attnum: 0});
+    expect(modelObj.cloneData('clonedNode')).toEqual({
+      name: 'clonedNode',
+      schema: 'erd',
+      columns: [{name: 'col1', not_null:false, attnum: 0}],
+    });
+  });
+
+  describe('setData', ()=>{
+    let existPort = jasmine.createSpyObj('port', ['removeAllLinks']);
+
+    beforeEach(()=>{
+      modelObj._data.columns = [
+        {name: 'col1', not_null:false, attnum: 0},
+        {name: 'col2', not_null:false, attnum: 1},
+        {name: 'col3', not_null:false, attnum: 2},
+      ];
+
+      spyOn(modelObj, 'getPort').and.callFake((portName)=>{
+        /* If new port added there will not be any port */
+        if(portName !== 'coll-port-3') {
+          return existPort;
+        }
+      });
+      spyOn(modelObj, 'removePort');
+      spyOn(modelObj, 'getPortName');
+    });
+
+    it('add columns', ()=>{
+      existPort.removeAllLinks.calls.reset();
+      modelObj.setData({
+        name: 'noname',
+        schema: 'erd',
+        columns: [
+          {name: 'col1', not_null:false, attnum: 0},
+          {name: 'col2', not_null:false, attnum: 1},
+          {name: 'col3', not_null:false, attnum: 2},
+          {name: 'col4', not_null:false, attnum: 3},
+        ],
+      });
+      expect(modelObj.getData()).toEqual({
+        name: 'noname',
+        schema: 'erd',
+        columns: [
+          {name: 'col1', not_null:false, attnum: 0},
+          {name: 'col2', not_null:false, attnum: 1},
+          {name: 'col3', not_null:false, attnum: 2},
+          {name: 'col4', not_null:false, attnum: 3},
+        ],
+      });
+      expect(existPort.removeAllLinks).not.toHaveBeenCalled();
+    });
+
+    it('update columns', ()=>{
+      existPort.removeAllLinks.calls.reset();
+      modelObj.setData({
+        name: 'noname',
+        schema: 'erd',
+        columns: [
+          {name: 'col1', not_null:false, attnum: 0},
+          {name: 'col2updated', not_null:false, attnum: 1},
+          {name: 'col3', not_null:true, attnum: 2},
+        ],
+      });
+      expect(modelObj.getData()).toEqual({
+        name: 'noname',
+        schema: 'erd',
+        columns: [
+          {name: 'col1', not_null:false, attnum: 0},
+          {name: 'col2updated', not_null:false, attnum: 1},
+          {name: 'col3', not_null:true, attnum: 2},
+        ],
+      });
+      expect(existPort.removeAllLinks).not.toHaveBeenCalled();
+    });
+
+    it('remove columns', ()=>{
+      existPort.removeAllLinks.calls.reset();
+      modelObj.setData({
+        name: 'noname',
+        schema: 'erd',
+        columns: [
+          {name: 'col2', not_null:false, attnum: 1},
+          {name: 'col3', not_null:false, attnum: 2},
+        ],
+      });
+      expect(modelObj.getData()).toEqual({
+        name: 'noname',
+        schema: 'erd',
+        columns: [
+          {name: 'col2', not_null:false, attnum: 1},
+          {name: 'col3', not_null:false, attnum: 2},
+        ],
+      });
+
+      expect(modelObj.getPortName).toHaveBeenCalledWith(0);
+      expect(existPort.removeAllLinks).toHaveBeenCalled();
+      expect(modelObj.removePort).toHaveBeenCalledWith(existPort);
+    });
+  });
+
+  it('getSchemaTableName', ()=>{
+    expect(modelObj.getSchemaTableName()).toEqual(['erd', 'table1']);
+  });
+
+  it('serializeData', ()=>{
+    modelObj.addColumn({name: 'col1', not_null:false, attnum: 0});
+    expect(modelObj.serializeData()).toEqual({
+      name: 'table1',
+      schema: 'erd',
+      columns: [{name: 'col1', not_null:false, attnum: 0}],
+    });
+  });
+
+  it('serialize', ()=>{
+    let retVal = modelObj.serialize();
+    expect(DefaultNodeModel.prototype.serialize).toHaveBeenCalled();
+    expect(retVal).toEqual({
+      key: 'value',
+      otherInfo:  {
+        data: {
+          columns: [],
+          name: 'table1',
+          schema: 'erd',
+        },
+        note: 'some note',
+      },
+    });
+  });
+});
+
+describe('ERD TableNodeWidget', ()=>{
+  let node = null;
+
+  beforeEach(()=>{
+    jasmineEnzyme();
+
+    node = new TableNodeModel({
+      color: '#000',
+      otherInfo: {
+        note: 'some note',
+        data: {
+          name: 'table1',
+          schema: 'erd',
+          columns: [{
+            attnum: 0,
+            is_primary_key: true,
+            name: 'id',
+            cltype: 'integer',
+            attlen: null,
+            attprecision: null,
+          }, {
+            attnum: 1,
+            is_primary_key: false,
+            name: 'amount',
+            cltype: 'number',
+            attlen: 10,
+            attprecision: 5,
+          }, {
+            attnum: 2,
+            is_primary_key: false,
+            name: 'desc',
+            cltype: 'character varrying',
+            attlen: 50,
+            attprecision: null,
+          }],
+        },
+      },
+    });
+  });
+
+  it('render', ()=>{
+    let nodeWidget = mount(<TableNodeWidget node={node}/>);
+    expect(nodeWidget.getDOMNode().className).toBe('table-node ');
+    expect(nodeWidget.find('.table-node .table-toolbar').length).toBe(1);
+    expect(nodeWidget.find('.table-node .table-schema').text()).toBe('erd');
+    expect(nodeWidget.find('.table-node .table-name').text()).toBe('table1');
+    expect(nodeWidget.find('.table-node .table-cols').length).toBe(1);
+    expect(nodeWidget.find(DetailsToggleButton).length).toBe(1);
+    expect(nodeWidget.find(IconButton).findWhere(n => n.prop('title')=='Check note').length).toBe(1);
+  });
+
+  it('node selected', ()=>{
+    spyOn(node, 'isSelected').and.returnValue(true);
+    let nodeWidget = mount(<TableNodeWidget node={node}/>);
+    expect(nodeWidget.getDOMNode().className).toBe('table-node selected');
+  });
+
+  it('remove note', ()=>{
+    node.setNote('');
+    let nodeWidget = mount(<TableNodeWidget node={node}/>);
+    expect(nodeWidget.find(IconButton).findWhere(n => n.prop('title')=='Check note').length).toBe(0);
+  });
+
+  describe('generateColumn', ()=>{
+    let nodeWidget = null;
+
+    beforeEach(()=>{
+      nodeWidget = mount(<TableNodeWidget node={node}/>);
+    });
+
+    it('count', ()=>{
+      expect(nodeWidget.find('.table-node .table-cols .col-row').length).toBe(3);
+    });
+
+    it('icons', ()=>{
+      let cols = nodeWidget.find('.table-node .table-cols .col-row-data');
+      expect(cols.at(0).find('.wcTabIcon').hasClass('icon-primary_key')).toBeTrue();
+      expect(cols.at(1).find('.wcTabIcon').hasClass('icon-column')).toBeTrue();
+      expect(cols.at(2).find('.wcTabIcon').hasClass('icon-column')).toBeTrue();
+    });
+
+    it('column names', ()=>{
+      let cols = nodeWidget.find('.table-node .table-cols .col-row-data');
+      expect(cols.at(0).find('.col-name').text()).toBe('id');
+      expect(cols.at(1).find('.col-name').text()).toBe('amount');
+      expect(cols.at(2).find('.col-name').text()).toBe('desc');
+    });
+
+    it('data types', ()=>{
+      let cols = nodeWidget.find('.table-node .table-cols .col-row-data');
+      expect(cols.at(0).find('.col-datatype').text()).toBe('integer');
+      expect(cols.at(1).find('.col-datatype').text()).toBe('number(10,5)');
+      expect(cols.at(2).find('.col-datatype').text()).toBe('character varrying(50)');
+    });
+
+    it('show_details', (done)=>{
+      nodeWidget.setState({show_details: false});
+      expect(nodeWidget.find('.table-node .table-cols .col-row-data .col-datatype').length).toBe(0);
+
+      nodeWidget.instance().toggleShowDetails(jasmine.createSpyObj('event', ['preventDefault']));
+      /* Dummy set state to wait for toggleShowDetails -> setState to complete */
+      nodeWidget.setState({}, ()=>{
+        expect(nodeWidget.find('.table-node .table-cols .col-row-data .col-datatype').length).toBe(3);
+        done();
+      });
+    });
+  });
+});
diff --git a/web/regression/javascript/erd/test_tables.js b/web/regression/javascript/erd/test_tables.js
new file mode 100644
index 000000000..e2dc43ef8
--- /dev/null
+++ b/web/regression/javascript/erd/test_tables.js
@@ -0,0 +1,651 @@
+export default [
+  {
+    'oid': 123456,
+    'name': 'test1',
+    'spcoid': 0,
+    'relacl_str': null,
+    'spcname': 'pg_default',
+    'schema': 'schema1',
+    'relowner': 'postgres',
+    'relkind': 'r',
+    'is_partitioned': false,
+    'relhassubclass': false,
+    'reltuples': '0',
+    'description': null,
+    'conname': null,
+    'conkey': null,
+    'isrepl': false,
+    'triggercount': '0',
+    'coll_inherits': [],
+    'inherited_tables_cnt': '0',
+    'relpersistence': false,
+    'fillfactor': null,
+    'parallel_workers': null,
+    'toast_tuple_target': null,
+    'autovacuum_enabled': 'x',
+    'autovacuum_vacuum_threshold': null,
+    'autovacuum_vacuum_scale_factor': null,
+    'autovacuum_analyze_threshold': null,
+    'autovacuum_analyze_scale_factor': null,
+    'autovacuum_vacuum_cost_delay': null,
+    'autovacuum_vacuum_cost_limit': null,
+    'autovacuum_freeze_min_age': null,
+    'autovacuum_freeze_max_age': null,
+    'autovacuum_freeze_table_age': null,
+    'toast_autovacuum_enabled': 'x',
+    'toast_autovacuum_vacuum_threshold': null,
+    'toast_autovacuum_vacuum_scale_factor': null,
+    'toast_autovacuum_analyze_threshold': null,
+    'toast_autovacuum_analyze_scale_factor': null,
+    'toast_autovacuum_vacuum_cost_delay': null,
+    'toast_autovacuum_vacuum_cost_limit': null,
+    'toast_autovacuum_freeze_min_age': null,
+    'toast_autovacuum_freeze_max_age': null,
+    'toast_autovacuum_freeze_table_age': null,
+    'reloptions': null,
+    'toast_reloptions': null,
+    'reloftype': 0,
+    'typname': null,
+    'typoid': null,
+    'rlspolicy': false,
+    'forcerlspolicy': false,
+    'hastoasttable': false,
+    'seclabels': null,
+    'is_sys_table': false,
+    'partition_scheme': '',
+    'autovacuum_custom': false,
+    'toast_autovacuum': false,
+    'rows_cnt': 0,
+    'vacuum_settings_str': '',
+    'vacuum_table': [
+      {
+        'name': 'autovacuum_analyze_scale_factor',
+        'setting': '0.1',
+        'label': 'ANALYZE scale factor',
+        'column_type': 'number',
+      },
+      {
+        'name': 'autovacuum_analyze_threshold',
+        'setting': '50',
+        'label': 'ANALYZE base threshold',
+        'column_type': 'integer',
+      },
+      {
+        'name': 'autovacuum_freeze_max_age',
+        'setting': '200000000',
+        'label': 'FREEZE maximum age',
+        'column_type': 'integer',
+      },
+      {
+        'name': 'autovacuum_vacuum_cost_delay',
+        'setting': '2',
+        'label': 'VACUUM cost delay',
+        'column_type': 'integer',
+      },
+      {
+        'name': 'autovacuum_vacuum_cost_limit',
+        'setting': '-1',
+        'label': 'VACUUM cost limit',
+        'column_type': 'integer',
+      },
+      {
+        'name': 'autovacuum_vacuum_scale_factor',
+        'setting': '0.2',
+        'label': 'VACUUM scale factor',
+        'column_type': 'number',
+      },
+      {
+        'name': 'autovacuum_vacuum_threshold',
+        'setting': '50',
+        'label': 'VACUUM base threshold',
+        'column_type': 'integer',
+      },
+      {
+        'name': 'autovacuum_freeze_min_age',
+        'setting': '50000000',
+        'label': 'FREEZE minimum age',
+        'column_type': 'integer',
+      },
+      {
+        'name': 'autovacuum_freeze_table_age',
+        'setting': '150000000',
+        'label': 'FREEZE table age',
+        'column_type': 'integer',
+      },
+    ],
+    'vacuum_toast': [
+      {
+        'name': 'autovacuum_freeze_max_age',
+        'setting': '200000000',
+        'label': 'FREEZE maximum age',
+        'column_type': 'integer',
+      },
+      {
+        'name': 'autovacuum_vacuum_cost_delay',
+        'setting': '2',
+        'label': 'VACUUM cost delay',
+        'column_type': 'integer',
+      },
+      {
+        'name': 'autovacuum_vacuum_cost_limit',
+        'setting': '-1',
+        'label': 'VACUUM cost limit',
+        'column_type': 'integer',
+      },
+      {
+        'name': 'autovacuum_vacuum_scale_factor',
+        'setting': '0.2',
+        'label': 'VACUUM scale factor',
+        'column_type': 'number',
+      },
+      {
+        'name': 'autovacuum_vacuum_threshold',
+        'setting': '50',
+        'label': 'VACUUM base threshold',
+        'column_type': 'integer',
+      },
+      {
+        'name': 'autovacuum_freeze_min_age',
+        'setting': '50000000',
+        'label': 'FREEZE minimum age',
+        'column_type': 'integer',
+      },
+      {
+        'name': 'autovacuum_freeze_table_age',
+        'setting': '150000000',
+        'label': 'FREEZE table age',
+        'column_type': 'integer',
+      },
+    ],
+    'columns': [{
+      'name': 'id',
+      'atttypid': 23,
+      'attlen': null,
+      'attnum': 1,
+      'attndims': 0,
+      'atttypmod': -1,
+      'attacl': [],
+      'attnotnull': true,
+      'attoptions': null,
+      'attstattarget': -1,
+      'attstorage': 'p',
+      'attidentity': '',
+      'defval': null,
+      'typname': 'integer',
+      'displaytypname': 'integer',
+      'cltype': 'integer',
+      'elemoid': 23,
+      'typnspname': 'pg_catalog',
+      'defaultstorage': 'p',
+      'description': null,
+      'indkey': '1',
+      'isdup': false,
+      'collspcname': '',
+      'is_fk': false,
+      'seclabels': null,
+      'is_sys_column': false,
+      'colconstype': 'n',
+      'genexpr': null,
+      'relname': 'tab1',
+      'is_view_only': false,
+      'seqrelid': null,
+      'seqtypid': null,
+      'seqstart': null,
+      'seqincrement': null,
+      'seqmax': null,
+      'seqmin': null,
+      'seqcache': null,
+      'seqcycle': null,
+      'is_pk': true,
+      'is_primary_key': true,
+      'attprecision': null,
+      'edit_types': [
+        'bigint',
+        'double precision',
+        'information_schema.cardinal_number',
+        'integer',
+        'money',
+        'numeric',
+        'oid',
+        'real',
+        'regclass',
+        'regconfig',
+        'regdictionary',
+        'regnamespace',
+        'regoper',
+        'regoperator',
+        'regproc',
+        'regprocedure',
+        'regrole',
+        'regtype',
+        'smallint',
+      ],
+    }],
+    'primary_key': [],
+    'unique_constraint': [],
+    'check_constraint': [],
+    'index': {},
+    'rule': {},
+    'trigger': {},
+    'row_security_policy': {},
+  },
+  {
+    'oid': 408229,
+    'name': 'test2',
+    'spcoid': 0,
+    'relacl_str': null,
+    'spcname': 'pg_default',
+    'schema': 'erd',
+    'relowner': 'postgres',
+    'relkind': 'r',
+    'is_partitioned': false,
+    'relhassubclass': false,
+    'reltuples': '0',
+    'description': null,
+    'conname': 'tab1_pkey',
+    'conkey': [
+      1,
+    ],
+    'isrepl': false,
+    'triggercount': '0',
+    'coll_inherits': [],
+    'inherited_tables_cnt': '0',
+    'relpersistence': false,
+    'fillfactor': null,
+    'parallel_workers': null,
+    'toast_tuple_target': null,
+    'autovacuum_enabled': 'x',
+    'autovacuum_vacuum_threshold': null,
+    'autovacuum_vacuum_scale_factor': null,
+    'autovacuum_analyze_threshold': null,
+    'autovacuum_analyze_scale_factor': null,
+    'autovacuum_vacuum_cost_delay': null,
+    'autovacuum_vacuum_cost_limit': null,
+    'autovacuum_freeze_min_age': null,
+    'autovacuum_freeze_max_age': null,
+    'autovacuum_freeze_table_age': null,
+    'toast_autovacuum_enabled': 'x',
+    'toast_autovacuum_vacuum_threshold': null,
+    'toast_autovacuum_vacuum_scale_factor': null,
+    'toast_autovacuum_analyze_threshold': null,
+    'toast_autovacuum_analyze_scale_factor': null,
+    'toast_autovacuum_vacuum_cost_delay': null,
+    'toast_autovacuum_vacuum_cost_limit': null,
+    'toast_autovacuum_freeze_min_age': null,
+    'toast_autovacuum_freeze_max_age': null,
+    'toast_autovacuum_freeze_table_age': null,
+    'reloptions': null,
+    'toast_reloptions': null,
+    'reloftype': 0,
+    'typname': null,
+    'typoid': null,
+    'rlspolicy': false,
+    'forcerlspolicy': false,
+    'hastoasttable': false,
+    'seclabels': null,
+    'is_sys_table': false,
+    'partition_scheme': '',
+    'autovacuum_custom': false,
+    'toast_autovacuum': false,
+    'rows_cnt': 0,
+    'vacuum_settings_str': '',
+    'vacuum_table': [
+      {
+        'name': 'autovacuum_analyze_scale_factor',
+        'setting': '0.1',
+        'label': 'ANALYZE scale factor',
+        'column_type': 'number',
+      },
+      {
+        'name': 'autovacuum_analyze_threshold',
+        'setting': '50',
+        'label': 'ANALYZE base threshold',
+        'column_type': 'integer',
+      },
+      {
+        'name': 'autovacuum_freeze_max_age',
+        'setting': '200000000',
+        'label': 'FREEZE maximum age',
+        'column_type': 'integer',
+      },
+      {
+        'name': 'autovacuum_vacuum_cost_delay',
+        'setting': '2',
+        'label': 'VACUUM cost delay',
+        'column_type': 'integer',
+      },
+      {
+        'name': 'autovacuum_vacuum_cost_limit',
+        'setting': '-1',
+        'label': 'VACUUM cost limit',
+        'column_type': 'integer',
+      },
+      {
+        'name': 'autovacuum_vacuum_scale_factor',
+        'setting': '0.2',
+        'label': 'VACUUM scale factor',
+        'column_type': 'number',
+      },
+      {
+        'name': 'autovacuum_vacuum_threshold',
+        'setting': '50',
+        'label': 'VACUUM base threshold',
+        'column_type': 'integer',
+      },
+      {
+        'name': 'autovacuum_freeze_min_age',
+        'setting': '50000000',
+        'label': 'FREEZE minimum age',
+        'column_type': 'integer',
+      },
+      {
+        'name': 'autovacuum_freeze_table_age',
+        'setting': '150000000',
+        'label': 'FREEZE table age',
+        'column_type': 'integer',
+      },
+    ],
+    'vacuum_toast': [
+      {
+        'name': 'autovacuum_freeze_max_age',
+        'setting': '200000000',
+        'label': 'FREEZE maximum age',
+        'column_type': 'integer',
+      },
+      {
+        'name': 'autovacuum_vacuum_cost_delay',
+        'setting': '2',
+        'label': 'VACUUM cost delay',
+        'column_type': 'integer',
+      },
+      {
+        'name': 'autovacuum_vacuum_cost_limit',
+        'setting': '-1',
+        'label': 'VACUUM cost limit',
+        'column_type': 'integer',
+      },
+      {
+        'name': 'autovacuum_vacuum_scale_factor',
+        'setting': '0.2',
+        'label': 'VACUUM scale factor',
+        'column_type': 'number',
+      },
+      {
+        'name': 'autovacuum_vacuum_threshold',
+        'setting': '50',
+        'label': 'VACUUM base threshold',
+        'column_type': 'integer',
+      },
+      {
+        'name': 'autovacuum_freeze_min_age',
+        'setting': '50000000',
+        'label': 'FREEZE minimum age',
+        'column_type': 'integer',
+      },
+      {
+        'name': 'autovacuum_freeze_table_age',
+        'setting': '150000000',
+        'label': 'FREEZE table age',
+        'column_type': 'integer',
+      },
+    ],
+    'columns': [
+      {
+        'name': 'id',
+        'atttypid': 23,
+        'attlen': null,
+        'attnum': 1,
+        'attndims': 0,
+        'atttypmod': -1,
+        'attacl': [],
+        'attnotnull': true,
+        'attoptions': null,
+        'attstattarget': -1,
+        'attstorage': 'p',
+        'attidentity': '',
+        'defval': null,
+        'typname': 'integer',
+        'displaytypname': 'integer',
+        'cltype': 'integer',
+        'elemoid': 23,
+        'typnspname': 'pg_catalog',
+        'defaultstorage': 'p',
+        'description': null,
+        'indkey': '1',
+        'isdup': false,
+        'collspcname': '',
+        'is_fk': false,
+        'seclabels': null,
+        'is_sys_column': false,
+        'colconstype': 'n',
+        'genexpr': null,
+        'relname': 'tab1',
+        'is_view_only': false,
+        'seqrelid': null,
+        'seqtypid': null,
+        'seqstart': null,
+        'seqincrement': null,
+        'seqmax': null,
+        'seqmin': null,
+        'seqcache': null,
+        'seqcycle': null,
+        'is_pk': true,
+        'is_primary_key': true,
+        'attprecision': null,
+        'edit_types': [
+          'bigint',
+          'double precision',
+          'information_schema.cardinal_number',
+          'integer',
+          'money',
+          'numeric',
+          'oid',
+          'real',
+          'regclass',
+          'regconfig',
+          'regdictionary',
+          'regnamespace',
+          'regoper',
+          'regoperator',
+          'regproc',
+          'regprocedure',
+          'regrole',
+          'regtype',
+          'smallint',
+        ],
+      },
+      {
+        'name': 'col1col1col1col1col1col1col1col1',
+        'atttypid': 23,
+        'attlen': null,
+        'attnum': 2,
+        'attndims': 0,
+        'atttypmod': -1,
+        'attacl': [],
+        'attnotnull': true,
+        'attoptions': null,
+        'attstattarget': -1,
+        'attstorage': 'p',
+        'attidentity': '',
+        'defval': null,
+        'typname': 'integer',
+        'displaytypname': 'integer',
+        'cltype': 'integer',
+        'elemoid': 23,
+        'typnspname': 'pg_catalog',
+        'defaultstorage': 'p',
+        'description': null,
+        'indkey': '1',
+        'isdup': false,
+        'collspcname': '',
+        'is_fk': true,
+        'seclabels': null,
+        'is_sys_column': false,
+        'colconstype': 'n',
+        'genexpr': null,
+        'relname': 'tab1',
+        'is_view_only': false,
+        'seqrelid': null,
+        'seqtypid': null,
+        'seqstart': null,
+        'seqincrement': null,
+        'seqmax': null,
+        'seqmin': null,
+        'seqcache': null,
+        'seqcycle': null,
+        'is_pk': false,
+        'is_primary_key': false,
+        'attprecision': null,
+        'edit_types': [
+          'bigint',
+          'double precision',
+          'information_schema.cardinal_number',
+          'integer',
+          'integer',
+          'money',
+          'numeric',
+          'oid',
+          'real',
+          'regclass',
+          'regconfig',
+          'regdictionary',
+          'regnamespace',
+          'regoper',
+          'regoperator',
+          'regproc',
+          'regprocedure',
+          'regrole',
+          'regtype',
+          'smallint',
+        ],
+      },
+      {
+        'name': 'col2',
+        'atttypid': 23,
+        'attlen': null,
+        'attnum': 3,
+        'attndims': 0,
+        'atttypmod': -1,
+        'attacl': [],
+        'attnotnull': false,
+        'attoptions': null,
+        'attstattarget': -1,
+        'attstorage': 'p',
+        'attidentity': '',
+        'defval': null,
+        'typname': 'integer',
+        'displaytypname': 'integer',
+        'cltype': 'integer',
+        'elemoid': 23,
+        'typnspname': 'pg_catalog',
+        'defaultstorage': 'p',
+        'description': null,
+        'indkey': '1',
+        'isdup': false,
+        'collspcname': '',
+        'is_fk': false,
+        'seclabels': null,
+        'is_sys_column': false,
+        'colconstype': 'n',
+        'genexpr': null,
+        'relname': 'tab1',
+        'is_view_only': false,
+        'seqrelid': null,
+        'seqtypid': null,
+        'seqstart': null,
+        'seqincrement': null,
+        'seqmax': null,
+        'seqmin': null,
+        'seqcache': null,
+        'seqcycle': null,
+        'is_pk': false,
+        'is_primary_key': false,
+        'attprecision': null,
+        'edit_types': [
+          'bigint',
+          'double precision',
+          'information_schema.cardinal_number',
+          'integer',
+          'integer',
+          'integer',
+          'money',
+          'numeric',
+          'oid',
+          'real',
+          'regclass',
+          'regconfig',
+          'regdictionary',
+          'regnamespace',
+          'regoper',
+          'regoperator',
+          'regproc',
+          'regprocedure',
+          'regrole',
+          'regtype',
+          'smallint',
+        ],
+      },
+    ],
+    'primary_key': [
+      {
+        'oid': 408232,
+        'name': 'tab1_pkey',
+        'col_count': 1,
+        'spcname': 'pg_default',
+        'comment': null,
+        'condeferrable': false,
+        'condeferred': false,
+        'fillfactor': null,
+        'columns': [
+          {
+            'column': 'id',
+          },
+        ],
+        'include': [],
+      },
+    ],
+    'unique_constraint': [],
+    'foreign_key': [
+      {
+        'oid': 408239,
+        'name': 'tab1_col1_fkey',
+        'condeferrable': false,
+        'condeferred': false,
+        'confupdtype': 'a',
+        'confdeltype': 'a',
+        'confmatchtype': false,
+        'conkey': [
+          2,
+        ],
+        'confkey': [
+          1,
+        ],
+        'confrelid': 408234,
+        'fknsp': 'erd',
+        'fktab': 'tab1',
+        'refnsp': 'erd',
+        'reftab': 'tab2',
+        'comment': null,
+        'convalidated': false,
+        'columns': [
+          {
+            'local_column': 'col1col1col1col1col1col1col1col1',
+            'references': 123456,
+            'referenced': 'id',
+            'references_table_name': 'schema1.test1',
+          },
+        ],
+        'remote_schema': 'schema1',
+        'remote_table': 'test1',
+        'coveringindex': null,
+        'autoindex': true,
+        'hasindex': false,
+      },
+    ],
+    'check_constraint': [],
+    'index': {},
+    'rule': {},
+    'trigger': {},
+    'row_security_policy': {},
+  },
+];
diff --git a/web/regression/javascript/erd/ui_components/body_widget_spec.js b/web/regression/javascript/erd/ui_components/body_widget_spec.js
new file mode 100644
index 000000000..1771ddcb9
--- /dev/null
+++ b/web/regression/javascript/erd/ui_components/body_widget_spec.js
@@ -0,0 +1,505 @@
+import jasmineEnzyme from 'jasmine-enzyme';
+import React from 'react';
+import {mount} from 'enzyme';
+import '../../helper/enzyme.helper';
+import MockAdapter from 'axios-mock-adapter';
+import axios from 'axios/index';
+
+import ERDCore from 'pgadmin.tools.erd/erd_tool/ERDCore';
+import * as erdModule from 'pgadmin.tools.erd/erd_module';
+import erdPref from './erd_preferences';
+import BodyWidget from 'pgadmin.tools.erd/erd_tool/ui_components/BodyWidget';
+import * as ERDSqlTool from 'tools/datagrid/static/js/show_query_tool';
+
+let pgAdmin = {
+  Browser: {
+    Events: {
+      on: jasmine.createSpy('on'),
+    },
+    get_preferences_for_module: function() {
+      return erdPref;
+    },
+    docker: {
+      findPanels: function() {
+        return [
+          {
+            isVisible: function() {
+              return true;
+            },
+          },
+        ];
+      },
+    },
+    onPreferencesChange: ()=>{},
+    utils: {
+      app_version_int: 1234,
+    },
+  },
+  FileManager: {
+    init: jasmine.createSpy(),
+    show_dialog: jasmine.createSpy(),
+  },
+};
+
+let alertify = jasmine.createSpyObj('alertify', {
+  'success': null,
+  'error': null,
+  'alert': {
+    'set': ()=>{},
+  },
+});
+
+let tableDialog = jasmine.createSpyObj('TableDialog', ['show']);
+let otmDialog = jasmine.createSpyObj('otmDialog', ['show']);
+let mtmDialog = jasmine.createSpyObj('mtmDialog', ['show']);
+
+let getDialog = (dialogName)=>{
+  switch(dialogName) {
+  case 'entity_dialog': return tableDialog;
+  case 'onetomany_dialog': return otmDialog;
+  case 'manytomany_dialog': return mtmDialog;
+  }
+};
+
+describe('ERD BodyWidget', ()=>{
+  let body = null;
+  let bodyInstance = null;
+  let networkMock = null;
+  let serverVersion = 120000;
+  let colTypes = [
+    {'label': 'integer', 'value': 'integer'},
+    {'label': 'character varrying', 'value': 'character varrying'},
+  ];
+  let schemas = [
+    {'oid': 111, 'name': 'erd1'},
+    {'oid': 222, 'name': 'erd2'},
+  ];
+  let params = {
+    bgcolor: null,
+    client_platform: 'macos',
+    did: '13637',
+    fgcolor: null,
+    gen: true,
+    is_desktop_mode: true,
+    is_linux: false,
+    server_type: 'pg',
+    sgid: '1',
+    sid: '5',
+    title: 'postgres/postgres@PostgreSQL 12',
+    trans_id: 110008,
+  };
+
+  beforeAll(()=>{
+    spyOn(erdModule, 'setPanelTitle');
+    spyOn(ERDCore.prototype, 'repaint');
+    spyOn(ERDCore.prototype, 'deserializeData');
+    spyOn(ERDCore.prototype, 'addNode').and.returnValue({
+      setSelected: ()=>{},
+      getColumns: ()=>([{attnum: 0}, {attnum: 1}]),
+      getID: ()=>'newid1',
+    });
+    spyOn(ERDCore.prototype, 'addLink').and.returnValue({
+      setSelected: ()=>{},
+    });
+
+    networkMock = new MockAdapter(axios);
+    networkMock.onPost('/erd/initialize/110008/1/5/13637').reply(200, {'data': {
+      serverVersion: serverVersion,
+    }});
+    networkMock.onGet('/erd/prequisite/110008/1/5/13637').reply(200, {'data': {
+      'col_types': colTypes,
+      'schemas': schemas,
+    }});
+    networkMock.onGet('/erd/tables/110008/1/5/13637').reply(200, {'data': []});
+
+    networkMock.onPost('/erd/sql/110008/1/5/13637').reply(200, {'data': 'SELECT 1;'});
+
+    networkMock.onPost('/sqleditor/load_file/').reply(200, {'data': 'data'});
+    networkMock.onPost('/sqleditor/save_file/').reply(200, {'data': 'data'});
+  });
+
+  beforeEach(()=>{
+    jasmineEnzyme();
+    body = mount(<BodyWidget params={params} pgAdmin={pgAdmin} getDialog={getDialog} alertify={alertify}/>);
+    bodyInstance = body.instance();
+  });
+
+  afterAll(() => {
+    networkMock.restore();
+    if(body) {
+      body.unmount();
+    }
+  });
+
+  it('constructor', (done)=>{
+
+    expect(body.find('ToolBar').length).toBe(1);
+    expect(body.find('ConnectionBar').length).toBe(1);
+    expect(body.find('FloatingNote').length).toBe(1);
+    expect(body.find('.diagram-container Loader').length).toBe(1);
+    expect(body.find('.diagram-container CanvasWidget').length).toBe(1);
+
+    body.instance().setState({}, ()=>{
+      let instance = body.instance();
+
+      setTimeout(()=>{
+        expect(body.state()).toEqual(jasmine.objectContaining({
+          server_version: serverVersion,
+          preferences: erdPref,
+        }));
+        expect(instance.diagram.getCache('colTypes')).toEqual(colTypes);
+        expect(instance.diagram.getCache('schemas')).toEqual(schemas);
+        done();
+      });
+    });
+  });
+
+  it('event offsetUpdated', (done)=>{
+    bodyInstance.diagram.fireEvent({offsetX: 4, offsetY: 5}, 'offsetUpdated', true);
+    setTimeout(()=>{
+      expect(bodyInstance.canvasEle.style.backgroundPosition).toBe('4px 5px');
+      done();
+    });
+  });
+
+  it('event zoomUpdated', (done)=>{
+    spyOn(bodyInstance.diagram.getModel(), 'getOptions').and.returnValue({gridSize: 15});
+    bodyInstance.diagram.fireEvent({zoom: 20}, 'zoomUpdated', true);
+    setTimeout(()=>{
+      expect(bodyInstance.canvasEle.style.backgroundSize).toBe('9px 9px');
+      done();
+    });
+  });
+
+  it('event nodesSelectionChanged', (done)=>{
+    spyOn(bodyInstance.diagram, 'getSelectedNodes').and.returnValue([{key:'value'}]);
+    bodyInstance.diagram.fireEvent({}, 'nodesSelectionChanged', true);
+    setTimeout(()=>{
+      expect(body.state().no_node_selected).toBe(false);
+      done();
+    });
+  });
+
+  it('event linksSelectionChanged', (done)=>{
+    spyOn(bodyInstance.diagram, 'getSelectedLinks').and.returnValue([{key:'value'}]);
+    bodyInstance.diagram.fireEvent({}, 'linksSelectionChanged', true);
+    setTimeout(()=>{
+      expect(body.state().no_link_selected).toBe(false);
+      done();
+    });
+  });
+
+  it('event linksUpdated', (done)=>{
+    bodyInstance.diagram.fireEvent({}, 'linksUpdated', true);
+    setTimeout(()=>{
+      expect(body.state().dirty).toBe(true);
+      done();
+    });
+  });
+
+  it('event nodesUpdated', (done)=>{
+    bodyInstance.diagram.fireEvent({}, 'nodesUpdated', true);
+    setTimeout(()=>{
+      expect(body.state().dirty).toBe(true);
+      done();
+    });
+  });
+
+  it('event showNote', (done)=>{
+    let noteNode = {key: 'value', getNote: ()=>'a note'};
+    spyOn(bodyInstance, 'showNote');
+    bodyInstance.diagram.fireEvent({node: noteNode}, 'showNote', true);
+    setTimeout(()=>{
+      expect(bodyInstance.showNote).toHaveBeenCalledWith(noteNode);
+      done();
+    });
+  });
+
+  it('event editNode', (done)=>{
+    let node = {key: 'value', getNote: ()=>'a note'};
+    spyOn(bodyInstance, 'addEditNode');
+    bodyInstance.diagram.fireEvent({node: node}, 'editNode', true);
+    setTimeout(()=>{
+      expect(bodyInstance.addEditNode).toHaveBeenCalledWith(node);
+      done();
+    });
+  });
+
+  it('getDialog', ()=>{
+    bodyInstance.getDialog('entity_dialog')();
+    expect(tableDialog.show).toHaveBeenCalled();
+
+    bodyInstance.getDialog('onetomany_dialog')();
+    expect(otmDialog.show).toHaveBeenCalled();
+
+    bodyInstance.getDialog('manytomany_dialog')();
+    expect(mtmDialog.show).toHaveBeenCalled();
+  });
+
+  it('addEditNode', ()=>{
+    /* New */
+    tableDialog.show.calls.reset();
+    bodyInstance.addEditNode();
+    expect(tableDialog.show).toHaveBeenCalled();
+
+    let saveCallback = tableDialog.show.calls.mostRecent().args[5];
+    let newData = {key: 'value'};
+    saveCallback(newData);
+    expect(bodyInstance.diagram.addNode).toHaveBeenCalledWith(newData);
+
+    /* Existing */
+    tableDialog.show.calls.reset();
+    let node = jasmine.createSpyObj('node',{
+      getSchemaTableName: ['erd1', 'table1'],
+      setData: null,
+      getData: null,
+    });
+    bodyInstance.addEditNode(node);
+    expect(tableDialog.show).toHaveBeenCalled();
+
+    saveCallback = tableDialog.show.calls.mostRecent().args[5];
+    newData = {key: 'value'};
+    saveCallback(newData);
+    expect(node.setData).toHaveBeenCalledWith(newData);
+  });
+
+  it('onEditNode', ()=>{
+    let node = {key: 'value'};
+    spyOn(bodyInstance, 'addEditNode');
+    spyOn(bodyInstance.diagram, 'getSelectedNodes').and.returnValue([node]);
+    bodyInstance.onEditNode();
+    expect(bodyInstance.addEditNode).toHaveBeenCalledWith(node);
+  });
+
+  it('onAddNewNode', ()=>{
+    spyOn(bodyInstance, 'addEditNode');
+    bodyInstance.onAddNewNode();
+    expect(bodyInstance.addEditNode).toHaveBeenCalled();
+  });
+
+  it('onCloneNode', ()=>{
+    let node = jasmine.createSpyObj('node',{
+      getSchemaTableName: ['erd1', 'table1'],
+      setData: null,
+      getData: null,
+      cloneData: {key: 'value'},
+      getPosition: {x: 30, y: 30},
+    });
+    spyOn(bodyInstance.diagram, 'getSelectedNodes').and.returnValue([node]);
+    spyOn(bodyInstance.diagram, 'getNextTableName').and.returnValue('newtable1');
+    bodyInstance.onCloneNode();
+    expect(bodyInstance.diagram.addNode).toHaveBeenCalledWith({key: 'value'}, [50, 50]);
+  });
+
+  it('onDeleteNode', ()=>{
+    let node = jasmine.createSpyObj('node',{
+      getSchemaTableName: ['erd1', 'table1'],
+      setData: null,
+      getData: null,
+      cloneData: {key: 'value'},
+      getPosition: {x: 30, y: 30},
+      remove: null,
+      setSelected: null,
+    });
+    let link = jasmine.createSpyObj('link', {
+      remove: null,
+      setSelected: null,
+      getTargetPort: jasmine.createSpyObj('port', ['remove']),
+      getSourcePort: jasmine.createSpyObj('port', ['remove']),
+    });
+    spyOn(bodyInstance.diagram, 'getSelectedNodes').and.returnValue([node]);
+    spyOn(bodyInstance.diagram, 'getSelectedLinks').and.returnValue([link]);
+
+    bodyInstance.onDeleteNode();
+    expect(node.remove).toHaveBeenCalled();
+    expect(link.remove).toHaveBeenCalled();
+  });
+
+  it('onAutoDistribute', ()=>{
+    spyOn(bodyInstance.diagram, 'dagreDistributeNodes');
+    bodyInstance.onAutoDistribute();
+    expect(bodyInstance.diagram.dagreDistributeNodes).toHaveBeenCalled();
+  });
+
+  it('onDetailsToggle', (done)=>{
+    let node = jasmine.createSpyObj('node',['fireEvent']);
+    spyOn(bodyInstance.diagram, 'getModel').and.returnValue({
+      'getNodes': ()=>[node],
+    });
+
+    let show_details = body.state().show_details;
+    bodyInstance.onDetailsToggle();
+    body.setState({}, ()=>{
+      expect(body.state().show_details).toBe(!show_details);
+      expect(node.fireEvent).toHaveBeenCalledWith({show_details: !show_details}, 'toggleDetails');
+      done();
+    });
+  });
+
+  it('onLoadDiagram', ()=>{
+    bodyInstance.onLoadDiagram();
+    expect(pgAdmin.FileManager.show_dialog).toHaveBeenCalled();
+  });
+
+  it('openFile', (done)=>{
+    spyOn(bodyInstance.diagram, 'deserialize');
+    bodyInstance.openFile('test.pgerd');
+    setTimeout(()=>{
+      expect(body.state()).toEqual(jasmine.objectContaining({
+        current_file: 'test.pgerd',
+        dirty: false,
+      }));
+      expect(bodyInstance.diagram.deserialize).toHaveBeenCalledWith({data: 'data'});
+      done();
+    });
+  });
+
+  it('onSaveDiagram', (done)=>{
+    body.setState({
+      current_file: 'newfile.pgerd',
+    });
+    bodyInstance.onSaveDiagram();
+    setTimeout(()=>{
+      expect(body.state()).toEqual(jasmine.objectContaining({
+        current_file: 'newfile.pgerd',
+        dirty: false,
+      }));
+      done();
+    });
+
+    bodyInstance.onSaveDiagram(true);
+    expect(pgAdmin.FileManager.show_dialog).toHaveBeenCalledWith({
+      'supported_types': ['pgerd'],
+      'dialog_type': 'create_file',
+      'dialog_title': 'Save File',
+      'btn_primary': 'Save',
+    });
+  });
+
+  it('onSaveAsDiagram', ()=>{
+    spyOn(bodyInstance, 'onSaveDiagram');
+    bodyInstance.onSaveAsDiagram();
+    expect(bodyInstance.onSaveDiagram).toHaveBeenCalledWith(true);
+  });
+
+  it('onSQLClick', (done)=>{
+    spyOn(bodyInstance.diagram, 'serializeData').and.returnValue({key: 'value'});
+    spyOn(ERDSqlTool, 'showERDSqlTool');
+    spyOn(sessionStorage, 'setItem');
+    bodyInstance.onSQLClick();
+
+    setTimeout(()=>{
+      let sql = '-- This script was generated by a beta version of the ERD tool in pgAdmin 4.\n'
+      + '-- Please log an issue at https://redmine.postgresql.org/projects/pgadmin4/issues/new if you find any bugs, including reproduction steps.\n'
+      + 'BEGIN;\nSELECT 1;\nEND;';
+
+      expect(sessionStorage.setItem).toHaveBeenCalledWith('erd'+params.trans_id, sql);
+      expect(ERDSqlTool.showERDSqlTool).toHaveBeenCalled();
+      done();
+    });
+  });
+
+  it('onOneToManyClick', ()=>{
+    let node = jasmine.createSpyObj('node',{
+      getID: 'id1',
+    });
+    spyOn(bodyInstance.diagram, 'getSelectedNodes').and.returnValue([node]);
+
+    otmDialog.show.calls.reset();
+    bodyInstance.onOneToManyClick();
+    expect(otmDialog.show).toHaveBeenCalled();
+
+    let saveCallback = otmDialog.show.calls.mostRecent().args[4];
+    let newData = {key: 'value'};
+    saveCallback(newData);
+    expect(bodyInstance.diagram.addLink).toHaveBeenCalledWith(newData, 'onetomany');
+  });
+
+  it('onManyToManyClick', ()=>{
+    let node = jasmine.createSpyObj('node',{
+      getID: 'id1',
+    });
+    spyOn(bodyInstance.diagram, 'getSelectedNodes').and.returnValue([node]);
+
+    mtmDialog.show.calls.reset();
+    bodyInstance.onManyToManyClick();
+    expect(mtmDialog.show).toHaveBeenCalled();
+
+    /* onSave */
+    let nodesDict = {
+      'id1': {
+        getID: ()=>'id1',
+        getData: ()=>({name: 'table1', schema: 'erd1'}),
+        getColumnAt: ()=>({name: 'col1', type: 'type1', attnum: 0}),
+        addPort: jasmine.createSpy('addPort').and.callFake((obj)=>obj),
+      },
+      'id2': {
+        getID: ()=>'id2',
+        getData: ()=>({name: 'table2', schema: 'erd2'}),
+        getColumnAt: ()=>({name: 'col2', type: 'type2', attnum: 1}),
+        addPort: jasmine.createSpy('addPort').and.callFake((obj)=>obj),
+      },
+    };
+    spyOn(bodyInstance.diagram, 'getModel').and.returnValue({
+      'getNodesDict': ()=>nodesDict,
+    });
+    spyOn(bodyInstance.diagram, 'addLink');
+    let saveCallback = mtmDialog.show.calls.mostRecent().args[4];
+    let newData = {
+      left_table_uid: 'id1',
+      left_table_column_attnum: 1,
+      right_table_uid: 'id2',
+      right_table_column_attnum: 2,
+    };
+
+    bodyInstance.diagram.addNode.calls.reset();
+    bodyInstance.diagram.addLink.calls.reset();
+    saveCallback(newData);
+    expect(bodyInstance.diagram.addNode).toHaveBeenCalledWith({
+      name: 'table1_table2',
+      schema: 'erd1',
+      columns: [
+        {
+          type: 'type1',
+          name: 'table1_col1',
+          is_primary_key: false,
+          attnum: 0,
+        },
+        {
+          type: 'type2',
+          name: 'table2_col2',
+          is_primary_key: false,
+          attnum: 1,
+        },
+      ],
+    });
+
+    let linkData = {
+      local_table_uid: 'newid1',
+      local_column_attnum: 0,
+      referenced_table_uid: 'id1',
+      referenced_column_attnum : 1,
+    };
+    expect(bodyInstance.diagram.addLink.calls.argsFor(0)).toEqual([linkData, 'onetomany']);
+    linkData = {
+      local_table_uid: 'newid1',
+      local_column_attnum: 1,
+      referenced_table_uid: 'id2',
+      referenced_column_attnum : 2,
+    };
+    expect(bodyInstance.diagram.addLink.calls.argsFor(1)).toEqual([linkData, 'onetomany']);
+  });
+
+  it('onNoteClick', ()=>{
+    let noteNode = {key: 'value', getNote: ()=>'a note'};
+    spyOn(bodyInstance.diagram, 'getSelectedNodes').and.returnValue([noteNode]);
+    spyOn(bodyInstance.diagram.getEngine(), 'getNodeElement').and.returnValue(null);
+    spyOn(bodyInstance.diagram.getEngine(), 'getNodeElement').and.returnValue(null);
+    spyOn(bodyInstance, 'setState');
+    bodyInstance.onNoteClick();
+    expect(bodyInstance.setState).toHaveBeenCalledWith({
+      note_node: noteNode,
+      note_open: true,
+    });
+  });
+});
diff --git a/web/regression/javascript/erd/ui_components/connection_bar_spec.js b/web/regression/javascript/erd/ui_components/connection_bar_spec.js
new file mode 100644
index 000000000..0f5b935a9
--- /dev/null
+++ b/web/regression/javascript/erd/ui_components/connection_bar_spec.js
@@ -0,0 +1,25 @@
+import jasmineEnzyme from 'jasmine-enzyme';
+import React from 'react';
+import {mount} from 'enzyme';
+import '../../helper/enzyme.helper';
+
+import ConnectionBar, {STATUS} from 'pgadmin.tools.erd/erd_tool/ui_components/ConnectionBar';
+
+describe('ERD ConnectionBar', ()=>{
+  beforeEach(()=>{
+    jasmineEnzyme();
+  });
+
+  it('<ConnectionBar /> comp', ()=>{
+    let connBar = mount(<ConnectionBar title="test title"/>);
+
+    expect(connBar.find('.editor-title').text()).toBe('test title');
+
+    connBar.setProps({status: STATUS.CONNECTING});
+    expect(connBar.find('.editor-title').text()).toBe('(Obtaining connection...) test title');
+
+    connBar.setProps({bgcolor: '#000', fgcolor: '#fff'});
+    expect(connBar.find('.editor-title').prop('style').backgroundColor).toBe('#000');
+    expect(connBar.find('.editor-title').prop('style').color).toBe('#fff');
+  });
+});
diff --git a/web/regression/javascript/erd/ui_components/erd_preferences.js b/web/regression/javascript/erd/ui_components/erd_preferences.js
new file mode 100644
index 000000000..d91b158ce
--- /dev/null
+++ b/web/regression/javascript/erd/ui_components/erd_preferences.js
@@ -0,0 +1,147 @@
+export default {
+  'erd_new_browser_tab': false,
+  'open_project': {
+    'alt': false,
+    'shift': false,
+    'control': true,
+    'key': {
+      'key_code': 79,
+      'char': 'o',
+    },
+  },
+  'save_project': {
+    'alt': false,
+    'shift': false,
+    'control': true,
+    'key': {
+      'key_code': 83,
+      'char': 's',
+    },
+  },
+  'save_project_as': {
+    'alt': false,
+    'shift': true,
+    'control': true,
+    'key': {
+      'key_code': 83,
+      'char': 's',
+    },
+  },
+  'generate_sql': {
+    'alt': true,
+    'shift': false,
+    'control': true,
+    'key': {
+      'key_code': 83,
+      'char': 's',
+    },
+  },
+  'download_image': {
+    'alt': true,
+    'shift': false,
+    'control': true,
+    'key': {
+      'key_code': 73,
+      'char': 'i',
+    },
+  },
+  'add_table': {
+    'alt': true,
+    'shift': false,
+    'control': true,
+    'key': {
+      'key_code': 65,
+      'char': 'a',
+    },
+  },
+  'edit_table': {
+    'alt': true,
+    'shift': false,
+    'control': true,
+    'key': {
+      'key_code': 69,
+      'char': 'e',
+    },
+  },
+  'clone_table': {
+    'alt': true,
+    'shift': false,
+    'control': true,
+    'key': {
+      'key_code': 67,
+      'char': 'c',
+    },
+  },
+  'drop_table': {
+    'alt': true,
+    'shift': false,
+    'control': true,
+    'key': {
+      'key_code': 68,
+      'char': 'd',
+    },
+  },
+  'add_edit_note': {
+    'alt': true,
+    'shift': false,
+    'control': true,
+    'key': {
+      'key_code': 78,
+      'char': 'n',
+    },
+  },
+  'one_to_many': {
+    'alt': true,
+    'shift': false,
+    'control': true,
+    'key': {
+      'key_code': 79,
+      'char': 'o',
+    },
+  },
+  'many_to_many': {
+    'alt': true,
+    'shift': false,
+    'control': true,
+    'key': {
+      'key_code': 77,
+      'char': 'm',
+    },
+  },
+  'auto_align': {
+    'alt': true,
+    'shift': false,
+    'control': true,
+    'key': {
+      'key_code': 76,
+      'char': 'l',
+    },
+  },
+  'zoom_to_fit': {
+    'alt': true,
+    'shift': true,
+    'control': false,
+    'key': {
+      'key_code': 70,
+      'char': 'f',
+    },
+  },
+  'zoom_in': {
+    'alt': true,
+    'shift': true,
+    'control': false,
+    'key': {
+      'key_code': 187,
+      'char': '+',
+    },
+  },
+  'zoom_out': {
+    'alt': true,
+    'shift': true,
+    'control': false,
+    'key': {
+      'key_code': 189,
+      'char': '-',
+    },
+  },
+};
diff --git a/web/regression/javascript/erd/ui_components/floating_note_spec.js b/web/regression/javascript/erd/ui_components/floating_note_spec.js
new file mode 100644
index 000000000..f786267bf
--- /dev/null
+++ b/web/regression/javascript/erd/ui_components/floating_note_spec.js
@@ -0,0 +1,39 @@
+import jasmineEnzyme from 'jasmine-enzyme';
+import React from 'react';
+import {mount} from 'enzyme';
+import '../../helper/enzyme.helper';
+
+import FloatingNote from 'pgadmin.tools.erd/erd_tool/ui_components/FloatingNote';
+
+describe('ERD FloatingNote', ()=>{
+
+  beforeEach(()=>{
+    jasmineEnzyme();
+  });
+
+  it('<FloatingNote /> on OK click', ()=>{
+    let floatNote = null;
+    let onClose = jasmine.createSpy('onClose');
+    let noteNode = {
+      getNote: function() {
+        return 'some note';
+      },
+      setNote: jasmine.createSpy('setNote'),
+      getSchemaTableName: function() {
+        return ['schema1', 'table1'];
+      },
+    };
+
+    floatNote = mount(<FloatingNote open={false} onClose={onClose}
+      reference={null} noteNode={noteNode} appendTo={document.body} rows={8}/>);
+
+    floatNote.find('textarea').simulate('change', {
+      target: {
+        value: 'the new note',
+      },
+    });
+    floatNote.find('button[data-label="OK"]').simulate('click');
+    expect(noteNode.setNote).toHaveBeenCalledWith('the new note');
+    expect(onClose).toHaveBeenCalled();
+  });
+});
diff --git a/web/regression/javascript/erd/ui_components/loader_spec.js b/web/regression/javascript/erd/ui_components/loader_spec.js
new file mode 100644
index 000000000..b14ed30aa
--- /dev/null
+++ b/web/regression/javascript/erd/ui_components/loader_spec.js
@@ -0,0 +1,23 @@
+import jasmineEnzyme from 'jasmine-enzyme';
+import React from 'react';
+import {shallow} from 'enzyme';
+import '../../helper/enzyme.helper';
+
+import Loader from 'pgadmin.tools.erd/erd_tool/ui_components/Loader';
+
+describe('ERD Loader', ()=>{
+  beforeEach(()=>{
+    jasmineEnzyme();
+  });
+
+  it('<Loader /> comp', ()=>{
+    let loaderComp = shallow(<Loader />);
+    expect(loaderComp.isEmptyRender()).toBeTrue();
+
+    loaderComp.setProps({message: 'test message'});
+    expect(loaderComp.find('.pg-sp-text').text()).toBe('test message');
+
+    loaderComp.setProps({autoEllipsis: true});
+    expect(loaderComp.find('.pg-sp-text').text()).toBe('test message...');
+  });
+});
diff --git a/web/regression/javascript/erd/ui_components/toolbar_spec.js b/web/regression/javascript/erd/ui_components/toolbar_spec.js
new file mode 100644
index 000000000..0429d718d
--- /dev/null
+++ b/web/regression/javascript/erd/ui_components/toolbar_spec.js
@@ -0,0 +1,76 @@
+import jasmineEnzyme from 'jasmine-enzyme';
+import React from 'react';
+import Tippy from '@tippyjs/react';
+import {mount, shallow} from 'enzyme';
+import '../../helper/enzyme.helper';
+
+import ToolBar, {ButtonGroup, DetailsToggleButton, IconButton, Shortcut} from 'pgadmin.tools.erd/erd_tool/ui_components/ToolBar';
+
+describe('ERD Toolbar', ()=>{
+  beforeEach(()=>{
+    jasmineEnzyme();
+  });
+
+  it('<Toolbar /> comp', ()=>{
+    let toolBar = mount(<ToolBar id="id1"><div className="test"></div></ToolBar>);
+    expect(toolBar.getDOMNode().id).toBe('id1');
+    expect(toolBar.find('.test').length).toBe(1);
+  });
+
+  it('<ButtonGroup /> comp', ()=>{
+    let btnGrp = mount(<ButtonGroup><div className="test"></div></ButtonGroup>);
+    expect(btnGrp.getDOMNode().className).toBe('btn-group mr-1 ');
+    expect(btnGrp.find('.test').length).toBe(1);
+    btnGrp.unmount();
+
+    btnGrp = mount(<ButtonGroup className="someclass"></ButtonGroup>);
+    expect(btnGrp.getDOMNode().className).toBe('btn-group mr-1 someclass');
+  });
+
+  it('<DetailsToggleButton /> comp', ()=>{
+    let toggle = shallow(<DetailsToggleButton showDetails={true} />);
+    let btn = toggle.find(IconButton);
+    expect(btn.prop('icon')).toBe('far fa-eye');
+    expect(btn.prop('title')).toBe('Show fewer details');
+
+    toggle.setProps({showDetails: false});
+    btn = toggle.find(IconButton);
+    expect(btn.prop('icon')).toBe('fas fa-low-vision');
+    expect(btn.prop('title')).toBe('Show more details');
+  });
+
+  it('<IconButton /> comp', ()=>{
+    let btn = mount(<IconButton />);
+
+    let tippy = btn.find(Tippy);
+    expect(tippy.length).toBe(0);
+
+    btn.setProps({title: 'test title'});
+    tippy = btn.find(Tippy);
+    expect(tippy.length).toBe(1);
+
+    expect(btn.find('button').getDOMNode().className).toBe('btn btn-sm btn-primary-icon ');
+
+    btn.setProps({icon: 'fa fa-icon'});
+    expect(btn.find('button .sql-icon-lg').getDOMNode().className).toBe('fa fa-icon sql-icon-lg');
+  });
+
+  it('<Shortcut /> comp', ()=>{
+    let key = {
+      alt: true,
+      control: true,
+      shift: false,
+      key: {
+        key_code: 65,
+        char: 'a',
+      },
+    };
+    let shortcutComp = mount(<Shortcut shortcut={key}/>);
+
+    expect(shortcutComp.find('.shortcut-key').length).toBe(3);
+
+    key.alt = false;
+    shortcutComp.setProps({shortcut: key});
+    expect(shortcutComp.find('.shortcut-key').length).toBe(2);
+  });
+});
diff --git a/web/regression/javascript/fake_endpoints.js b/web/regression/javascript/fake_endpoints.js
index 806f59001..3a028c415 100644
--- a/web/regression/javascript/fake_endpoints.js
+++ b/web/regression/javascript/fake_endpoints.js
@@ -22,5 +22,11 @@ define(function () {
     'search_objects.types': '/search_objects/types/<int:sid>/<int:did>',
     'search_objects.search': '/search_objects/search/<int:sid>/<int:did>',
     'dashboard.dashboard_stats': '/dashboard/dashboard_stats',
+    'sqleditor.load_file': '/sqleditor/load_file/',
+    'sqleditor.save_file': '/sqleditor/save_file/',
+    'erd.initialize': '/erd/initialize/<int:trans_id>/<int:sgid>/<int:sid>/<int:did>',
+    'erd.sql': '/erd/sql/<int:trans_id>/<int:sgid>/<int:sid>/<int:did>',
+    'erd.prequisite': '/erd/prequisite/<int:trans_id>/<int:sgid>/<int:sid>/<int:did>',
+    'erd.tables': '/erd/tables/<int:trans_id>/<int:sgid>/<int:sid>/<int:did>',
   };
 });
diff --git a/web/webpack.config.js b/web/webpack.config.js
index c9ddf80c2..b7744552f 100644
--- a/web/webpack.config.js
+++ b/web/webpack.config.js
@@ -355,6 +355,7 @@ module.exports = [{
     sqleditor: './pgadmin/tools/sqleditor/static/js/sqleditor.js',
     debugger_direct: './pgadmin/tools/debugger/static/js/direct.js',
     schema_diff: './pgadmin/tools/schema_diff/static/js/schema_diff_hook.js',
+    erd_tool: './pgadmin/tools/erd/static/js/erd_tool_hook.js',
     file_utils: './pgadmin/misc/file_manager/static/js/utility.js',
     'pgadmin.style': pgadminCssStyles,
     pgadmin: pgadminScssStyles,
@@ -501,7 +502,8 @@ module.exports = [{
         ',pgadmin.node.pga_job' +
         ',pgadmin.tools.schema_diff' +
         ',pgadmin.tools.storage_manager' +
-        ',pgadmin.tools.search_objects',
+        ',pgadmin.tools.search_objects' +
+        ',pgadmin.tools.erd_module',
       },
     }, {
       test: require.resolve('snapsvg'),
diff --git a/web/webpack.shim.js b/web/webpack.shim.js
index 155fb9eaf..e3d9df89c 100644
--- a/web/webpack.shim.js
+++ b/web/webpack.shim.js
@@ -147,6 +147,10 @@ var webpackShimConfig = {
     'color-picker': path.join(__dirname, './node_modules/@simonwep/pickr/dist/pickr.es5.min'),
     'mousetrap': path.join(__dirname, './node_modules/mousetrap'),
     'tablesorter-metric': path.join(__dirname, './node_modules/tablesorter/dist/js/parsers/parser-metric.min'),
+    'pathfinding':  path.join(__dirname, 'node_modules/pathfinding'),
+    'dagre':  path.join(__dirname, 'node_modules/dagre'),
+    'graphlib': path.join(__dirname, 'node_modules/graphlib'),
+    'react': path.join(__dirname, 'node_modules/react'),
 
     // AciTree
     'jquery.acitree': path.join(__dirname, './node_modules/acitree/js/jquery.aciTree.min'),
@@ -275,6 +279,8 @@ var webpackShimConfig = {
     'pgadmin.tools.schema_diff_ui': path.join(__dirname, './pgadmin/tools/schema_diff/static/js/schema_diff_ui'),
     'pgadmin.tools.search_objects': path.join(__dirname, './pgadmin/tools/search_objects/static/js/search_objects'),
     'pgadmin.tools.storage_manager': path.join(__dirname, './pgadmin/tools/storage_manager/static/js/storage_manager'),
+    'pgadmin.tools.erd_module': path.join(__dirname, './pgadmin/tools/erd/static/js/erd_module'),
+    'pgadmin.tools.erd': path.join(__dirname, './pgadmin/tools/erd/static/js'),
     'pgadmin.search_objects': path.join(__dirname, './pgadmin/tools/search_objects/static/js'),
     'pgadmin.tools.user_management': path.join(__dirname, './pgadmin/tools/user_management/static/js/user_management'),
     'pgadmin.user_management.current_user': '/user_management/current_user',
diff --git a/web/webpack.test.config.js b/web/webpack.test.config.js
index ba6f99d6b..a4383d6aa 100644
--- a/web/webpack.test.config.js
+++ b/web/webpack.test.config.js
@@ -113,6 +113,7 @@ module.exports = {
       'pgadmin.browser.layout': path.join(__dirname, './pgadmin/browser/static/js/layout'),
       'pgadmin.browser.preferences': path.join(__dirname, './pgadmin/browser/static/js/preferences'),
       'pgadmin.browser.activity': path.join(__dirname, './pgadmin/browser/static/js/activity'),
+      'pgadmin.tools.erd': path.join(__dirname, './pgadmin/tools/erd/static/js'),
       'bundled_codemirror': path.join(__dirname, './pgadmin/static/bundle/codemirror'),
       'tools': path.join(__dirname, './pgadmin/tools/'),
     },
diff --git a/web/yarn.lock b/web/yarn.lock
index c9450d755..3b1943994 100644
--- a/web/yarn.lock
+++ b/web/yarn.lock
@@ -2,70 +2,54 @@
 # yarn lockfile v1
 
 
-"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.5.5":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a"
-  integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==
+"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.11":
+  version "7.12.11"
+  resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f"
+  integrity sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==
   dependencies:
     "@babel/highlight" "^7.10.4"
 
-"@babel/core@^7.7.5":
-  version "7.11.6"
-  resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.11.6.tgz#3a9455dc7387ff1bac45770650bc13ba04a15651"
-  integrity sha512-Wpcv03AGnmkgm6uS6k8iwhIwTrcP0m17TL1n1sy7qD0qelDu4XNeW0dN0mHfa+Gei211yDaLoEe/VlbXQzM4Bg==
+"@babel/compat-data@^7.12.5", "@babel/compat-data@^7.12.7":
+  version "7.12.7"
+  resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.12.7.tgz#9329b4782a7d6bbd7eef57e11addf91ee3ef1e41"
+  integrity sha512-YaxPMGs/XIWtYqrdEOZOCPsVWfEoriXopnsz3/i7apYPXQ3698UFhS6dVT1KN5qOsWmVgw/FOrmQgpRaZayGsw==
+
+"@babel/core@^7.10.2", "@babel/core@^7.7.5":
+  version "7.12.10"
+  resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.12.10.tgz#b79a2e1b9f70ed3d84bbfb6d8c4ef825f606bccd"
+  integrity sha512-eTAlQKq65zHfkHZV0sIVODCPGVgoo1HdBlbSLi9CqOzuZanMv2ihzY+4paiKr1mH+XmYESMAmJ/dpZ68eN6d8w==
   dependencies:
     "@babel/code-frame" "^7.10.4"
-    "@babel/generator" "^7.11.6"
-    "@babel/helper-module-transforms" "^7.11.0"
-    "@babel/helpers" "^7.10.4"
-    "@babel/parser" "^7.11.5"
-    "@babel/template" "^7.10.4"
-    "@babel/traverse" "^7.11.5"
-    "@babel/types" "^7.11.5"
+    "@babel/generator" "^7.12.10"
+    "@babel/helper-module-transforms" "^7.12.1"
+    "@babel/helpers" "^7.12.5"
+    "@babel/parser" "^7.12.10"
+    "@babel/template" "^7.12.7"
+    "@babel/traverse" "^7.12.10"
+    "@babel/types" "^7.12.10"
     convert-source-map "^1.7.0"
     debug "^4.1.0"
     gensync "^1.0.0-beta.1"
     json5 "^2.1.2"
     lodash "^4.17.19"
-    resolve "^1.3.2"
-    semver "^5.4.1"
-    source-map "^0.5.0"
-
-"@babel/core@~7.6.0":
-  version "7.6.4"
-  resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.6.4.tgz#6ebd9fe00925f6c3e177bb726a188b5f578088ff"
-  integrity sha512-Rm0HGw101GY8FTzpWSyRbki/jzq+/PkNQJ+nSulrdY6gFGOsNseCqD6KHRYe2E+EdzuBdr2pxCp6s4Uk6eJ+XQ==
-  dependencies:
-    "@babel/code-frame" "^7.5.5"
-    "@babel/generator" "^7.6.4"
-    "@babel/helpers" "^7.6.2"
-    "@babel/parser" "^7.6.4"
-    "@babel/template" "^7.6.0"
-    "@babel/traverse" "^7.6.3"
-    "@babel/types" "^7.6.3"
-    convert-source-map "^1.1.0"
-    debug "^4.1.0"
-    json5 "^2.1.0"
-    lodash "^4.17.13"
-    resolve "^1.3.2"
     semver "^5.4.1"
     source-map "^0.5.0"
 
-"@babel/generator@^7.11.5", "@babel/generator@^7.11.6", "@babel/generator@^7.6.4":
-  version "7.11.6"
-  resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.11.6.tgz#b868900f81b163b4d464ea24545c61cbac4dc620"
-  integrity sha512-DWtQ1PV3r+cLbySoHrwn9RWEgKMBLLma4OBQloPRyDYvc5msJM9kvTLo1YnlJd1P/ZuKbdli3ijr5q3FvAF3uA==
+"@babel/generator@^7.12.10", "@babel/generator@^7.12.11":
+  version "7.12.11"
+  resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.12.11.tgz#98a7df7b8c358c9a37ab07a24056853016aba3af"
+  integrity sha512-Ggg6WPOJtSi8yYQvLVjG8F/TlpWDlKx0OpS4Kt+xMQPs5OaGYWy+v1A+1TvxI6sAMGZpKWWoAQ1DaeQbImlItA==
   dependencies:
-    "@babel/types" "^7.11.5"
+    "@babel/types" "^7.12.11"
     jsesc "^2.5.1"
     source-map "^0.5.0"
 
-"@babel/helper-annotate-as-pure@^7.10.4":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz#5bf0d495a3f757ac3bda48b5bf3b3ba309c72ba3"
-  integrity sha512-XQlqKQP4vXFB7BN8fEEerrmYvHp3fK/rBkRFz9jaJbzK0B1DSfej9Kc7ZzE8Z/OnId1jpJdNAZ3BFQjWG68rcA==
+"@babel/helper-annotate-as-pure@^7.10.4", "@babel/helper-annotate-as-pure@^7.12.10":
+  version "7.12.10"
+  resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.12.10.tgz#54ab9b000e60a93644ce17b3f37d313aaf1d115d"
+  integrity sha512-XplmVbC1n+KY6jL8/fgLVXXUauDIB+lD5+GsQEh6F6GBF1dq1qy4DP4yXWzDKcoqXB3X58t61e85Fitoww4JVQ==
   dependencies:
-    "@babel/types" "^7.10.4"
+    "@babel/types" "^7.12.10"
 
 "@babel/helper-builder-binary-assignment-operator-visitor@^7.10.4":
   version "7.10.4"
@@ -75,43 +59,34 @@
     "@babel/helper-explode-assignable-expression" "^7.10.4"
     "@babel/types" "^7.10.4"
 
-"@babel/helper-builder-react-jsx-experimental@^7.10.4", "@babel/helper-builder-react-jsx-experimental@^7.11.5":
-  version "7.11.5"
-  resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx-experimental/-/helper-builder-react-jsx-experimental-7.11.5.tgz#4ea43dd63857b0a35cd1f1b161dc29b43414e79f"
-  integrity sha512-Vc4aPJnRZKWfzeCBsqTBnzulVNjABVdahSPhtdMD3Vs80ykx4a87jTHtF/VR+alSrDmNvat7l13yrRHauGcHVw==
+"@babel/helper-compilation-targets@^7.12.5":
+  version "7.12.5"
+  resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.12.5.tgz#cb470c76198db6a24e9dbc8987275631e5d29831"
+  integrity sha512-+qH6NrscMolUlzOYngSBMIOQpKUGPPsc61Bu5W10mg84LxZ7cmvnBHzARKbDoFxVvqqAbj6Tg6N7bSrWSPXMyw==
   dependencies:
-    "@babel/helper-annotate-as-pure" "^7.10.4"
-    "@babel/helper-module-imports" "^7.10.4"
-    "@babel/types" "^7.11.5"
-
-"@babel/helper-builder-react-jsx@^7.10.4":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.10.4.tgz#8095cddbff858e6fa9c326daee54a2f2732c1d5d"
-  integrity sha512-5nPcIZ7+KKDxT1427oBivl9V9YTal7qk0diccnh7RrcgrT/pGFOjgGw1dgryyx1GvHEpXVfoDF6Ak3rTiWh8Rg==
-  dependencies:
-    "@babel/helper-annotate-as-pure" "^7.10.4"
-    "@babel/types" "^7.10.4"
+    "@babel/compat-data" "^7.12.5"
+    "@babel/helper-validator-option" "^7.12.1"
+    browserslist "^4.14.5"
+    semver "^5.5.0"
 
-"@babel/helper-create-class-features-plugin@^7.10.4":
-  version "7.10.5"
-  resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.5.tgz#9f61446ba80e8240b0a5c85c6fdac8459d6f259d"
-  integrity sha512-0nkdeijB7VlZoLT3r/mY3bUkw3T8WG/hNw+FATs/6+pG2039IJWjTYL0VTISqsNHMUTEnwbVnc89WIJX9Qed0A==
+"@babel/helper-create-class-features-plugin@^7.12.1":
+  version "7.12.1"
+  resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.12.1.tgz#3c45998f431edd4a9214c5f1d3ad1448a6137f6e"
+  integrity sha512-hkL++rWeta/OVOBTRJc9a5Azh5mt5WgZUGAKMD8JM141YsE08K//bp1unBBieO6rUKkIPyUE0USQ30jAy3Sk1w==
   dependencies:
     "@babel/helper-function-name" "^7.10.4"
-    "@babel/helper-member-expression-to-functions" "^7.10.5"
+    "@babel/helper-member-expression-to-functions" "^7.12.1"
     "@babel/helper-optimise-call-expression" "^7.10.4"
-    "@babel/helper-plugin-utils" "^7.10.4"
-    "@babel/helper-replace-supers" "^7.10.4"
+    "@babel/helper-replace-supers" "^7.12.1"
     "@babel/helper-split-export-declaration" "^7.10.4"
 
-"@babel/helper-create-regexp-features-plugin@^7.10.4":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.10.4.tgz#fdd60d88524659a0b6959c0579925e425714f3b8"
-  integrity sha512-2/hu58IEPKeoLF45DBwx3XFqsbCXmkdAay4spVr2x0jYgRxrSNp+ePwvSsy9g6YSaNDcKIQVPXk1Ov8S2edk2g==
+"@babel/helper-create-regexp-features-plugin@^7.12.1":
+  version "7.12.7"
+  resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.12.7.tgz#2084172e95443fa0a09214ba1bb328f9aea1278f"
+  integrity sha512-idnutvQPdpbduutvi3JVfEgcVIHooQnhvhx0Nk9isOINOIGYkZea1Pk2JlJRiUnMefrlvr0vkByATBY/mB4vjQ==
   dependencies:
     "@babel/helper-annotate-as-pure" "^7.10.4"
-    "@babel/helper-regex" "^7.10.4"
-    regexpu-core "^4.7.0"
+    regexpu-core "^4.7.1"
 
 "@babel/helper-define-map@^7.10.4":
   version "7.10.5"
@@ -123,27 +98,27 @@
     lodash "^4.17.19"
 
 "@babel/helper-explode-assignable-expression@^7.10.4":
-  version "7.11.4"
-  resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.11.4.tgz#2d8e3470252cc17aba917ede7803d4a7a276a41b"
-  integrity sha512-ux9hm3zR4WV1Y3xXxXkdG/0gxF9nvI0YVmKVhvK9AfMoaQkemL3sJpXw+Xbz65azo8qJiEz2XVDUpK3KYhH3ZQ==
+  version "7.12.1"
+  resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.12.1.tgz#8006a466695c4ad86a2a5f2fb15b5f2c31ad5633"
+  integrity sha512-dmUwH8XmlrUpVqgtZ737tK88v07l840z9j3OEhCLwKTkjlvKpfqXVIZ0wpK3aeOxspwGrf/5AP5qLx4rO3w5rA==
   dependencies:
-    "@babel/types" "^7.10.4"
+    "@babel/types" "^7.12.1"
 
-"@babel/helper-function-name@^7.10.4":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz#d2d3b20c59ad8c47112fa7d2a94bc09d5ef82f1a"
-  integrity sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==
+"@babel/helper-function-name@^7.10.4", "@babel/helper-function-name@^7.12.11":
+  version "7.12.11"
+  resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.12.11.tgz#1fd7738aee5dcf53c3ecff24f1da9c511ec47b42"
+  integrity sha512-AtQKjtYNolKNi6nNNVLQ27CP6D9oFR6bq/HPYSizlzbp7uC1M59XJe8L+0uXjbIaZaUJF99ruHqVGiKXU/7ybA==
   dependencies:
-    "@babel/helper-get-function-arity" "^7.10.4"
-    "@babel/template" "^7.10.4"
-    "@babel/types" "^7.10.4"
+    "@babel/helper-get-function-arity" "^7.12.10"
+    "@babel/template" "^7.12.7"
+    "@babel/types" "^7.12.11"
 
-"@babel/helper-get-function-arity@^7.10.4":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz#98c1cbea0e2332f33f9a4661b8ce1505b2c19ba2"
-  integrity sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==
+"@babel/helper-get-function-arity@^7.12.10":
+  version "7.12.10"
+  resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.10.tgz#b158817a3165b5faa2047825dfa61970ddcc16cf"
+  integrity sha512-mm0n5BPjR06wh9mPQaDdXWDoll/j5UpCAPl1x8fS71GHm7HA6Ua2V4ylG1Ju8lvcTOietbPNNPaSilKj+pj+Ag==
   dependencies:
-    "@babel/types" "^7.10.4"
+    "@babel/types" "^7.12.10"
 
 "@babel/helper-hoist-variables@^7.10.4":
   version "7.10.4"
@@ -152,117 +127,115 @@
   dependencies:
     "@babel/types" "^7.10.4"
 
-"@babel/helper-member-expression-to-functions@^7.10.4", "@babel/helper-member-expression-to-functions@^7.10.5":
-  version "7.11.0"
-  resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.11.0.tgz#ae69c83d84ee82f4b42f96e2a09410935a8f26df"
-  integrity sha512-JbFlKHFntRV5qKw3YC0CvQnDZ4XMwgzzBbld7Ly4Mj4cbFy3KywcR8NtNctRToMWJOVvLINJv525Gd6wwVEx/Q==
+"@babel/helper-member-expression-to-functions@^7.12.1", "@babel/helper-member-expression-to-functions@^7.12.7":
+  version "7.12.7"
+  resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.7.tgz#aa77bd0396ec8114e5e30787efa78599d874a855"
+  integrity sha512-DCsuPyeWxeHgh1Dus7APn7iza42i/qXqiFPWyBDdOFtvS581JQePsc1F/nD+fHrcswhLlRc2UpYS1NwERxZhHw==
   dependencies:
-    "@babel/types" "^7.11.0"
+    "@babel/types" "^7.12.7"
 
-"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.10.4":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz#4c5c54be04bd31670a7382797d75b9fa2e5b5620"
-  integrity sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw==
+"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.12.1", "@babel/helper-module-imports@^7.12.5":
+  version "7.12.5"
+  resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz#1bfc0229f794988f76ed0a4d4e90860850b54dfb"
+  integrity sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==
   dependencies:
-    "@babel/types" "^7.10.4"
+    "@babel/types" "^7.12.5"
 
-"@babel/helper-module-transforms@^7.10.4", "@babel/helper-module-transforms@^7.10.5", "@babel/helper-module-transforms@^7.11.0":
-  version "7.11.0"
-  resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.11.0.tgz#b16f250229e47211abdd84b34b64737c2ab2d359"
-  integrity sha512-02EVu8COMuTRO1TAzdMtpBPbe6aQ1w/8fePD2YgQmxZU4gpNWaL9gK3Jp7dxlkUlUCJOTaSeA+Hrm1BRQwqIhg==
+"@babel/helper-module-transforms@^7.12.1":
+  version "7.12.1"
+  resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz#7954fec71f5b32c48e4b303b437c34453fd7247c"
+  integrity sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w==
   dependencies:
-    "@babel/helper-module-imports" "^7.10.4"
-    "@babel/helper-replace-supers" "^7.10.4"
-    "@babel/helper-simple-access" "^7.10.4"
+    "@babel/helper-module-imports" "^7.12.1"
+    "@babel/helper-replace-supers" "^7.12.1"
+    "@babel/helper-simple-access" "^7.12.1"
     "@babel/helper-split-export-declaration" "^7.11.0"
+    "@babel/helper-validator-identifier" "^7.10.4"
     "@babel/template" "^7.10.4"
-    "@babel/types" "^7.11.0"
+    "@babel/traverse" "^7.12.1"
+    "@babel/types" "^7.12.1"
     lodash "^4.17.19"
 
-"@babel/helper-optimise-call-expression@^7.10.4":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz#50dc96413d594f995a77905905b05893cd779673"
-  integrity sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg==
+"@babel/helper-optimise-call-expression@^7.10.4", "@babel/helper-optimise-call-expression@^7.12.10":
+  version "7.12.10"
+  resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.10.tgz#94ca4e306ee11a7dd6e9f42823e2ac6b49881e2d"
+  integrity sha512-4tpbU0SrSTjjt65UMWSrUOPZTsgvPgGG4S8QSTNHacKzpS51IVWGDj0yCwyeZND/i+LSN2g/O63jEXEWm49sYQ==
   dependencies:
-    "@babel/types" "^7.10.4"
+    "@babel/types" "^7.12.10"
 
-"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.8.0":
+"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3":
   version "7.10.4"
   resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz#2f75a831269d4f677de49986dff59927533cf375"
   integrity sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==
 
-"@babel/helper-regex@^7.10.4":
-  version "7.10.5"
-  resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.10.5.tgz#32dfbb79899073c415557053a19bd055aae50ae0"
-  integrity sha512-68kdUAzDrljqBrio7DYAEgCoJHxppJOERHOgOrDN7WjOzP0ZQ1LsSDRXcemzVZaLvjaJsJEESb6qt+znNuENDg==
-  dependencies:
-    lodash "^4.17.19"
-
-"@babel/helper-remap-async-to-generator@^7.10.4":
-  version "7.11.4"
-  resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.11.4.tgz#4474ea9f7438f18575e30b0cac784045b402a12d"
-  integrity sha512-tR5vJ/vBa9wFy3m5LLv2faapJLnDFxNWff2SAYkSE4rLUdbp7CdObYFgI7wK4T/Mj4UzpjPwzR8Pzmr5m7MHGA==
+"@babel/helper-remap-async-to-generator@^7.12.1":
+  version "7.12.1"
+  resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.12.1.tgz#8c4dbbf916314f6047dc05e6a2217074238347fd"
+  integrity sha512-9d0KQCRM8clMPcDwo8SevNs+/9a8yWVVmaE80FGJcEP8N1qToREmWEGnBn8BUlJhYRFz6fqxeRL1sl5Ogsed7A==
   dependencies:
     "@babel/helper-annotate-as-pure" "^7.10.4"
     "@babel/helper-wrap-function" "^7.10.4"
-    "@babel/template" "^7.10.4"
-    "@babel/types" "^7.10.4"
+    "@babel/types" "^7.12.1"
 
-"@babel/helper-replace-supers@^7.10.4":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.10.4.tgz#d585cd9388ea06e6031e4cd44b6713cbead9e6cf"
-  integrity sha512-sPxZfFXocEymYTdVK1UNmFPBN+Hv5mJkLPsYWwGBxZAxaWfFu+xqp7b6qWD0yjNuNL2VKc6L5M18tOXUP7NU0A==
+"@babel/helper-replace-supers@^7.12.1":
+  version "7.12.11"
+  resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.12.11.tgz#ea511658fc66c7908f923106dd88e08d1997d60d"
+  integrity sha512-q+w1cqmhL7R0FNzth/PLLp2N+scXEK/L2AHbXUyydxp828F4FEa5WcVoqui9vFRiHDQErj9Zof8azP32uGVTRA==
   dependencies:
-    "@babel/helper-member-expression-to-functions" "^7.10.4"
-    "@babel/helper-optimise-call-expression" "^7.10.4"
-    "@babel/traverse" "^7.10.4"
-    "@babel/types" "^7.10.4"
+    "@babel/helper-member-expression-to-functions" "^7.12.7"
+    "@babel/helper-optimise-call-expression" "^7.12.10"
+    "@babel/traverse" "^7.12.10"
+    "@babel/types" "^7.12.11"
 
-"@babel/helper-simple-access@^7.10.4":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.10.4.tgz#0f5ccda2945277a2a7a2d3a821e15395edcf3461"
-  integrity sha512-0fMy72ej/VEvF8ULmX6yb5MtHG4uH4Dbd6I/aHDb/JVg0bbivwt9Wg+h3uMvX+QSFtwr5MeItvazbrc4jtRAXw==
+"@babel/helper-simple-access@^7.12.1":
+  version "7.12.1"
+  resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz#32427e5aa61547d38eb1e6eaf5fd1426fdad9136"
+  integrity sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA==
   dependencies:
-    "@babel/template" "^7.10.4"
-    "@babel/types" "^7.10.4"
+    "@babel/types" "^7.12.1"
 
-"@babel/helper-skip-transparent-expression-wrappers@^7.11.0":
-  version "7.11.0"
-  resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.11.0.tgz#eec162f112c2f58d3af0af125e3bb57665146729"
-  integrity sha512-0XIdiQln4Elglgjbwo9wuJpL/K7AGCY26kmEt0+pRP0TAj4jjyNq1MjoRvikrTVqKcx4Gysxt4cXvVFXP/JO2Q==
+"@babel/helper-skip-transparent-expression-wrappers@^7.12.1":
+  version "7.12.1"
+  resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.12.1.tgz#462dc63a7e435ade8468385c63d2b84cce4b3cbf"
+  integrity sha512-Mf5AUuhG1/OCChOJ/HcADmvcHM42WJockombn8ATJG3OnyiSxBK/Mm5x78BQWvmtXZKHgbjdGL2kin/HOLlZGA==
   dependencies:
-    "@babel/types" "^7.11.0"
+    "@babel/types" "^7.12.1"
 
-"@babel/helper-split-export-declaration@^7.10.4", "@babel/helper-split-export-declaration@^7.11.0":
-  version "7.11.0"
-  resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz#f8a491244acf6a676158ac42072911ba83ad099f"
-  integrity sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==
+"@babel/helper-split-export-declaration@^7.10.4", "@babel/helper-split-export-declaration@^7.11.0", "@babel/helper-split-export-declaration@^7.12.11":
+  version "7.12.11"
+  resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.11.tgz#1b4cc424458643c47d37022223da33d76ea4603a"
+  integrity sha512-LsIVN8j48gHgwzfocYUSkO/hjYAOJqlpJEc7tGXcIm4cubjVUf8LGW6eWRyxEu7gA25q02p0rQUWoCI33HNS5g==
   dependencies:
-    "@babel/types" "^7.11.0"
+    "@babel/types" "^7.12.11"
 
-"@babel/helper-validator-identifier@^7.10.4":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2"
-  integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==
+"@babel/helper-validator-identifier@^7.10.4", "@babel/helper-validator-identifier@^7.12.11":
+  version "7.12.11"
+  resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz#c9a1f021917dcb5ccf0d4e453e399022981fc9ed"
+  integrity sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==
+
+"@babel/helper-validator-option@^7.12.1", "@babel/helper-validator-option@^7.12.11":
+  version "7.12.11"
+  resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.12.11.tgz#d66cb8b7a3e7fe4c6962b32020a131ecf0847f4f"
+  integrity sha512-TBFCyj939mFSdeX7U7DDj32WtzYY7fDcalgq8v3fBZMNOJQNn7nOYzMaUCiPxPYfCup69mtIpqlKgMZLvQ8Xhw==
 
 "@babel/helper-wrap-function@^7.10.4":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.10.4.tgz#8a6f701eab0ff39f765b5a1cfef409990e624b87"
-  integrity sha512-6py45WvEF0MhiLrdxtRjKjufwLL1/ob2qDJgg5JgNdojBAZSAKnAjkyOCNug6n+OBl4VW76XjvgSFTdaMcW0Ug==
+  version "7.12.3"
+  resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.12.3.tgz#3332339fc4d1fbbf1c27d7958c27d34708e990d9"
+  integrity sha512-Cvb8IuJDln3rs6tzjW3Y8UeelAOdnpB8xtQ4sme2MSZ9wOxrbThporC0y/EtE16VAtoyEfLM404Xr1e0OOp+ow==
   dependencies:
     "@babel/helper-function-name" "^7.10.4"
     "@babel/template" "^7.10.4"
     "@babel/traverse" "^7.10.4"
     "@babel/types" "^7.10.4"
 
-"@babel/helpers@^7.10.4", "@babel/helpers@^7.6.2":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.10.4.tgz#2abeb0d721aff7c0a97376b9e1f6f65d7a475044"
-  integrity sha512-L2gX/XeUONeEbI78dXSrJzGdz4GQ+ZTA/aazfUsFaWjSe95kiCuOZ5HsXvkiw3iwF+mFHSRUfJU8t6YavocdXA==
+"@babel/helpers@^7.12.5":
+  version "7.12.5"
+  resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.12.5.tgz#1a1ba4a768d9b58310eda516c449913fe647116e"
+  integrity sha512-lgKGMQlKqA8meJqKsW6rUnc4MdUk35Ln0ATDqdM1a/UpARODdI4j5Y5lVfUScnSNkJcdCRAaWkspykNoFg9sJA==
   dependencies:
     "@babel/template" "^7.10.4"
-    "@babel/traverse" "^7.10.4"
-    "@babel/types" "^7.10.4"
+    "@babel/traverse" "^7.12.5"
+    "@babel/types" "^7.12.5"
 
 "@babel/highlight@^7.10.4":
   version "7.10.4"
@@ -273,514 +246,735 @@
     chalk "^2.0.0"
     js-tokens "^4.0.0"
 
-"@babel/parser@^7.10.4", "@babel/parser@^7.11.5", "@babel/parser@^7.6.4":
-  version "7.11.5"
-  resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.11.5.tgz#c7ff6303df71080ec7a4f5b8c003c58f1cf51037"
-  integrity sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q==
+"@babel/parser@^7.12.10", "@babel/parser@^7.12.11", "@babel/parser@^7.12.7", "@babel/parser@^7.7.0":
+  version "7.12.11"
+  resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.12.11.tgz#9ce3595bcd74bc5c466905e86c535b8b25011e79"
+  integrity sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg==
 
-"@babel/plugin-proposal-async-generator-functions@^7.2.0":
-  version "7.10.5"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.10.5.tgz#3491cabf2f7c179ab820606cec27fed15e0e8558"
-  integrity sha512-cNMCVezQbrRGvXJwm9fu/1sJj9bHdGAgKodZdLqOQIpfoH3raqmRPBM17+lh7CzhiKRRBrGtZL9WcjxSoGYUSg==
+"@babel/plugin-proposal-async-generator-functions@^7.12.1":
+  version "7.12.12"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.12.12.tgz#04b8f24fd4532008ab4e79f788468fd5a8476566"
+  integrity sha512-nrz9y0a4xmUrRq51bYkWJIO5SBZyG2ys2qinHsN0zHDHVsUaModrkpyWWWXfGqYQmOL3x9sQIcTNN/pBGpo09A==
   dependencies:
     "@babel/helper-plugin-utils" "^7.10.4"
-    "@babel/helper-remap-async-to-generator" "^7.10.4"
+    "@babel/helper-remap-async-to-generator" "^7.12.1"
     "@babel/plugin-syntax-async-generators" "^7.8.0"
 
-"@babel/plugin-proposal-class-properties@^7.10.4":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.10.4.tgz#a33bf632da390a59c7a8c570045d1115cd778807"
-  integrity sha512-vhwkEROxzcHGNu2mzUC0OFFNXdZ4M23ib8aRRcJSsW8BZK9pQMD7QB7csl97NBbgGZO7ZyHUyKDnxzOaP4IrCg==
+"@babel/plugin-proposal-class-properties@^7.10.4", "@babel/plugin-proposal-class-properties@^7.12.1":
+  version "7.12.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.12.1.tgz#a082ff541f2a29a4821065b8add9346c0c16e5de"
+  integrity sha512-cKp3dlQsFsEs5CWKnN7BnSHOd0EOW8EKpEjkoz1pO2E5KzIDNV9Ros1b0CnmbVgAGXJubOYVBOGCT1OmJwOI7w==
   dependencies:
-    "@babel/helper-create-class-features-plugin" "^7.10.4"
+    "@babel/helper-create-class-features-plugin" "^7.12.1"
     "@babel/helper-plugin-utils" "^7.10.4"
 
-"@babel/plugin-proposal-dynamic-import@^7.5.0":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.10.4.tgz#ba57a26cb98b37741e9d5bca1b8b0ddf8291f17e"
-  integrity sha512-up6oID1LeidOOASNXgv/CFbgBqTuKJ0cJjz6An5tWD+NVBNlp3VNSBxv2ZdU7SYl3NxJC7agAQDApZusV6uFwQ==
+"@babel/plugin-proposal-dynamic-import@^7.12.1":
+  version "7.12.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.12.1.tgz#43eb5c2a3487ecd98c5c8ea8b5fdb69a2749b2dc"
+  integrity sha512-a4rhUSZFuq5W8/OO8H7BL5zspjnc1FLd9hlOxIK/f7qG4a0qsqk8uvF/ywgBA8/OmjsapjpvaEOYItfGG1qIvQ==
   dependencies:
     "@babel/helper-plugin-utils" "^7.10.4"
     "@babel/plugin-syntax-dynamic-import" "^7.8.0"
 
-"@babel/plugin-proposal-json-strings@^7.2.0":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.10.4.tgz#593e59c63528160233bd321b1aebe0820c2341db"
-  integrity sha512-fCL7QF0Jo83uy1K0P2YXrfX11tj3lkpN7l4dMv9Y9VkowkhkQDwFHFd8IiwyK5MZjE8UpbgokkgtcReH88Abaw==
+"@babel/plugin-proposal-export-namespace-from@^7.12.1":
+  version "7.12.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.12.1.tgz#8b9b8f376b2d88f5dd774e4d24a5cc2e3679b6d4"
+  integrity sha512-6CThGf0irEkzujYS5LQcjBx8j/4aQGiVv7J9+2f7pGfxqyKh3WnmVJYW3hdrQjyksErMGBPQrCnHfOtna+WLbw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.4"
+    "@babel/plugin-syntax-export-namespace-from" "^7.8.3"
+
+"@babel/plugin-proposal-json-strings@^7.12.1":
+  version "7.12.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.12.1.tgz#d45423b517714eedd5621a9dfdc03fa9f4eb241c"
+  integrity sha512-GoLDUi6U9ZLzlSda2Df++VSqDJg3CG+dR0+iWsv6XRw1rEq+zwt4DirM9yrxW6XWaTpmai1cWJLMfM8qQJf+yw==
   dependencies:
     "@babel/helper-plugin-utils" "^7.10.4"
     "@babel/plugin-syntax-json-strings" "^7.8.0"
 
-"@babel/plugin-proposal-object-rest-spread@^7.6.2":
-  version "7.11.0"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.11.0.tgz#bd81f95a1f746760ea43b6c2d3d62b11790ad0af"
-  integrity sha512-wzch41N4yztwoRw0ak+37wxwJM2oiIiy6huGCoqkvSTA9acYWcPfn9Y4aJqmFFJ70KTJUu29f3DQ43uJ9HXzEA==
+"@babel/plugin-proposal-logical-assignment-operators@^7.12.1":
+  version "7.12.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.12.1.tgz#f2c490d36e1b3c9659241034a5d2cd50263a2751"
+  integrity sha512-k8ZmVv0JU+4gcUGeCDZOGd0lCIamU/sMtIiX3UWnUc5yzgq6YUGyEolNYD+MLYKfSzgECPcqetVcJP9Afe/aCA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.4"
+    "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4"
+
+"@babel/plugin-proposal-nullish-coalescing-operator@^7.12.1":
+  version "7.12.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.12.1.tgz#3ed4fff31c015e7f3f1467f190dbe545cd7b046c"
+  integrity sha512-nZY0ESiaQDI1y96+jk6VxMOaL4LPo/QDHBqL+SF3/vl6dHkTwHlOI8L4ZwuRBHgakRBw5zsVylel7QPbbGuYgg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.4"
+    "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0"
+
+"@babel/plugin-proposal-numeric-separator@^7.12.7":
+  version "7.12.7"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.12.7.tgz#8bf253de8139099fea193b297d23a9d406ef056b"
+  integrity sha512-8c+uy0qmnRTeukiGsjLGy6uVs/TFjJchGXUeBqlG4VWYOdJWkhhVPdQ3uHwbmalfJwv2JsV0qffXP4asRfL2SQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.4"
+    "@babel/plugin-syntax-numeric-separator" "^7.10.4"
+
+"@babel/plugin-proposal-object-rest-spread@^7.12.1", "@babel/plugin-proposal-object-rest-spread@^7.9.6":
+  version "7.12.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.12.1.tgz#def9bd03cea0f9b72283dac0ec22d289c7691069"
+  integrity sha512-s6SowJIjzlhx8o7lsFx5zmY4At6CTtDvgNQDdPzkBQucle58A6b/TTeEBYtyDgmcXjUTM+vE8YOGHZzzbc/ioA==
   dependencies:
     "@babel/helper-plugin-utils" "^7.10.4"
     "@babel/plugin-syntax-object-rest-spread" "^7.8.0"
-    "@babel/plugin-transform-parameters" "^7.10.4"
+    "@babel/plugin-transform-parameters" "^7.12.1"
 
-"@babel/plugin-proposal-optional-catch-binding@^7.2.0":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.10.4.tgz#31c938309d24a78a49d68fdabffaa863758554dd"
-  integrity sha512-LflT6nPh+GK2MnFiKDyLiqSqVHkQnVf7hdoAvyTnnKj9xB3docGRsdPuxp6qqqW19ifK3xgc9U5/FwrSaCNX5g==
+"@babel/plugin-proposal-optional-catch-binding@^7.12.1":
+  version "7.12.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.12.1.tgz#ccc2421af64d3aae50b558a71cede929a5ab2942"
+  integrity sha512-hFvIjgprh9mMw5v42sJWLI1lzU5L2sznP805zeT6rySVRA0Y18StRhDqhSxlap0oVgItRsB6WSROp4YnJTJz0g==
   dependencies:
     "@babel/helper-plugin-utils" "^7.10.4"
     "@babel/plugin-syntax-optional-catch-binding" "^7.8.0"
 
-"@babel/plugin-proposal-unicode-property-regex@^7.6.2":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.10.4.tgz#4483cda53041ce3413b7fe2f00022665ddfaa75d"
-  integrity sha512-H+3fOgPnEXFL9zGYtKQe4IDOPKYlZdF1kqFDQRRb8PK4B8af1vAGK04tF5iQAAsui+mHNBQSAtd2/ndEDe9wuA==
+"@babel/plugin-proposal-optional-chaining@^7.12.7":
+  version "7.12.7"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.12.7.tgz#e02f0ea1b5dc59d401ec16fb824679f683d3303c"
+  integrity sha512-4ovylXZ0PWmwoOvhU2vhnzVNnm88/Sm9nx7V8BPgMvAzn5zDou3/Awy0EjglyubVHasJj+XCEkr/r1X3P5elCA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.4"
+    "@babel/helper-skip-transparent-expression-wrappers" "^7.12.1"
+    "@babel/plugin-syntax-optional-chaining" "^7.8.0"
+
+"@babel/plugin-proposal-private-methods@^7.12.1":
+  version "7.12.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.12.1.tgz#86814f6e7a21374c980c10d38b4493e703f4a389"
+  integrity sha512-mwZ1phvH7/NHK6Kf8LP7MYDogGV+DKB1mryFOEwx5EBNQrosvIczzZFTUmWaeujd5xT6G1ELYWUz3CutMhjE1w==
   dependencies:
-    "@babel/helper-create-regexp-features-plugin" "^7.10.4"
+    "@babel/helper-create-class-features-plugin" "^7.12.1"
     "@babel/helper-plugin-utils" "^7.10.4"
 
-"@babel/plugin-syntax-async-generators@^7.2.0", "@babel/plugin-syntax-async-generators@^7.8.0":
+"@babel/plugin-proposal-unicode-property-regex@^7.12.1", "@babel/plugin-proposal-unicode-property-regex@^7.4.4":
+  version "7.12.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.12.1.tgz#2a183958d417765b9eae334f47758e5d6a82e072"
+  integrity sha512-MYq+l+PvHuw/rKUz1at/vb6nCnQ2gmJBNaM62z0OgH7B2W1D9pvkpYtlti9bGtizNIU1K3zm4bZF9F91efVY0w==
+  dependencies:
+    "@babel/helper-create-regexp-features-plugin" "^7.12.1"
+    "@babel/helper-plugin-utils" "^7.10.4"
+
+"@babel/plugin-syntax-async-generators@^7.8.0":
   version "7.8.4"
   resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d"
   integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==
   dependencies:
     "@babel/helper-plugin-utils" "^7.8.0"
 
-"@babel/plugin-syntax-dynamic-import@^7.2.0", "@babel/plugin-syntax-dynamic-import@^7.8.0":
+"@babel/plugin-syntax-class-properties@^7.12.1":
+  version "7.12.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.1.tgz#bcb297c5366e79bebadef509549cd93b04f19978"
+  integrity sha512-U40A76x5gTwmESz+qiqssqmeEsKvcSyvtgktrm0uzcARAmM9I1jR221f6Oq+GmHrcD+LvZDag1UTOTe2fL3TeA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.4"
+
+"@babel/plugin-syntax-dynamic-import@^7.8.0":
   version "7.8.3"
   resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3"
   integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==
   dependencies:
     "@babel/helper-plugin-utils" "^7.8.0"
 
-"@babel/plugin-syntax-json-strings@^7.2.0", "@babel/plugin-syntax-json-strings@^7.8.0":
+"@babel/plugin-syntax-export-namespace-from@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz#028964a9ba80dbc094c915c487ad7c4e7a66465a"
+  integrity sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-syntax-json-strings@^7.8.0":
   version "7.8.3"
   resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a"
   integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==
   dependencies:
     "@babel/helper-plugin-utils" "^7.8.0"
 
-"@babel/plugin-syntax-jsx@^7.10.4":
+"@babel/plugin-syntax-jsx@^7.12.1":
+  version "7.12.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.1.tgz#9d9d357cc818aa7ae7935917c1257f67677a0926"
+  integrity sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.4"
+
+"@babel/plugin-syntax-logical-assignment-operators@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699"
+  integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.4"
+
+"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.0":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9"
+  integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-numeric-separator@^7.10.4":
   version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.10.4.tgz#39abaae3cbf710c4373d8429484e6ba21340166c"
-  integrity sha512-KCg9mio9jwiARCB7WAcQ7Y1q+qicILjoK8LP/VkPkEKaf5dkaZZK1EcTe91a3JJlZ3qy6L5s9X52boEYi8DM9g==
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97"
+  integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==
   dependencies:
     "@babel/helper-plugin-utils" "^7.10.4"
 
-"@babel/plugin-syntax-object-rest-spread@^7.2.0", "@babel/plugin-syntax-object-rest-spread@^7.8.0":
+"@babel/plugin-syntax-object-rest-spread@^7.8.0":
   version "7.8.3"
   resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871"
   integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==
   dependencies:
     "@babel/helper-plugin-utils" "^7.8.0"
 
-"@babel/plugin-syntax-optional-catch-binding@^7.2.0", "@babel/plugin-syntax-optional-catch-binding@^7.8.0":
+"@babel/plugin-syntax-optional-catch-binding@^7.8.0":
   version "7.8.3"
   resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1"
   integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==
   dependencies:
     "@babel/helper-plugin-utils" "^7.8.0"
 
-"@babel/plugin-transform-arrow-functions@^7.2.0":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.10.4.tgz#e22960d77e697c74f41c501d44d73dbf8a6a64cd"
-  integrity sha512-9J/oD1jV0ZCBcgnoFWFq1vJd4msoKb/TCpGNFyyLt0zABdcvgK3aYikZ8HjzB14c26bc7E3Q1yugpwGy2aTPNA==
+"@babel/plugin-syntax-optional-chaining@^7.8.0":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a"
+  integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-top-level-await@^7.12.1":
+  version "7.12.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.1.tgz#dd6c0b357ac1bb142d98537450a319625d13d2a0"
+  integrity sha512-i7ooMZFS+a/Om0crxZodrTzNEPJHZrlMVGMTEpFAj6rYY/bKCddB0Dk/YxfPuYXOopuhKk/e1jV6h+WUU9XN3A==
   dependencies:
     "@babel/helper-plugin-utils" "^7.10.4"
 
-"@babel/plugin-transform-async-to-generator@^7.5.0":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.10.4.tgz#41a5017e49eb6f3cda9392a51eef29405b245a37"
-  integrity sha512-F6nREOan7J5UXTLsDsZG3DXmZSVofr2tGNwfdrVwkDWHfQckbQXnXSPfD7iO+c/2HGqycwyLST3DnZ16n+cBJQ==
+"@babel/plugin-transform-arrow-functions@^7.12.1":
+  version "7.12.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.12.1.tgz#8083ffc86ac8e777fbe24b5967c4b2521f3cb2b3"
+  integrity sha512-5QB50qyN44fzzz4/qxDPQMBCTHgxg3n0xRBLJUmBlLoU/sFvxVWGZF/ZUfMVDQuJUKXaBhbupxIzIfZ6Fwk/0A==
   dependencies:
-    "@babel/helper-module-imports" "^7.10.4"
     "@babel/helper-plugin-utils" "^7.10.4"
-    "@babel/helper-remap-async-to-generator" "^7.10.4"
 
-"@babel/plugin-transform-block-scoped-functions@^7.2.0":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.10.4.tgz#1afa595744f75e43a91af73b0d998ecfe4ebc2e8"
-  integrity sha512-WzXDarQXYYfjaV1szJvN3AD7rZgZzC1JtjJZ8dMHUyiK8mxPRahynp14zzNjU3VkPqPsO38CzxiWO1c9ARZ8JA==
+"@babel/plugin-transform-async-to-generator@^7.12.1":
+  version "7.12.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.12.1.tgz#3849a49cc2a22e9743cbd6b52926d30337229af1"
+  integrity sha512-SDtqoEcarK1DFlRJ1hHRY5HvJUj5kX4qmtpMAm2QnhOlyuMC4TMdCRgW6WXpv93rZeYNeLP22y8Aq2dbcDRM1A==
   dependencies:
+    "@babel/helper-module-imports" "^7.12.1"
     "@babel/helper-plugin-utils" "^7.10.4"
+    "@babel/helper-remap-async-to-generator" "^7.12.1"
 
-"@babel/plugin-transform-block-scoping@^7.6.3":
-  version "7.11.1"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.11.1.tgz#5b7efe98852bef8d652c0b28144cd93a9e4b5215"
-  integrity sha512-00dYeDE0EVEHuuM+26+0w/SCL0BH2Qy7LwHuI4Hi4MH5gkC8/AqMN5uWFJIsoXZrAphiMm1iXzBw6L2T+eA0ew==
+"@babel/plugin-transform-block-scoped-functions@^7.12.1":
+  version "7.12.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.12.1.tgz#f2a1a365bde2b7112e0a6ded9067fdd7c07905d9"
+  integrity sha512-5OpxfuYnSgPalRpo8EWGPzIYf0lHBWORCkj5M0oLBwHdlux9Ri36QqGW3/LR13RSVOAoUUMzoPI/jpE4ABcHoA==
   dependencies:
     "@babel/helper-plugin-utils" "^7.10.4"
 
-"@babel/plugin-transform-classes@^7.5.5":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.10.4.tgz#405136af2b3e218bc4a1926228bc917ab1a0adc7"
-  integrity sha512-2oZ9qLjt161dn1ZE0Ms66xBncQH4In8Sqw1YWgBUZuGVJJS5c0OFZXL6dP2MRHrkU/eKhWg8CzFJhRQl50rQxA==
+"@babel/plugin-transform-block-scoping@^7.12.11":
+  version "7.12.12"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.12.12.tgz#d93a567a152c22aea3b1929bb118d1d0a175cdca"
+  integrity sha512-VOEPQ/ExOVqbukuP7BYJtI5ZxxsmegTwzZ04j1aF0dkSypGo9XpDHuOrABsJu+ie+penpSJheDJ11x1BEZNiyQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.4"
+
+"@babel/plugin-transform-classes@^7.12.1":
+  version "7.12.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.12.1.tgz#65e650fcaddd3d88ddce67c0f834a3d436a32db6"
+  integrity sha512-/74xkA7bVdzQTBeSUhLLJgYIcxw/dpEpCdRDiHgPJ3Mv6uC11UhjpOhl72CgqbBCmt1qtssCyB2xnJm1+PFjog==
   dependencies:
     "@babel/helper-annotate-as-pure" "^7.10.4"
     "@babel/helper-define-map" "^7.10.4"
     "@babel/helper-function-name" "^7.10.4"
     "@babel/helper-optimise-call-expression" "^7.10.4"
     "@babel/helper-plugin-utils" "^7.10.4"
-    "@babel/helper-replace-supers" "^7.10.4"
+    "@babel/helper-replace-supers" "^7.12.1"
     "@babel/helper-split-export-declaration" "^7.10.4"
     globals "^11.1.0"
 
-"@babel/plugin-transform-computed-properties@^7.2.0":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.10.4.tgz#9ded83a816e82ded28d52d4b4ecbdd810cdfc0eb"
-  integrity sha512-JFwVDXcP/hM/TbyzGq3l/XWGut7p46Z3QvqFMXTfk6/09m7xZHJUN9xHfsv7vqqD4YnfI5ueYdSJtXqqBLyjBw==
+"@babel/plugin-transform-computed-properties@^7.12.1":
+  version "7.12.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.12.1.tgz#d68cf6c9b7f838a8a4144badbe97541ea0904852"
+  integrity sha512-vVUOYpPWB7BkgUWPo4C44mUQHpTZXakEqFjbv8rQMg7TC6S6ZhGZ3otQcRH6u7+adSlE5i0sp63eMC/XGffrzg==
   dependencies:
     "@babel/helper-plugin-utils" "^7.10.4"
 
-"@babel/plugin-transform-destructuring@^7.6.0":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.10.4.tgz#70ddd2b3d1bea83d01509e9bb25ddb3a74fc85e5"
-  integrity sha512-+WmfvyfsyF603iPa6825mq6Qrb7uLjTOsa3XOFzlYcYDHSS4QmpOWOL0NNBY5qMbvrcf3tq0Cw+v4lxswOBpgA==
+"@babel/plugin-transform-destructuring@^7.12.1":
+  version "7.12.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.12.1.tgz#b9a570fe0d0a8d460116413cb4f97e8e08b2f847"
+  integrity sha512-fRMYFKuzi/rSiYb2uRLiUENJOKq4Gnl+6qOv5f8z0TZXg3llUwUhsNNwrwaT/6dUhJTzNpBr+CUvEWBtfNY1cw==
   dependencies:
     "@babel/helper-plugin-utils" "^7.10.4"
 
-"@babel/plugin-transform-dotall-regex@^7.6.2":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.10.4.tgz#469c2062105c1eb6a040eaf4fac4b488078395ee"
-  integrity sha512-ZEAVvUTCMlMFAbASYSVQoxIbHm2OkG2MseW6bV2JjIygOjdVv8tuxrCTzj1+Rynh7ODb8GivUy7dzEXzEhuPaA==
+"@babel/plugin-transform-dotall-regex@^7.12.1", "@babel/plugin-transform-dotall-regex@^7.4.4":
+  version "7.12.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.12.1.tgz#a1d16c14862817b6409c0a678d6f9373ca9cd975"
+  integrity sha512-B2pXeRKoLszfEW7J4Hg9LoFaWEbr/kzo3teWHmtFCszjRNa/b40f9mfeqZsIDLLt/FjwQ6pz/Gdlwy85xNckBA==
   dependencies:
-    "@babel/helper-create-regexp-features-plugin" "^7.10.4"
+    "@babel/helper-create-regexp-features-plugin" "^7.12.1"
     "@babel/helper-plugin-utils" "^7.10.4"
 
-"@babel/plugin-transform-duplicate-keys@^7.5.0":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.10.4.tgz#697e50c9fee14380fe843d1f306b295617431e47"
-  integrity sha512-GL0/fJnmgMclHiBTTWXNlYjYsA7rDrtsazHG6mglaGSTh0KsrW04qml+Bbz9FL0LcJIRwBWL5ZqlNHKTkU3xAA==
+"@babel/plugin-transform-duplicate-keys@^7.12.1":
+  version "7.12.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.12.1.tgz#745661baba295ac06e686822797a69fbaa2ca228"
+  integrity sha512-iRght0T0HztAb/CazveUpUQrZY+aGKKaWXMJ4uf9YJtqxSUe09j3wteztCUDRHs+SRAL7yMuFqUsLoAKKzgXjw==
   dependencies:
     "@babel/helper-plugin-utils" "^7.10.4"
 
-"@babel/plugin-transform-exponentiation-operator@^7.2.0":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.10.4.tgz#5ae338c57f8cf4001bdb35607ae66b92d665af2e"
-  integrity sha512-S5HgLVgkBcRdyQAHbKj+7KyuWx8C6t5oETmUuwz1pt3WTWJhsUV0WIIXuVvfXMxl/QQyHKlSCNNtaIamG8fysw==
+"@babel/plugin-transform-exponentiation-operator@^7.12.1":
+  version "7.12.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.12.1.tgz#b0f2ed356ba1be1428ecaf128ff8a24f02830ae0"
+  integrity sha512-7tqwy2bv48q+c1EHbXK0Zx3KXd2RVQp6OC7PbwFNt/dPTAV3Lu5sWtWuAj8owr5wqtWnqHfl2/mJlUmqkChKug==
   dependencies:
     "@babel/helper-builder-binary-assignment-operator-visitor" "^7.10.4"
     "@babel/helper-plugin-utils" "^7.10.4"
 
-"@babel/plugin-transform-for-of@^7.4.4":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.10.4.tgz#c08892e8819d3a5db29031b115af511dbbfebae9"
-  integrity sha512-ItdQfAzu9AlEqmusA/65TqJ79eRcgGmpPPFvBnGILXZH975G0LNjP1yjHvGgfuCxqrPPueXOPe+FsvxmxKiHHQ==
+"@babel/plugin-transform-for-of@^7.12.1":
+  version "7.12.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.12.1.tgz#07640f28867ed16f9511c99c888291f560921cfa"
+  integrity sha512-Zaeq10naAsuHo7heQvyV0ptj4dlZJwZgNAtBYBnu5nNKJoW62m0zKcIEyVECrUKErkUkg6ajMy4ZfnVZciSBhg==
   dependencies:
     "@babel/helper-plugin-utils" "^7.10.4"
 
-"@babel/plugin-transform-function-name@^7.4.4":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.10.4.tgz#6a467880e0fc9638514ba369111811ddbe2644b7"
-  integrity sha512-OcDCq2y5+E0dVD5MagT5X+yTRbcvFjDI2ZVAottGH6tzqjx/LKpgkUepu3hp/u4tZBzxxpNGwLsAvGBvQ2mJzg==
+"@babel/plugin-transform-function-name@^7.12.1":
+  version "7.12.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.12.1.tgz#2ec76258c70fe08c6d7da154003a480620eba667"
+  integrity sha512-JF3UgJUILoFrFMEnOJLJkRHSk6LUSXLmEFsA23aR2O5CSLUxbeUX1IZ1YQ7Sn0aXb601Ncwjx73a+FVqgcljVw==
   dependencies:
     "@babel/helper-function-name" "^7.10.4"
     "@babel/helper-plugin-utils" "^7.10.4"
 
-"@babel/plugin-transform-literals@^7.2.0":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.10.4.tgz#9f42ba0841100a135f22712d0e391c462f571f3c"
-  integrity sha512-Xd/dFSTEVuUWnyZiMu76/InZxLTYilOSr1UlHV+p115Z/Le2Fi1KXkJUYz0b42DfndostYlPub3m8ZTQlMaiqQ==
+"@babel/plugin-transform-literals@^7.12.1":
+  version "7.12.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.12.1.tgz#d73b803a26b37017ddf9d3bb8f4dc58bfb806f57"
+  integrity sha512-+PxVGA+2Ag6uGgL0A5f+9rklOnnMccwEBzwYFL3EUaKuiyVnUipyXncFcfjSkbimLrODoqki1U9XxZzTvfN7IQ==
   dependencies:
     "@babel/helper-plugin-utils" "^7.10.4"
 
-"@babel/plugin-transform-member-expression-literals@^7.2.0":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.10.4.tgz#b1ec44fcf195afcb8db2c62cd8e551c881baf8b7"
-  integrity sha512-0bFOvPyAoTBhtcJLr9VcwZqKmSjFml1iVxvPL0ReomGU53CX53HsM4h2SzckNdkQcHox1bpAqzxBI1Y09LlBSw==
+"@babel/plugin-transform-member-expression-literals@^7.12.1":
+  version "7.12.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.12.1.tgz#496038602daf1514a64d43d8e17cbb2755e0c3ad"
+  integrity sha512-1sxePl6z9ad0gFMB9KqmYofk34flq62aqMt9NqliS/7hPEpURUCMbyHXrMPlo282iY7nAvUB1aQd5mg79UD9Jg==
   dependencies:
     "@babel/helper-plugin-utils" "^7.10.4"
 
-"@babel/plugin-transform-modules-amd@^7.5.0":
-  version "7.10.5"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.10.5.tgz#1b9cddaf05d9e88b3aad339cb3e445c4f020a9b1"
-  integrity sha512-elm5uruNio7CTLFItVC/rIzKLfQ17+fX7EVz5W0TMgIHFo1zY0Ozzx+lgwhL4plzl8OzVn6Qasx5DeEFyoNiRw==
+"@babel/plugin-transform-modules-amd@^7.12.1":
+  version "7.12.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.12.1.tgz#3154300b026185666eebb0c0ed7f8415fefcf6f9"
+  integrity sha512-tDW8hMkzad5oDtzsB70HIQQRBiTKrhfgwC/KkJeGsaNFTdWhKNt/BiE8c5yj19XiGyrxpbkOfH87qkNg1YGlOQ==
   dependencies:
-    "@babel/helper-module-transforms" "^7.10.5"
+    "@babel/helper-module-transforms" "^7.12.1"
     "@babel/helper-plugin-utils" "^7.10.4"
     babel-plugin-dynamic-import-node "^2.3.3"
 
-"@babel/plugin-transform-modules-commonjs@^7.6.0":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.10.4.tgz#66667c3eeda1ebf7896d41f1f16b17105a2fbca0"
-  integrity sha512-Xj7Uq5o80HDLlW64rVfDBhao6OX89HKUmb+9vWYaLXBZOma4gA6tw4Ni1O5qVDoZWUV0fxMYA0aYzOawz0l+1w==
+"@babel/plugin-transform-modules-commonjs@^7.12.1":
+  version "7.12.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.12.1.tgz#fa403124542636c786cf9b460a0ffbb48a86e648"
+  integrity sha512-dY789wq6l0uLY8py9c1B48V8mVL5gZh/+PQ5ZPrylPYsnAvnEMjqsUXkuoDVPeVK+0VyGar+D08107LzDQ6pag==
   dependencies:
-    "@babel/helper-module-transforms" "^7.10.4"
+    "@babel/helper-module-transforms" "^7.12.1"
     "@babel/helper-plugin-utils" "^7.10.4"
-    "@babel/helper-simple-access" "^7.10.4"
+    "@babel/helper-simple-access" "^7.12.1"
     babel-plugin-dynamic-import-node "^2.3.3"
 
-"@babel/plugin-transform-modules-systemjs@^7.5.0":
-  version "7.10.5"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.10.5.tgz#6270099c854066681bae9e05f87e1b9cadbe8c85"
-  integrity sha512-f4RLO/OL14/FP1AEbcsWMzpbUz6tssRaeQg11RH1BP/XnPpRoVwgeYViMFacnkaw4k4wjRSjn3ip1Uw9TaXuMw==
+"@babel/plugin-transform-modules-systemjs@^7.12.1":
+  version "7.12.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.12.1.tgz#663fea620d593c93f214a464cd399bf6dc683086"
+  integrity sha512-Hn7cVvOavVh8yvW6fLwveFqSnd7rbQN3zJvoPNyNaQSvgfKmDBO9U1YL9+PCXGRlZD9tNdWTy5ACKqMuzyn32Q==
   dependencies:
     "@babel/helper-hoist-variables" "^7.10.4"
-    "@babel/helper-module-transforms" "^7.10.5"
+    "@babel/helper-module-transforms" "^7.12.1"
     "@babel/helper-plugin-utils" "^7.10.4"
+    "@babel/helper-validator-identifier" "^7.10.4"
     babel-plugin-dynamic-import-node "^2.3.3"
 
-"@babel/plugin-transform-modules-umd@^7.2.0":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.10.4.tgz#9a8481fe81b824654b3a0b65da3df89f3d21839e"
-  integrity sha512-mohW5q3uAEt8T45YT7Qc5ws6mWgJAaL/8BfWD9Dodo1A3RKWli8wTS+WiQ/knF+tXlPirW/1/MqzzGfCExKECA==
+"@babel/plugin-transform-modules-umd@^7.12.1":
+  version "7.12.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.12.1.tgz#eb5a218d6b1c68f3d6217b8fa2cc82fec6547902"
+  integrity sha512-aEIubCS0KHKM0zUos5fIoQm+AZUMt1ZvMpqz0/H5qAQ7vWylr9+PLYurT+Ic7ID/bKLd4q8hDovaG3Zch2uz5Q==
   dependencies:
-    "@babel/helper-module-transforms" "^7.10.4"
+    "@babel/helper-module-transforms" "^7.12.1"
     "@babel/helper-plugin-utils" "^7.10.4"
 
-"@babel/plugin-transform-named-capturing-groups-regex@^7.6.3":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.10.4.tgz#78b4d978810b6f3bcf03f9e318f2fc0ed41aecb6"
-  integrity sha512-V6LuOnD31kTkxQPhKiVYzYC/Jgdq53irJC/xBSmqcNcqFGV+PER4l6rU5SH2Vl7bH9mLDHcc0+l9HUOe4RNGKA==
+"@babel/plugin-transform-named-capturing-groups-regex@^7.12.1":
+  version "7.12.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.12.1.tgz#b407f5c96be0d9f5f88467497fa82b30ac3e8753"
+  integrity sha512-tB43uQ62RHcoDp9v2Nsf+dSM8sbNodbEicbQNA53zHz8pWUhsgHSJCGpt7daXxRydjb0KnfmB+ChXOv3oADp1Q==
   dependencies:
-    "@babel/helper-create-regexp-features-plugin" "^7.10.4"
+    "@babel/helper-create-regexp-features-plugin" "^7.12.1"
 
-"@babel/plugin-transform-new-target@^7.4.4":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.10.4.tgz#9097d753cb7b024cb7381a3b2e52e9513a9c6888"
-  integrity sha512-YXwWUDAH/J6dlfwqlWsztI2Puz1NtUAubXhOPLQ5gjR/qmQ5U96DY4FQO8At33JN4XPBhrjB8I4eMmLROjjLjw==
+"@babel/plugin-transform-new-target@^7.12.1":
+  version "7.12.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.12.1.tgz#80073f02ee1bb2d365c3416490e085c95759dec0"
+  integrity sha512-+eW/VLcUL5L9IvJH7rT1sT0CzkdUTvPrXC2PXTn/7z7tXLBuKvezYbGdxD5WMRoyvyaujOq2fWoKl869heKjhw==
   dependencies:
     "@babel/helper-plugin-utils" "^7.10.4"
 
-"@babel/plugin-transform-object-super@^7.5.5":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.10.4.tgz#d7146c4d139433e7a6526f888c667e314a093894"
-  integrity sha512-5iTw0JkdRdJvr7sY0vHqTpnruUpTea32JHmq/atIWqsnNussbRzjEDyWep8UNztt1B5IusBYg8Irb0bLbiEBCQ==
+"@babel/plugin-transform-object-super@^7.12.1":
+  version "7.12.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.12.1.tgz#4ea08696b8d2e65841d0c7706482b048bed1066e"
+  integrity sha512-AvypiGJH9hsquNUn+RXVcBdeE3KHPZexWRdimhuV59cSoOt5kFBmqlByorAeUlGG2CJWd0U+4ZtNKga/TB0cAw==
   dependencies:
     "@babel/helper-plugin-utils" "^7.10.4"
-    "@babel/helper-replace-supers" "^7.10.4"
+    "@babel/helper-replace-supers" "^7.12.1"
 
-"@babel/plugin-transform-parameters@^7.10.4", "@babel/plugin-transform-parameters@^7.4.4":
-  version "7.10.5"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.10.5.tgz#59d339d58d0b1950435f4043e74e2510005e2c4a"
-  integrity sha512-xPHwUj5RdFV8l1wuYiu5S9fqWGM2DrYc24TMvUiRrPVm+SM3XeqU9BcokQX/kEUe+p2RBwy+yoiR1w/Blq6ubw==
+"@babel/plugin-transform-parameters@^7.12.1":
+  version "7.12.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.12.1.tgz#d2e963b038771650c922eff593799c96d853255d"
+  integrity sha512-xq9C5EQhdPK23ZeCdMxl8bbRnAgHFrw5EOC3KJUsSylZqdkCaFEXxGSBuTSObOpiiHHNyb82es8M1QYgfQGfNg==
   dependencies:
-    "@babel/helper-get-function-arity" "^7.10.4"
     "@babel/helper-plugin-utils" "^7.10.4"
 
-"@babel/plugin-transform-property-literals@^7.2.0":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.10.4.tgz#f6fe54b6590352298785b83edd815d214c42e3c0"
-  integrity sha512-ofsAcKiUxQ8TY4sScgsGeR2vJIsfrzqvFb9GvJ5UdXDzl+MyYCaBj/FGzXuv7qE0aJcjWMILny1epqelnFlz8g==
+"@babel/plugin-transform-property-literals@^7.12.1":
+  version "7.12.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.12.1.tgz#41bc81200d730abb4456ab8b3fbd5537b59adecd"
+  integrity sha512-6MTCR/mZ1MQS+AwZLplX4cEySjCpnIF26ToWo942nqn8hXSm7McaHQNeGx/pt7suI1TWOWMfa/NgBhiqSnX0cQ==
   dependencies:
     "@babel/helper-plugin-utils" "^7.10.4"
 
-"@babel/plugin-transform-react-display-name@^7.10.4":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.10.4.tgz#b5795f4e3e3140419c3611b7a2a3832b9aef328d"
-  integrity sha512-Zd4X54Mu9SBfPGnEcaGcOrVAYOtjT2on8QZkLKEq1S/tHexG39d9XXGZv19VfRrDjPJzFmPfTAqOQS1pfFOujw==
+"@babel/plugin-transform-react-display-name@^7.12.1":
+  version "7.12.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.12.1.tgz#1cbcd0c3b1d6648c55374a22fc9b6b7e5341c00d"
+  integrity sha512-cAzB+UzBIrekfYxyLlFqf/OagTvHLcVBb5vpouzkYkBclRPraiygVnafvAoipErZLI8ANv8Ecn6E/m5qPXD26w==
   dependencies:
     "@babel/helper-plugin-utils" "^7.10.4"
 
-"@babel/plugin-transform-react-jsx-development@^7.10.4":
-  version "7.11.5"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.11.5.tgz#e1439e6a57ee3d43e9f54ace363fb29cefe5d7b6"
-  integrity sha512-cImAmIlKJ84sDmpQzm4/0q/2xrXlDezQoixy3qoz1NJeZL/8PRon6xZtluvr4H4FzwlDGI5tCcFupMnXGtr+qw==
+"@babel/plugin-transform-react-jsx-development@^7.12.7":
+  version "7.12.12"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.12.12.tgz#bccca33108fe99d95d7f9e82046bfe762e71f4e7"
+  integrity sha512-i1AxnKxHeMxUaWVXQOSIco4tvVvvCxMSfeBMnMM06mpaJt3g+MpxYQQrDfojUQldP1xxraPSJYSMEljoWM/dCg==
   dependencies:
-    "@babel/helper-builder-react-jsx-experimental" "^7.11.5"
-    "@babel/helper-plugin-utils" "^7.10.4"
-    "@babel/plugin-syntax-jsx" "^7.10.4"
+    "@babel/plugin-transform-react-jsx" "^7.12.12"
 
-"@babel/plugin-transform-react-jsx-self@^7.10.4":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.10.4.tgz#cd301a5fed8988c182ed0b9d55e9bd6db0bd9369"
-  integrity sha512-yOvxY2pDiVJi0axdTWHSMi5T0DILN+H+SaeJeACHKjQLezEzhLx9nEF9xgpBLPtkZsks9cnb5P9iBEi21En3gg==
+"@babel/plugin-transform-react-jsx@^7.12.10", "@babel/plugin-transform-react-jsx@^7.12.12":
+  version "7.12.12"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.12.12.tgz#b0da51ffe5f34b9a900e9f1f5fb814f9e512d25e"
+  integrity sha512-JDWGuzGNWscYcq8oJVCtSE61a5+XAOos+V0HrxnDieUus4UMnBEosDnY1VJqU5iZ4pA04QY7l0+JvHL1hZEfsw==
   dependencies:
+    "@babel/helper-annotate-as-pure" "^7.12.10"
+    "@babel/helper-module-imports" "^7.12.5"
     "@babel/helper-plugin-utils" "^7.10.4"
-    "@babel/plugin-syntax-jsx" "^7.10.4"
+    "@babel/plugin-syntax-jsx" "^7.12.1"
+    "@babel/types" "^7.12.12"
 
-"@babel/plugin-transform-react-jsx-source@^7.10.4":
-  version "7.10.5"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.10.5.tgz#34f1779117520a779c054f2cdd9680435b9222b4"
-  integrity sha512-wTeqHVkN1lfPLubRiZH3o73f4rfon42HpgxUSs86Nc+8QIcm/B9s8NNVXu/gwGcOyd7yDib9ikxoDLxJP0UiDA==
+"@babel/plugin-transform-react-pure-annotations@^7.12.1":
+  version "7.12.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.12.1.tgz#05d46f0ab4d1339ac59adf20a1462c91b37a1a42"
+  integrity sha512-RqeaHiwZtphSIUZ5I85PEH19LOSzxfuEazoY7/pWASCAIBuATQzpSVD+eT6MebeeZT2F4eSL0u4vw6n4Nm0Mjg==
   dependencies:
+    "@babel/helper-annotate-as-pure" "^7.10.4"
     "@babel/helper-plugin-utils" "^7.10.4"
-    "@babel/plugin-syntax-jsx" "^7.10.4"
 
-"@babel/plugin-transform-react-jsx@^7.10.4":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.10.4.tgz#673c9f913948764a4421683b2bef2936968fddf2"
-  integrity sha512-L+MfRhWjX0eI7Js093MM6MacKU4M6dnCRa/QPDwYMxjljzSCzzlzKzj9Pk4P3OtrPcxr2N3znR419nr3Xw+65A==
+"@babel/plugin-transform-regenerator@^7.12.1":
+  version "7.12.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.12.1.tgz#5f0a28d842f6462281f06a964e88ba8d7ab49753"
+  integrity sha512-gYrHqs5itw6i4PflFX3OdBPMQdPbF4bj2REIUxlMRUFk0/ZOAIpDFuViuxPjUL7YC8UPnf+XG7/utJvqXdPKng==
   dependencies:
-    "@babel/helper-builder-react-jsx" "^7.10.4"
-    "@babel/helper-builder-react-jsx-experimental" "^7.10.4"
-    "@babel/helper-plugin-utils" "^7.10.4"
-    "@babel/plugin-syntax-jsx" "^7.10.4"
+    regenerator-transform "^0.14.2"
 
-"@babel/plugin-transform-react-pure-annotations@^7.10.4":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.10.4.tgz#3eefbb73db94afbc075f097523e445354a1c6501"
-  integrity sha512-+njZkqcOuS8RaPakrnR9KvxjoG1ASJWpoIv/doyWngId88JoFlPlISenGXjrVacZUIALGUr6eodRs1vmPnF23A==
+"@babel/plugin-transform-reserved-words@^7.12.1":
+  version "7.12.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.12.1.tgz#6fdfc8cc7edcc42b36a7c12188c6787c873adcd8"
+  integrity sha512-pOnUfhyPKvZpVyBHhSBoX8vfA09b7r00Pmm1sH+29ae2hMTKVmSp4Ztsr8KBKjLjx17H0eJqaRC3bR2iThM54A==
   dependencies:
-    "@babel/helper-annotate-as-pure" "^7.10.4"
     "@babel/helper-plugin-utils" "^7.10.4"
 
-"@babel/plugin-transform-regenerator@^7.4.5":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.10.4.tgz#2015e59d839074e76838de2159db421966fd8b63"
-  integrity sha512-3thAHwtor39A7C04XucbMg17RcZ3Qppfxr22wYzZNcVIkPHfpM9J0SO8zuCV6SZa265kxBJSrfKTvDCYqBFXGw==
+"@babel/plugin-transform-shorthand-properties@^7.12.1":
+  version "7.12.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.12.1.tgz#0bf9cac5550fce0cfdf043420f661d645fdc75e3"
+  integrity sha512-GFZS3c/MhX1OusqB1MZ1ct2xRzX5ppQh2JU1h2Pnfk88HtFTM+TWQqJNfwkmxtPQtb/s1tk87oENfXJlx7rSDw==
   dependencies:
-    regenerator-transform "^0.14.2"
+    "@babel/helper-plugin-utils" "^7.10.4"
 
-"@babel/plugin-transform-reserved-words@^7.2.0":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.10.4.tgz#8f2682bcdcef9ed327e1b0861585d7013f8a54dd"
-  integrity sha512-hGsw1O6Rew1fkFbDImZIEqA8GoidwTAilwCyWqLBM9f+e/u/sQMQu7uX6dyokfOayRuuVfKOW4O7HvaBWM+JlQ==
+"@babel/plugin-transform-spread@^7.12.1":
+  version "7.12.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.12.1.tgz#527f9f311be4ec7fdc2b79bb89f7bf884b3e1e1e"
+  integrity sha512-vuLp8CP0BE18zVYjsEBZ5xoCecMK6LBMMxYzJnh01rxQRvhNhH1csMMmBfNo5tGpGO+NhdSNW2mzIvBu3K1fng==
   dependencies:
     "@babel/helper-plugin-utils" "^7.10.4"
+    "@babel/helper-skip-transparent-expression-wrappers" "^7.12.1"
 
-"@babel/plugin-transform-shorthand-properties@^7.2.0":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.10.4.tgz#9fd25ec5cdd555bb7f473e5e6ee1c971eede4dd6"
-  integrity sha512-AC2K/t7o07KeTIxMoHneyX90v3zkm5cjHJEokrPEAGEy3UCp8sLKfnfOIGdZ194fyN4wfX/zZUWT9trJZ0qc+Q==
+"@babel/plugin-transform-sticky-regex@^7.12.7":
+  version "7.12.7"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.12.7.tgz#560224613ab23987453948ed21d0b0b193fa7fad"
+  integrity sha512-VEiqZL5N/QvDbdjfYQBhruN0HYjSPjC4XkeqW4ny/jNtH9gcbgaqBIXYEZCNnESMAGs0/K/R7oFGMhOyu/eIxg==
   dependencies:
     "@babel/helper-plugin-utils" "^7.10.4"
 
-"@babel/plugin-transform-spread@^7.6.2":
-  version "7.11.0"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.11.0.tgz#fa84d300f5e4f57752fe41a6d1b3c554f13f17cc"
-  integrity sha512-UwQYGOqIdQJe4aWNyS7noqAnN2VbaczPLiEtln+zPowRNlD+79w3oi2TWfYe0eZgd+gjZCbsydN7lzWysDt+gw==
+"@babel/plugin-transform-template-literals@^7.12.1":
+  version "7.12.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.12.1.tgz#b43ece6ed9a79c0c71119f576d299ef09d942843"
+  integrity sha512-b4Zx3KHi+taXB1dVRBhVJtEPi9h1THCeKmae2qP0YdUHIFhVjtpqqNfxeVAa1xeHVhAy4SbHxEwx5cltAu5apw==
   dependencies:
     "@babel/helper-plugin-utils" "^7.10.4"
-    "@babel/helper-skip-transparent-expression-wrappers" "^7.11.0"
 
-"@babel/plugin-transform-sticky-regex@^7.2.0":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.10.4.tgz#8f3889ee8657581130a29d9cc91d7c73b7c4a28d"
-  integrity sha512-Ddy3QZfIbEV0VYcVtFDCjeE4xwVTJWTmUtorAJkn6u/92Z/nWJNV+mILyqHKrUxXYKA2EoCilgoPePymKL4DvQ==
+"@babel/plugin-transform-typeof-symbol@^7.12.10":
+  version "7.12.10"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.12.10.tgz#de01c4c8f96580bd00f183072b0d0ecdcf0dec4b"
+  integrity sha512-JQ6H8Rnsogh//ijxspCjc21YPd3VLVoYtAwv3zQmqAt8YGYUtdo5usNhdl4b9/Vir2kPFZl6n1h0PfUz4hJhaA==
   dependencies:
     "@babel/helper-plugin-utils" "^7.10.4"
-    "@babel/helper-regex" "^7.10.4"
 
-"@babel/plugin-transform-template-literals@^7.4.4":
-  version "7.10.5"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.10.5.tgz#78bc5d626a6642db3312d9d0f001f5e7639fde8c"
-  integrity sha512-V/lnPGIb+KT12OQikDvgSuesRX14ck5FfJXt6+tXhdkJ+Vsd0lDCVtF6jcB4rNClYFzaB2jusZ+lNISDk2mMMw==
+"@babel/plugin-transform-unicode-escapes@^7.12.1":
+  version "7.12.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.12.1.tgz#5232b9f81ccb07070b7c3c36c67a1b78f1845709"
+  integrity sha512-I8gNHJLIc7GdApm7wkVnStWssPNbSRMPtgHdmH3sRM1zopz09UWPS4x5V4n1yz/MIWTVnJ9sp6IkuXdWM4w+2Q==
   dependencies:
-    "@babel/helper-annotate-as-pure" "^7.10.4"
     "@babel/helper-plugin-utils" "^7.10.4"
 
-"@babel/plugin-transform-typeof-symbol@^7.2.0":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.10.4.tgz#9509f1a7eec31c4edbffe137c16cc33ff0bc5bfc"
-  integrity sha512-QqNgYwuuW0y0H+kUE/GWSR45t/ccRhe14Fs/4ZRouNNQsyd4o3PG4OtHiIrepbM2WKUBDAXKCAK/Lk4VhzTaGA==
+"@babel/plugin-transform-unicode-regex@^7.12.1":
+  version "7.12.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.12.1.tgz#cc9661f61390db5c65e3febaccefd5c6ac3faecb"
+  integrity sha512-SqH4ClNngh/zGwHZOOQMTD+e8FGWexILV+ePMyiDJttAWRh5dhDL8rcl5lSgU3Huiq6Zn6pWTMvdPAb21Dwdyg==
   dependencies:
+    "@babel/helper-create-regexp-features-plugin" "^7.12.1"
     "@babel/helper-plugin-utils" "^7.10.4"
 
-"@babel/plugin-transform-unicode-regex@^7.6.2":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.10.4.tgz#e56d71f9282fac6db09c82742055576d5e6d80a8"
-  integrity sha512-wNfsc4s8N2qnIwpO/WP2ZiSyjfpTamT2C9V9FDH/Ljub9zw6P3SjkXcFmc0RQUt96k2fmIvtla2MMjgTwIAC+A==
+"@babel/preset-env@^7.10.2":
+  version "7.12.11"
+  resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.12.11.tgz#55d5f7981487365c93dbbc84507b1c7215e857f9"
+  integrity sha512-j8Tb+KKIXKYlDBQyIOy4BLxzv1NUOwlHfZ74rvW+Z0Gp4/cI2IMDPBWAgWceGcE7aep9oL/0K9mlzlMGxA8yNw==
   dependencies:
-    "@babel/helper-create-regexp-features-plugin" "^7.10.4"
+    "@babel/compat-data" "^7.12.7"
+    "@babel/helper-compilation-targets" "^7.12.5"
+    "@babel/helper-module-imports" "^7.12.5"
     "@babel/helper-plugin-utils" "^7.10.4"
+    "@babel/helper-validator-option" "^7.12.11"
+    "@babel/plugin-proposal-async-generator-functions" "^7.12.1"
+    "@babel/plugin-proposal-class-properties" "^7.12.1"
+    "@babel/plugin-proposal-dynamic-import" "^7.12.1"
+    "@babel/plugin-proposal-export-namespace-from" "^7.12.1"
+    "@babel/plugin-proposal-json-strings" "^7.12.1"
+    "@babel/plugin-proposal-logical-assignment-operators" "^7.12.1"
+    "@babel/plugin-proposal-nullish-coalescing-operator" "^7.12.1"
+    "@babel/plugin-proposal-numeric-separator" "^7.12.7"
+    "@babel/plugin-proposal-object-rest-spread" "^7.12.1"
+    "@babel/plugin-proposal-optional-catch-binding" "^7.12.1"
+    "@babel/plugin-proposal-optional-chaining" "^7.12.7"
+    "@babel/plugin-proposal-private-methods" "^7.12.1"
+    "@babel/plugin-proposal-unicode-property-regex" "^7.12.1"
+    "@babel/plugin-syntax-async-generators" "^7.8.0"
+    "@babel/plugin-syntax-class-properties" "^7.12.1"
+    "@babel/plugin-syntax-dynamic-import" "^7.8.0"
+    "@babel/plugin-syntax-export-namespace-from" "^7.8.3"
+    "@babel/plugin-syntax-json-strings" "^7.8.0"
+    "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4"
+    "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0"
+    "@babel/plugin-syntax-numeric-separator" "^7.10.4"
+    "@babel/plugin-syntax-object-rest-spread" "^7.8.0"
+    "@babel/plugin-syntax-optional-catch-binding" "^7.8.0"
+    "@babel/plugin-syntax-optional-chaining" "^7.8.0"
+    "@babel/plugin-syntax-top-level-await" "^7.12.1"
+    "@babel/plugin-transform-arrow-functions" "^7.12.1"
+    "@babel/plugin-transform-async-to-generator" "^7.12.1"
+    "@babel/plugin-transform-block-scoped-functions" "^7.12.1"
+    "@babel/plugin-transform-block-scoping" "^7.12.11"
+    "@babel/plugin-transform-classes" "^7.12.1"
+    "@babel/plugin-transform-computed-properties" "^7.12.1"
+    "@babel/plugin-transform-destructuring" "^7.12.1"
+    "@babel/plugin-transform-dotall-regex" "^7.12.1"
+    "@babel/plugin-transform-duplicate-keys" "^7.12.1"
+    "@babel/plugin-transform-exponentiation-operator" "^7.12.1"
+    "@babel/plugin-transform-for-of" "^7.12.1"
+    "@babel/plugin-transform-function-name" "^7.12.1"
+    "@babel/plugin-transform-literals" "^7.12.1"
+    "@babel/plugin-transform-member-expression-literals" "^7.12.1"
+    "@babel/plugin-transform-modules-amd" "^7.12.1"
+    "@babel/plugin-transform-modules-commonjs" "^7.12.1"
+    "@babel/plugin-transform-modules-systemjs" "^7.12.1"
+    "@babel/plugin-transform-modules-umd" "^7.12.1"
+    "@babel/plugin-transform-named-capturing-groups-regex" "^7.12.1"
+    "@babel/plugin-transform-new-target" "^7.12.1"
+    "@babel/plugin-transform-object-super" "^7.12.1"
+    "@babel/plugin-transform-parameters" "^7.12.1"
+    "@babel/plugin-transform-property-literals" "^7.12.1"
+    "@babel/plugin-transform-regenerator" "^7.12.1"
+    "@babel/plugin-transform-reserved-words" "^7.12.1"
+    "@babel/plugin-transform-shorthand-properties" "^7.12.1"
+    "@babel/plugin-transform-spread" "^7.12.1"
+    "@babel/plugin-transform-sticky-regex" "^7.12.7"
+    "@babel/plugin-transform-template-literals" "^7.12.1"
+    "@babel/plugin-transform-typeof-symbol" "^7.12.10"
+    "@babel/plugin-transform-unicode-escapes" "^7.12.1"
+    "@babel/plugin-transform-unicode-regex" "^7.12.1"
+    "@babel/preset-modules" "^0.1.3"
+    "@babel/types" "^7.12.11"
+    core-js-compat "^3.8.0"
+    semver "^5.5.0"
 
-"@babel/preset-env@~7.6.0":
-  version "7.6.3"
-  resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.6.3.tgz#9e1bf05a2e2d687036d24c40e4639dc46cef2271"
-  integrity sha512-CWQkn7EVnwzlOdR5NOm2+pfgSNEZmvGjOhlCHBDq0J8/EStr+G+FvPEiz9B56dR6MoiUFjXhfE4hjLoAKKJtIQ==
+"@babel/preset-modules@^0.1.3":
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.4.tgz#362f2b68c662842970fdb5e254ffc8fc1c2e415e"
+  integrity sha512-J36NhwnfdzpmH41M1DrnkkgAqhZaqr/NBdPfQ677mLzlaXo+oDiv1deyCDtgAhz8p328otdob0Du7+xgHGZbKg==
   dependencies:
-    "@babel/helper-module-imports" "^7.0.0"
     "@babel/helper-plugin-utils" "^7.0.0"
-    "@babel/plugin-proposal-async-generator-functions" "^7.2.0"
-    "@babel/plugin-proposal-dynamic-import" "^7.5.0"
-    "@babel/plugin-proposal-json-strings" "^7.2.0"
-    "@babel/plugin-proposal-object-rest-spread" "^7.6.2"
-    "@babel/plugin-proposal-optional-catch-binding" "^7.2.0"
-    "@babel/plugin-proposal-unicode-property-regex" "^7.6.2"
-    "@babel/plugin-syntax-async-generators" "^7.2.0"
-    "@babel/plugin-syntax-dynamic-import" "^7.2.0"
-    "@babel/plugin-syntax-json-strings" "^7.2.0"
-    "@babel/plugin-syntax-object-rest-spread" "^7.2.0"
-    "@babel/plugin-syntax-optional-catch-binding" "^7.2.0"
-    "@babel/plugin-transform-arrow-functions" "^7.2.0"
-    "@babel/plugin-transform-async-to-generator" "^7.5.0"
-    "@babel/plugin-transform-block-scoped-functions" "^7.2.0"
-    "@babel/plugin-transform-block-scoping" "^7.6.3"
-    "@babel/plugin-transform-classes" "^7.5.5"
-    "@babel/plugin-transform-computed-properties" "^7.2.0"
-    "@babel/plugin-transform-destructuring" "^7.6.0"
-    "@babel/plugin-transform-dotall-regex" "^7.6.2"
-    "@babel/plugin-transform-duplicate-keys" "^7.5.0"
-    "@babel/plugin-transform-exponentiation-operator" "^7.2.0"
-    "@babel/plugin-transform-for-of" "^7.4.4"
-    "@babel/plugin-transform-function-name" "^7.4.4"
-    "@babel/plugin-transform-literals" "^7.2.0"
-    "@babel/plugin-transform-member-expression-literals" "^7.2.0"
-    "@babel/plugin-transform-modules-amd" "^7.5.0"
-    "@babel/plugin-transform-modules-commonjs" "^7.6.0"
-    "@babel/plugin-transform-modules-systemjs" "^7.5.0"
-    "@babel/plugin-transform-modules-umd" "^7.2.0"
-    "@babel/plugin-transform-named-capturing-groups-regex" "^7.6.3"
-    "@babel/plugin-transform-new-target" "^7.4.4"
-    "@babel/plugin-transform-object-super" "^7.5.5"
-    "@babel/plugin-transform-parameters" "^7.4.4"
-    "@babel/plugin-transform-property-literals" "^7.2.0"
-    "@babel/plugin-transform-regenerator" "^7.4.5"
-    "@babel/plugin-transform-reserved-words" "^7.2.0"
-    "@babel/plugin-transform-shorthand-properties" "^7.2.0"
-    "@babel/plugin-transform-spread" "^7.6.2"
-    "@babel/plugin-transform-sticky-regex" "^7.2.0"
-    "@babel/plugin-transform-template-literals" "^7.4.4"
-    "@babel/plugin-transform-typeof-symbol" "^7.2.0"
-    "@babel/plugin-transform-unicode-regex" "^7.6.2"
-    "@babel/types" "^7.6.3"
-    browserslist "^4.6.0"
-    core-js-compat "^3.1.1"
-    invariant "^2.2.2"
-    js-levenshtein "^1.1.3"
-    semver "^5.5.0"
+    "@babel/plugin-proposal-unicode-property-regex" "^7.4.4"
+    "@babel/plugin-transform-dotall-regex" "^7.4.4"
+    "@babel/types" "^7.4.4"
+    esutils "^2.0.2"
 
 "@babel/preset-react@^7.10.4":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.10.4.tgz#92e8a66d816f9911d11d4cc935be67adfc82dbcf"
-  integrity sha512-BrHp4TgOIy4M19JAfO1LhycVXOPWdDbTRep7eVyatf174Hff+6Uk53sDyajqZPu8W1qXRBiYOfIamek6jA7YVw==
+  version "7.12.10"
+  resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.12.10.tgz#4fed65f296cbb0f5fb09de6be8cddc85cc909be9"
+  integrity sha512-vtQNjaHRl4DUpp+t+g4wvTHsLQuye+n0H/wsXIZRn69oz/fvNC7gQ4IK73zGJBaxvHoxElDvnYCthMcT7uzFoQ==
   dependencies:
     "@babel/helper-plugin-utils" "^7.10.4"
-    "@babel/plugin-transform-react-display-name" "^7.10.4"
-    "@babel/plugin-transform-react-jsx" "^7.10.4"
-    "@babel/plugin-transform-react-jsx-development" "^7.10.4"
-    "@babel/plugin-transform-react-jsx-self" "^7.10.4"
-    "@babel/plugin-transform-react-jsx-source" "^7.10.4"
-    "@babel/plugin-transform-react-pure-annotations" "^7.10.4"
-
-"@babel/runtime@^7.8.4":
-  version "7.11.2"
-  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.11.2.tgz#f549c13c754cc40b87644b9fa9f09a6a95fe0736"
-  integrity sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw==
+    "@babel/plugin-transform-react-display-name" "^7.12.1"
+    "@babel/plugin-transform-react-jsx" "^7.12.10"
+    "@babel/plugin-transform-react-jsx-development" "^7.12.7"
+    "@babel/plugin-transform-react-pure-annotations" "^7.12.1"
+
+"@babel/runtime-corejs3@^7.9.6":
+  version "7.12.5"
+  resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.12.5.tgz#ffee91da0eb4c6dae080774e94ba606368e414f4"
+  integrity sha512-roGr54CsTmNPPzZoCP1AmDXuBoNao7tnSA83TXTwt+UK5QVyh1DIJnrgYRPWKCF2flqZQXwa7Yr8v7VmLzF0YQ==
   dependencies:
+    core-js-pure "^3.0.0"
     regenerator-runtime "^0.13.4"
 
-"@babel/template@^7.10.4", "@babel/template@^7.6.0":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.4.tgz#3251996c4200ebc71d1a8fc405fba940f36ba278"
-  integrity sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==
+"@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4":
+  version "7.12.5"
+  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.5.tgz#410e7e487441e1b360c29be715d870d9b985882e"
+  integrity sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==
   dependencies:
-    "@babel/code-frame" "^7.10.4"
-    "@babel/parser" "^7.10.4"
-    "@babel/types" "^7.10.4"
+    regenerator-runtime "^0.13.4"
 
-"@babel/traverse@^7.10.4", "@babel/traverse@^7.11.5", "@babel/traverse@^7.6.3":
-  version "7.11.5"
-  resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.11.5.tgz#be777b93b518eb6d76ee2e1ea1d143daa11e61c3"
-  integrity sha512-EjiPXt+r7LiCZXEfRpSJd+jUMnBd4/9OUv7Nx3+0u9+eimMwJmG0Q98lw4/289JCoxSE8OolDMNZaaF/JZ69WQ==
+"@babel/template@^7.10.4", "@babel/template@^7.12.7":
+  version "7.12.7"
+  resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.12.7.tgz#c817233696018e39fbb6c491d2fb684e05ed43bc"
+  integrity sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow==
   dependencies:
     "@babel/code-frame" "^7.10.4"
-    "@babel/generator" "^7.11.5"
-    "@babel/helper-function-name" "^7.10.4"
-    "@babel/helper-split-export-declaration" "^7.11.0"
-    "@babel/parser" "^7.11.5"
-    "@babel/types" "^7.11.5"
+    "@babel/parser" "^7.12.7"
+    "@babel/types" "^7.12.7"
+
+"@babel/traverse@^7.10.4", "@babel/traverse@^7.12.1", "@babel/traverse@^7.12.10", "@babel/traverse@^7.12.5", "@babel/traverse@^7.7.0":
+  version "7.12.12"
+  resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.12.12.tgz#d0cd87892704edd8da002d674bc811ce64743376"
+  integrity sha512-s88i0X0lPy45RrLM8b9mz8RPH5FqO9G9p7ti59cToE44xFm1Q+Pjh5Gq4SXBbtb88X7Uy7pexeqRIQDDMNkL0w==
+  dependencies:
+    "@babel/code-frame" "^7.12.11"
+    "@babel/generator" "^7.12.11"
+    "@babel/helper-function-name" "^7.12.11"
+    "@babel/helper-split-export-declaration" "^7.12.11"
+    "@babel/parser" "^7.12.11"
+    "@babel/types" "^7.12.12"
     debug "^4.1.0"
     globals "^11.1.0"
     lodash "^4.17.19"
 
-"@babel/types@^7.10.4", "@babel/types@^7.10.5", "@babel/types@^7.11.0", "@babel/types@^7.11.5", "@babel/types@^7.6.3":
-  version "7.11.5"
-  resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.11.5.tgz#d9de577d01252d77c6800cee039ee64faf75662d"
-  integrity sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==
+"@babel/types@^7.10.4", "@babel/types@^7.10.5", "@babel/types@^7.12.1", "@babel/types@^7.12.10", "@babel/types@^7.12.11", "@babel/types@^7.12.12", "@babel/types@^7.12.5", "@babel/types@^7.12.7", "@babel/types@^7.4.4", "@babel/types@^7.7.0":
+  version "7.12.12"
+  resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.12.12.tgz#4608a6ec313abbd87afa55004d373ad04a96c299"
+  integrity sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==
   dependencies:
-    "@babel/helper-validator-identifier" "^7.10.4"
+    "@babel/helper-validator-identifier" "^7.12.11"
     lodash "^4.17.19"
     to-fast-properties "^2.0.0"
 
+"@emotion/cache@^10.0.27":
+  version "10.0.29"
+  resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-10.0.29.tgz#87e7e64f412c060102d589fe7c6dc042e6f9d1e0"
+  integrity sha512-fU2VtSVlHiF27empSbxi1O2JFdNWZO+2NFHfwO0pxgTep6Xa3uGb+3pVKfLww2l/IBGLNEZl5Xf/++A4wAYDYQ==
+  dependencies:
+    "@emotion/sheet" "0.9.4"
+    "@emotion/stylis" "0.8.5"
+    "@emotion/utils" "0.11.3"
+    "@emotion/weak-memoize" "0.2.5"
+
+"@emotion/core@^10.0.14":
+  version "10.1.1"
+  resolved "https://registry.yarnpkg.com/@emotion/core/-/core-10.1.1.tgz#c956c1365f2f2481960064bcb8c4732e5fb612c3"
+  integrity sha512-ZMLG6qpXR8x031NXD8HJqugy/AZSkAuMxxqB46pmAR7ze47MhNJ56cdoX243QPZdGctrdfo+s08yZTiwaUcRKA==
+  dependencies:
+    "@babel/runtime" "^7.5.5"
+    "@emotion/cache" "^10.0.27"
+    "@emotion/css" "^10.0.27"
+    "@emotion/serialize" "^0.11.15"
+    "@emotion/sheet" "0.9.4"
+    "@emotion/utils" "0.11.3"
+
+"@emotion/css@^10.0.27":
+  version "10.0.27"
+  resolved "https://registry.yarnpkg.com/@emotion/css/-/css-10.0.27.tgz#3a7458198fbbebb53b01b2b87f64e5e21241e14c"
+  integrity sha512-6wZjsvYeBhyZQYNrGoR5yPMYbMBNEnanDrqmsqS1mzDm1cOTu12shvl2j4QHNS36UaTE0USIJawCH9C8oW34Zw==
+  dependencies:
+    "@emotion/serialize" "^0.11.15"
+    "@emotion/utils" "0.11.3"
+    babel-plugin-emotion "^10.0.27"
+
+"@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==
+
+"@emotion/is-prop-valid@0.8.8":
+  version "0.8.8"
+  resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz#db28b1c4368a259b60a97311d6a952d4fd01ac1a"
+  integrity sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==
+  dependencies:
+    "@emotion/memoize" "0.7.4"
+
+"@emotion/memoize@0.7.4":
+  version "0.7.4"
+  resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.4.tgz#19bf0f5af19149111c40d98bb0cf82119f5d9eeb"
+  integrity sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==
+
+"@emotion/serialize@^0.11.15", "@emotion/serialize@^0.11.16":
+  version "0.11.16"
+  resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-0.11.16.tgz#dee05f9e96ad2fb25a5206b6d759b2d1ed3379ad"
+  integrity sha512-G3J4o8by0VRrO+PFeSc3js2myYNOXVJ3Ya+RGVxnshRYgsvErfAOglKAiy1Eo1vhzxqtUvjCyS5gtewzkmvSSg==
+  dependencies:
+    "@emotion/hash" "0.8.0"
+    "@emotion/memoize" "0.7.4"
+    "@emotion/unitless" "0.7.5"
+    "@emotion/utils" "0.11.3"
+    csstype "^2.5.7"
+
+"@emotion/sheet@0.9.4":
+  version "0.9.4"
+  resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-0.9.4.tgz#894374bea39ec30f489bbfc3438192b9774d32e5"
+  integrity sha512-zM9PFmgVSqBw4zL101Q0HrBVTGmpAxFZH/pYx/cjJT5advXguvcgjHFTCaIO3enL/xr89vK2bh0Mfyj9aa0ANA==
+
+"@emotion/styled-base@^10.0.27":
+  version "10.0.31"
+  resolved "https://registry.yarnpkg.com/@emotion/styled-base/-/styled-base-10.0.31.tgz#940957ee0aa15c6974adc7d494ff19765a2f742a"
+  integrity sha512-wTOE1NcXmqMWlyrtwdkqg87Mu6Rj1MaukEoEmEkHirO5IoHDJ8LgCQL4MjJODgxWxXibGR3opGp1p7YvkNEdXQ==
+  dependencies:
+    "@babel/runtime" "^7.5.5"
+    "@emotion/is-prop-valid" "0.8.8"
+    "@emotion/serialize" "^0.11.15"
+    "@emotion/utils" "0.11.3"
+
+"@emotion/styled@^10.0.14":
+  version "10.0.27"
+  resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-10.0.27.tgz#12cb67e91f7ad7431e1875b1d83a94b814133eaf"
+  integrity sha512-iK/8Sh7+NLJzyp9a5+vIQIXTYxfT4yB/OJbjzQanB2RZpvmzBQOHZWhpAMZWYEKRNNbsD6WfBw5sVWkb6WzS/Q==
+  dependencies:
+    "@emotion/styled-base" "^10.0.27"
+    babel-plugin-emotion "^10.0.27"
+
+"@emotion/stylis@0.8.5":
+  version "0.8.5"
+  resolved "https://registry.yarnpkg.com/@emotion/stylis/-/stylis-0.8.5.tgz#deacb389bd6ee77d1e7fcaccce9e16c5c7e78e04"
+  integrity sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==
+
+"@emotion/unitless@0.7.5":
+  version "0.7.5"
+  resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed"
+  integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==
+
+"@emotion/utils@0.11.3":
+  version "0.11.3"
+  resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-0.11.3.tgz#a759863867befa7e583400d322652a3f44820924"
+  integrity sha512-0o4l6pZC+hI88+bzuaX/6BgOvQVhbt2PfmxauVaYOGgbsAw14wdKyvMCZXnsnsHys94iadcF+RG/wZyx6+ZZBw==
+
+"@emotion/weak-memoize@0.2.5":
+  version "0.2.5"
+  resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz#8eed982e2ee6f7f4e44c253e12962980791efd46"
+  integrity sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==
+
 "@fortawesome/fontawesome-free@^5.14.0":
   version "5.15.1"
   resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-free/-/fontawesome-free-5.15.1.tgz#ccfef6ddbe59f8fe8f694783e1d3eb88902dc5eb"
@@ -791,12 +985,62 @@
   resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd"
   integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==
 
+"@popperjs/core@^2.4.4":
+  version "2.6.0"
+  resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.6.0.tgz#f022195afdfc942e088ee2101285a1d31c7d727f"
+  integrity sha512-cPqjjzuFWNK3BSKLm0abspP0sp/IGOli4p5I5fKFAzdS8fvjdOwDCfZqAaIiXd9lPkOWi3SUUfZof3hEb7J/uw==
+
+"@projectstorm/geometry@^6.3.0":
+  version "6.3.0"
+  resolved "https://registry.yarnpkg.com/@projectstorm/geometry/-/geometry-6.3.0.tgz#2c37dee4b907ed0086e91680b815cd16188353f0"
+  integrity sha512-Fc2JVAkZPnQMAKdn4FtAApvl40S+sUL9E2usRoXcnqRbwmhD3WHBLhmDIvUTArIyKJJOTzyQW4+O7+NcyrK9/Q==
+
+"@projectstorm/react-canvas-core@^6.3.0":
+  version "6.3.0"
+  resolved "https://registry.yarnpkg.com/@projectstorm/react-canvas-core/-/react-canvas-core-6.3.0.tgz#dfe3072bb3817910c650a1fa004ce439f5bd0669"
+  integrity sha512-mChKZpdfrTWdGvg1OB0EOaCz5ovC6spY/MJfGtxYek5gU36mjoAXE+L6uLzjELTM3J/TJhDkm3Wd7jW9vCNUyA==
+  dependencies:
+    "@projectstorm/geometry" "^6.3.0"
+
+"@projectstorm/react-diagrams-core@^6.3.0":
+  version "6.3.0"
+  resolved "https://registry.yarnpkg.com/@projectstorm/react-diagrams-core/-/react-diagrams-core-6.3.0.tgz#4e225e86a0389f383020b4a9e8bbce2e55781cd2"
+  integrity sha512-xdAA+Rz5vE0adNa/rloXvjDQ2MFlicmdcZHEFTrPH+nV8miAqhHimzbCge8ypAwQyVXS8CLdxUF2RQA/1B8Sng==
+  dependencies:
+    "@projectstorm/geometry" "^6.3.0"
+    "@projectstorm/react-canvas-core" "^6.3.0"
+
+"@projectstorm/react-diagrams-defaults@^6.3.0":
+  version "6.3.0"
+  resolved "https://registry.yarnpkg.com/@projectstorm/react-diagrams-defaults/-/react-diagrams-defaults-6.3.0.tgz#2479de1abb3e638df04f33f09413d326b27e29ea"
+  integrity sha512-5qBFg9sSl3OlIt5Edv45uECgkjqjpg+PSRYderOXJjEQi3SYhf9l0YlfMzpTvt5GmVnyX7gQProyt/p/e4e9rQ==
+  dependencies:
+    "@projectstorm/react-diagrams-core" "^6.3.0"
+
+"@projectstorm/react-diagrams-routing@^6.3.0":
+  version "6.3.0"
+  resolved "https://registry.yarnpkg.com/@projectstorm/react-diagrams-routing/-/react-diagrams-routing-6.3.0.tgz#38b8973ac35e40639dde7490840217d8cb1e66fb"
+  integrity sha512-kgq0MNrjbeEcpQceOORL/5bOQJNF70c/If2x/jqdEKfswzGC9VeUYOKZvzYCRhxsOcpn9hCA6+6Wkd8e4zFzYQ==
+  dependencies:
+    "@projectstorm/geometry" "^6.3.0"
+    "@projectstorm/react-diagrams-core" "^6.3.0"
+    "@projectstorm/react-diagrams-defaults" "^6.3.0"
+
+"@projectstorm/react-diagrams@^6.2.0":
+  version "6.3.0"
+  resolved "https://registry.yarnpkg.com/@projectstorm/react-diagrams/-/react-diagrams-6.3.0.tgz#bd435699f04b63ff57b8434a259aa2dc3b494129"
+  integrity sha512-qHPhlTBsZk/iwXAPXMh6qzmk66+7U4cShSwXM/3OqDkrBi6yI7nLtdY5VhioO0qBhhOyR8v1C8oeYGFqY0pa8Q==
+  dependencies:
+    "@projectstorm/react-diagrams-core" "^6.3.0"
+    "@projectstorm/react-diagrams-defaults" "^6.3.0"
+    "@projectstorm/react-diagrams-routing" "^6.3.0"
+
 "@simonwep/pickr@^1.5.1":
-  version "1.7.4"
-  resolved "https://registry.yarnpkg.com/@simonwep/pickr/-/pickr-1.7.4.tgz#b14fcd945890388b870cd6db4d6c78d531f25141"
-  integrity sha512-fq7jgKJT21uWGC1mARBHvvd1JYlEf93o7SuVOB4Lr0x/2UPuNC9Oe9n/GzVeg4oVtqMDfh1wIEJpsdOJEZb+3g==
+  version "1.8.0"
+  resolved "https://registry.yarnpkg.com/@simonwep/pickr/-/pickr-1.8.0.tgz#adbff9a4f7f0e59dec9946508c5e481b7abae0f8"
+  integrity sha512-VaSD7TwktOsro5nQ/FjRx5JAJ09k5CNfGRHacgVRxeVPolUQwelz1SjL8HAOKZwTSmcnIObptpHABQS4zgN7sw==
   dependencies:
-    core-js "^3.6.5"
+    core-js "^3.8.0"
     nanopop "^2.1.0"
 
 "@sindresorhus/is@^0.7.0":
@@ -804,21 +1048,43 @@
   resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd"
   integrity sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow==
 
+"@tippyjs/react@^4.2.0":
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/@tippyjs/react/-/react-4.2.0.tgz#a1cb369d0051099e8a7e4ceb59f809abd9955283"
+  integrity sha512-T6UcHtwtGkvgsBQ4bNp8BtXGxa2ujfOkWUogYkRtN4UVJ2QRgDdFoJeaPxdndnVYFEa2uTVxSFxs8QkSkZ2Gdw==
+  dependencies:
+    tippy.js "^6.2.0"
+
+"@types/estree@*":
+  version "0.0.45"
+  resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.45.tgz#e9387572998e5ecdac221950dab3e8c3b16af884"
+  integrity sha512-jnqIUKDUqJbDIUxm0Uj7bnlMnRm1T/eZ9N+AVMqhPgzrba2GhGG5o/jCTwmdPK709nEZsGoMzXEDUjcXHa3W0g==
+
 "@types/json-schema@^7.0.5":
   version "7.0.6"
   resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.6.tgz#f4c7ec43e81b319a9815115031709f26987891f0"
   integrity sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==
 
 "@types/node@*":
-  version "14.11.5"
-  resolved "https://registry.yarnpkg.com/@types/node/-/node-14.11.5.tgz#fecad41c041cae7f2404ad4b2d0742fdb628b305"
-  integrity sha512-jVFzDV6NTbrLMxm4xDSIW/gKnk8rQLF9wAzLWIOg+5nU6ACrIMndeBdXci0FGtqJbP9tQvm6V39eshc96TO2wQ==
+  version "14.14.16"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.16.tgz#3cc351f8d48101deadfed4c9e4f116048d437b4b"
+  integrity sha512-naXYePhweTi+BMv11TgioE2/FXU4fSl29HAH1ffxVciNsH3rYXjNP2yM8wqmSm7jS20gM8TIklKiTen+1iVncw==
+
+"@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"
+  integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
 
 "@types/q@^1.5.1":
   version "1.5.4"
   resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.4.tgz#15925414e0ad2cd765bfef58842f7e26a7accb24"
   integrity sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==
 
+"@types/raf@^3.4.0":
+  version "3.4.0"
+  resolved "https://registry.yarnpkg.com/@types/raf/-/raf-3.4.0.tgz#2b72cbd55405e071f1c4d29992638e022b20acc2"
+  integrity sha512-taW5/WYqo36N7V39oYyHP9Ipfd5pNFvGTIQsNGj86xV88YQ7GnI30/yMfKDF7Zgin0m3e+ikX88FvImnK4RjGw==
+
 "@webassemblyjs/ast@1.9.0":
   version "1.9.0"
   resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964"
@@ -1023,7 +1289,7 @@ acorn@^6.0.7, acorn@^6.4.1:
   resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6"
   integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==
 
-acorn@^7.0.0, acorn@^7.1.1:
+acorn@^7.0.0, acorn@^7.1.0, acorn@^7.1.1:
   version "7.4.1"
   resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
   integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
@@ -1077,9 +1343,9 @@ ajv@^5.0.0:
     json-schema-traverse "^0.3.0"
 
 ajv@^6.1.0, ajv@^6.10.2, ajv@^6.12.4, ajv@^6.9.1:
-  version "6.12.5"
-  resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.5.tgz#19b0e8bae8f476e5ba666300387775fb1a00a4da"
-  integrity sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag==
+  version "6.12.6"
+  resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
+  integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
   dependencies:
     fast-deep-equal "^3.1.1"
     fast-json-stable-stringify "^2.0.0"
@@ -1088,7 +1354,7 @@ ajv@^6.1.0, ajv@^6.10.2, ajv@^6.12.4, ajv@^6.9.1:
 
 "alertifyjs@git+https://github.com/EnterpriseDB/AlertifyJS/#72c1d794f5b6d4ec13a68d123c08f19021afe263":
   version "1.7.1"
-  resolved "git+https://github.com/EnterpriseDB/AlertifyJS.git#72c1d794f5b6d4ec13a68d123c08f19021afe263"
+  resolved "git+https://github.com/EnterpriseDB/AlertifyJS/#72c1d794f5b6d4ec13a68d123c08f19021afe263"
 
 alphanum-sort@^1.0.0:
   version "1.0.2"
@@ -1161,9 +1427,9 @@ aproba@^1.1.1:
   integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==
 
 arch@^2.1.0:
-  version "2.1.2"
-  resolved "https://registry.yarnpkg.com/arch/-/arch-2.1.2.tgz#0c52bbe7344bb4fa260c443d2cbad9c00ff2f0bf"
-  integrity sha512-NTBIIbAfkJeIletyABbVtdPgeKfDafR+1mZV/AyyfC1UkVkp9iUjV+wwmqtUgphHYajbI86jejBJp5e+jkGTiQ==
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/arch/-/arch-2.2.0.tgz#1bc47818f305764f23ab3306b0bfc086c5a29d11"
+  integrity sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==
 
 archive-type@^4.0.0:
   version "4.0.0"
@@ -1209,13 +1475,15 @@ array-flatten@1.1.1:
   resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
   integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=
 
-array-includes@^3.1.1:
-  version "3.1.1"
-  resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.1.tgz#cdd67e6852bdf9c1215460786732255ed2459348"
-  integrity sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ==
+array-includes@^3.1.1, array-includes@^3.1.2:
+  version "3.1.2"
+  resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.2.tgz#a8db03e0b88c8c6aeddc49cb132f9bcab4ebf9c8"
+  integrity sha512-w2GspexNQpx+PutG3QpT437/BenZBj0M/MZGn5mzv/MofYqo0xmRHzn4lFsoDlWJ+THYsGJmFlW68WlDFx7VRw==
   dependencies:
+    call-bind "^1.0.0"
     define-properties "^1.1.3"
-    es-abstract "^1.17.0"
+    es-abstract "^1.18.0-next.1"
+    get-intrinsic "^1.0.1"
     is-string "^1.0.5"
 
 array-union@^1.0.1:
@@ -1244,20 +1512,22 @@ array.prototype.find@^2.1.1:
     es-abstract "^1.17.4"
 
 array.prototype.flat@^1.2.3:
-  version "1.2.3"
-  resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz#0de82b426b0318dbfdb940089e38b043d37f6c7b"
-  integrity sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ==
+  version "1.2.4"
+  resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz#6ef638b43312bd401b4c6199fdec7e2dc9e9a123"
+  integrity sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg==
   dependencies:
+    call-bind "^1.0.0"
     define-properties "^1.1.3"
-    es-abstract "^1.17.0-next.1"
+    es-abstract "^1.18.0-next.1"
 
 array.prototype.flatmap@^1.2.3:
-  version "1.2.3"
-  resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.2.3.tgz#1c13f84a178566042dd63de4414440db9222e443"
-  integrity sha512-OOEk+lkePcg+ODXIpvuU9PAryCikCJyo7GlDG1upleEpQRx6mzL9puEBkozQ5iAx20KV0l3DbyQwqciJtqe5Pg==
+  version "1.2.4"
+  resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.2.4.tgz#94cfd47cc1556ec0747d97f7c7738c58122004c9"
+  integrity sha512-r9Z0zYoxqHz60vvQbWEdXIEtCwHF0yxaWfno9qzXeNHvfyl3BZqygmGzb84dsubyaXLH4husF+NFgMSdpZhk2Q==
   dependencies:
+    call-bind "^1.0.0"
     define-properties "^1.1.3"
-    es-abstract "^1.17.0-next.1"
+    es-abstract "^1.18.0-next.1"
     function-bind "^1.1.1"
 
 arraybuffer.slice@~0.0.7:
@@ -1334,11 +1604,11 @@ autoprefixer@^9.6.4:
     postcss-value-parser "^4.1.0"
 
 axios-mock-adapter@^1.17.0:
-  version "1.18.2"
-  resolved "https://registry.yarnpkg.com/axios-mock-adapter/-/axios-mock-adapter-1.18.2.tgz#01fa9e88e692e8f1bbc1ad1200dde672486e03c7"
-  integrity sha512-e5aTsPy2Viov22zNpFTlid76W1Scz82pXeEwwCXdtO85LROhHAF8pHF2qDhiyMONLxKyY3lQ+S4UCsKgrlx8Hw==
+  version "1.19.0"
+  resolved "https://registry.yarnpkg.com/axios-mock-adapter/-/axios-mock-adapter-1.19.0.tgz#9d72e321a6c5418e1eff067aa99761a86c5188a4"
+  integrity sha512-D+0U4LNPr7WroiBDvWilzTMYPYTuZlbo6BI8YHZtj7wYQS8NkARlP9KBt8IWWHTQJ0q/8oZ0ClPBtKCCkx8cQg==
   dependencies:
-    fast-deep-equal "^3.1.1"
+    fast-deep-equal "^3.1.3"
     is-buffer "^2.0.3"
 
 axios@^0.18.1:
@@ -1358,6 +1628,18 @@ babel-code-frame@^6.26.0:
     esutils "^2.0.2"
     js-tokens "^3.0.2"
 
+babel-eslint@^10.1.0:
+  version "10.1.0"
+  resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.1.0.tgz#6968e568a910b78fb3779cdd8b6ac2f479943232"
+  integrity sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==
+  dependencies:
+    "@babel/code-frame" "^7.0.0"
+    "@babel/parser" "^7.7.0"
+    "@babel/traverse" "^7.7.0"
+    "@babel/types" "^7.7.0"
+    eslint-visitor-keys "^1.0.0"
+    resolve "^1.12.0"
+
 babel-generator@^6.18.0:
   version "6.26.1"
   resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.26.1.tgz#1844408d3b8f0d35a404ea7ac180f087a601bd90"
@@ -1372,91 +1654,15 @@ babel-generator@^6.18.0:
     source-map "^0.5.7"
     trim-right "^1.0.1"
 
-babel-helper-call-delegate@^6.24.1:
-  version "6.24.1"
-  resolved "https://registry.yarnpkg.com/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz#ece6aacddc76e41c3461f88bfc575bd0daa2df8d"
-  integrity sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=
+babel-loader@^8.1.0:
+  version "8.2.2"
+  resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.2.2.tgz#9363ce84c10c9a40e6c753748e1441b60c8a0b81"
+  integrity sha512-JvTd0/D889PQBtUXJ2PXaKU/pjZDMtHA9V2ecm+eNRmmBCMR09a+fmpGTNwnJtFmFl5Ei7Vy47LjBb+L0wQ99g==
   dependencies:
-    babel-helper-hoist-variables "^6.24.1"
-    babel-runtime "^6.22.0"
-    babel-traverse "^6.24.1"
-    babel-types "^6.24.1"
-
-babel-helper-define-map@^6.24.1:
-  version "6.26.0"
-  resolved "https://registry.yarnpkg.com/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz#a5f56dab41a25f97ecb498c7ebaca9819f95be5f"
-  integrity sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=
-  dependencies:
-    babel-helper-function-name "^6.24.1"
-    babel-runtime "^6.26.0"
-    babel-types "^6.26.0"
-    lodash "^4.17.4"
-
-babel-helper-function-name@^6.24.1:
-  version "6.24.1"
-  resolved "https://registry.yarnpkg.com/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz#d3475b8c03ed98242a25b48351ab18399d3580a9"
-  integrity sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=
-  dependencies:
-    babel-helper-get-function-arity "^6.24.1"
-    babel-runtime "^6.22.0"
-    babel-template "^6.24.1"
-    babel-traverse "^6.24.1"
-    babel-types "^6.24.1"
-
-babel-helper-get-function-arity@^6.24.1:
-  version "6.24.1"
-  resolved "https://registry.yarnpkg.com/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz#8f7782aa93407c41d3aa50908f89b031b1b6853d"
-  integrity sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=
-  dependencies:
-    babel-runtime "^6.22.0"
-    babel-types "^6.24.1"
-
-babel-helper-hoist-variables@^6.24.1:
-  version "6.24.1"
-  resolved "https://registry.yarnpkg.com/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz#1ecb27689c9d25513eadbc9914a73f5408be7a76"
-  integrity sha1-HssnaJydJVE+rbyZFKc/VAi+enY=
-  dependencies:
-    babel-runtime "^6.22.0"
-    babel-types "^6.24.1"
-
-babel-helper-optimise-call-expression@^6.24.1:
-  version "6.24.1"
-  resolved "https://registry.yarnpkg.com/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz#f7a13427ba9f73f8f4fa993c54a97882d1244257"
-  integrity sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=
-  dependencies:
-    babel-runtime "^6.22.0"
-    babel-types "^6.24.1"
-
-babel-helper-regex@^6.24.1:
-  version "6.26.0"
-  resolved "https://registry.yarnpkg.com/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz#325c59f902f82f24b74faceed0363954f6495e72"
-  integrity sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=
-  dependencies:
-    babel-runtime "^6.26.0"
-    babel-types "^6.26.0"
-    lodash "^4.17.4"
-
-babel-helper-replace-supers@^6.24.1:
-  version "6.24.1"
-  resolved "https://registry.yarnpkg.com/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz#bf6dbfe43938d17369a213ca8a8bf74b6a90ab1a"
-  integrity sha1-v22/5Dk40XNpohPKiov3S2qQqxo=
-  dependencies:
-    babel-helper-optimise-call-expression "^6.24.1"
-    babel-messages "^6.23.0"
-    babel-runtime "^6.22.0"
-    babel-template "^6.24.1"
-    babel-traverse "^6.24.1"
-    babel-types "^6.24.1"
-
-babel-loader@~8.0.5:
-  version "8.0.6"
-  resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.0.6.tgz#e33bdb6f362b03f4bb141a0c21ab87c501b70dfb"
-  integrity sha512-4BmWKtBOBm13uoUwd08UwjZlaw3O9GWf456R9j+5YykFZ6LUIjIKLc0zEZf+hauxPOJs96C8k6FvYD09vWzhYw==
-  dependencies:
-    find-cache-dir "^2.0.0"
-    loader-utils "^1.0.2"
-    mkdirp "^0.5.1"
-    pify "^4.0.1"
+    find-cache-dir "^3.3.1"
+    loader-utils "^1.4.0"
+    make-dir "^3.1.0"
+    schema-utils "^2.6.5"
 
 babel-messages@^6.23.0:
   version "6.23.0"
@@ -1465,13 +1671,6 @@ babel-messages@^6.23.0:
   dependencies:
     babel-runtime "^6.22.0"
 
-babel-plugin-check-es2015-constants@^6.3.13:
-  version "6.22.0"
-  resolved "https://registry.yarnpkg.com/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz#35157b101426fd2ffd3da3f75c7d1e91835bbf8a"
-  integrity sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=
-  dependencies:
-    babel-runtime "^6.22.0"
-
 babel-plugin-dynamic-import-node@^2.3.3:
   version "2.3.3"
   resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3"
@@ -1479,233 +1678,37 @@ babel-plugin-dynamic-import-node@^2.3.3:
   dependencies:
     object.assign "^4.1.0"
 
-babel-plugin-syntax-object-rest-spread@7.0.0-beta.3:
-  version "7.0.0-beta.3"
-  resolved "https://registry.yarnpkg.com/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-7.0.0-beta.3.tgz#7f781c180899dafd88f132f69472397549be48e5"
-  integrity sha512-21/MnmUFduLr4JzxrKMm/MeF+Jjyi5UdZo38IqzrP0sLhmPbal5ZAUJ4HgWH4339SdjnYgENacbY5wfk/zxTGg==
-
-babel-plugin-transform-es2015-arrow-functions@^6.3.13:
-  version "6.22.0"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz#452692cb711d5f79dc7f85e440ce41b9f244d221"
-  integrity sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=
-  dependencies:
-    babel-runtime "^6.22.0"
-
-babel-plugin-transform-es2015-block-scoped-functions@^6.3.13:
-  version "6.22.0"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz#bbc51b49f964d70cb8d8e0b94e820246ce3a6141"
-  integrity sha1-u8UbSflk1wy42OC5ToICRs46YUE=
-  dependencies:
-    babel-runtime "^6.22.0"
-
-babel-plugin-transform-es2015-block-scoping@^6.9.0:
-  version "6.26.0"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz#d70f5299c1308d05c12f463813b0a09e73b1895f"
-  integrity sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=
-  dependencies:
-    babel-runtime "^6.26.0"
-    babel-template "^6.26.0"
-    babel-traverse "^6.26.0"
-    babel-types "^6.26.0"
-    lodash "^4.17.4"
-
-babel-plugin-transform-es2015-classes@^6.9.0:
-  version "6.24.1"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz#5a4c58a50c9c9461e564b4b2a3bfabc97a2584db"
-  integrity sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=
-  dependencies:
-    babel-helper-define-map "^6.24.1"
-    babel-helper-function-name "^6.24.1"
-    babel-helper-optimise-call-expression "^6.24.1"
-    babel-helper-replace-supers "^6.24.1"
-    babel-messages "^6.23.0"
-    babel-runtime "^6.22.0"
-    babel-template "^6.24.1"
-    babel-traverse "^6.24.1"
-    babel-types "^6.24.1"
-
-babel-plugin-transform-es2015-computed-properties@^6.3.13:
-  version "6.24.1"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz#6fe2a8d16895d5634f4cd999b6d3480a308159b3"
-  integrity sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=
-  dependencies:
-    babel-runtime "^6.22.0"
-    babel-template "^6.24.1"
-
-babel-plugin-transform-es2015-destructuring@^6.9.0:
-  version "6.23.0"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz#997bb1f1ab967f682d2b0876fe358d60e765c56d"
-  integrity sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=
-  dependencies:
-    babel-runtime "^6.22.0"
-
-babel-plugin-transform-es2015-duplicate-keys@^6.6.0:
-  version "6.24.1"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz#73eb3d310ca969e3ef9ec91c53741a6f1576423e"
-  integrity sha1-c+s9MQypaePvnskcU3QabxV2Qj4=
+babel-plugin-emotion@^10.0.27:
+  version "10.0.33"
+  resolved "https://registry.yarnpkg.com/babel-plugin-emotion/-/babel-plugin-emotion-10.0.33.tgz#ce1155dcd1783bbb9286051efee53f4e2be63e03"
+  integrity sha512-bxZbTTGz0AJQDHm8k6Rf3RQJ8tX2scsfsRyKVgAbiUPUNIRtlK+7JxP+TAd1kRLABFxe0CFm2VdK4ePkoA9FxQ==
   dependencies:
-    babel-runtime "^6.22.0"
-    babel-types "^6.24.1"
-
-babel-plugin-transform-es2015-for-of@^6.6.0:
-  version "6.23.0"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz#f47c95b2b613df1d3ecc2fdb7573623c75248691"
-  integrity sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=
-  dependencies:
-    babel-runtime "^6.22.0"
-
-babel-plugin-transform-es2015-function-name@^6.9.0:
-  version "6.24.1"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz#834c89853bc36b1af0f3a4c5dbaa94fd8eacaa8b"
-  integrity sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=
-  dependencies:
-    babel-helper-function-name "^6.24.1"
-    babel-runtime "^6.22.0"
-    babel-types "^6.24.1"
-
-babel-plugin-transform-es2015-literals@^6.3.13:
-  version "6.22.0"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz#4f54a02d6cd66cf915280019a31d31925377ca2e"
-  integrity sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=
-  dependencies:
-    babel-runtime "^6.22.0"
-
-babel-plugin-transform-es2015-modules-amd@^6.24.1:
-  version "6.24.1"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz#3b3e54017239842d6d19c3011c4bd2f00a00d154"
-  integrity sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=
-  dependencies:
-    babel-plugin-transform-es2015-modules-commonjs "^6.24.1"
-    babel-runtime "^6.22.0"
-    babel-template "^6.24.1"
-
-babel-plugin-transform-es2015-modules-commonjs@^6.24.1, babel-plugin-transform-es2015-modules-commonjs@^6.6.0:
-  version "6.26.2"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz#58a793863a9e7ca870bdc5a881117ffac27db6f3"
-  integrity sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q==
-  dependencies:
-    babel-plugin-transform-strict-mode "^6.24.1"
-    babel-runtime "^6.26.0"
-    babel-template "^6.26.0"
-    babel-types "^6.26.0"
-
-babel-plugin-transform-es2015-object-super@^6.3.13:
-  version "6.24.1"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz#24cef69ae21cb83a7f8603dad021f572eb278f8d"
-  integrity sha1-JM72muIcuDp/hgPa0CH1cusnj40=
-  dependencies:
-    babel-helper-replace-supers "^6.24.1"
-    babel-runtime "^6.22.0"
-
-babel-plugin-transform-es2015-parameters@^6.9.0:
-  version "6.24.1"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz#57ac351ab49caf14a97cd13b09f66fdf0a625f2b"
-  integrity sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=
-  dependencies:
-    babel-helper-call-delegate "^6.24.1"
-    babel-helper-get-function-arity "^6.24.1"
-    babel-runtime "^6.22.0"
-    babel-template "^6.24.1"
-    babel-traverse "^6.24.1"
-    babel-types "^6.24.1"
-
-babel-plugin-transform-es2015-shorthand-properties@^6.3.13:
-  version "6.24.1"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz#24f875d6721c87661bbd99a4622e51f14de38aa0"
-  integrity sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=
-  dependencies:
-    babel-runtime "^6.22.0"
-    babel-types "^6.24.1"
-
-babel-plugin-transform-es2015-spread@^6.3.13:
-  version "6.22.0"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz#d6d68a99f89aedc4536c81a542e8dd9f1746f8d1"
-  integrity sha1-1taKmfia7cRTbIGlQujdnxdG+NE=
-  dependencies:
-    babel-runtime "^6.22.0"
-
-babel-plugin-transform-es2015-sticky-regex@^6.3.13:
-  version "6.24.1"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz#00c1cdb1aca71112cdf0cf6126c2ed6b457ccdbc"
-  integrity sha1-AMHNsaynERLN8M9hJsLta0V8zbw=
-  dependencies:
-    babel-helper-regex "^6.24.1"
-    babel-runtime "^6.22.0"
-    babel-types "^6.24.1"
-
-babel-plugin-transform-es2015-template-literals@^6.6.0:
-  version "6.22.0"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz#a84b3450f7e9f8f1f6839d6d687da84bb1236d8d"
-  integrity sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=
-  dependencies:
-    babel-runtime "^6.22.0"
-
-babel-plugin-transform-es2015-typeof-symbol@^6.6.0:
-  version "6.23.0"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz#dec09f1cddff94b52ac73d505c84df59dcceb372"
-  integrity sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=
-  dependencies:
-    babel-runtime "^6.22.0"
-
-babel-plugin-transform-es2015-unicode-regex@^6.3.13:
-  version "6.24.1"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz#d38b12f42ea7323f729387f18a7c5ae1faeb35e9"
-  integrity sha1-04sS9C6nMj9yk4fxinxa4frrNek=
-  dependencies:
-    babel-helper-regex "^6.24.1"
-    babel-runtime "^6.22.0"
-    regexpu-core "^2.0.0"
-
-babel-plugin-transform-object-rest-spread@^7.0.0-beta.3:
-  version "7.0.0-beta.3"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-7.0.0-beta.3.tgz#5c409f3cd70819dbb3382d2056971c5ebe01393a"
-  integrity sha512-NOlhrq1CmxyuI94vNsqMhRPMuL5VG2EKUOIJQ0bwNiXBiwWRLdPoWyPT+Irrx5g4g0PkFgA46tnRj7Dc4ZGsxg==
-  dependencies:
-    babel-plugin-syntax-object-rest-spread "7.0.0-beta.3"
+    "@babel/helper-module-imports" "^7.0.0"
+    "@emotion/hash" "0.8.0"
+    "@emotion/memoize" "0.7.4"
+    "@emotion/serialize" "^0.11.16"
+    babel-plugin-macros "^2.0.0"
+    babel-plugin-syntax-jsx "^6.18.0"
+    convert-source-map "^1.5.0"
+    escape-string-regexp "^1.0.5"
+    find-root "^1.1.0"
+    source-map "^0.5.7"
 
-babel-plugin-transform-regenerator@^6.9.0:
-  version "6.26.0"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz#e0703696fbde27f0a3efcacf8b4dca2f7b3a8f2f"
-  integrity sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=
+babel-plugin-macros@^2.0.0:
+  version "2.8.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz#0f958a7cc6556b1e65344465d99111a1e5e10138"
+  integrity sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==
   dependencies:
-    regenerator-transform "^0.10.0"
+    "@babel/runtime" "^7.7.2"
+    cosmiconfig "^6.0.0"
+    resolve "^1.12.0"
 
-babel-plugin-transform-strict-mode@^6.24.1:
-  version "6.24.1"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz#d5faf7aa578a65bbe591cf5edae04a0c67020758"
-  integrity sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=
-  dependencies:
-    babel-runtime "^6.22.0"
-    babel-types "^6.24.1"
+babel-plugin-syntax-jsx@^6.18.0:
+  version "6.18.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946"
+  integrity sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=
 
-babel-preset-es2015-without-strict@~0.0.4:
-  version "0.0.4"
-  resolved "https://registry.yarnpkg.com/babel-preset-es2015-without-strict/-/babel-preset-es2015-without-strict-0.0.4.tgz#88c9f36e79d4762c58347b1a698a07c35b6bda5d"
-  integrity sha1-iMnzbnnUdixYNHsaaYoHw1tr2l0=
-  dependencies:
-    babel-plugin-check-es2015-constants "^6.3.13"
-    babel-plugin-transform-es2015-arrow-functions "^6.3.13"
-    babel-plugin-transform-es2015-block-scoped-functions "^6.3.13"
-    babel-plugin-transform-es2015-block-scoping "^6.9.0"
-    babel-plugin-transform-es2015-classes "^6.9.0"
-    babel-plugin-transform-es2015-computed-properties "^6.3.13"
-    babel-plugin-transform-es2015-destructuring "^6.9.0"
-    babel-plugin-transform-es2015-duplicate-keys "^6.6.0"
-    babel-plugin-transform-es2015-for-of "^6.6.0"
-    babel-plugin-transform-es2015-function-name "^6.9.0"
-    babel-plugin-transform-es2015-literals "^6.3.13"
-    babel-plugin-transform-es2015-modules-commonjs "^6.6.0"
-    babel-plugin-transform-es2015-object-super "^6.3.13"
-    babel-plugin-transform-es2015-parameters "^6.9.0"
-    babel-plugin-transform-es2015-shorthand-properties "^6.3.13"
-    babel-plugin-transform-es2015-spread "^6.3.13"
-    babel-plugin-transform-es2015-sticky-regex "^6.3.13"
-    babel-plugin-transform-es2015-template-literals "^6.6.0"
-    babel-plugin-transform-es2015-typeof-symbol "^6.6.0"
-    babel-plugin-transform-es2015-unicode-regex "^6.3.13"
-    babel-plugin-transform-regenerator "^6.9.0"
-
-babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0:
+babel-runtime@^6.22.0, babel-runtime@^6.26.0:
   version "6.26.0"
   resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
   integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4=
@@ -1713,7 +1716,7 @@ babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0:
     core-js "^2.4.0"
     regenerator-runtime "^0.11.0"
 
-babel-template@^6.16.0, babel-template@^6.24.1, babel-template@^6.26.0:
+babel-template@^6.16.0:
   version "6.26.0"
   resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.26.0.tgz#de03e2d16396b069f46dd9fff8521fb1a0e35e02"
   integrity sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=
@@ -1724,7 +1727,7 @@ babel-template@^6.16.0, babel-template@^6.24.1, babel-template@^6.26.0:
     babylon "^6.18.0"
     lodash "^4.17.4"
 
-babel-traverse@^6.18.0, babel-traverse@^6.24.1, babel-traverse@^6.26.0:
+babel-traverse@^6.18.0, babel-traverse@^6.26.0:
   version "6.26.0"
   resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee"
   integrity sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=
@@ -1739,7 +1742,7 @@ babel-traverse@^6.18.0, babel-traverse@^6.24.1, babel-traverse@^6.26.0:
     invariant "^2.2.2"
     lodash "^4.17.4"
 
-babel-types@^6.18.0, babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.26.0:
+babel-types@^6.18.0, babel-types@^6.26.0:
   version "6.26.0"
   resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497"
   integrity sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=
@@ -1823,10 +1826,15 @@ base64-arraybuffer@0.1.5:
   resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8"
   integrity sha1-c5JncZI7Whl0etZmqlzUv5xunOg=
 
-base64-js@^1.0.2:
-  version "1.3.1"
-  resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1"
-  integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==
+base64-arraybuffer@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.2.0.tgz#4b944fac0191aa5907afe2d8c999ccc57ce80f45"
+  integrity sha512-7emyCsu1/xiBXgQZrscw/8KPRT44I4Yq9Pe6EGs3aPRTsWuggML1/1DTuZUuIaJPIm1FTDUVXl4x/yW8s0kQDQ==
+
+base64-js@^1.0.2, base64-js@^1.3.1:
+  version "1.5.1"
+  resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
+  integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
 
 base64id@1.0.0:
   version "1.0.0"
@@ -1966,7 +1974,7 @@ bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.4.0:
   resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.9.tgz#26d556829458f9d1e81fc48952493d0ba3507828"
   integrity sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==
 
-bn.js@^5.1.1:
+bn.js@^5.0.0, bn.js@^5.1.1:
   version "5.1.3"
   resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.1.3.tgz#beca005408f642ebebea80b042b4d18d2ac0ee6b"
   integrity sha512-GkTiFpjFtUzU9CbMeJ5iazkCzGL3jrhzerzZIuqLABjbwRaFt33I9tUdSNryIptM+RxDet6OKm2WnLXzW51KsQ==
@@ -2004,10 +2012,10 @@ bootstrap4-toggle@3.4.0:
   resolved "https://registry.yarnpkg.com/bootstrap4-toggle/-/bootstrap4-toggle-3.4.0.tgz#58264d4c7fd24eb2e09cca5156a338b2b22a4792"
   integrity sha512-vKfBgsjICW6mOqb264qwwbbGtpcOx+p9jOlQmLfRZ07iGE+b5YbQlY4ft9aAhPulh7V3oKOtEuuq0FcSKAI4bQ==
 
-bootstrap@>=4.1.2, bootstrap@^4.3.1:
-  version "4.5.2"
-  resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.5.2.tgz#a85c4eda59155f0d71186b6e6ad9b875813779ab"
-  integrity sha512-vlGn0bcySYl/iV+BGA544JkkZP5LB3jsmkeKLFQakCOwCM3AOk7VkldBz4jrzSe+Z0Ezn99NVXa1o45cQY4R6A==
+bootstrap@^4.3.1, bootstrap@^4.5.2:
+  version "4.5.3"
+  resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.5.3.tgz#c6a72b355aaf323920be800246a6e4ef30997fe6"
+  integrity sha512-o9ppKQioXGqhw8Z7mah6KdTYpNQY//tipnkxppWhPbiSWdD+1raYsnhwEZjkTHYbGee4cVQ0Rx65EhOY/HNLcQ==
 
 bowser@2.1.2:
   version "2.1.2"
@@ -2108,11 +2116,11 @@ browserify-des@^1.0.0:
     safe-buffer "^5.1.2"
 
 browserify-rsa@^4.0.0, browserify-rsa@^4.0.1:
-  version "4.0.1"
-  resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524"
-  integrity sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.1.0.tgz#b2fd06b5b75ae297f7ce2dc651f918f5be158c8d"
+  integrity sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==
   dependencies:
-    bn.js "^4.1.0"
+    bn.js "^5.0.0"
     randombytes "^2.0.1"
 
 browserify-sign@^4.0.0:
@@ -2137,7 +2145,61 @@ browserify-zlib@^0.2.0, browserify-zlib@~0.2.0:
   dependencies:
     pako "~1.0.5"
 
-browserify@^16.1.0, browserify@~16.2.3:
+browserify@^16.1.0:
+  version "16.5.2"
+  resolved "https://registry.yarnpkg.com/browserify/-/browserify-16.5.2.tgz#d926835e9280fa5fd57f5bc301f2ef24a972ddfe"
+  integrity sha512-TkOR1cQGdmXU9zW4YukWzWVSJwrxmNdADFbqbE3HFgQWe5wqZmOawqZ7J/8MPCwk/W8yY7Y0h+7mOtcZxLP23g==
+  dependencies:
+    JSONStream "^1.0.3"
+    assert "^1.4.0"
+    browser-pack "^6.0.1"
+    browser-resolve "^2.0.0"
+    browserify-zlib "~0.2.0"
+    buffer "~5.2.1"
+    cached-path-relative "^1.0.0"
+    concat-stream "^1.6.0"
+    console-browserify "^1.1.0"
+    constants-browserify "~1.0.0"
+    crypto-browserify "^3.0.0"
+    defined "^1.0.0"
+    deps-sort "^2.0.0"
+    domain-browser "^1.2.0"
+    duplexer2 "~0.1.2"
+    events "^2.0.0"
+    glob "^7.1.0"
+    has "^1.0.0"
+    htmlescape "^1.1.0"
+    https-browserify "^1.0.0"
+    inherits "~2.0.1"
+    insert-module-globals "^7.0.0"
+    labeled-stream-splicer "^2.0.0"
+    mkdirp-classic "^0.5.2"
+    module-deps "^6.2.3"
+    os-browserify "~0.3.0"
+    parents "^1.0.1"
+    path-browserify "~0.0.0"
+    process "~0.11.0"
+    punycode "^1.3.2"
+    querystring-es3 "~0.2.0"
+    read-only-stream "^2.0.0"
+    readable-stream "^2.0.2"
+    resolve "^1.1.4"
+    shasum "^1.0.0"
+    shell-quote "^1.6.1"
+    stream-browserify "^2.0.0"
+    stream-http "^3.0.0"
+    string_decoder "^1.1.1"
+    subarg "^1.0.0"
+    syntax-error "^1.1.1"
+    through2 "^2.0.0"
+    timers-browserify "^1.0.1"
+    tty-browserify "0.0.1"
+    url "~0.11.0"
+    util "~0.10.1"
+    vm-browserify "^1.0.0"
+    xtend "^4.0.0"
+
+browserify@~16.2.3:
   version "16.2.3"
   resolved "https://registry.yarnpkg.com/browserify/-/browserify-16.2.3.tgz#7ee6e654ba4f92bce6ab3599c3485b1cc7a0ad0b"
   integrity sha512-zQt/Gd1+W+IY+h/xX2NYMW4orQWhqSwyV+xsblycTtpOuB27h1fZhhNQuipJ4t79ohw4P4mMem0jp/ZkISQtjQ==
@@ -2191,15 +2253,16 @@ browserify@^16.1.0, browserify@~16.2.3:
     vm-browserify "^1.0.0"
     xtend "^4.0.0"
 
-browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.6.0, browserslist@^4.8.5:
-  version "4.14.5"
-  resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.14.5.tgz#1c751461a102ddc60e40993639b709be7f2c4015"
-  integrity sha512-Z+vsCZIvCBvqLoYkBFTwEYH3v5MCQbsAjp50ERycpOjnPmolg1Gjy4+KaWWpm8QOJt9GHkhdqAl14NpCX73CWA==
+browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.14.5, browserslist@^4.15.0:
+  version "4.16.0"
+  resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.0.tgz#410277627500be3cb28a1bfe037586fbedf9488b"
+  integrity sha512-/j6k8R0p3nxOC6kx5JGAxsnhc9ixaWJfYc+TNTzxg6+ARaESAvQGV7h0uNOB4t+pLQJZWzcrMxXOxjgsCj3dqQ==
   dependencies:
-    caniuse-lite "^1.0.30001135"
-    electron-to-chromium "^1.3.571"
-    escalade "^3.1.0"
-    node-releases "^1.1.61"
+    caniuse-lite "^1.0.30001165"
+    colorette "^1.2.1"
+    electron-to-chromium "^1.3.621"
+    escalade "^3.1.1"
+    node-releases "^1.1.67"
 
 buffer-alloc-unsafe@^1.1.0:
   version "1.1.0"
@@ -2244,9 +2307,17 @@ buffer@^4.3.0:
     isarray "^1.0.0"
 
 buffer@^5.0.2, buffer@^5.2.1:
-  version "5.6.0"
-  resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.6.0.tgz#a31749dc7d81d84db08abf937b6b8c4033f62786"
-  integrity sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==
+  version "5.7.1"
+  resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0"
+  integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==
+  dependencies:
+    base64-js "^1.3.1"
+    ieee754 "^1.1.13"
+
+buffer@~5.2.1:
+  version "5.2.1"
+  resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.2.1.tgz#dd57fa0f109ac59c602479044dca7b8b3d0b71d6"
+  integrity sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==
   dependencies:
     base64-js "^1.0.2"
     ieee754 "^1.1.4"
@@ -2339,6 +2410,14 @@ cached-path-relative@^1.0.0, cached-path-relative@^1.0.2:
   resolved "https://registry.yarnpkg.com/cached-path-relative/-/cached-path-relative-1.0.2.tgz#a13df4196d26776220cc3356eb147a52dba2c6db"
   integrity sha512-5r2GqsoEb4qMTTN9J+WzXfjov+hjxT+j3u5K+kIVNIwAd99DLCJE9pBIMP1qVeybV6JiijL385Oz0DcYxfbOIg==
 
+call-bind@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.0.tgz#24127054bb3f9bdcb4b1fb82418186072f77b8ce"
+  integrity sha512-AEXsYIyyDY3MCzbwdhzG3Jx1R0J2wetQyUynn6dYHAO+bg8l1k7jwZtRv4ryryFs7EP+NDlikJlVe59jr0cM2w==
+  dependencies:
+    function-bind "^1.1.1"
+    get-intrinsic "^1.0.0"
+
 caller-callsite@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134"
@@ -2396,10 +2475,22 @@ caniuse-api@^3.0.0:
     lodash.memoize "^4.1.2"
     lodash.uniq "^4.5.0"
 
-caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001135:
-  version "1.0.30001146"
-  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001146.tgz#c61fcb1474520c1462913689201fb292ba6f447c"
-  integrity sha512-VAy5RHDfTJhpxnDdp2n40GPPLp3KqNrXz1QqFv4J64HvArKs8nuNMOWkB3ICOaBTU/Aj4rYAo/ytdQDDFF/Pug==
+caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001165:
+  version "1.0.30001170"
+  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001170.tgz#0088bfecc6a14694969e391cc29d7eb6362ca6a7"
+  integrity sha512-Dd4d/+0tsK0UNLrZs3CvNukqalnVTRrxb5mcQm8rHL49t7V5ZaTygwXkrq+FB+dVDf++4ri8eJnFEJAB8332PA==
+
+canvg@^3.0.7:
+  version "3.0.7"
+  resolved "https://registry.yarnpkg.com/canvg/-/canvg-3.0.7.tgz#e45b87a64116af906917f7cad57d370ea372d682"
+  integrity sha512-4sq6iL5Q4VOXS3PL1BapiXIZItpxYyANVzsAKpTPS5oq4u3SKbGfUcbZh2gdLCQ3jWpG/y5wRkMlBBAJhXeiZA==
+  dependencies:
+    "@babel/runtime-corejs3" "^7.9.6"
+    "@types/raf" "^3.4.0"
+    raf "^3.4.1"
+    rgbcolor "^1.0.1"
+    stackblur-canvas "^2.0.0"
+    svg-pathdata "^5.0.5"
 
 caw@^2.0.0, caw@^2.0.1:
   version "2.0.1"
@@ -2445,9 +2536,9 @@ chardet@^0.7.0:
   integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
 
 chart.js@^2.9.3:
-  version "2.9.3"
-  resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-2.9.3.tgz#ae3884114dafd381bc600f5b35a189138aac1ef7"
-  integrity sha512-+2jlOobSk52c1VU6fzkh3UwqHMdSlgH1xFv9FKMqHiNCpXsGPQa/+81AFa+i3jZ253Mq9aAycPwDjnn1XbRNNw==
+  version "2.9.4"
+  resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-2.9.4.tgz#0827f9563faffb2dc5c06562f8eb10337d5b9684"
+  integrity sha512-B07aAzxcrikjAPyV+01j7BmOpxtQETxTSlQ26BEYJ+3iUkbNKaOJ/nDbT6JjyqYxseM0ON12COHYdU2cTIjC7A==
   dependencies:
     chartjs-color "^2.1.0"
     moment "^2.10.2"
@@ -2472,19 +2563,46 @@ check-types@^8.0.3:
   resolved "https://registry.yarnpkg.com/check-types/-/check-types-8.0.3.tgz#3356cca19c889544f2d7a95ed49ce508a0ecf552"
   integrity sha512-YpeKZngUmG65rLudJ4taU7VLkOCTMhNl/u4ctNC56LQS/zJTyNH0Lrtwm1tfTsbLlwvlfsA2d1c8vCf/Kh2KwQ==
 
+cheerio-select-tmp@^0.1.0:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/cheerio-select-tmp/-/cheerio-select-tmp-0.1.1.tgz#55bbef02a4771710195ad736d5e346763ca4e646"
+  integrity sha512-YYs5JvbpU19VYJyj+F7oYrIE2BOll1/hRU7rEy/5+v9BzkSo3bK81iAeeQEMI92vRIxz677m72UmJUiVwwgjfQ==
+  dependencies:
+    css-select "^3.1.2"
+    css-what "^4.0.0"
+    domelementtype "^2.1.0"
+    domhandler "^4.0.0"
+    domutils "^2.4.4"
+
 cheerio@^1.0.0-rc.3:
-  version "1.0.0-rc.3"
-  resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.3.tgz#094636d425b2e9c0f4eb91a46c05630c9a1a8bf6"
-  integrity sha512-0td5ijfUPuubwLUu0OBoe98gZj8C/AA+RW3v67GPlGOrvxWjZmBXiBCRU+I8VEiNyJzjth40POfHiz2RB3gImA==
-  dependencies:
-    css-select "~1.2.0"
-    dom-serializer "~0.1.1"
-    entities "~1.1.1"
-    htmlparser2 "^3.9.1"
-    lodash "^4.15.0"
-    parse5 "^3.0.1"
-
-"chokidar@>=2.0.0 <4.0.0", chokidar@^2.1.1, chokidar@^2.1.8:
+  version "1.0.0-rc.5"
+  resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.5.tgz#88907e1828674e8f9fee375188b27dadd4f0fa2f"
+  integrity sha512-yoqps/VCaZgN4pfXtenwHROTp8NG6/Hlt4Jpz2FEP0ZJQ+ZUkVDd0hAPDNKhj3nakpfPt/CNs57yEtxD1bXQiw==
+  dependencies:
+    cheerio-select-tmp "^0.1.0"
+    dom-serializer "~1.2.0"
+    domhandler "^4.0.0"
+    entities "~2.1.0"
+    htmlparser2 "^6.0.0"
+    parse5 "^6.0.0"
+    parse5-htmlparser2-tree-adapter "^6.0.0"
+
+"chokidar@>=2.0.0 <4.0.0", chokidar@^3.0.0, chokidar@^3.4.1:
+  version "3.4.3"
+  resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.3.tgz#c1df38231448e45ca4ac588e6c79573ba6a57d5b"
+  integrity sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==
+  dependencies:
+    anymatch "~3.1.1"
+    braces "~3.0.2"
+    glob-parent "~5.1.0"
+    is-binary-path "~2.1.0"
+    is-glob "~4.0.1"
+    normalize-path "~3.0.0"
+    readdirp "~3.5.0"
+  optionalDependencies:
+    fsevents "~2.1.2"
+
+chokidar@^2.1.1, chokidar@^2.1.8:
   version "2.1.8"
   resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917"
   integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==
@@ -2503,21 +2621,6 @@ cheerio@^1.0.0-rc.3:
   optionalDependencies:
     fsevents "^1.2.7"
 
-chokidar@^3.0.0, chokidar@^3.4.1:
-  version "3.4.2"
-  resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.2.tgz#38dc8e658dec3809741eb3ef7bb0a47fe424232d"
-  integrity sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A==
-  dependencies:
-    anymatch "~3.1.1"
-    braces "~3.0.2"
-    glob-parent "~5.1.0"
-    is-binary-path "~2.1.0"
-    is-glob "~4.0.1"
-    normalize-path "~3.0.0"
-    readdirp "~3.4.0"
-  optionalDependencies:
-    fsevents "~2.1.2"
-
 chownr@^1.1.1, chownr@^1.1.2:
   version "1.1.4"
   resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b"
@@ -2595,6 +2698,13 @@ clone-response@1.0.2:
   dependencies:
     mimic-response "^1.0.0"
 
+closest@^0.0.1:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/closest/-/closest-0.0.1.tgz#26da6f80b3e0e17e71f80f12782819e9f653495c"
+  integrity sha1-JtpvgLPg4X5x+A8SeCgZ6fZTSVw=
+  dependencies:
+    matches-selector "0.0.1"
+
 co@^4.6.0:
   version "4.6.0"
   resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
@@ -2610,9 +2720,9 @@ coa@^2.0.2:
     q "^1.1.2"
 
 codemirror@^5.54.0:
-  version "5.58.1"
-  resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.58.1.tgz#ec6bf38ad2a17f74c61bd00cc6dc5a69bd167854"
-  integrity sha512-UGb/ueu20U4xqWk8hZB3xIfV2/SFqnSLYONiM3wTMDqko0bsYrsAkGGhqUzbRkYm89aBKPyHtuNEbVWF9FTFzw==
+  version "5.59.0"
+  resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.59.0.tgz#6d8132055459aabf21d04cae5cf5c430e5c57bb9"
+  integrity sha512-UGzSkCacY9z0rSpQ3wnTWRN2nvRE6foDXnJltWW8pazInR/R+3gXHrao4IFQMv/bSBvFBxt8/HPpkpKAS54x5Q==
 
 collection-visit@^1.0.0:
   version "1.0.0"
@@ -2636,31 +2746,31 @@ color-convert@^2.0.1:
   dependencies:
     color-name "~1.1.4"
 
-color-name@1.1.3, color-name@^1.0.0:
+color-name@1.1.3:
   version "1.1.3"
   resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
   integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
 
-color-name@~1.1.4:
+color-name@^1.0.0, color-name@~1.1.4:
   version "1.1.4"
   resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
   integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
 
-color-string@^1.5.2:
-  version "1.5.3"
-  resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.3.tgz#c9bbc5f01b58b5492f3d6857459cb6590ce204cc"
-  integrity sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==
+color-string@^1.5.4:
+  version "1.5.4"
+  resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.4.tgz#dd51cd25cfee953d138fe4002372cc3d0e504cb6"
+  integrity sha512-57yF5yt8Xa3czSEW1jfQDE79Idk0+AkN/4KWad6tbdxUmAs3MvjxlWSWD4deYytcRfoZ9nhKyFl1kj5tBvidbw==
   dependencies:
     color-name "^1.0.0"
     simple-swizzle "^0.2.2"
 
 color@^3.0.0:
-  version "3.1.2"
-  resolved "https://registry.yarnpkg.com/color/-/color-3.1.2.tgz#68148e7f85d41ad7649c5fa8c8106f098d229e10"
-  integrity sha512-vXTJhHebByxZn3lDvDJYw4lR5+uB3vuoHsuYA5AKuxRVn5wzzIfQKGLBmgdVRHKTJYeK5rvJcHnrd0Li49CFpg==
+  version "3.1.3"
+  resolved "https://registry.yarnpkg.com/color/-/color-3.1.3.tgz#ca67fb4e7b97d611dcde39eceed422067d91596e"
+  integrity sha512-xgXAcTHa2HeFCGLE9Xs/R82hujGtu9Jd9x4NW3T34+OMs7VoPsjwzRczKHvTAHeJwWFwX5j15+MgAppE8ztObQ==
   dependencies:
     color-convert "^1.9.1"
-    color-string "^1.5.2"
+    color-string "^1.5.4"
 
 colorette@^1.2.1:
   version "1.2.1"
@@ -2688,9 +2798,9 @@ commander@^2.12.2, commander@^2.18.0, commander@^2.19.0, commander@^2.20.0, comm
   integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
 
 commander@^6.0.0:
-  version "6.1.0"
-  resolved "https://registry.yarnpkg.com/commander/-/commander-6.1.0.tgz#f8d722b78103141006b66f4c7ba1e97315ba75bc"
-  integrity sha512-wl7PNrYWd2y5mp1OK/LhTlv8Ff4kQJQRXXAvF+uU/TPNiVJUxZLRYGj/B0y/lPGAVcSbJqH2Za/cvHmrPMC8mA==
+  version "6.2.1"
+  resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c"
+  integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==
 
 commondir@^1.0.1:
   version "1.0.1"
@@ -2777,18 +2887,18 @@ content-type@~1.0.4:
   resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
   integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
 
-convert-source-map@^1.1.0, convert-source-map@^1.1.3, convert-source-map@~1.1.0:
-  version "1.1.3"
-  resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.1.3.tgz#4829c877e9fe49b3161f3bf3673888e204699860"
-  integrity sha1-SCnId+n+SbMWHzvzZziI4gRpmGA=
-
-convert-source-map@^1.5.0, convert-source-map@^1.7.0:
+convert-source-map@^1.1.3, convert-source-map@^1.5.0, convert-source-map@^1.7.0:
   version "1.7.0"
   resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442"
   integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==
   dependencies:
     safe-buffer "~5.1.1"
 
+convert-source-map@~1.1.0:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.1.3.tgz#4829c877e9fe49b3161f3bf3673888e204699860"
+  integrity sha1-SCnId+n+SbMWHzvzZziI4gRpmGA=
+
 cookie-signature@1.0.6:
   version "1.0.6"
   resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
@@ -2839,23 +2949,28 @@ copy-webpack-plugin@^5.1.0:
     serialize-javascript "^4.0.0"
     webpack-log "^2.0.0"
 
-core-js-compat@^3.1.1:
-  version "3.6.5"
-  resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.6.5.tgz#2a51d9a4e25dfd6e690251aa81f99e3c05481f1c"
-  integrity sha512-7ItTKOhOZbznhXAQ2g/slGg1PJV5zDO/WdkTwi7UEOJmkvsE32PWvx6mKtDjiMpjnR2CNf6BAD6sSxIlv7ptng==
+core-js-compat@^3.8.0:
+  version "3.8.1"
+  resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.8.1.tgz#8d1ddd341d660ba6194cbe0ce60f4c794c87a36e"
+  integrity sha512-a16TLmy9NVD1rkjUGbwuyWkiDoN0FDpAwrfLONvHFQx0D9k7J9y0srwMT8QP/Z6HE3MIFaVynEeYwZwPX1o5RQ==
   dependencies:
-    browserslist "^4.8.5"
+    browserslist "^4.15.0"
     semver "7.0.0"
 
+core-js-pure@^3.0.0:
+  version "3.8.1"
+  resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.8.1.tgz#23f84048f366fdfcf52d3fd1c68fec349177d119"
+  integrity sha512-Se+LaxqXlVXGvmexKGPvnUIYC1jwXu1H6Pkyb3uBM5d8/NELMYCHs/4/roD7721NxrTLyv7e5nXd5/QLBO+10g==
+
 core-js@^2.4.0:
-  version "2.6.11"
-  resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.11.tgz#38831469f9922bded8ee21c9dc46985e0399308c"
-  integrity sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==
+  version "2.6.12"
+  resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec"
+  integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==
 
-core-js@^3.2.1, core-js@^3.6.5:
-  version "3.6.5"
-  resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.6.5.tgz#7395dc273af37fb2e50e9bd3d9fe841285231d1a"
-  integrity sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==
+core-js@^3.2.1, core-js@^3.8.0:
+  version "3.8.1"
+  resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.8.1.tgz#f51523668ac8a294d1285c3b9db44025fda66d47"
+  integrity sha512-9Id2xHY1W7m8hCl8NkhQn5CufmF/WuR30BTRewvCXc1aZd3kMECwNZ69ndLbekKfakw9Rf2Xyc+QR6E7Gg+obg==
 
 core-util-is@~1.0.0:
   version "1.0.2"
@@ -2872,6 +2987,17 @@ cosmiconfig@^5.0.0:
     js-yaml "^3.13.1"
     parse-json "^4.0.0"
 
+cosmiconfig@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-6.0.0.tgz#da4fee853c52f6b1e6935f41c1a2fc50bd4a9982"
+  integrity sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==
+  dependencies:
+    "@types/parse-json" "^4.0.0"
+    import-fresh "^3.1.0"
+    parse-json "^5.0.0"
+    path-type "^4.0.0"
+    yaml "^1.7.2"
+
 create-ecdh@^4.0.0:
   version "4.0.4"
   resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e"
@@ -2960,6 +3086,13 @@ css-declaration-sorter@^4.0.1:
     postcss "^7.0.1"
     timsort "^0.3.0"
 
+css-line-break@1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/css-line-break/-/css-line-break-1.1.1.tgz#d5e9bdd297840099eb0503c7310fd34927a026ef"
+  integrity sha512-1feNVaM4Fyzdj4mKPIQNL2n70MmuYzAXZ1aytlROFX1JsOo070OsugwGjj7nl6jnDJWHDM8zRZswkmeYVWZJQA==
+  dependencies:
+    base64-arraybuffer "^0.2.0"
+
 css-loader@2.1.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-2.1.0.tgz#42952ac22bca5d076978638e9813abce49b8f0cc"
@@ -2991,15 +3124,16 @@ css-select@^2.0.0:
     domutils "^1.7.0"
     nth-check "^1.0.2"
 
-css-select@~1.2.0:
-  version "1.2.0"
-  resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858"
-  integrity sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=
+css-select@^3.1.2:
+  version "3.1.2"
+  resolved "https://registry.yarnpkg.com/css-select/-/css-select-3.1.2.tgz#d52cbdc6fee379fba97fb0d3925abbd18af2d9d8"
+  integrity sha512-qmss1EihSuBNWNNhHjxzxSfJoFBM/lERB/Q4EnsJQQC62R2evJDW481091oAdOr9uh46/0n4nrg0It5cAnj1RA==
   dependencies:
-    boolbase "~1.0.0"
-    css-what "2.1"
-    domutils "1.5.1"
-    nth-check "~1.0.1"
+    boolbase "^1.0.0"
+    css-what "^4.0.0"
+    domhandler "^4.0.0"
+    domutils "^2.4.3"
+    nth-check "^2.0.0"
 
 css-tree@1.0.0-alpha.37:
   version "1.0.0-alpha.37"
@@ -3009,23 +3143,23 @@ css-tree@1.0.0-alpha.37:
     mdn-data "2.0.4"
     source-map "^0.6.1"
 
-css-tree@1.0.0-alpha.39:
-  version "1.0.0-alpha.39"
-  resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.39.tgz#2bff3ffe1bb3f776cf7eefd91ee5cba77a149eeb"
-  integrity sha512-7UvkEYgBAHRG9Nt980lYxjsTrCyHFN53ky3wVsDkiMdVqylqRt+Zc+jm5qw7/qyOvN2dHSYtX0e4MbCCExSvnA==
+css-tree@^1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.2.tgz#9ae393b5dafd7dae8a622475caec78d3d8fbd7b5"
+  integrity sha512-wCoWush5Aeo48GLhfHPbmvZs59Z+M7k5+B1xDnXbdWNcEF423DoFdqSWE0PM5aNk5nI5cp1q7ms36zGApY/sKQ==
   dependencies:
-    mdn-data "2.0.6"
+    mdn-data "2.0.14"
     source-map "^0.6.1"
 
-css-what@2.1:
-  version "2.1.3"
-  resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2"
-  integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==
-
 css-what@^3.2.1:
-  version "3.4.1"
-  resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.4.1.tgz#81cb70b609e4b1351b1e54cbc90fd9c54af86e2e"
-  integrity sha512-wHOppVDKl4vTAOWzJt5Ek37Sgd9qq1Bmj/T1OjvicWbU5W7ru7Pqbn0Jdqii3Drx/h+dixHKXNhZYx7blthL7g==
+  version "3.4.2"
+  resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.4.2.tgz#ea7026fcb01777edbde52124e21f327e7ae950e4"
+  integrity sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==
+
+css-what@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/css-what/-/css-what-4.0.0.tgz#35e73761cab2eeb3d3661126b23d7aa0e8432233"
+  integrity sha512-teijzG7kwYfNVsUh2H/YN62xW3KK9YhXEgSlbxMlcyjPNvdKJqFx5lrwlJgoFP1ZHlB89iGDlo/JyshKeRhv5A==
 
 cssesc@^3.0.0:
   version "3.0.0"
@@ -3101,11 +3235,16 @@ cssnano@^4.1.10:
     postcss "^7.0.0"
 
 csso@^4.0.2:
-  version "4.0.3"
-  resolved "https://registry.yarnpkg.com/csso/-/csso-4.0.3.tgz#0d9985dc852c7cc2b2cacfbbe1079014d1a8e903"
-  integrity sha512-NL3spysxUkcrOgnpsT4Xdl2aiEiBG6bXswAABQVHcMrfjjBisFOKwLDOmf4wf32aPdcJws1zds2B0Rg+jqMyHQ==
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/csso/-/csso-4.2.0.tgz#ea3a561346e8dc9f546d6febedd50187cf389529"
+  integrity sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==
   dependencies:
-    css-tree "1.0.0-alpha.39"
+    css-tree "^1.1.2"
+
+csstype@^2.5.7:
+  version "2.6.14"
+  resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.14.tgz#004822a4050345b55ad4dcc00be1d9cf2f4296de"
+  integrity sha512-2mSc+VEpGPblzAxyeR+vZhJKgYg0Og0nnRi7pmRXFYYxSfnOnW8A5wwQb4n4cE2nIOzqKOAzLCaEX6aBmNEv8A==
 
 cubic2quad@^1.0.0:
   version "1.1.1"
@@ -3138,6 +3277,14 @@ cyclist@^1.0.1:
   resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9"
   integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=
 
+dagre@^0.8.4:
+  version "0.8.5"
+  resolved "https://registry.yarnpkg.com/dagre/-/dagre-0.8.5.tgz#ba30b0055dac12b6c1fcc247817442777d06afee"
+  integrity sha512-/aTqmnRta7x7MCCpExk7HQL2O4owCT2h8NT//9I1OQ9vt29Pa0BzSAkR5lwFUcQ7491yVi/3CXU9jQ5o0Mn2Sw==
+  dependencies:
+    graphlib "^2.1.8"
+    lodash "^4.17.15"
+
 dash-ast@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/dash-ast/-/dash-ast-1.0.0.tgz#12029ba5fb2f8aa6f0a861795b23c1b4b6c27d37"
@@ -3163,16 +3310,16 @@ debug@=3.1.0, debug@~3.1.0:
     ms "2.0.0"
 
 debug@^3.2.6:
-  version "3.2.6"
-  resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
-  integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
+  version "3.2.7"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
+  integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==
   dependencies:
     ms "^2.1.1"
 
 debug@^4.0.1, debug@^4.1.0, debug@^4.1.1:
-  version "4.2.0"
-  resolved "https://registry.yarnpkg.com/debug/-/debug-4.2.0.tgz#7f150f93920e94c58f5574c2fd01a3110effe7f1"
-  integrity sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==
+  version "4.3.1"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee"
+  integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==
   dependencies:
     ms "2.1.2"
 
@@ -3391,40 +3538,46 @@ dom-serialize@^2.2.0:
     extend "^3.0.0"
     void-elements "^2.0.0"
 
-dom-serializer@0, dom-serializer@~0.1.1:
-  version "0.1.1"
-  resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.1.tgz#1ec4059e284babed36eec2941d4a970a189ce7c0"
-  integrity sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==
+dom-serializer@0:
+  version "0.2.2"
+  resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51"
+  integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==
+  dependencies:
+    domelementtype "^2.0.1"
+    entities "^2.0.0"
+
+dom-serializer@^1.0.1, dom-serializer@~1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.2.0.tgz#3433d9136aeb3c627981daa385fc7f32d27c48f1"
+  integrity sha512-n6kZFH/KlCrqs/1GHMOd5i2fd/beQHuehKdWvNNffbGHTr/almdhuVvTVFb3V7fglz+nC50fFusu3lY33h12pA==
   dependencies:
-    domelementtype "^1.3.0"
-    entities "^1.1.1"
+    domelementtype "^2.0.1"
+    domhandler "^4.0.0"
+    entities "^2.0.0"
 
 domain-browser@^1.1.1, domain-browser@^1.2.0:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda"
   integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==
 
-domelementtype@1, domelementtype@^1.3.0, domelementtype@^1.3.1:
+domelementtype@1:
   version "1.3.1"
   resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f"
   integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==
 
-domhandler@^2.3.0:
-  version "2.4.2"
-  resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803"
-  integrity sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==
-  dependencies:
-    domelementtype "1"
+domelementtype@^2.0.1, domelementtype@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.1.0.tgz#a851c080a6d1c3d94344aed151d99f669edf585e"
+  integrity sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w==
 
-domutils@1.5.1:
-  version "1.5.1"
-  resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf"
-  integrity sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=
+domhandler@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.0.0.tgz#01ea7821de996d85f69029e81fa873c21833098e"
+  integrity sha512-KPTbnGQ1JeEMQyO1iYXoagsI6so/C96HZiFyByU3T6iAzpXn8EGEvct6unm1ZGoed8ByO2oirxgwxBmqKF9haA==
   dependencies:
-    dom-serializer "0"
-    domelementtype "1"
+    domelementtype "^2.1.0"
 
-domutils@^1.5.1, domutils@^1.7.0:
+domutils@^1.7.0:
   version "1.7.0"
   resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a"
   integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==
@@ -3432,6 +3585,15 @@ domutils@^1.5.1, domutils@^1.7.0:
     dom-serializer "0"
     domelementtype "1"
 
+domutils@^2.4.3, domutils@^2.4.4:
+  version "2.4.4"
+  resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.4.4.tgz#282739c4b150d022d34699797369aad8d19bbbd3"
+  integrity sha512-jBC0vOsECI4OMdD0GC9mGn7NXPLb+Qt6KW1YDQzeQYRUFKmNG8lh7mO5HiELfr+lLQE7loDVI4QcAxV80HS+RA==
+  dependencies:
+    dom-serializer "^1.0.1"
+    domelementtype "^2.0.1"
+    domhandler "^4.0.0"
+
 dot-prop@^5.2.0:
   version "5.3.0"
   resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88"
@@ -3521,10 +3683,10 @@ ejs@~3.0.2:
   resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.0.2.tgz#745b01cdcfe38c1c6a2da3bbb2d9957060a31226"
   integrity sha512-IncmUpn1yN84hy2shb0POJ80FWrfGNY0cxO9f4v+/sG7qcBvAtVWUA1IdzY/8EYUmOVhoKJVdJjNd3AZcnxOjA==
 
-electron-to-chromium@^1.3.571:
-  version "1.3.578"
-  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.578.tgz#e6671936f4571a874eb26e2e833aa0b2c0b776e0"
-  integrity sha512-z4gU6dA1CbBJsAErW5swTGAaU2TBzc2mPAonJb00zqW1rOraDo2zfBMDRvaz9cVic+0JEZiYbHWPw/fTaZlG2Q==
+electron-to-chromium@^1.3.621:
+  version "1.3.633"
+  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.633.tgz#16dd5aec9de03894e8d14a1db4cda8a369b9b7fe"
+  integrity sha512-bsVCsONiVX1abkWdH7KtpuDAhsQ3N3bjPYhROSAXE78roJKet0Y5wznA14JE9pzbwSZmSMAW6KiKYf1RvbTJkA==
 
 elliptic@^6.5.3:
   version "6.5.3"
@@ -3615,10 +3777,10 @@ ent@~2.2.0:
   resolved "https://registry.yarnpkg.com/ent/-/ent-2.2.0.tgz#e964219325a21d05f44466a2f686ed6ce5f5dd1d"
   integrity sha1-6WQhkyWiHQX0RGai9obtbOX13R0=
 
-entities@^1.1.1, entities@~1.1.1:
-  version "1.1.2"
-  resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56"
-  integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==
+entities@^2.0.0, entities@~2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/entities/-/entities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5"
+  integrity sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==
 
 enzyme-adapter-react-16@^1.15.2:
   version "1.15.5"
@@ -3636,14 +3798,15 @@ enzyme-adapter-react-16@^1.15.2:
     semver "^5.7.0"
 
 enzyme-adapter-utils@^1.13.1:
-  version "1.13.1"
-  resolved "https://registry.yarnpkg.com/enzyme-adapter-utils/-/enzyme-adapter-utils-1.13.1.tgz#59c1b734b0927543e3d8dc477299ec957feb312d"
-  integrity sha512-5A9MXXgmh/Tkvee3bL/9RCAAgleHqFnsurTYCbymecO4ohvtNO5zqIhHxV370t7nJAwaCfkgtffarKpC0GPt0g==
+  version "1.14.0"
+  resolved "https://registry.yarnpkg.com/enzyme-adapter-utils/-/enzyme-adapter-utils-1.14.0.tgz#afbb0485e8033aa50c744efb5f5711e64fbf1ad0"
+  integrity sha512-F/z/7SeLt+reKFcb7597IThpDp0bmzcH1E9Oabqv+o01cID2/YInlqHbFl7HzWBl4h3OdZYedtwNDOmSKkk0bg==
   dependencies:
     airbnb-prop-types "^2.16.0"
-    function.prototype.name "^1.1.2"
-    object.assign "^4.1.0"
-    object.fromentries "^2.0.2"
+    function.prototype.name "^1.1.3"
+    has "^1.0.3"
+    object.assign "^4.1.2"
+    object.fromentries "^2.0.3"
     prop-types "^15.7.2"
     semver "^5.7.1"
 
@@ -3692,9 +3855,9 @@ enzyme@^3.11.0:
     string.prototype.trim "^1.2.1"
 
 errno@^0.1.3, errno@~0.1.7:
-  version "0.1.7"
-  resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618"
-  integrity sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==
+  version "0.1.8"
+  resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f"
+  integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==
   dependencies:
     prr "~1.0.1"
 
@@ -3705,7 +3868,7 @@ error-ex@^1.2.0, error-ex@^1.3.1:
   dependencies:
     is-arrayish "^0.2.1"
 
-es-abstract@^1.17.0, es-abstract@^1.17.0-next.1, es-abstract@^1.17.2, es-abstract@^1.17.4, es-abstract@^1.17.5:
+es-abstract@^1.17.0-next.1, es-abstract@^1.17.2, es-abstract@^1.17.4:
   version "1.17.7"
   resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.7.tgz#a4de61b2f66989fc7421676c1cb9787573ace54c"
   integrity sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==
@@ -3749,10 +3912,10 @@ es-to-primitive@^1.2.1:
     is-date-object "^1.0.1"
     is-symbol "^1.0.2"
 
-escalade@^3.1.0:
-  version "3.1.0"
-  resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.0.tgz#e8e2d7c7a8b76f6ee64c2181d6b8151441602d4e"
-  integrity sha512-mAk+hPSO8fLDkhV7V0dXazH5pDc6MrjBTPyD3VeKzxnVFjH1MIxbCdqGZB9O8+EwWakZs3ZCbDS4IpRt79V1ig==
+escalade@^3.1.1:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
+  integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
 
 escape-html@~1.0.3:
   version "1.0.3"
@@ -3765,20 +3928,20 @@ escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
   integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
 
 eslint-plugin-react@^7.20.5:
-  version "7.21.3"
-  resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.21.3.tgz#71655d2af5155b19285ec929dd2cdc67a4470b52"
-  integrity sha512-OI4GwTCqyIb4ipaOEGLWdaOHCXZZydStAsBEPB2e1ZfNM37bojpgO1BoOQbFb0eLVz3QLDx7b+6kYcrxCuJfhw==
+  version "7.21.5"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.21.5.tgz#50b21a412b9574bfe05b21db176e8b7b3b15bff3"
+  integrity sha512-8MaEggC2et0wSF6bUeywF7qQ46ER81irOdWS4QWxnnlAEsnzeBevk1sWh7fhpCghPpXb+8Ks7hvaft6L/xsR6g==
   dependencies:
     array-includes "^3.1.1"
     array.prototype.flatmap "^1.2.3"
     doctrine "^2.1.0"
     has "^1.0.3"
-    jsx-ast-utils "^2.4.1"
+    jsx-ast-utils "^2.4.1 || ^3.0.0"
     object.entries "^1.1.2"
     object.fromentries "^2.0.2"
     object.values "^1.1.1"
     prop-types "^15.7.2"
-    resolve "^1.17.0"
+    resolve "^1.18.1"
     string.prototype.matchall "^4.0.2"
 
 eslint-scope@^4.0.2, eslint-scope@^4.0.3:
@@ -4103,7 +4266,7 @@ fast-deep-equal@^1.0.0:
   resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614"
   integrity sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=
 
-fast-deep-equal@^3.1.1:
+fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
   version "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==
@@ -4249,7 +4412,7 @@ finalhandler@1.1.2, finalhandler@~1.1.2:
     statuses "~1.5.0"
     unpipe "~1.0.0"
 
-find-cache-dir@^2.0.0, find-cache-dir@^2.1.0:
+find-cache-dir@^2.1.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7"
   integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==
@@ -4267,6 +4430,11 @@ find-cache-dir@^3.3.1:
     make-dir "^3.0.2"
     pkg-dir "^4.1.0"
 
+find-root@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4"
+  integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==
+
 find-up@^1.0.0:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f"
@@ -4329,13 +4497,18 @@ flush-write-stream@^1.0.0:
     inherits "^2.0.3"
     readable-stream "^2.3.6"
 
-follow-redirects@1.5.10, follow-redirects@^1.0.0:
+follow-redirects@1.5.10:
   version "1.5.10"
   resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a"
   integrity sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==
   dependencies:
     debug "=3.1.0"
 
+follow-redirects@^1.0.0:
+  version "1.13.1"
+  resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.1.tgz#5f69b813376cee4fd0474a3aba835df04ab763b7"
+  integrity sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg==
+
 for-in@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
@@ -4427,29 +4600,30 @@ function-bind@^1.1.1:
   resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
   integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
 
-function.prototype.name@^1.1.2:
-  version "1.1.2"
-  resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.2.tgz#5cdf79d7c05db401591dfde83e3b70c5123e9a45"
-  integrity sha512-C8A+LlHBJjB2AdcRPorc5JvJ5VUoWlXdEHLOJdCI7kjHEtGTpHQUiqMvCIKUwIsGwZX2jZJy761AXsn356bJQg==
+function.prototype.name@^1.1.2, function.prototype.name@^1.1.3:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.3.tgz#0bb034bb308e7682826f215eb6b2ae64918847fe"
+  integrity sha512-H51qkbNSp8mtkJt+nyW1gyStBiKZxfRqySNUR99ylq6BPXHKI4SEvIlTKp4odLfjRKJV04DFWMU3G/YRlQOsag==
   dependencies:
+    call-bind "^1.0.0"
     define-properties "^1.1.3"
-    es-abstract "^1.17.0-next.1"
-    functions-have-names "^1.2.0"
+    es-abstract "^1.18.0-next.1"
+    functions-have-names "^1.2.1"
 
 functional-red-black-tree@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
   integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=
 
-functions-have-names@^1.2.0:
-  version "1.2.1"
-  resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.1.tgz#a981ac397fa0c9964551402cdc5533d7a4d52f91"
-  integrity sha512-j48B/ZI7VKs3sgeI2cZp7WXWmZXu7Iq5pl5/vptV5N2mq+DGFuS/ulaDjtaoLpYzuD6u8UgrUKHfgo7fDTSiBA==
+functions-have-names@^1.2.1:
+  version "1.2.2"
+  resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.2.tgz#98d93991c39da9361f8e50b337c4f6e41f120e21"
+  integrity sha512-bLgc3asbWdwPbx2mNk2S49kmJCuQeu0nfmaOgbs8WIyzzkw3r4htszdIi9Q9EMezDPTYuJx2wvjZ/EwgAthpnA==
 
 gensync@^1.0.0-beta.1:
-  version "1.0.0-beta.1"
-  resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269"
-  integrity sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==
+  version "1.0.0-beta.2"
+  resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
+  integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==
 
 geometry-interfaces@^1.1.4:
   version "1.1.4"
@@ -4466,6 +4640,15 @@ get-caller-file@^2.0.1:
   resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
   integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
 
+get-intrinsic@^1.0.0, get-intrinsic@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.0.2.tgz#6820da226e50b24894e08859469dc68361545d49"
+  integrity sha512-aeX0vrFm21ILl3+JpFFRNe9aUvp6VFZb2/CTbgLb8j75kOhvoNYjt9d8KA/tJG4gSo8nzEDedRl0h7vDmBYRVg==
+  dependencies:
+    function-bind "^1.1.1"
+    has "^1.0.3"
+    has-symbols "^1.0.1"
+
 get-proxy@^2.0.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/get-proxy/-/get-proxy-2.1.0.tgz#349f2b4d91d44c4d4d4e9cba2ad90143fac5ef93"
@@ -4657,6 +4840,13 @@ graceful-fs@^4.1.10, graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.
   resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb"
   integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==
 
+graphlib@^2.1.8:
+  version "2.1.8"
+  resolved "https://registry.yarnpkg.com/graphlib/-/graphlib-2.1.8.tgz#5761d414737870084c92ec7b5dbcb0592c9d35da"
+  integrity sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A==
+  dependencies:
+    lodash "^4.17.15"
+
 gzip-size@^5.0.0:
   version "5.1.1"
   resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-5.1.1.tgz#cb9bee692f87c0612b232840a873904e4c135274"
@@ -4771,6 +4961,11 @@ hat@^0.0.3:
   resolved "https://registry.yarnpkg.com/hat/-/hat-0.0.3.tgz#bb014a9e64b3788aed8005917413d4ff3d502d8a"
   integrity sha1-uwFKnmSzeIrtgAWRdBPU/z1QLYo=
 
+heap@0.2.5:
+  version "0.2.5"
+  resolved "https://registry.yarnpkg.com/heap/-/heap-0.2.5.tgz#713b65590ebcc40fcbeeaf55e851694092b39af1"
+  integrity sha1-cTtlWQ68xA/L7q9V6FFpQJKzmvE=
+
 hex-color-regex@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e"
@@ -4829,29 +5024,39 @@ html-escaper@^2.0.0:
   resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453"
   integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==
 
+html-to-image@^0.1.1:
+  version "0.1.3"
+  resolved "https://registry.yarnpkg.com/html-to-image/-/html-to-image-0.1.3.tgz#9661a54c30ed9c4b269f4612416b1c7e887de3a7"
+  integrity sha512-8JTGEAAdJGL/nlp3wb/WI8fLMx2dHKOFZMdsvdon23D45ZdtsXDeRm39Wddf04ludQe3OPmvjMJ9nPjI/7hPlg==
+
+html2canvas@^1.0.0-rc.7:
+  version "1.0.0-rc.7"
+  resolved "https://registry.yarnpkg.com/html2canvas/-/html2canvas-1.0.0-rc.7.tgz#70c159ce0e63954a91169531894d08ad5627ac98"
+  integrity sha512-yvPNZGejB2KOyKleZspjK/NruXVQuowu8NnV2HYG7gW7ytzl+umffbtUI62v2dCHQLDdsK6HIDtyJZ0W3neerA==
+  dependencies:
+    css-line-break "1.1.1"
+
 htmlescape@^1.1.0:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/htmlescape/-/htmlescape-1.1.1.tgz#3a03edc2214bca3b66424a3e7959349509cb0351"
   integrity sha1-OgPtwiFLyjtmQko+eVk0lQnLA1E=
 
-htmlparser2@^3.9.1:
-  version "3.10.1"
-  resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f"
-  integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==
+htmlparser2@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.0.0.tgz#c2da005030390908ca4c91e5629e418e0665ac01"
+  integrity sha512-numTQtDZMoh78zJpaNdJ9MXb2cv5G3jwUoe3dMQODubZvLoGvTE/Ofp6sHvH8OGKcN/8A47pGLi/k58xHP/Tfw==
   dependencies:
-    domelementtype "^1.3.1"
-    domhandler "^2.3.0"
-    domutils "^1.5.1"
-    entities "^1.1.1"
-    inherits "^2.0.1"
-    readable-stream "^3.1.1"
+    domelementtype "^2.0.1"
+    domhandler "^4.0.0"
+    domutils "^2.4.4"
+    entities "^2.0.0"
 
 http-cache-semantics@3.8.1:
   version "3.8.1"
   resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz#39b0e16add9b605bf0a9ef3d9daaf4843b4cacd2"
   integrity sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==
 
-http-errors@1.7.2, http-errors@~1.7.2:
+http-errors@1.7.2:
   version "1.7.2"
   resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f"
   integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==
@@ -4862,6 +5067,17 @@ http-errors@1.7.2, http-errors@~1.7.2:
     statuses ">= 1.5.0 < 2"
     toidentifier "1.0.0"
 
+http-errors@~1.7.2:
+  version "1.7.3"
+  resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06"
+  integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==
+  dependencies:
+    depd "~1.1.2"
+    inherits "2.0.4"
+    setprototypeof "1.1.1"
+    statuses ">= 1.5.0 < 2"
+    toidentifier "1.0.0"
+
 http-proxy@^1.13.0:
   version "1.18.1"
   resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549"
@@ -4906,10 +5122,10 @@ icss-utils@^4.0.0:
   dependencies:
     postcss "^7.0.14"
 
-ieee754@^1.1.4:
-  version "1.1.13"
-  resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84"
-  integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==
+ieee754@^1.1.13, ieee754@^1.1.4:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
+  integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
 
 iferr@^0.1.5:
   version "0.1.5"
@@ -5027,10 +5243,10 @@ import-fresh@^2.0.0:
     caller-path "^2.0.0"
     resolve-from "^3.0.0"
 
-import-fresh@^3.0.0:
-  version "3.2.1"
-  resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.2.1.tgz#633ff618506e793af5ac91bf48b72677e15cbe66"
-  integrity sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==
+import-fresh@^3.0.0, import-fresh@^3.1.0:
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
+  integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==
   dependencies:
     parent-module "^1.0.0"
     resolve-from "^4.0.0"
@@ -5103,7 +5319,7 @@ inflight@^1.0.4:
     once "^1.3.0"
     wrappy "1"
 
-inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3:
+inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3:
   version "2.0.4"
   resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
   integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
@@ -5119,9 +5335,9 @@ inherits@2.0.3:
   integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
 
 ini@^1.3.4, ini@^1.3.5:
-  version "1.3.5"
-  resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
-  integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==
+  version "1.3.8"
+  resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
+  integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
 
 inline-source-map@~0.6.0:
   version "0.6.2"
@@ -5150,9 +5366,9 @@ inquirer@^6.2.2:
     through "^2.3.6"
 
 insert-module-globals@^7.0.0:
-  version "7.2.0"
-  resolved "https://registry.yarnpkg.com/insert-module-globals/-/insert-module-globals-7.2.0.tgz#ec87e5b42728479e327bd5c5c71611ddfb4752ba"
-  integrity sha512-VE6NlW+WGn2/AeOMd496AHFYmE7eLKkUY6Ty31k4og5vmA3Fjuwe9v6ifH6Xx/Hz27QvdoMoviw1/pqWRB09Sw==
+  version "7.2.1"
+  resolved "https://registry.yarnpkg.com/insert-module-globals/-/insert-module-globals-7.2.1.tgz#d5e33185181a4e1f33b15f7bf100ee91890d5cb3"
+  integrity sha512-ufS5Qq9RZN+Bu899eA9QCAYThY+gGW7oRkmb0vC93Vlyu/CFGcH0OYPEjVkDXA5FEbTt1+VWzdoOD3Ny9N+8tg==
   dependencies:
     JSONStream "^1.0.3"
     acorn-node "^1.5.2"
@@ -5227,6 +5443,13 @@ is-accessor-descriptor@^1.0.0:
   dependencies:
     kind-of "^6.0.0"
 
+is-any-array@^0.1.0:
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/is-any-array/-/is-any-array-0.1.0.tgz#e5a27965e3371610c10c66bca3be37ee177bdf8e"
+  integrity sha512-6Kkl1RnvfdkmXM6ZlP+kELGBMA74Nq5pSOm9gIKDaPRe9KQlIJzonrOgq0Jzn/iElB6F2/olpLgWYeVySzrSRg==
+  dependencies:
+    rollup "^1.31.1"
+
 is-arrayish@^0.2.1:
   version "0.2.1"
   resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
@@ -5252,9 +5475,11 @@ is-binary-path@~2.1.0:
     binary-extensions "^2.0.0"
 
 is-boolean-object@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.0.1.tgz#10edc0900dd127697a92f6f9807c7617d68ac48e"
-  integrity sha512-TqZuVwa/sppcrhUCAYkGBk7w0yxfQQnxq28fjkO53tnK9FQXmdwz2JS5+GjsWQ6RByES1K40nI+yDic5c9/aAQ==
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.0.tgz#e2aaad3a3a8fca34c28f6eee135b156ed2587ff0"
+  integrity sha512-a7Uprx8UtD+HWdyYwnD1+ExtTgqQtD2k/1yJgtXP6wnMm8byhkoTZRl+95LLThpzNZJ5aEvi46cdH+ayMFRwmA==
+  dependencies:
+    call-bind "^1.0.0"
 
 is-buffer@^1.1.0, is-buffer@^1.1.5:
   version "1.1.6"
@@ -5262,9 +5487,9 @@ is-buffer@^1.1.0, is-buffer@^1.1.5:
   integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
 
 is-buffer@^2.0.2, is-buffer@^2.0.3:
-  version "2.0.4"
-  resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.4.tgz#3e572f23c8411a5cfd9557c849e3665e0b290623"
-  integrity sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191"
+  integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==
 
 is-callable@^1.1.4, is-callable@^1.1.5, is-callable@^1.2.2:
   version "1.2.2"
@@ -5283,6 +5508,13 @@ is-color-stop@^1.0.0:
     rgb-regex "^1.0.1"
     rgba-regex "^1.0.0"
 
+is-core-module@^2.1.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.2.0.tgz#97037ef3d52224d85163f5597b2b63d9afed981a"
+  integrity sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==
+  dependencies:
+    has "^1.0.3"
+
 is-cwebp-readable@^2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/is-cwebp-readable/-/is-cwebp-readable-2.0.1.tgz#afb93b0c0abd0a25101016ae33aea8aedf926d26"
@@ -5396,9 +5628,9 @@ is-natural-number@^4.0.1:
   integrity sha1-q5124dtM7VHjXeDHLr7PCfc0zeg=
 
 is-negative-zero@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.0.tgz#9553b121b0fac28869da9ed459e20c7543788461"
-  integrity sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24"
+  integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==
 
 is-number-object@^1.0.4:
   version "1.0.4"
@@ -5423,9 +5655,9 @@ is-obj@^2.0.0:
   integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==
 
 is-object@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.1.tgz#8952688c5ec2ffd6b03ecc85e769e02903083470"
-  integrity sha1-iVJojF7C/9awPsyF52ngKQMINHA=
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.2.tgz#a56552e1c665c9e950b4a025461da87e72f86fcf"
+  integrity sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA==
 
 is-plain-obj@^1.0.0, is-plain-obj@^1.1.0:
   version "1.1.0"
@@ -5628,7 +5860,12 @@ isurl@^1.0.0-alpha5:
     has-to-string-tag-x "^1.2.0"
     is-object "^1.0.1"
 
-jasmine-core@^3.3, jasmine-core@~3.3.0:
+jasmine-core@^3.3:
+  version "3.6.0"
+  resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-3.6.0.tgz#491f3bb23941799c353ceb7a45b38a950ebc5a20"
+  integrity sha512-8uQYa7zJN8hq9z+g8z1bqCfdC8eoDAeVnM5sfqs7KHv9/ifoJ500m018fpFc7RDaO6SWCLCXwo/wPSNcdYTgcw==
+
+jasmine-core@~3.3.0:
   version "3.3.0"
   resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-3.3.0.tgz#dea1cdc634bc93c7e0d4ad27185df30fa971b10e"
   integrity sha512-3/xSmG/d35hf80BEN66Y6g9Ca5l/Isdeg/j6zvbTYlTzeKinzmaTM4p9am5kYqOmE05D7s1t8FGjzdSnbUbceA==
@@ -5660,16 +5897,11 @@ jquery-ui@>=1.8.0, jquery-ui@^1.12.1:
   resolved "https://registry.yarnpkg.com/jquery-ui/-/jquery-ui-1.12.1.tgz#bcb4045c8dd0539c134bc1488cdd3e768a7a9e51"
   integrity sha1-vLQEXI3QU5wTS8FIjN0+dop6nlE=
 
-jquery@>=1.2.6, "jquery@>=1.7.1 <4.0.0", jquery@>=1.8.0, jquery@^3.0, jquery@^3.3.1, jquery@^3.5.0, jquery@^3.5.1:
+jquery@>=1.2.6, "jquery@>=1.7.1 <4.0.0", jquery@>=1.8.0, jquery@^3.3.1, jquery@^3.5.0, jquery@^3.5.1:
   version "3.5.1"
   resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.5.1.tgz#d7b4d08e1bfdb86ad2f1a3d039ea17304717abb5"
   integrity sha512-XwIBPqcMn57FxfT+Go5pzySnm4KWkT1Tv7gjrpT1srtf8Weynl6R273VJ5GjkRb51IzMp5nbaPjJXMWeju2MKg==
 
-js-levenshtein@^1.1.3:
-  version "1.1.6"
-  resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.6.tgz#c6cee58eb3550372df8deb85fad5ce66ce01d59d"
-  integrity sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==
-
 js-string-escape@^1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/js-string-escape/-/js-string-escape-1.0.1.tgz#e2625badbc0d67c7533e9edc1068c587ae4137ef"
@@ -5686,9 +5918,9 @@ js-tokens@^3.0.2:
   integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls=
 
 js-yaml@^3.12.0, js-yaml@^3.13.1:
-  version "3.14.0"
-  resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482"
-  integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==
+  version "3.14.1"
+  resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537"
+  integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==
   dependencies:
     argparse "^1.0.7"
     esprima "^4.0.0"
@@ -5730,6 +5962,11 @@ json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2:
   resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9"
   integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==
 
+json-parse-even-better-errors@^2.3.0:
+  version "2.3.1"
+  resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d"
+  integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==
+
 json-schema-traverse@^0.3.0:
   version "0.3.1"
   resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340"
@@ -5759,7 +5996,7 @@ json5@^1.0.1:
   dependencies:
     minimist "^1.2.0"
 
-json5@^2.1.0, json5@^2.1.2:
+json5@^2.1.2:
   version "2.1.3"
   resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.3.tgz#c9b0f7fa9233bfe5807fe66fcf3a5617ed597d43"
   integrity sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==
@@ -5783,13 +6020,13 @@ jsonparse@^1.2.0:
   resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280"
   integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=
 
-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"
-  integrity sha512-z1xSldJ6imESSzOjd3NNkieVJKRlKYSOtMG8SFyCj2FIrvSaSuli/WjpBkEzCBoR9bYYYFgqJw61Xhu7Lcgk+w==
+"jsx-ast-utils@^2.4.1 || ^3.0.0":
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.2.0.tgz#41108d2cec408c3453c1bbe8a4aae9e1e2bd8f82"
+  integrity sha512-EIsmt3O3ljsU6sot/J4E1zDRxfBNrhjyf/OKjlydwgEimQuznlM4Wv7U+ueONJMyEn1WRE0K8dhi3dVAXYT24Q==
   dependencies:
-    array-includes "^3.1.1"
-    object.assign "^4.1.0"
+    array-includes "^3.1.2"
+    object.assign "^4.1.2"
 
 karma-babel-preprocessor@^8.0.0:
   version "8.0.1"
@@ -5963,6 +6200,11 @@ levn@^0.3.0, levn@~0.3.0:
     prelude-ls "~1.1.2"
     type-check "~0.3.2"
 
+lines-and-columns@^1.1.6:
+  version "1.1.6"
+  resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00"
+  integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=
+
 load-json-file@^1.0.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0"
@@ -6093,7 +6335,7 @@ lodash.uniq@^4.5.0:
   resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
   integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
 
-lodash@^4.14.1, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, 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.*, lodash@^4.14.1, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.4, lodash@^4.17.5:
   version "4.17.20"
   resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
   integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
@@ -6192,7 +6434,7 @@ make-dir@^2.0.0:
     pify "^4.0.1"
     semver "^5.6.0"
 
-make-dir@^3.0.0, make-dir@^3.0.2:
+make-dir@^3.0.0, make-dir@^3.0.2, make-dir@^3.1.0:
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f"
   integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==
@@ -6217,9 +6459,14 @@ map-visit@^1.0.0:
     object-visit "^1.0.0"
 
 marked@^1.1.1:
-  version "1.2.0"
-  resolved "https://registry.yarnpkg.com/marked/-/marked-1.2.0.tgz#7221ce2395fa6cf6d722e6f2871a32d3513c85ca"
-  integrity sha512-tiRxakgbNPBr301ihe/785NntvYyhxlqcL3YaC8CaxJQh7kiaEtrN9B/eK2I2943Yjkh5gw25chYFDQhOMCwMA==
+  version "1.2.7"
+  resolved "https://registry.yarnpkg.com/marked/-/marked-1.2.7.tgz#6e14b595581d2319cdcf033a24caaf41455a01fb"
+  integrity sha512-No11hFYcXr/zkBvL6qFmAp1z6BKY3zqLMHny/JN/ey+al7qwCM2+CMBL9BOgqMxZU36fz4cCWfn2poWIf7QRXA==
+
+matches-selector@0.0.1:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/matches-selector/-/matches-selector-0.0.1.tgz#1df5262243ae341c1a0804dd302048267ac713bb"
+  integrity sha1-HfUmIkOuNBwaCATdMCBIJnrHE7s=
 
 md5.js@^1.3.4:
   version "1.3.5"
@@ -6230,16 +6477,16 @@ md5.js@^1.3.4:
     inherits "^2.0.1"
     safe-buffer "^5.1.2"
 
+mdn-data@2.0.14:
+  version "2.0.14"
+  resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50"
+  integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==
+
 mdn-data@2.0.4:
   version "2.0.4"
   resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b"
   integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==
 
-mdn-data@2.0.6:
-  version "2.0.6"
-  resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.6.tgz#852dc60fcaa5daa2e8cf6c9189c440ed3e042978"
-  integrity sha512-rQvjv71olwNHgiTbfPZFkJtjNMciWgswYeciZhtvWLO8bmX3TnhyA62I6sTWOyZssWHJJjY6/KiWwqQsWWsqOA==
-
 media-typer@0.3.0:
   version "0.3.0"
   resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
@@ -6347,9 +6594,9 @@ mime@1.6.0:
   integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
 
 mime@^2.0.3, mime@^2.3.1, mime@^2.4.4:
-  version "2.4.6"
-  resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.6.tgz#e5b407c90db442f2beb5b162373d07b69affa4d1"
-  integrity sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==
+  version "2.4.7"
+  resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.7.tgz#962aed9be0ed19c91fd7dc2ece5d7f4e89a90d74"
+  integrity sha512-dhNd1uA2u397uQk3Nv5LM4lm93WYDUXFn3Fu291FJerns4jyTudqhIWe4W04YLy7Uk1tm1Ore04NpjRvQp/NPA==
 
 mimic-fn@^1.0.0:
   version "1.2.0"
@@ -6449,6 +6696,11 @@ mixin-deep@^1.2.0:
     for-in "^1.0.2"
     is-extendable "^1.0.1"
 
+mkdirp-classic@^0.5.2:
+  version "0.5.3"
+  resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113"
+  integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==
+
 mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@~0.5.1:
   version "0.5.5"
   resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
@@ -6456,7 +6708,37 @@ mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@~0.5.1:
   dependencies:
     minimist "^1.2.5"
 
-module-deps@^6.0.0:
+ml-array-max@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/ml-array-max/-/ml-array-max-1.2.0.tgz#141595131fe10208dd89897ce98ab7fd382a3951"
+  integrity sha512-3UH7XCdjINxbtBWj1EuHMeI242Q3uLuC4rTpSybBWUpGjnG/BefAFxmTolUCuXDM59mJ/G/re80CQbaVIuMjQA==
+  dependencies:
+    is-any-array "^0.1.0"
+
+ml-array-min@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/ml-array-min/-/ml-array-min-1.2.0.tgz#6880ea319250a99ec73bc2799ac005c10a0f0489"
+  integrity sha512-Wgf2+lCndLy1SbeOZSUqlkxD9T1CXPT7CIlNGAZRRQI35wsqvfuNtLNH4qKFx8kNjlq3VGXKOSBHeiXR31vaTA==
+  dependencies:
+    is-any-array "^0.1.0"
+
+ml-array-rescale@^1.3.2:
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/ml-array-rescale/-/ml-array-rescale-1.3.2.tgz#ebdeaaa84d15f714dbb00e94a6a9336ddcf10413"
+  integrity sha512-kiXwdVCGrer7rLnjR6Q9ZgP6e9rbnmQvYVUMLXyqNg4+zOs+jek8yBupqPZPDr+NvlSE5OuMnfAbP1oA63kHBA==
+  dependencies:
+    is-any-array "^0.1.0"
+    ml-array-max "^1.2.0"
+    ml-array-min "^1.2.0"
+
+ml-matrix@^6.5.0:
+  version "6.5.3"
+  resolved "https://registry.yarnpkg.com/ml-matrix/-/ml-matrix-6.5.3.tgz#0c4bb26714607cac76d289b943171c7083418eed"
+  integrity sha512-wXrn+ccApJ6gHktxmosOzs6B6M0huadahDpcgPYIAJggpqN7CtV4Vd7zpW6Lel/1oM5yCULcrbRJ1A5gF/GYDA==
+  dependencies:
+    ml-array-rescale "^1.3.2"
+
+module-deps@^6.0.0, module-deps@^6.2.3:
   version "6.2.3"
   resolved "https://registry.yarnpkg.com/module-deps/-/module-deps-6.2.3.tgz#15490bc02af4b56cf62299c7c17cba32d71a96ee"
   integrity sha512-fg7OZaQBcL4/L+AK5f4iVqf9OMbCclXfy/znXRxTVhJSeW5AIlS9AwheYwDaXM3lVW7OBeaeUEY3gbaC6cLlSA==
@@ -6477,25 +6759,23 @@ module-deps@^6.0.0:
     through2 "^2.0.0"
     xtend "^4.0.0"
 
-moment-timezone@^0.4.0:
-  version "0.4.1"
-  resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.4.1.tgz#81f598c3ad5e22cdad796b67ecd8d88d0f5baa06"
-  integrity sha1-gfWYw61eIs2teWtn7NjYjQ9bqgY=
-  dependencies:
-    moment ">= 2.6.0"
-
-moment-timezone@^0.5.11, moment-timezone@^0.5.23:
-  version "0.5.31"
-  resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.31.tgz#9c40d8c5026f0c7ab46eda3d63e49c155148de05"
-  integrity sha512-+GgHNg8xRhMXfEbv81iDtrVeTcWt0kWmTEY1XQK14dICTXnWJnT0dxdlPspwqF3keKMVPXwayEsk1DI0AA/jdA==
+moment-timezone@^0.5.23, moment-timezone@^0.5.28, moment-timezone@^0.5.31:
+  version "0.5.32"
+  resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.32.tgz#db7677cc3cc680fd30303ebd90b0da1ca0dfecc2"
+  integrity sha512-Z8QNyuQHQAmWucp8Knmgei8YNo28aLjJq6Ma+jy1ZSpSk5nyfRT8xgUbSQvD2+2UajISfenndwvFuH3NGS+nvA==
   dependencies:
     moment ">= 2.9.0"
 
-"moment@>= 2.6.0", "moment@>= 2.9.0", moment@^2.10.2, moment@^2.22.2, moment@^2.24.0:
+"moment@>= 2.9.0", moment@^2.10.2, moment@^2.24.0, moment@^2.29.0:
   version "2.29.1"
   resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
   integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==
 
+moment@~2.24.0:
+  version "2.24.0"
+  resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b"
+  integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==
+
 moo@^0.5.0:
   version "0.5.1"
   resolved "https://registry.yarnpkg.com/moo/-/moo-0.5.1.tgz#7aae7f384b9b09f620b6abf6f74ebbcd1b65dbc4"
@@ -6537,20 +6817,25 @@ ms@2.1.1:
   resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
   integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
 
-ms@2.1.2, ms@^2.1.1:
+ms@2.1.2:
   version "2.1.2"
   resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
   integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
 
+ms@^2.1.1:
+  version "2.1.3"
+  resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
+  integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
+
 mute-stream@0.0.7:
   version "0.0.7"
   resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
   integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=
 
 nan@^2.12.1:
-  version "2.14.1"
-  resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01"
-  integrity sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==
+  version "2.14.2"
+  resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19"
+  integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==
 
 nanomatch@^1.2.9:
   version "1.2.13"
@@ -6580,15 +6865,14 @@ natural-compare@^1.4.0:
   integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
 
 nearley@^2.7.10:
-  version "2.19.7"
-  resolved "https://registry.yarnpkg.com/nearley/-/nearley-2.19.7.tgz#eafbe3e2d8ccfe70adaa5c026ab1f9709c116218"
-  integrity sha512-Y+KNwhBPcSJKeyQCFjn8B/MIe+DDlhaaDgjVldhy5xtFewIbiQgcbZV8k2gCVwkI1ZsKCnjIYZbR+0Fim5QYgg==
+  version "2.20.1"
+  resolved "https://registry.yarnpkg.com/nearley/-/nearley-2.20.1.tgz#246cd33eff0d012faf197ff6774d7ac78acdd474"
+  integrity sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==
   dependencies:
     commander "^2.19.0"
     moo "^0.5.0"
     railroad-diagrams "^1.0.0"
     randexp "0.4.6"
-    semver "^5.4.1"
 
 neatequal@^1.0.0:
   version "1.0.0"
@@ -6641,10 +6925,10 @@ node-libs-browser@^2.2.1:
     util "^0.11.0"
     vm-browserify "^1.0.1"
 
-node-releases@^1.1.61:
-  version "1.1.61"
-  resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.61.tgz#707b0fca9ce4e11783612ba4a2fcba09047af16e"
-  integrity sha512-DD5vebQLg8jLCOzwupn954fbIiZht05DAZs0k2u8NStSe6h9XdsuIQL8hSRKYiU8WUQRznmSDrKGbv3ObOmC7g==
+node-releases@^1.1.67:
+  version "1.1.67"
+  resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.67.tgz#28ebfcccd0baa6aad8e8d4d8fe4cbc49ae239c12"
+  integrity sha512-V5QF9noGFl3EymEwUYzO+3NTDpGfQB4ve6Qfnzf3UNydMhjQRVPR1DZTuvWiLzaFJYw2fmDwAfnRNEVb64hSIg==
 
 normalize-package-data@^2.3.2, normalize-package-data@^2.3.4:
   version "2.5.0"
@@ -6702,13 +6986,20 @@ npm-run-path@^2.0.0:
   dependencies:
     path-key "^2.0.0"
 
-nth-check@^1.0.2, nth-check@~1.0.1:
+nth-check@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c"
   integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==
   dependencies:
     boolbase "~1.0.0"
 
+nth-check@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.0.0.tgz#1bb4f6dac70072fc313e8c9cd1417b5074c0a125"
+  integrity sha512-i4sc/Kj8htBrAiH1viZ0TgU8Y5XqCaV/FziYK6TBczxmeKm3AEFWqqF3195yKudrarqy7Zu80Ra5dobFjn9X/Q==
+  dependencies:
+    boolbase "^1.0.0"
+
 null-check@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/null-check/-/null-check-1.0.0.tgz#977dffd7176012b9ec30d2a39db5cf72a0439edd"
@@ -6739,17 +7030,17 @@ object-copy@^0.1.0:
     kind-of "^3.0.3"
 
 object-inspect@^1.7.0, object-inspect@^1.8.0:
-  version "1.8.0"
-  resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.8.0.tgz#df807e5ecf53a609cc6bfe93eac3cc7be5b3a9d0"
-  integrity sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==
+  version "1.9.0"
+  resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.9.0.tgz#c90521d74e1127b67266ded3394ad6116986533a"
+  integrity sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==
 
 object-is@^1.0.2, object-is@^1.1.2:
-  version "1.1.3"
-  resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.3.tgz#2e3b9e65560137455ee3bd62aec4d90a2ea1cc81"
-  integrity sha512-teyqLvFWzLkq5B9ki8FVWA902UER2qkxmdA4nLf+wjOLAWgxzCWZNCxpDq9MvE8MmhWNr+I8w3BN49Vx36Y6Xg==
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.4.tgz#63d6c83c00a43f4cbc9434eb9757c8a5b8565068"
+  integrity sha512-1ZvAZ4wlF7IyPVOcE1Omikt7UpaFlOQq0HlSti+ZvDH3UiD2brwGMwDbyV43jao2bKJ+4+WdPJHSd7kgzKYVqg==
   dependencies:
+    call-bind "^1.0.0"
     define-properties "^1.1.3"
-    es-abstract "^1.18.0-next.1"
 
 object-keys@^1.0.12, object-keys@^1.1.1:
   version "1.1.1"
@@ -6763,42 +7054,44 @@ object-visit@^1.0.0:
   dependencies:
     isobject "^3.0.0"
 
-object.assign@^4.1.0, object.assign@^4.1.1:
-  version "4.1.1"
-  resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.1.tgz#303867a666cdd41936ecdedfb1f8f3e32a478cdd"
-  integrity sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==
+object.assign@^4.1.0, object.assign@^4.1.1, object.assign@^4.1.2:
+  version "4.1.2"
+  resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940"
+  integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==
   dependencies:
+    call-bind "^1.0.0"
     define-properties "^1.1.3"
-    es-abstract "^1.18.0-next.0"
     has-symbols "^1.0.1"
     object-keys "^1.1.1"
 
 object.entries@^1.1.1, object.entries@^1.1.2:
-  version "1.1.2"
-  resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.2.tgz#bc73f00acb6b6bb16c203434b10f9a7e797d3add"
-  integrity sha512-BQdB9qKmb/HyNdMNWVr7O3+z5MUIx3aiegEIJqjMBbBf0YT9RRxTJSim4mzFqtyr7PDAHigq0N9dO0m0tRakQA==
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.3.tgz#c601c7f168b62374541a07ddbd3e2d5e4f7711a6"
+  integrity sha512-ym7h7OZebNS96hn5IJeyUmaWhaSM4SVtAPPfNLQEI2MYWCO2egsITb9nab2+i/Pwibx+R0mtn+ltKJXRSeTMGg==
   dependencies:
+    call-bind "^1.0.0"
     define-properties "^1.1.3"
-    es-abstract "^1.17.5"
+    es-abstract "^1.18.0-next.1"
     has "^1.0.3"
 
-object.fromentries@^2.0.2:
-  version "2.0.2"
-  resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.2.tgz#4a09c9b9bb3843dd0f89acdb517a794d4f355ac9"
-  integrity sha512-r3ZiBH7MQppDJVLx6fhD618GKNG40CZYH9wgwdhKxBDDbQgjeWGGd4AtkZad84d291YxvWe7bJGuE65Anh0dxQ==
+object.fromentries@^2.0.2, object.fromentries@^2.0.3:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.3.tgz#13cefcffa702dc67750314a3305e8cb3fad1d072"
+  integrity sha512-IDUSMXs6LOSJBWE++L0lzIbSqHl9KDCfff2x/JSEIDtEUavUnyMYC2ZGay/04Zq4UT8lvd4xNhU4/YHKibAOlw==
   dependencies:
+    call-bind "^1.0.0"
     define-properties "^1.1.3"
-    es-abstract "^1.17.0-next.1"
-    function-bind "^1.1.1"
+    es-abstract "^1.18.0-next.1"
     has "^1.0.3"
 
 object.getownpropertydescriptors@^2.1.0:
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz#369bf1f9592d8ab89d712dced5cb81c7c5352649"
-  integrity sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.1.tgz#0dfda8d108074d9c563e80490c883b6661091544"
+  integrity sha512-6DtXgZ/lIZ9hqx4GtZETobXLR/ZLaa0aqV0kzbn80Rf8Z2e/XFnhA0I7p07N2wH8bBBltr2xQPi6sbKWAY2Eng==
   dependencies:
+    call-bind "^1.0.0"
     define-properties "^1.1.3"
-    es-abstract "^1.17.0-next.1"
+    es-abstract "^1.18.0-next.1"
 
 object.pick@^1.3.0:
   version "1.3.0"
@@ -6808,13 +7101,13 @@ object.pick@^1.3.0:
     isobject "^3.0.1"
 
 object.values@^1.1.0, object.values@^1.1.1:
-  version "1.1.1"
-  resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.1.tgz#68a99ecde356b7e9295a3c5e0ce31dc8c953de5e"
-  integrity sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.2.tgz#7a2015e06fcb0f546bd652486ce8583a4731c731"
+  integrity sha512-MYC0jvJopr8EK6dPBiO8Nb9mvjdypOachO5REGk6MXzujbBrAisKo3HmdEI6kZDL6fC31Mwee/5YbtMebixeag==
   dependencies:
+    call-bind "^1.0.0"
     define-properties "^1.1.3"
-    es-abstract "^1.17.0-next.1"
-    function-bind "^1.1.1"
+    es-abstract "^1.18.0-next.1"
     has "^1.0.3"
 
 on-finished@~2.3.0:
@@ -7061,17 +7354,32 @@ parse-json@^4.0.0:
     error-ex "^1.3.1"
     json-parse-better-errors "^1.0.1"
 
+parse-json@^5.0.0:
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.1.0.tgz#f96088cdf24a8faa9aea9a009f2d9d942c999646"
+  integrity sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==
+  dependencies:
+    "@babel/code-frame" "^7.0.0"
+    error-ex "^1.3.1"
+    json-parse-even-better-errors "^2.3.0"
+    lines-and-columns "^1.1.6"
+
 parse-passwd@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6"
   integrity sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=
 
-parse5@^3.0.1:
-  version "3.0.3"
-  resolved "https://registry.yarnpkg.com/parse5/-/parse5-3.0.3.tgz#042f792ffdd36851551cf4e9e066b3874ab45b5c"
-  integrity sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA==
+parse5-htmlparser2-tree-adapter@^6.0.0:
+  version "6.0.1"
+  resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz#2cdf9ad823321140370d4dbf5d3e92c7c8ddc6e6"
+  integrity sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==
   dependencies:
-    "@types/node" "*"
+    parse5 "^6.0.1"
+
+parse5@^6.0.0, parse5@^6.0.1:
+  version "6.0.1"
+  resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b"
+  integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==
 
 parseqs@0.0.5:
   version "0.0.5"
@@ -7170,6 +7478,23 @@ path-type@^3.0.0:
   dependencies:
     pify "^3.0.0"
 
+path-type@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
+  integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
+
+pathfinding@^0.4.18:
+  version "0.4.18"
+  resolved "https://registry.yarnpkg.com/pathfinding/-/pathfinding-0.4.18.tgz#a9990f6fa22b7ef196e5651b049165403a045fe8"
+  integrity sha1-qZkPb6IrfvGW5WUbBJFlQDoEX+g=
+  dependencies:
+    heap "0.2.5"
+
+paths-js@^0.4.9:
+  version "0.4.11"
+  resolved "https://registry.yarnpkg.com/paths-js/-/paths-js-0.4.11.tgz#b2a9d5f94ee9949aa8fee945f78a12abff44599e"
+  integrity sha512-3mqcLomDBXOo7Fo+UlaenG6f71bk1ZezPQy2JCmYHy2W2k5VKpP+Jbin9H0bjXynelTbglCqdFhSEkeIkKTYUA==
+
 pbkdf2@^3.0.3:
   version "3.1.1"
   resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.1.tgz#cb8724b0fada984596856d1a6ebafd3584654b94"
@@ -7247,7 +7572,7 @@ pngquant-bin@^5.0.0:
     execa "^0.10.0"
     logalot "^2.0.0"
 
-popper.js@^1.14.3, popper.js@^1.14.7:
+popper.js@^1.14.7, popper.js@^1.16.1:
   version "1.16.1"
   resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.16.1.tgz#2a223cb3dc7b6213d740e40372be40de43e65b1b"
   integrity sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==
@@ -7621,11 +7946,6 @@ prepend-http@^2.0.0:
   resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897"
   integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=
 
-private@^0.1.6:
-  version "0.1.8"
-  resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff"
-  integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==
-
 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"
@@ -7823,18 +8143,18 @@ raw-body@2.4.0:
     iconv-lite "0.4.24"
     unpipe "1.0.0"
 
-raw-loader@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-1.0.0.tgz#3f9889e73dadbda9a424bce79809b4133ad46405"
-  integrity sha512-Uqy5AqELpytJTRxYT4fhltcKPj0TyaEpzJDcGz7DFJi+pQOOi3GjR/DOdxTkTsF+NzhnldIoG6TORaBlInUuqA==
+raw-loader@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-3.1.0.tgz#5e9d399a5a222cc0de18f42c3bc5e49677532b3f"
+  integrity sha512-lzUVMuJ06HF4rYveaz9Tv0WRlUMxJ0Y1hgSkkgg+50iEdaI0TthyEDe08KIHb0XsF6rn8WYTqPCaGTZg3sX+qA==
   dependencies:
     loader-utils "^1.1.0"
-    schema-utils "^1.0.0"
+    schema-utils "^2.0.1"
 
 react-dom@^16.13.1:
-  version "16.13.1"
-  resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.13.1.tgz#c1bd37331a0486c078ee54c4740720993b2e0e7f"
-  integrity sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag==
+  version "16.14.0"
+  resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.14.0.tgz#7ad838ec29a777fb3c75c3a190f661cf92ab8b89"
+  integrity sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==
   dependencies:
     loose-envify "^1.1.0"
     object-assign "^4.1.1"
@@ -7847,19 +8167,26 @@ react-is@^16.13.1, react-is@^16.8.1, react-is@^16.8.6:
   integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
 
 react-test-renderer@^16.0.0-0:
-  version "16.13.1"
-  resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.13.1.tgz#de25ea358d9012606de51e012d9742e7f0deabc1"
-  integrity sha512-Sn2VRyOK2YJJldOqoh8Tn/lWQ+ZiKhyZTPtaO0Q6yNj+QDbmRkVFap6pZPy3YQk8DScRDfyqm/KxKYP9gCMRiQ==
+  version "16.14.0"
+  resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.14.0.tgz#e98360087348e260c56d4fe2315e970480c228ae"
+  integrity sha512-L8yPjqPE5CZO6rKsKXRO/rVPiaCOy0tQQJbC+UjPNlobl5mad59lvPjwFsQHTvL03caVDIVr9x9/OSgDe6I5Eg==
   dependencies:
     object-assign "^4.1.1"
     prop-types "^15.6.2"
     react-is "^16.8.6"
     scheduler "^0.19.1"
 
+react-to-print@^2.10.3:
+  version "2.12.1"
+  resolved "https://registry.yarnpkg.com/react-to-print/-/react-to-print-2.12.1.tgz#120f3116fef55a141f553548388624b976596835"
+  integrity sha512-+zGNUYQKaae7Wp0JL2JcoWM0lDd5csasRmTqRZlJLOj6F4sRmJsT7ZPgH0SL6ZQ7gWz0hwglhLlLoyF85lGgHw==
+  dependencies:
+    prop-types "^15.7.2"
+
 react@^16.13.1:
-  version "16.13.1"
-  resolved "https://registry.yarnpkg.com/react/-/react-16.13.1.tgz#2e818822f1a9743122c063d6410d85c1e3afe48e"
-  integrity sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w==
+  version "16.14.0"
+  resolved "https://registry.yarnpkg.com/react/-/react-16.14.0.tgz#94d776ddd0aaa37da3eda8fc5b6b18a4c9a3114d"
+  integrity sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==
   dependencies:
     loose-envify "^1.1.0"
     object-assign "^4.1.1"
@@ -7912,7 +8239,7 @@ readable-stream@^1.0.33:
     isarray "0.0.1"
     string_decoder "~0.10.x"
 
-readable-stream@^3.1.1, readable-stream@^3.6.0:
+readable-stream@^3.6.0:
   version "3.6.0"
   resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
   integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
@@ -7930,10 +8257,10 @@ readdirp@^2.2.1:
     micromatch "^3.1.10"
     readable-stream "^2.0.2"
 
-readdirp@~3.4.0:
-  version "3.4.0"
-  resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.4.0.tgz#9fdccdf9e9155805449221ac645e8303ab5b9ada"
-  integrity sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==
+readdirp@~3.5.0:
+  version "3.5.0"
+  resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e"
+  integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==
   dependencies:
     picomatch "^2.2.1"
 
@@ -7957,10 +8284,10 @@ regenerate-unicode-properties@^8.2.0:
   dependencies:
     regenerate "^1.4.0"
 
-regenerate@^1.2.1, regenerate@^1.4.0:
-  version "1.4.1"
-  resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.1.tgz#cad92ad8e6b591773485fbe05a485caf4f457e6f"
-  integrity sha512-j2+C8+NtXQgEKWk49MMP5P/u2GhnahTtVkRIHr5R5lVRlbKvmQ+oS+A5aLKWp2ma5VkT8sh6v+v4hbH0YHR66A==
+regenerate@^1.4.0:
+  version "1.4.2"
+  resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a"
+  integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==
 
 regenerator-runtime@^0.11.0:
   version "0.11.1"
@@ -7972,15 +8299,6 @@ regenerator-runtime@^0.13.4:
   resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55"
   integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==
 
-regenerator-transform@^0.10.0:
-  version "0.10.1"
-  resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.10.1.tgz#1e4996837231da8b7f3cf4114d71b5691a0680dd"
-  integrity sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==
-  dependencies:
-    babel-runtime "^6.18.0"
-    babel-types "^6.19.0"
-    private "^0.1.6"
-
 regenerator-transform@^0.14.2:
   version "0.14.5"
   resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.5.tgz#c98da154683671c9c4dcb16ece736517e1b7feb4"
@@ -8009,16 +8327,7 @@ regexpp@^2.0.1:
   resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f"
   integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==
 
-regexpu-core@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-2.0.0.tgz#49d038837b8dcf8bfa5b9a42139938e6ea2ae240"
-  integrity sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=
-  dependencies:
-    regenerate "^1.2.1"
-    regjsgen "^0.2.0"
-    regjsparser "^0.1.4"
-
-regexpu-core@^4.7.0:
+regexpu-core@^4.7.1:
   version "4.7.1"
   resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.7.1.tgz#2dea5a9a07233298fbf0db91fa9abc4c6e0f8ad6"
   integrity sha512-ywH2VUraA44DZQuRKzARmw6S66mr48pQVva4LBeRhcOltJ6hExvWly5ZjFLYo67xbIxb6W1q4bAGtgfEl20zfQ==
@@ -8030,23 +8339,11 @@ regexpu-core@^4.7.0:
     unicode-match-property-ecmascript "^1.0.4"
     unicode-match-property-value-ecmascript "^1.2.0"
 
-regjsgen@^0.2.0:
-  version "0.2.0"
-  resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7"
-  integrity sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=
-
 regjsgen@^0.5.1:
   version "0.5.2"
   resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.2.tgz#92ff295fb1deecbf6ecdab2543d207e91aa33733"
   integrity sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==
 
-regjsparser@^0.1.4:
-  version "0.1.5"
-  resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.1.5.tgz#7ee8f84dc6fa792d3fd0ae228d24bd949ead205c"
-  integrity sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=
-  dependencies:
-    jsesc "~0.5.0"
-
 regjsparser@^0.6.4:
   version "0.6.4"
   resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.4.tgz#a769f8684308401a66e9b529d2436ff4d0666272"
@@ -8101,6 +8398,11 @@ requires-port@^1.0.0:
   resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
   integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=
 
+resize-observer-polyfill@^1.5.1:
+  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@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a"
@@ -8136,11 +8438,12 @@ resolve@1.1.7:
   resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
   integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=
 
-resolve@^1.1.4, resolve@^1.10.0, resolve@^1.17.0, resolve@^1.3.2, resolve@^1.4.0:
-  version "1.17.0"
-  resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444"
-  integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==
+resolve@^1.1.4, resolve@^1.10.0, resolve@^1.12.0, resolve@^1.17.0, resolve@^1.18.1, resolve@^1.4.0:
+  version "1.19.0"
+  resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.19.0.tgz#1af5bf630409734a067cae29318aac7fa29a267c"
+  integrity sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==
   dependencies:
+    is-core-module "^2.1.0"
     path-parse "^1.0.6"
 
 responselike@1.0.2:
@@ -8178,6 +8481,11 @@ rgba-regex@^1.0.0:
   resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3"
   integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=
 
+rgbcolor@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/rgbcolor/-/rgbcolor-1.0.1.tgz#d6505ecdb304a6595da26fa4b43307306775945d"
+  integrity sha1-1lBezbMEplldom+ktDMHMGd1lF0=
+
 rimraf@2.6.3:
   version "2.6.3"
   resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab"
@@ -8200,6 +8508,15 @@ ripemd160@^2.0.0, ripemd160@^2.0.1:
     hash-base "^3.0.0"
     inherits "^2.0.1"
 
+rollup@^1.31.1:
+  version "1.32.1"
+  resolved "https://registry.yarnpkg.com/rollup/-/rollup-1.32.1.tgz#4480e52d9d9e2ae4b46ba0d9ddeaf3163940f9c4"
+  integrity sha512-/2HA0Ec70TvQnXdzynFffkjA6XN+1e2pEv/uKS5Ulca40g2L7KuOE3riasHoNVHOsFD5KKZgDsMk1CP3Tw9s+A==
+  dependencies:
+    "@types/estree" "*"
+    "@types/node" "*"
+    acorn "^7.1.0"
+
 rst-selector-parser@^2.2.3:
   version "2.2.3"
   resolved "https://registry.yarnpkg.com/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz#81b230ea2fcc6066c89e3472de794285d9b03d91"
@@ -8271,9 +8588,9 @@ sass-resources-loader@^2.0.0:
     loader-utils "^2.0.0"
 
 sass@^1.24.4:
-  version "1.27.0"
-  resolved "https://registry.yarnpkg.com/sass/-/sass-1.27.0.tgz#0657ff674206b95ec20dc638a93e179c78f6ada2"
-  integrity sha512-0gcrER56OkzotK/GGwgg4fPrKuiFlPNitO7eUJ18Bs+/NBlofJfMxmxqpqJxjae9vu0Wq8TZzrSyxZal00WDig==
+  version "1.30.0"
+  resolved "https://registry.yarnpkg.com/sass/-/sass-1.30.0.tgz#60bbbbaf76ba10117e61c6c24f00161c3d60610e"
+  integrity sha512-26EUhOXRLaUY7+mWuRFqGeGGNmhB1vblpTENO1Z7mAzzIZeVxZr9EZoaY1kyGLFWdSOZxRMAufiN2mkbO6dAlw==
   dependencies:
     chokidar ">=2.0.0 <4.0.0"
 
@@ -8306,7 +8623,7 @@ schema-utils@^1.0.0:
     ajv-errors "^1.0.0"
     ajv-keywords "^3.1.0"
 
-schema-utils@^2.6.6:
+schema-utils@^2.0.1, schema-utils@^2.6.5, schema-utils@^2.6.6:
   version "2.7.1"
   resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.1.tgz#1ca4f32d1b24c590c203b8e7a50bf0ea4cd394d7"
   integrity sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==
@@ -8472,7 +8789,7 @@ shim-loader@^1.0.1:
     precond "^0.2.3"
     webpack-sources "^0.2.3"
 
-side-channel@^1.0.2:
+side-channel@^1.0.2, side-channel@^1.0.3:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.3.tgz#cdc46b057550bbab63706210838df5d4c19519c3"
   integrity sha512-A6+ByhlLkksFoUepsGxfj5x1gTSrs+OydsRptUxeNCabQpCFUvcwIczgOigI8vhY/OJCnPnyE9rGiwgvr9cS1g==
@@ -8698,9 +9015,9 @@ spdx-expression-parse@^3.0.0:
     spdx-license-ids "^3.0.0"
 
 spdx-license-ids@^3.0.0:
-  version "3.0.6"
-  resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.6.tgz#c80757383c28abf7296744998cbc106ae8b854ce"
-  integrity sha512-+orQK83kyMva3WyPf59k1+Y525csj5JejicWut55zeTWANuN17qSiSLUXWtzHeNWORSvT7GLDJ/E/XiIWoXBTw==
+  version "3.0.7"
+  resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz#e9c18a410e5ed7e12442a549fbd8afa767038d65"
+  integrity sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==
 
 split-string@^3.0.1, split-string@^3.0.2:
   version "3.1.0"
@@ -8753,6 +9070,11 @@ stable@^0.1.8:
   resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf"
   integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==
 
+stackblur-canvas@^2.0.0:
+  version "2.4.0"
+  resolved "https://registry.yarnpkg.com/stackblur-canvas/-/stackblur-canvas-2.4.0.tgz#2b2eba910cb46f6feae918e1c402f863d602c01b"
+  integrity sha512-Z+HixfgYV0ss3C342DxPwc+UvN1SYWqoz7Wsi3xEDWEnaBkSCL3Ey21gF4io+WlLm8/RIrSnCrDBIEcH4O+q5Q==
+
 static-extend@^0.1.1:
   version "0.1.2"
   resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6"
@@ -8801,6 +9123,16 @@ stream-http@^2.0.0, stream-http@^2.7.2:
     to-arraybuffer "^1.0.0"
     xtend "^4.0.0"
 
+stream-http@^3.0.0:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-3.1.1.tgz#0370a8017cf8d050b9a8554afe608f043eaff564"
+  integrity sha512-S7OqaYu0EkFpgeGFb/NPOoPLxFko7TPqtEeFg5DXPB4v/KETHG0Ln6fRFrNezoelpaDKmycEmmZ81cC9DAwgYg==
+  dependencies:
+    builtin-status-codes "^3.0.0"
+    inherits "^2.0.4"
+    readable-stream "^3.6.0"
+    xtend "^4.0.2"
+
 stream-shift@^1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d"
@@ -8858,40 +9190,42 @@ string.prototype.codepointat@^0.2.0:
   integrity sha512-2cBVCj6I4IOvEnjgO/hWqXjqBGsY+zwPmHl12Srk9IXSZ56Jwwmy+66XO5Iut/oQVR7t5ihYdLB0GMa4alEUcg==
 
 string.prototype.matchall@^4.0.2:
-  version "4.0.2"
-  resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.2.tgz#48bb510326fb9fdeb6a33ceaa81a6ea04ef7648e"
-  integrity sha512-N/jp6O5fMf9os0JU3E72Qhf590RSRZU/ungsL/qJUYVTNv7hTG0P/dbPjxINVN9jpscu3nzYwKESU3P3RY5tOg==
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.3.tgz#24243399bc31b0a49d19e2b74171a15653ec996a"
+  integrity sha512-OBxYDA2ifZQ2e13cP82dWFMaCV9CGF8GzmN4fljBVw5O5wep0lu4gacm1OL6MjROoUnB8VbkWRThqkV2YFLNxw==
   dependencies:
+    call-bind "^1.0.0"
     define-properties "^1.1.3"
-    es-abstract "^1.17.0"
+    es-abstract "^1.18.0-next.1"
     has-symbols "^1.0.1"
     internal-slot "^1.0.2"
     regexp.prototype.flags "^1.3.0"
-    side-channel "^1.0.2"
+    side-channel "^1.0.3"
 
 string.prototype.trim@^1.2.1:
-  version "1.2.2"
-  resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.2.tgz#f538d0bacd98fc4297f0bef645226d5aaebf59f3"
-  integrity sha512-b5yrbl3BXIjHau9Prk7U0RRYcUYdN4wGSVaqoBQS50CCE3KBuYU0TYRNPFCP7aVoNMX87HKThdMRVIP3giclKg==
+  version "1.2.3"
+  resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.3.tgz#d23a22fde01c1e6571a7fadcb9be11decd8061a7"
+  integrity sha512-16IL9pIBA5asNOSukPfxX2W68BaBvxyiRK16H3RA/lWW9BDosh+w7f+LhomPHpXJ82QEe7w7/rY/S1CV97raLg==
   dependencies:
+    call-bind "^1.0.0"
     define-properties "^1.1.3"
-    es-abstract "^1.18.0-next.0"
+    es-abstract "^1.18.0-next.1"
 
 string.prototype.trimend@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz#85812a6b847ac002270f5808146064c995fb6913"
-  integrity sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz#a22bd53cca5c7cf44d7c9d5c732118873d6cd18b"
+  integrity sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw==
   dependencies:
+    call-bind "^1.0.0"
     define-properties "^1.1.3"
-    es-abstract "^1.17.5"
 
 string.prototype.trimstart@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz#14af6d9f34b053f7cfc89b72f8f2ee14b9039a54"
-  integrity sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz#9b4cb590e123bb36564401d59824298de50fd5aa"
+  integrity sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg==
   dependencies:
+    call-bind "^1.0.0"
     define-properties "^1.1.3"
-    es-abstract "^1.17.5"
 
 string_decoder@^1.0.0, string_decoder@^1.1.1:
   version "1.3.0"
@@ -9021,7 +9355,7 @@ supports-color@^7.0.0, supports-color@^7.1.0:
   dependencies:
     has-flag "^4.0.0"
 
-svg-pathdata@^5.0.0:
+svg-pathdata@^5.0.0, svg-pathdata@^5.0.5:
   version "5.0.5"
   resolved "https://registry.yarnpkg.com/svg-pathdata/-/svg-pathdata-5.0.5.tgz#65e8d765642ba15fe15434444087d082bc526b29"
   integrity sha512-TAAvLNSE3fEhyl/Da19JWfMAdhSXTYeviXsLSoDT1UM76ADj5ndwAPX1FKQEgB/gFMPavOy6tOqfalXKUiXrow==
@@ -9134,24 +9468,24 @@ tempfile@^2.0.0:
     uuid "^3.0.1"
 
 tempusdominus-bootstrap-4@^5.1.2:
-  version "5.1.2"
-  resolved "https://registry.yarnpkg.com/tempusdominus-bootstrap-4/-/tempusdominus-bootstrap-4-5.1.2.tgz#3c9906ca6e5d563faa0b81b2fdc6aa79cad9c0be"
-  integrity sha512-ksD8qc4wOJeE19wvryXmEpRzMUSZu4wSOdG6zKSn8l4ccad16249KOX1j0CccyZpuuES/n4FLqLAUB+Dd1LTBA==
+  version "5.39.0"
+  resolved "https://registry.yarnpkg.com/tempusdominus-bootstrap-4/-/tempusdominus-bootstrap-4-5.39.0.tgz#f13dcfec6c41b37c5fe509f08bd513590c64411f"
+  integrity sha512-vYnkmQYQq4+A51WyRc/6e03eM0BHDoPaxd556K1pd4Nhr0eGeB3+Mi9b+3CDx4189fg3gQlrsKzgJiHPRwSX3Q==
   dependencies:
-    bootstrap ">=4.1.2"
-    jquery "^3.0"
-    moment "^2.22.2"
-    moment-timezone "^0.5.11"
-    popper.js "^1.14.3"
+    bootstrap "^4.5.2"
+    jquery "^3.5.1"
+    moment "^2.29.0"
+    moment-timezone "^0.5.31"
+    popper.js "^1.16.1"
 
 tempusdominus-core@^5.0.3:
-  version "5.0.3"
-  resolved "https://registry.yarnpkg.com/tempusdominus-core/-/tempusdominus-core-5.0.3.tgz#808642e47a83f45d7ef18c1597fd7b1d413d69e5"
-  integrity sha512-52lClmU33gb6J6I/S9uGDrgQwccq3Yw9SlZerTgGLOzOB3Sc9pgIVBirfPMsMcx8nPsg6mA5ItFAH/5BZiQThg==
+  version "5.19.0"
+  resolved "https://registry.yarnpkg.com/tempusdominus-core/-/tempusdominus-core-5.19.0.tgz#ccbd2c35109b0a4b96c61513e53e0175ec4896bd"
+  integrity sha512-7a4oBQw4cjz6C87BLRg3KHVvzpnPlnRTkuDZ7SwcJayQQ4QgOryX5u6wj0q07TXhgtMQLCntZO6nVhHIKPaeUw==
   dependencies:
-    jquery "^3.0"
-    moment "^2.22.2"
-    moment-timezone "^0.4.0"
+    jquery "^3.5.0"
+    moment "~2.24.0"
+    moment-timezone "^0.5.28"
 
 terser-webpack-plugin@^1.4.3:
   version "1.4.5"
@@ -9223,9 +9557,9 @@ timers-browserify@^1.0.1:
     process "~0.11.0"
 
 timers-browserify@^2.0.4:
-  version "2.0.11"
-  resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.11.tgz#800b1f3eee272e5bc53ee465a04d0e804c31211f"
-  integrity sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ==
+  version "2.0.12"
+  resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.12.tgz#44a45c11fbf407f34f97bccd1577c652361b00ee"
+  integrity sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==
   dependencies:
     setimmediate "^1.0.4"
 
@@ -9234,6 +9568,13 @@ timsort@^0.3.0:
   resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4"
   integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=
 
+tippy.js@^6.2.0:
+  version "6.2.7"
+  resolved "https://registry.yarnpkg.com/tippy.js/-/tippy.js-6.2.7.tgz#62fb34eda23f7d78151ddca922b62818c1ab9869"
+  integrity sha512-k+kWF9AJz5xLQHBi3K/XlmJiyu+p9gsCyc5qZhxxGaJWIW8SMjw1R+C7saUnP33IM8gUhDA2xX//ejRSwqR0tA==
+  dependencies:
+    "@popperjs/core" "^2.4.4"
+
 tmp@0.0.33, tmp@0.0.x, tmp@^0.0.33:
   version "0.0.33"
   resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
@@ -9331,9 +9672,9 @@ tryer@^1.0.1:
   integrity sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==
 
 tslib@^1.9.0:
-  version "1.14.0"
-  resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.0.tgz#d624983f3e2c5e0b55307c3dd6c86acd737622c6"
-  integrity sha512-+Zw5lu0D9tvBMjGP8LpvMb0u2WW2QV3y+D8mO6J+cNzCYIN4sVy43Bf9vl92nqFahutN0I8zHa7cc4vihIshnw==
+  version "1.14.1"
+  resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
+  integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
 
 ttf2woff@2.0.1:
   version "2.0.1"
@@ -9411,9 +9752,9 @@ undeclared-identifiers@^1.1.2:
     xtend "^4.0.1"
 
 underscore@>=1.7.0, underscore@>=1.8.3, underscore@^1.8.0, underscore@^1.8.3, underscore@^1.9.1:
-  version "1.11.0"
-  resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.11.0.tgz#dd7c23a195db34267186044649870ff1bab5929e"
-  integrity sha512-xY96SsN3NA461qIRKZ/+qox37YXPtSBswMGfiNptr+wrt6ds4HaMw23TP612fEyGekRE6LNRiLYr/aqbHXNedw==
+  version "1.12.0"
+  resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.12.0.tgz#4814940551fc80587cef7840d1ebb0f16453be97"
+  integrity sha512-21rQzss/XPMjolTiIezSu3JAjgagXKROtNrYFEOWK109qY1Uv2tVjPTZ1ci2HgvQDA16gHYSthQIJfB+XId/rQ==
 
 unicode-canonical-property-names-ecmascript@^1.0.4:
   version "1.0.4"
@@ -9608,9 +9949,9 @@ uuid@^3.0.1, uuid@^3.3.2:
   integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
 
 v8-compile-cache@^2.1.1:
-  version "2.1.1"
-  resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz#54bc3cdd43317bca91e35dcaf305b1a7237de745"
-  integrity sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ==
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz#9471efa3ef9128d2f7c6a7ca39c4dd6b5055b132"
+  integrity sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==
 
 validate-npm-package-license@^3.0.1:
   version "3.0.4"
@@ -9660,23 +10001,23 @@ watchify@~3.11.1:
     through2 "^2.0.0"
     xtend "^4.0.0"
 
-watchpack-chokidar2@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/watchpack-chokidar2/-/watchpack-chokidar2-2.0.0.tgz#9948a1866cbbd6cb824dea13a7ed691f6c8ddff0"
-  integrity sha512-9TyfOyN/zLUbA288wZ8IsMZ+6cbzvsNyEzSBp6e/zkifi6xxbl8SmQ/CxQq32k8NNqrdVEVUVSEf56L4rQ/ZxA==
+watchpack-chokidar2@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz#38500072ee6ece66f3769936950ea1771be1c957"
+  integrity sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==
   dependencies:
     chokidar "^2.1.8"
 
 watchpack@^1.7.4:
-  version "1.7.4"
-  resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.7.4.tgz#6e9da53b3c80bb2d6508188f5b200410866cd30b"
-  integrity sha512-aWAgTW4MoSJzZPAicljkO1hsi1oKj/RRq/OJQh2PKI2UKL04c2Bs+MBOB+BBABHTXJpf9mCwHN7ANCvYsvY2sg==
+  version "1.7.5"
+  resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.7.5.tgz#1267e6c55e0b9b5be44c2023aed5437a2c26c453"
+  integrity sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==
   dependencies:
     graceful-fs "^4.1.2"
     neo-async "^2.5.0"
   optionalDependencies:
     chokidar "^3.4.1"
-    watchpack-chokidar2 "^2.0.0"
+    watchpack-chokidar2 "^2.0.1"
 
 "webcabin-docker@git+https://github.com/EnterpriseDB/wcDocker/#c4a3398b89588408dc705895675bce7bd7660d36":
   version "2.2.4-dev"
@@ -9706,7 +10047,7 @@ webpack-bundle-analyzer@^3.5.1:
     opener "^1.5.1"
     ws "^6.0.0"
 
-webpack-cli@^3.2.3:
+webpack-cli@^3.3.11:
   version "3.3.12"
   resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.3.12.tgz#94e9ada081453cd0aa609c99e500012fd3ad2d4a"
   integrity sha512-NVWBaz9k839ZH/sinurM+HcDvJOTXwSjYp1ku+5XKeOC03z8v5QitnK/x+lAxGXFyhdayoIf/GOpv85z3/xPag==
@@ -9724,9 +10065,9 @@ webpack-cli@^3.2.3:
     yargs "^13.3.2"
 
 webpack-dev-middleware@^3.7.0:
-  version "3.7.2"
-  resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.2.tgz#0019c3db716e3fa5cecbf64f2ab88a74bab331f3"
-  integrity sha512-1xC42LxbYoqLNAhV6YzTYacicgMZQTqRd27Sim9wn5hJrX3I5nxYy1SxSd4+gjUFsz1dQFj+yEe6zEVmSkeJjw==
+  version "3.7.3"
+  resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.3.tgz#0639372b143262e2b84ab95d3b91a7597061c2c5"
+  integrity sha512-djelc/zGiz9nZj/U7PTBi2ViorGJXEWo/3ltkPbDyxCXhhEXkW0ce99falaok4TPj+AsxLiXJR0EBOb0zh9fKQ==
   dependencies:
     memory-fs "^0.4.1"
     mime "^2.4.4"
@@ -9743,9 +10084,9 @@ webpack-log@^2.0.0:
     uuid "^3.3.2"
 
 webpack-require-from@^1.8.0:
-  version "1.8.1"
-  resolved "https://registry.yarnpkg.com/webpack-require-from/-/webpack-require-from-1.8.1.tgz#18fd6fc18f0920a097a6f43855ac335f486bfb06"
-  integrity sha512-jKpr/6CH6sW3QH4E5cf/UNK4Dpsk4E1x6IpiIjeZQ9rOhr5jqpGCyFW6e/LEFpJ3U9AzRtaUHgahR7QFQnttLQ==
+  version "1.8.2"
+  resolved "https://registry.yarnpkg.com/webpack-require-from/-/webpack-require-from-1.8.2.tgz#d0c6838553315bb8802f879ab43c29309f5c80fb"
+  integrity sha512-N9kDFNGNEnmuM/riUm/yNXhefAUCUG50sVV509n0WtCdKlIjMYebVwGsEujDKRRiy539J84oOZlrZim9FJXNPA==
 
 webpack-sources@^0.2.3:
   version "0.2.3"
@@ -9881,9 +10222,9 @@ xtend@^4.0.0, xtend@^4.0.1, xtend@^4.0.2, xtend@~4.0.1:
   integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
 
 y18n@^4.0.0:
-  version "4.0.0"
-  resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b"
-  integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.1.tgz#8db2b83c31c5d75099bb890b23f3094891e247d4"
+  integrity sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==
 
 yallist@^2.1.2:
   version "2.1.2"
@@ -9900,6 +10241,11 @@ yallist@^4.0.0:
   resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
   integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
 
+yaml@^1.7.2:
+  version "1.10.0"
+  resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.0.tgz#3b593add944876077d4d683fee01081bd9fff31e"
+  integrity sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==
+
 yargs-parser@^13.1.2:
   version "13.1.2"
   resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38"
