Merge "Use gerrit-theme.js by default when enableDefault set to true"
diff --git a/Documentation/dev-crafting-changes.txt b/Documentation/dev-crafting-changes.txt
index e1b2147..02ac2f5 100644
--- a/Documentation/dev-crafting-changes.txt
+++ b/Documentation/dev-crafting-changes.txt
@@ -147,7 +147,7 @@
 link:https://github.com/google/google-java-format[`google-java-format`,role=external,window=_blank]
 tool (version 1.7), and to format Bazel BUILD, WORKSPACE and .bzl files the
 link:https://github.com/bazelbuild/buildtools/tree/master/buildifier[`buildifier`,role=external,window=_blank]
-tool (version 3.2.1). Unused dependencies are found and removed using the
+tool (version 3.3.0). Unused dependencies are found and removed using the
 link:https://github.com/bazelbuild/buildtools/tree/master/unused_deps[`unused_deps`,role=external,window=_blank]
 build tool, a sibling of `buildifier`.
 
diff --git a/Documentation/dev-e2e-tests.txt b/Documentation/dev-e2e-tests.txt
index 8fe8f4e..78b2c15 100644
--- a/Documentation/dev-e2e-tests.txt
+++ b/Documentation/dev-e2e-tests.txt
@@ -116,7 +116,7 @@
     "cmd": "clone"
   },
   {
-    "url": "http://HOSTNAME:HTTP_PORT/_PROJECT",
+    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/_PROJECT",
     "cmd": "clone"
   }
 ]
@@ -153,10 +153,12 @@
 * `-Dcom.google.gerrit.scenarios.hostname=localhost`
 * `-Dcom.google.gerrit.scenarios.ssh_port=29418`
 * `-Dcom.google.gerrit.scenarios.http_port=8080`
+* `-Dcom.google.gerrit.scenarios.http_scheme=http`
 
 Above, the properties can be set with values matching specific deployment topologies under test.
-The example values shown above are the currently coded default ones. The framework could support
-differing or more properties over time.
+The example values shown above are the currently coded default ones. For example, the `http` scheme
+above could be replaced with `https`. The framework could support differing or more properties over
+time.
 
 Plugin or otherwise non-core scenarios may do so just as well. The core java package
 `com.google.gerrit.scenarios` from the example above has to be replaced with the one under which
diff --git a/Documentation/rest-api-accounts.txt b/Documentation/rest-api-accounts.txt
index 6fbedb0..86f546d 100644
--- a/Documentation/rest-api-accounts.txt
+++ b/Documentation/rest-api-accounts.txt
@@ -1284,6 +1284,7 @@
   )]}'
   {
     "changes_per_page": 25,
+    "theme": "LIGHT",
     "date_format": "STD",
     "time_format": "HHMM_12",
     "diff_view": "SIDE_BY_SIDE",
@@ -1336,6 +1337,7 @@
 
   {
     "changes_per_page": 50,
+    "theme": "DARK",
     "expand_inline_diffs": true,
     "date_format": "STD",
     "time_format": "HHMM_12",
@@ -1383,6 +1385,7 @@
   )]}'
   {
     "changes_per_page": 50,
+    "theme" "DARK",
     "expand_inline_diffs": true,
     "date_format": "STD",
     "time_format": "HHMM_12",
@@ -2759,6 +2762,9 @@
 |`changes_per_page`             ||
 The number of changes to show on each page.
 Allowed values are `10`, `25`, `50`, `100`.
+|`theme`                        ||
+Which theme to use.
+Allowed values are `DARK` or `LIGHT`.
 |`expand_inline_diffs`          |not set if `false`|
 Whether to expand diffs inline instead of opening as separate page
 (PolyGerrit only).
@@ -2821,6 +2827,9 @@
 |`changes_per_page`             |optional|
 The number of changes to show on each page.
 Allowed values are `10`, `25`, `50`, `100`.
+|`theme`                        |optional|
+Which theme to use.
+Allowed values are `DARK` or `LIGHT`.
 |`expand_inline_diffs`          |not set if `false`|
 Whether to expand diffs inline instead of opening as separate page
 (PolyGerrit only).
diff --git a/WORKSPACE b/WORKSPACE
index b8d08dc..63a0852 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -64,8 +64,8 @@
 
 http_archive(
     name = "build_bazel_rules_nodejs",
-    sha256 = "84abf7ac4234a70924628baa9a73a5a5cbad944c4358cf9abdb4aab29c9a5b77",
-    urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/1.7.0/rules_nodejs-1.7.0.tar.gz"],
+    sha256 = "5bf77cc2d13ddf9124f4c1453dd96063774d755d4fc75d922471540d1c9a8ea8",
+    urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/2.0.0/rules_nodejs-2.0.0.tar.gz"],
 )
 
 # Golang support for PolyGerrit local dev server.
@@ -1003,11 +1003,6 @@
     yarn_lock = "//:plugins/yarn.lock",
 )
 
-# Install all Bazel dependencies needed for npm packages that supply Bazel rules
-load("@npm//:install_bazel_dependencies.bzl", "install_bazel_dependencies")
-
-install_bazel_dependencies()
-
 load("//tools/bzl:js.bzl", "bower_archive", "npm_binary")
 
 # NPM binaries bundled along with their dependencies.
@@ -1199,8 +1194,4 @@
     version = "6.5.1",
 )
 
-load("@npm_bazel_typescript//:index.bzl", "ts_setup_workspace")
-
-ts_setup_workspace()
-
 external_plugin_deps()
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ApproveChange.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ApproveChange.json
index 3577a6a..665cc4d 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ApproveChange.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ApproveChange.json
@@ -1,6 +1,6 @@
 [
   {
-    "url": "http://HOSTNAME:HTTP_PORT/a/changes/",
+    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/a/changes/",
     "number": "NUMBER"
   }
 ]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CheckMasterBranchReplica1.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CheckMasterBranchReplica1.json
index 54c54f8..5b892aa 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CheckMasterBranchReplica1.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CheckMasterBranchReplica1.json
@@ -1,5 +1,5 @@
 [
   {
-    "url": "http://HOSTNAME:HTTP_PORT1/a/projects/PROJECT/branches/master"
+    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT1/a/projects/PROJECT/branches/master"
   }
 ]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CheckProjectsCacheFlushEntries.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CheckProjectsCacheFlushEntries.json
index 6210deb..467661b 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CheckProjectsCacheFlushEntries.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CheckProjectsCacheFlushEntries.json
@@ -1,6 +1,6 @@
 [
   {
-    "url": "http://HOSTNAME:HTTP_PORT/a/config/server/caches/projects",
+    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/a/config/server/caches/projects",
     "entries": "PROJECTS_ENTRIES"
   }
 ]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CloneUsingBothProtocols.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CloneUsingBothProtocols.json
index 2389124..30f5f23 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CloneUsingBothProtocols.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CloneUsingBothProtocols.json
@@ -4,7 +4,7 @@
     "cmd": "clone"
   },
   {
-    "url": "http://HOSTNAME:HTTP_PORT/_PROJECT",
+    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/_PROJECT",
     "cmd": "clone"
   }
 ]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateChange.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateChange.json
index b4ee549..70e79ca 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateChange.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateChange.json
@@ -1,6 +1,6 @@
 [
   {
-    "url": "http://HOSTNAME:HTTP_PORT/a/changes/",
+    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/a/changes/",
     "project": "PROJECT"
   }
 ]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateProject.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateProject.json
index 40e5a45..cd90739 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateProject.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateProject.json
@@ -1,5 +1,5 @@
 [
   {
-    "url": "http://HOSTNAME:HTTP_PORT/a/projects/PROJECT"
+    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/a/projects/PROJECT"
   }
 ]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/DeleteChange.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/DeleteChange.json
index 3577a6a..665cc4d 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/DeleteChange.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/DeleteChange.json
@@ -1,6 +1,6 @@
 [
   {
-    "url": "http://HOSTNAME:HTTP_PORT/a/changes/",
+    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/a/changes/",
     "number": "NUMBER"
   }
 ]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/DeleteProject.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/DeleteProject.json
index 7cc8293..5720f53 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/DeleteProject.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/DeleteProject.json
@@ -1,5 +1,5 @@
 [
   {
-    "url": "http://HOSTNAME:HTTP_PORT/a/projects/PROJECT/delete-project~delete"
+    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/a/projects/PROJECT/delete-project~delete"
   }
 ]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/FlushProjectsCache.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/FlushProjectsCache.json
index 9ff15a7..e30a2cf 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/FlushProjectsCache.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/FlushProjectsCache.json
@@ -1,5 +1,5 @@
 [
   {
-    "url": "http://HOSTNAME:HTTP_PORT/a/config/server/caches/projects/flush"
+    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/a/config/server/caches/projects/flush"
   }
 ]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/GetMasterBranchRevision.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/GetMasterBranchRevision.json
index 2b8809a..86a3c28 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/GetMasterBranchRevision.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/GetMasterBranchRevision.json
@@ -1,5 +1,5 @@
 [
   {
-    "url": "http://HOSTNAME:HTTP_PORT/a/projects/PROJECT/branches/master"
+    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/a/projects/PROJECT/branches/master"
   }
 ]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/GetProjectsCacheEntries.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/GetProjectsCacheEntries.json
index fcf4bc9..e4e2643 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/GetProjectsCacheEntries.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/GetProjectsCacheEntries.json
@@ -1,5 +1,5 @@
 [
   {
-    "url": "http://HOSTNAME:HTTP_PORT/a/config/server/caches/projects"
+    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/a/config/server/caches/projects"
   }
 ]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ReplayRecordsFromFeeder.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ReplayRecordsFromFeeder.json
index 2389124..30f5f23 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ReplayRecordsFromFeeder.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ReplayRecordsFromFeeder.json
@@ -4,7 +4,7 @@
     "cmd": "clone"
   },
   {
-    "url": "http://HOSTNAME:HTTP_PORT/_PROJECT",
+    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/_PROJECT",
     "cmd": "clone"
   }
 ]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/SubmitChange.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/SubmitChange.json
index a371757..301c65b 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/SubmitChange.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/SubmitChange.json
@@ -1,5 +1,5 @@
 [
   {
-    "url": "http://HOSTNAME:HTTP_PORT/a/changes/"
+    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/a/changes/"
   }
 ]
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/GerritSimulation.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/GerritSimulation.scala
index fc68f97..4832392 100644
--- a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/GerritSimulation.scala
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/GerritSimulation.scala
@@ -62,6 +62,7 @@
       var in = replaceOverride(url.toString)
       in = replaceProperty("hostname", "localhost", in)
       in = replaceProperty("http_port", 8080, in)
+      in = replaceProperty("http_scheme", "http", in)
       replaceProperty("ssh_port", 29418, in)
     case ("number", number) =>
       val precedes = replaceKeyWith("_number", 0, number.toString)
diff --git a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
index 9d79855..0cd0402 100644
--- a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
+++ b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
@@ -50,19 +50,21 @@
 import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.common.data.AccessSection;
-import com.google.gerrit.common.data.GroupDescription;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.common.data.LabelFunction;
 import com.google.gerrit.common.data.LabelType;
-import com.google.gerrit.common.data.LabelValue;
 import com.google.gerrit.common.data.Permission;
 import com.google.gerrit.common.data.PermissionRule;
 import com.google.gerrit.common.data.PermissionRule.Action;
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.Address;
 import com.google.gerrit.entities.BooleanProjectConfig;
 import com.google.gerrit.entities.BranchNameKey;
 import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.EmailHeader;
+import com.google.gerrit.entities.GroupDescription;
+import com.google.gerrit.entities.GroupReference;
+import com.google.gerrit.entities.LabelValue;
 import com.google.gerrit.entities.PatchSet;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.entities.RefNames;
@@ -89,8 +91,6 @@
 import com.google.gerrit.index.project.ProjectIndex;
 import com.google.gerrit.index.project.ProjectIndexCollection;
 import com.google.gerrit.json.OutputFormat;
-import com.google.gerrit.mail.Address;
-import com.google.gerrit.mail.EmailHeader;
 import com.google.gerrit.server.GerritPersonIdent;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.PatchSetUtil;
diff --git a/java/com/google/gerrit/acceptance/AbstractNotificationTest.java b/java/com/google/gerrit/acceptance/AbstractNotificationTest.java
index bb3901e..452df67 100644
--- a/java/com/google/gerrit/acceptance/AbstractNotificationTest.java
+++ b/java/com/google/gerrit/acceptance/AbstractNotificationTest.java
@@ -28,6 +28,10 @@
 import com.google.common.truth.Truth;
 import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
 import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Address;
+import com.google.gerrit.entities.EmailHeader;
+import com.google.gerrit.entities.EmailHeader.AddressList;
+import com.google.gerrit.entities.NotifyConfig.NotifyType;
 import com.google.gerrit.extensions.api.changes.RecipientType;
 import com.google.gerrit.extensions.api.changes.ReviewInput;
 import com.google.gerrit.extensions.api.changes.ReviewResult;
@@ -36,10 +40,6 @@
 import com.google.gerrit.extensions.client.GeneralPreferencesInfo.EmailStrategy;
 import com.google.gerrit.extensions.client.InheritableBoolean;
 import com.google.gerrit.extensions.client.ReviewerState;
-import com.google.gerrit.mail.Address;
-import com.google.gerrit.mail.EmailHeader;
-import com.google.gerrit.mail.EmailHeader.AddressList;
-import com.google.gerrit.server.account.ProjectWatches.NotifyType;
 import com.google.gerrit.testing.FakeEmailSender;
 import com.google.gerrit.testing.FakeEmailSender.Message;
 import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/acceptance/PushOneCommit.java b/java/com/google/gerrit/acceptance/PushOneCommit.java
index 3ccbe4d..afd451a 100644
--- a/java/com/google/gerrit/acceptance/PushOneCommit.java
+++ b/java/com/google/gerrit/acceptance/PushOneCommit.java
@@ -25,6 +25,8 @@
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Sets;
 import com.google.gerrit.common.Nullable;
+import com.google.gerrit.common.UsedAt;
+import com.google.gerrit.common.UsedAt.Project;
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.entities.Change;
 import com.google.gerrit.entities.PatchSet;
@@ -91,6 +93,14 @@
         @Assisted String subject,
         @Assisted Map<String, String> files);
 
+    @UsedAt(Project.PLUGIN_CODE_OWNERS)
+    PushOneCommit create(
+        PersonIdent i,
+        TestRepository<?> testRepo,
+        @Assisted("subject") String subject,
+        @Assisted Map<String, String> files,
+        @Assisted("changeId") String changeId);
+
     PushOneCommit create(
         PersonIdent i,
         TestRepository<?> testRepo,
@@ -227,15 +237,16 @@
         changeId);
   }
 
-  private PushOneCommit(
+  @AssistedInject
+  PushOneCommit(
       ChangeNotes.Factory notesFactory,
       ApprovalsUtil approvalsUtil,
       Provider<InternalChangeQuery> queryProvider,
-      PersonIdent i,
-      TestRepository<?> testRepo,
-      String subject,
-      Map<String, String> files,
-      String changeId)
+      @Assisted PersonIdent i,
+      @Assisted TestRepository<?> testRepo,
+      @Assisted("subject") String subject,
+      @Assisted Map<String, String> files,
+      @Nullable @Assisted("changeId") String changeId)
       throws Exception {
     this.testRepo = testRepo;
     this.notesFactory = notesFactory;
diff --git a/java/com/google/gerrit/acceptance/ReindexGroupsAtStartup.java b/java/com/google/gerrit/acceptance/ReindexGroupsAtStartup.java
index b985e40..0b2282e 100644
--- a/java/com/google/gerrit/acceptance/ReindexGroupsAtStartup.java
+++ b/java/com/google/gerrit/acceptance/ReindexGroupsAtStartup.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.acceptance;
 
-import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.extensions.events.LifecycleListener;
 import com.google.gerrit.lifecycle.LifecycleModule;
 import com.google.gerrit.server.config.GerritServerConfig;
diff --git a/java/com/google/gerrit/acceptance/TestAccount.java b/java/com/google/gerrit/acceptance/TestAccount.java
index a7a4a89..e9c0899 100644
--- a/java/com/google/gerrit/acceptance/TestAccount.java
+++ b/java/com/google/gerrit/acceptance/TestAccount.java
@@ -22,7 +22,7 @@
 import com.google.common.net.InetAddresses;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.entities.Account;
-import com.google.gerrit.mail.Address;
+import com.google.gerrit.entities.Address;
 import java.net.InetSocketAddress;
 import java.util.Arrays;
 import org.apache.http.client.utils.URIBuilder;
diff --git a/java/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImpl.java b/java/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImpl.java
index a3ddd98..60b3720 100644
--- a/java/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImpl.java
+++ b/java/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImpl.java
@@ -27,10 +27,10 @@
 import com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.TestLabelPermission;
 import com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.TestPermission;
 import com.google.gerrit.common.data.AccessSection;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.common.data.Permission;
 import com.google.gerrit.common.data.PermissionRule;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.entities.RefNames;
 import com.google.gerrit.server.config.AllProjectsName;
diff --git a/java/com/google/gerrit/common/UsedAt.java b/java/com/google/gerrit/common/UsedAt.java
index 9f8b255..e38ad9a 100644
--- a/java/com/google/gerrit/common/UsedAt.java
+++ b/java/com/google/gerrit/common/UsedAt.java
@@ -34,6 +34,7 @@
     GOOGLE,
     COLLABNET,
     PLUGIN_CHECKS,
+    PLUGIN_CODE_OWNERS,
     PLUGIN_DELETE_PROJECT,
     PLUGIN_SERVICEUSER,
     PLUGINS_ALL, // Use this project if a method/type is generally made available to all plugins.
diff --git a/java/com/google/gerrit/common/data/AccessSection.java b/java/com/google/gerrit/common/data/AccessSection.java
index b9bd995..0974c47 100644
--- a/java/com/google/gerrit/common/data/AccessSection.java
+++ b/java/com/google/gerrit/common/data/AccessSection.java
@@ -161,6 +161,6 @@
 
     protected abstract ImmutableList<Permission> getPermissions();
 
-    protected abstract Builder setPermissions(ImmutableList<Permission> permissions);
+    abstract Builder setPermissions(ImmutableList<Permission> permissions);
   }
 }
diff --git a/java/com/google/gerrit/common/data/ContributorAgreement.java b/java/com/google/gerrit/common/data/ContributorAgreement.java
index ed05203..0f10367 100644
--- a/java/com/google/gerrit/common/data/ContributorAgreement.java
+++ b/java/com/google/gerrit/common/data/ContributorAgreement.java
@@ -17,7 +17,9 @@
 import com.google.auto.value.AutoValue;
 import com.google.common.collect.ImmutableList;
 import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.entities.Project;
+import java.util.List;
 
 /** Portion of a {@link Project} describing a single contributor agreement. */
 @AutoValue
@@ -71,9 +73,9 @@
 
     public abstract Builder setAgreementUrl(@Nullable String agreementUrl);
 
-    public abstract Builder setExcludeProjectsRegexes(ImmutableList<String> excludeProjectsRegexes);
+    public abstract Builder setExcludeProjectsRegexes(List<String> excludeProjectsRegexes);
 
-    public abstract Builder setMatchProjectsRegexes(ImmutableList<String> matchProjectsRegexes);
+    public abstract Builder setMatchProjectsRegexes(List<String> matchProjectsRegexes);
 
     public abstract ContributorAgreement build();
   }
diff --git a/java/com/google/gerrit/common/data/LabelType.java b/java/com/google/gerrit/common/data/LabelType.java
index 3e58be7..d272810 100644
--- a/java/com/google/gerrit/common/data/LabelType.java
+++ b/java/com/google/gerrit/common/data/LabelType.java
@@ -22,6 +22,7 @@
 import com.google.common.collect.ImmutableMap;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.entities.LabelId;
+import com.google.gerrit.entities.LabelValue;
 import com.google.gerrit.entities.PatchSetApproval;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -228,7 +229,7 @@
 
     public abstract Builder setIgnoreSelfApproval(boolean ignoreSelfApproval);
 
-    public abstract Builder setRefPatterns(@Nullable ImmutableList<String> refPatterns);
+    public abstract Builder setRefPatterns(@Nullable List<String> refPatterns);
 
     public abstract Builder setValues(List<LabelValue> values);
 
diff --git a/java/com/google/gerrit/common/data/Permission.java b/java/com/google/gerrit/common/data/Permission.java
index 93155d5..6190957 100644
--- a/java/com/google/gerrit/common/data/Permission.java
+++ b/java/com/google/gerrit/common/data/Permission.java
@@ -19,6 +19,7 @@
 import com.google.auto.value.AutoValue;
 import com.google.common.collect.ImmutableList;
 import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.GroupReference;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
diff --git a/java/com/google/gerrit/common/data/PermissionRule.java b/java/com/google/gerrit/common/data/PermissionRule.java
index c411652..1d2230c 100644
--- a/java/com/google/gerrit/common/data/PermissionRule.java
+++ b/java/com/google/gerrit/common/data/PermissionRule.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.common.data;
 
 import com.google.auto.value.AutoValue;
+import com.google.gerrit.entities.GroupReference;
 
 @AutoValue
 public abstract class PermissionRule implements Comparable<PermissionRule> {
diff --git a/java/com/google/gerrit/common/data/testing/GroupReferenceSubject.java b/java/com/google/gerrit/common/data/testing/GroupReferenceSubject.java
index d841aa6..beb62b4 100644
--- a/java/com/google/gerrit/common/data/testing/GroupReferenceSubject.java
+++ b/java/com/google/gerrit/common/data/testing/GroupReferenceSubject.java
@@ -20,8 +20,8 @@
 import com.google.common.truth.FailureMetadata;
 import com.google.common.truth.StringSubject;
 import com.google.common.truth.Subject;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupReference;
 
 public class GroupReferenceSubject extends Subject {
 
diff --git a/java/com/google/gerrit/mail/Address.java b/java/com/google/gerrit/entities/Address.java
similarity index 98%
rename from java/com/google/gerrit/mail/Address.java
rename to java/com/google/gerrit/entities/Address.java
index 520a4c8..2324330 100644
--- a/java/com/google/gerrit/mail/Address.java
+++ b/java/com/google/gerrit/entities/Address.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.mail;
+package com.google.gerrit.entities;
 
 import com.google.auto.value.AutoValue;
 import com.google.gerrit.common.Nullable;
diff --git a/java/com/google/gerrit/entities/BUILD b/java/com/google/gerrit/entities/BUILD
index 26265ae..66d1869 100644
--- a/java/com/google/gerrit/entities/BUILD
+++ b/java/com/google/gerrit/entities/BUILD
@@ -16,6 +16,7 @@
         "//lib/auto:auto-value",
         "//lib/auto:auto-value-annotations",
         "//lib/errorprone:annotations",
+        "//lib/flogger:api",
         "//proto:cache_java_proto",
         "//proto:entities_java_proto",
     ],
diff --git a/java/com/google/gerrit/server/git/BranchOrderSection.java b/java/com/google/gerrit/entities/BranchOrderSection.java
similarity index 96%
rename from java/com/google/gerrit/server/git/BranchOrderSection.java
rename to java/com/google/gerrit/entities/BranchOrderSection.java
index 826067f..f964e59 100644
--- a/java/com/google/gerrit/server/git/BranchOrderSection.java
+++ b/java/com/google/gerrit/entities/BranchOrderSection.java
@@ -12,13 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.git;
+package com.google.gerrit.entities;
 
 import static com.google.common.collect.ImmutableList.toImmutableList;
 
 import com.google.auto.value.AutoValue;
 import com.google.common.collect.ImmutableList;
-import com.google.gerrit.entities.RefNames;
 import java.util.Collection;
 
 /**
diff --git a/java/com/google/gerrit/server/project/ConfiguredMimeTypes.java b/java/com/google/gerrit/entities/ConfiguredMimeTypes.java
similarity index 66%
rename from java/com/google/gerrit/server/project/ConfiguredMimeTypes.java
rename to java/com/google/gerrit/entities/ConfiguredMimeTypes.java
index 0447edb..c28a573 100644
--- a/java/com/google/gerrit/server/project/ConfiguredMimeTypes.java
+++ b/java/com/google/gerrit/entities/ConfiguredMimeTypes.java
@@ -12,11 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.entities;
 
 import com.google.auto.value.AutoValue;
 import com.google.common.collect.ImmutableList;
 import com.google.common.flogger.FluentLogger;
+import java.util.Objects;
 import java.util.Set;
 import java.util.regex.Pattern;
 import java.util.regex.PatternSyntaxException;
@@ -34,7 +35,7 @@
 
   protected abstract ImmutableList<TypeMatcher> matchers();
 
-  static ConfiguredMimeTypes create(String projectName, Config rc) {
+  public static ConfiguredMimeTypes create(String projectName, Config rc) {
     Set<String> types = rc.getSubsections(MIMETYPE);
     ImmutableList.Builder<TypeMatcher> matchers = ImmutableList.builder();
     if (!types.isEmpty()) {
@@ -67,21 +68,31 @@
     return null;
   }
 
-  protected abstract static class TypeMatcher {
+  public abstract static class TypeMatcher {
     private final String type;
+    private final String pattern;
 
-    private TypeMatcher(String type) {
+    private TypeMatcher(String type, String pattern) {
       this.type = type;
+      this.pattern = pattern;
+    }
+
+    public String getPattern() {
+      return pattern;
+    }
+
+    public String getType() {
+      return type;
     }
 
     protected abstract boolean matches(String path);
   }
 
-  protected static class FnType extends TypeMatcher {
+  public static class FnType extends TypeMatcher {
     private final FileNameMatcher matcher;
 
-    private FnType(String type, String pattern) throws InvalidPatternException {
-      super(type);
+    public FnType(String type, String pattern) throws InvalidPatternException {
+      super(type, pattern);
       this.matcher = new FileNameMatcher(pattern, null);
     }
 
@@ -91,13 +102,28 @@
       m.append(input);
       return m.isMatch();
     }
+
+    @Override
+    public boolean equals(Object o) {
+      if (!(o instanceof FnType)) {
+        return false;
+      }
+      FnType other = (FnType) o;
+      return Objects.equals(other.getType(), getType())
+          && Objects.equals(other.getPattern(), getPattern());
+    }
+
+    @Override
+    public int hashCode() {
+      return Objects.hash(getType(), getPattern());
+    }
   }
 
-  protected static class ReType extends TypeMatcher {
+  public static class ReType extends TypeMatcher {
     private final Pattern re;
 
-    private ReType(String type, String pattern) throws PatternSyntaxException {
-      super(type);
+    public ReType(String type, String pattern) throws PatternSyntaxException {
+      super(type, pattern);
       this.re = Pattern.compile(pattern);
     }
 
@@ -105,5 +131,20 @@
     protected boolean matches(String input) {
       return re.matcher(input).matches();
     }
+
+    @Override
+    public boolean equals(Object o) {
+      if (!(o instanceof ReType)) {
+        return false;
+      }
+      ReType other = (ReType) o;
+      return Objects.equals(other.getType(), getType())
+          && Objects.equals(other.getPattern(), getPattern());
+    }
+
+    @Override
+    public int hashCode() {
+      return Objects.hash(getType(), getPattern());
+    }
   }
 }
diff --git a/java/com/google/gerrit/mail/EmailHeader.java b/java/com/google/gerrit/entities/EmailHeader.java
similarity index 99%
rename from java/com/google/gerrit/mail/EmailHeader.java
rename to java/com/google/gerrit/entities/EmailHeader.java
index 9b11101..71414c7 100644
--- a/java/com/google/gerrit/mail/EmailHeader.java
+++ b/java/com/google/gerrit/entities/EmailHeader.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.mail;
+package com.google.gerrit.entities;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
 
diff --git a/java/com/google/gerrit/common/data/GroupDescription.java b/java/com/google/gerrit/entities/GroupDescription.java
similarity index 93%
rename from java/com/google/gerrit/common/data/GroupDescription.java
rename to java/com/google/gerrit/entities/GroupDescription.java
index ed8b39d..e950257 100644
--- a/java/com/google/gerrit/common/data/GroupDescription.java
+++ b/java/com/google/gerrit/entities/GroupDescription.java
@@ -12,11 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.common.data;
+package com.google.gerrit.entities;
 
 import com.google.gerrit.common.Nullable;
-import com.google.gerrit.entities.Account;
-import com.google.gerrit.entities.AccountGroup;
 import java.sql.Timestamp;
 import java.util.Set;
 
diff --git a/java/com/google/gerrit/common/data/GroupReference.java b/java/com/google/gerrit/entities/GroupReference.java
similarity index 94%
rename from java/com/google/gerrit/common/data/GroupReference.java
rename to java/com/google/gerrit/entities/GroupReference.java
index 2620138..9185d53 100644
--- a/java/com/google/gerrit/common/data/GroupReference.java
+++ b/java/com/google/gerrit/entities/GroupReference.java
@@ -12,15 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.common.data;
+package com.google.gerrit.entities;
 
 import static java.util.Objects.requireNonNull;
 
 import com.google.auto.value.AutoValue;
 import com.google.gerrit.common.Nullable;
-import com.google.gerrit.entities.AccountGroup;
 
-/** Describes a group within a projects {@link AccessSection}s. */
+/** Describes a group within a projects {@link com.google.gerrit.common.data.AccessSection}s. */
 @AutoValue
 public abstract class GroupReference implements Comparable<GroupReference> {
 
diff --git a/java/com/google/gerrit/common/data/LabelValue.java b/java/com/google/gerrit/entities/LabelValue.java
similarity index 97%
rename from java/com/google/gerrit/common/data/LabelValue.java
rename to java/com/google/gerrit/entities/LabelValue.java
index ec16fb2..ec5a37e 100644
--- a/java/com/google/gerrit/common/data/LabelValue.java
+++ b/java/com/google/gerrit/entities/LabelValue.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.common.data;
+package com.google.gerrit.entities;
 
 import com.google.auto.value.AutoValue;
 
diff --git a/java/com/google/gerrit/server/git/NotifyConfig.java b/java/com/google/gerrit/entities/NotifyConfig.java
similarity index 90%
rename from java/com/google/gerrit/server/git/NotifyConfig.java
rename to java/com/google/gerrit/entities/NotifyConfig.java
index 1a1bbb6..17da81f 100644
--- a/java/com/google/gerrit/server/git/NotifyConfig.java
+++ b/java/com/google/gerrit/entities/NotifyConfig.java
@@ -12,17 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.git;
+package com.google.gerrit.entities;
 
 import com.google.auto.value.AutoValue;
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableSet;
-import com.google.gerrit.common.data.GroupReference;
-import com.google.gerrit.mail.Address;
-import com.google.gerrit.server.account.ProjectWatches.NotifyType;
+import com.google.gerrit.common.Nullable;
 import java.util.EnumSet;
 import java.util.Set;
-import org.eclipse.jgit.annotations.Nullable;
 
 @AutoValue
 public abstract class NotifyConfig implements Comparable<NotifyConfig> {
@@ -32,7 +29,17 @@
     BCC
   }
 
-  @Nullable
+  public enum NotifyType {
+    // sort by name, except 'ALL' which should stay last
+    ABANDONED_CHANGES,
+    ALL_COMMENTS,
+    NEW_CHANGES,
+    NEW_PATCHSETS,
+    SUBMITTED_CHANGES,
+
+    ALL
+  }
+
   public abstract String getName();
 
   public abstract ImmutableSet<NotifyType> getNotify();
diff --git a/java/com/google/gerrit/server/project/StoredCommentLinkInfo.java b/java/com/google/gerrit/entities/StoredCommentLinkInfo.java
similarity index 96%
rename from java/com/google/gerrit/server/project/StoredCommentLinkInfo.java
rename to java/com/google/gerrit/entities/StoredCommentLinkInfo.java
index 4e311b8..ce24d31 100644
--- a/java/com/google/gerrit/server/project/StoredCommentLinkInfo.java
+++ b/java/com/google/gerrit/entities/StoredCommentLinkInfo.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.entities;
 
 import static com.google.common.base.Preconditions.checkArgument;
 
@@ -68,7 +68,7 @@
   }
 
   /** Creates and returns a new {@link StoredCommentLinkInfo} instance with the same values. */
-  static StoredCommentLinkInfo fromInfo(CommentLinkInfo src, boolean enabled) {
+  public static StoredCommentLinkInfo fromInfo(CommentLinkInfo src, Boolean enabled) {
     return builder(src.name)
         .setMatch(src.match)
         .setLink(src.link)
@@ -79,7 +79,7 @@
   }
 
   /** Returns an {@link CommentLinkInfo} instance with the same values. */
-  CommentLinkInfo toInfo() {
+  public CommentLinkInfo toInfo() {
     CommentLinkInfo info = new CommentLinkInfo();
     info.name = getName();
     info.match = getMatch();
diff --git a/java/com/google/gerrit/extensions/client/GeneralPreferencesInfo.java b/java/com/google/gerrit/extensions/client/GeneralPreferencesInfo.java
index 212f6da..c6555b9 100644
--- a/java/com/google/gerrit/extensions/client/GeneralPreferencesInfo.java
+++ b/java/com/google/gerrit/extensions/client/GeneralPreferencesInfo.java
@@ -102,6 +102,11 @@
     }
   }
 
+  public enum Theme {
+    DARK,
+    LIGHT
+  }
+
   public enum TimeFormat {
     /** 12-hour clock: 1:15 am, 2:13 pm */
     HHMM_12("h:mm a"),
@@ -125,6 +130,7 @@
   /** Type of download URL the user prefers to use. */
   public String downloadScheme;
 
+  public Theme theme;
   public DateFormat dateFormat;
   public TimeFormat timeFormat;
   public Boolean expandInlineDiffs;
@@ -182,6 +188,7 @@
     GeneralPreferencesInfo p = new GeneralPreferencesInfo();
     p.changesPerPage = DEFAULT_PAGESIZE;
     p.downloadScheme = null;
+    p.theme = Theme.LIGHT;
     p.dateFormat = DateFormat.STD;
     p.timeFormat = TimeFormat.HHMM_12;
     p.expandInlineDiffs = false;
diff --git a/java/com/google/gerrit/mail/MailMessage.java b/java/com/google/gerrit/mail/MailMessage.java
index bb83dfd..2ce6cbb 100644
--- a/java/com/google/gerrit/mail/MailMessage.java
+++ b/java/com/google/gerrit/mail/MailMessage.java
@@ -17,6 +17,7 @@
 import com.google.auto.value.AutoValue;
 import com.google.common.collect.ImmutableList;
 import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Address;
 import java.time.Instant;
 
 /**
diff --git a/java/com/google/gerrit/mail/RawMailParser.java b/java/com/google/gerrit/mail/RawMailParser.java
index 4e005a5..213cc3f 100644
--- a/java/com/google/gerrit/mail/RawMailParser.java
+++ b/java/com/google/gerrit/mail/RawMailParser.java
@@ -21,6 +21,7 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.common.io.CharStreams;
 import com.google.common.primitives.Ints;
+import com.google.gerrit.entities.Address;
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStreamReader;
diff --git a/java/com/google/gerrit/pgm/init/GroupsOnInit.java b/java/com/google/gerrit/pgm/init/GroupsOnInit.java
index 0333942..ca28255 100644
--- a/java/com/google/gerrit/pgm/init/GroupsOnInit.java
+++ b/java/com/google/gerrit/pgm/init/GroupsOnInit.java
@@ -19,9 +19,9 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
 import com.google.gerrit.common.Nullable;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.exceptions.NoSuchGroupException;
 import com.google.gerrit.pgm.init.api.AllUsersNameOnInitProvider;
 import com.google.gerrit.pgm.init.api.InitFlags;
diff --git a/java/com/google/gerrit/pgm/init/InitAdminUser.java b/java/com/google/gerrit/pgm/init/InitAdminUser.java
index cf208ae..effb4c6 100644
--- a/java/com/google/gerrit/pgm/init/InitAdminUser.java
+++ b/java/com/google/gerrit/pgm/init/InitAdminUser.java
@@ -17,8 +17,8 @@
 import static java.nio.charset.StandardCharsets.UTF_8;
 
 import com.google.common.base.Strings;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.exceptions.NoSuchGroupException;
 import com.google.gerrit.extensions.client.AuthType;
 import com.google.gerrit.pgm.init.api.ConsoleUI;
diff --git a/java/com/google/gerrit/pgm/util/BatchProgramModule.java b/java/com/google/gerrit/pgm/util/BatchProgramModule.java
index a831b8e..21ce2d1 100644
--- a/java/com/google/gerrit/pgm/util/BatchProgramModule.java
+++ b/java/com/google/gerrit/pgm/util/BatchProgramModule.java
@@ -18,8 +18,8 @@
 
 import com.google.common.cache.Cache;
 import com.google.common.collect.ImmutableSet;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.extensions.api.projects.CommentLinkInfo;
 import com.google.gerrit.extensions.common.AccountVisibility;
 import com.google.gerrit.extensions.config.FactoryModule;
diff --git a/java/com/google/gerrit/server/ReviewerByEmailSet.java b/java/com/google/gerrit/server/ReviewerByEmailSet.java
index caae45e..4a317c3 100644
--- a/java/com/google/gerrit/server/ReviewerByEmailSet.java
+++ b/java/com/google/gerrit/server/ReviewerByEmailSet.java
@@ -17,7 +17,7 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableTable;
 import com.google.common.collect.Table;
-import com.google.gerrit.mail.Address;
+import com.google.gerrit.entities.Address;
 import com.google.gerrit.server.notedb.ReviewerStateInternal;
 import java.sql.Timestamp;
 
diff --git a/java/com/google/gerrit/server/account/AccountConfig.java b/java/com/google/gerrit/server/account/AccountConfig.java
index 76d9471..e95bc1c 100644
--- a/java/com/google/gerrit/server/account/AccountConfig.java
+++ b/java/com/google/gerrit/server/account/AccountConfig.java
@@ -22,9 +22,9 @@
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.NotifyConfig.NotifyType;
 import com.google.gerrit.entities.RefNames;
 import com.google.gerrit.exceptions.DuplicateKeyException;
-import com.google.gerrit.server.account.ProjectWatches.NotifyType;
 import com.google.gerrit.server.account.ProjectWatches.ProjectWatchKey;
 import com.google.gerrit.server.account.externalids.ExternalIds;
 import com.google.gerrit.server.config.AllUsersName;
diff --git a/java/com/google/gerrit/server/account/AccountState.java b/java/com/google/gerrit/server/account/AccountState.java
index 1e9914d..b7a54f4 100644
--- a/java/com/google/gerrit/server/account/AccountState.java
+++ b/java/com/google/gerrit/server/account/AccountState.java
@@ -20,10 +20,10 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.NotifyConfig.NotifyType;
 import com.google.gerrit.extensions.client.DiffPreferencesInfo;
 import com.google.gerrit.extensions.client.EditPreferencesInfo;
 import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
-import com.google.gerrit.server.account.ProjectWatches.NotifyType;
 import com.google.gerrit.server.account.ProjectWatches.ProjectWatchKey;
 import com.google.gerrit.server.account.externalids.ExternalId;
 import com.google.gerrit.server.account.externalids.ExternalIdNotes;
diff --git a/java/com/google/gerrit/server/account/CachedAccountDetails.java b/java/com/google/gerrit/server/account/CachedAccountDetails.java
index 2eb5770..f23a766 100644
--- a/java/com/google/gerrit/server/account/CachedAccountDetails.java
+++ b/java/com/google/gerrit/server/account/CachedAccountDetails.java
@@ -23,6 +23,7 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.gerrit.common.UsedAt;
 import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.NotifyConfig;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.proto.Protos;
 import com.google.gerrit.server.cache.proto.Cache;
@@ -80,7 +81,7 @@
   abstract Account account();
 
   /** Projects that the user has configured to watch. */
-  abstract ImmutableMap<ProjectWatches.ProjectWatchKey, ImmutableSet<ProjectWatches.NotifyType>>
+  abstract ImmutableMap<ProjectWatches.ProjectWatchKey, ImmutableSet<NotifyConfig.NotifyType>>
       projectWatches();
 
   /** Preferences that this user has. Serialized as Git-config style string. */
@@ -88,7 +89,7 @@
 
   static CachedAccountDetails create(
       Account account,
-      ImmutableMap<ProjectWatches.ProjectWatchKey, ImmutableSet<ProjectWatches.NotifyType>>
+      ImmutableMap<ProjectWatches.ProjectWatchKey, ImmutableSet<NotifyConfig.NotifyType>>
           projectWatches,
       CachedPreferences preferences) {
     return new AutoValue_CachedAccountDetails(account, projectWatches, preferences);
@@ -115,8 +116,8 @@
               .setMetaId(Strings.nullToEmpty(account.metaId()));
       serialized.setAccount(accountProto);
 
-      for (Map.Entry<ProjectWatches.ProjectWatchKey, ImmutableSet<ProjectWatches.NotifyType>>
-          watch : cachedAccountDetails.projectWatches().entrySet()) {
+      for (Map.Entry<ProjectWatches.ProjectWatchKey, ImmutableSet<NotifyConfig.NotifyType>> watch :
+          cachedAccountDetails.projectWatches().entrySet()) {
         Cache.ProjectWatchProto.Builder proto =
             Cache.ProjectWatchProto.newBuilder().setProject(watch.getKey().project().get());
         if (watch.getKey().filter() != null) {
@@ -127,9 +128,7 @@
             .forEach(
                 n ->
                     proto.addNotifyType(
-                        Enums.stringConverter(ProjectWatches.NotifyType.class)
-                            .reverse()
-                            .convert(n)));
+                        Enums.stringConverter(NotifyConfig.NotifyType.class).reverse().convert(n)));
         serialized.addProjectWatchProto(proto);
       }
 
@@ -153,7 +152,7 @@
               .setMetaId(Strings.emptyToNull(proto.getAccount().getMetaId()))
               .build();
 
-      ImmutableMap.Builder<ProjectWatches.ProjectWatchKey, ImmutableSet<ProjectWatches.NotifyType>>
+      ImmutableMap.Builder<ProjectWatches.ProjectWatchKey, ImmutableSet<NotifyConfig.NotifyType>>
           projectWatches = ImmutableMap.builder();
       proto.getProjectWatchProtoList().stream()
           .forEach(
@@ -162,9 +161,7 @@
                       ProjectWatches.ProjectWatchKey.create(
                           Project.nameKey(p.getProject()), p.getFilter()),
                       p.getNotifyTypeList().stream()
-                          .map(
-                              e ->
-                                  Enums.stringConverter(ProjectWatches.NotifyType.class).convert(e))
+                          .map(e -> Enums.stringConverter(NotifyConfig.NotifyType.class).convert(e))
                           .collect(toImmutableSet())));
 
       return CachedAccountDetails.create(
diff --git a/java/com/google/gerrit/server/account/CapabilityCollection.java b/java/com/google/gerrit/server/account/CapabilityCollection.java
index de522ae..c1a8f73 100644
--- a/java/com/google/gerrit/server/account/CapabilityCollection.java
+++ b/java/com/google/gerrit/server/account/CapabilityCollection.java
@@ -20,10 +20,10 @@
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.common.data.AccessSection;
 import com.google.gerrit.common.data.GlobalCapability;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.common.data.Permission;
 import com.google.gerrit.common.data.PermissionRange;
 import com.google.gerrit.common.data.PermissionRule;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.server.config.AdministrateServerGroups;
 import com.google.gerrit.server.group.SystemGroupBackend;
 import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/server/account/GroupBackend.java b/java/com/google/gerrit/server/account/GroupBackend.java
index 3a874bb..545da6e 100644
--- a/java/com/google/gerrit/server/account/GroupBackend.java
+++ b/java/com/google/gerrit/server/account/GroupBackend.java
@@ -15,9 +15,9 @@
 package com.google.gerrit.server.account;
 
 import com.google.gerrit.common.Nullable;
-import com.google.gerrit.common.data.GroupDescription;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupDescription;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.extensions.annotations.ExtensionPoint;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.project.ProjectState;
diff --git a/java/com/google/gerrit/server/account/GroupBackends.java b/java/com/google/gerrit/server/account/GroupBackends.java
index 1b15512..26b3a82 100644
--- a/java/com/google/gerrit/server/account/GroupBackends.java
+++ b/java/com/google/gerrit/server/account/GroupBackends.java
@@ -18,7 +18,7 @@
 
 import com.google.common.collect.Iterables;
 import com.google.gerrit.common.Nullable;
-import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.server.project.ProjectState;
 import java.util.Collection;
 import java.util.Comparator;
diff --git a/java/com/google/gerrit/server/account/GroupControl.java b/java/com/google/gerrit/server/account/GroupControl.java
index 64fd7c6..d42db60 100644
--- a/java/com/google/gerrit/server/account/GroupControl.java
+++ b/java/com/google/gerrit/server/account/GroupControl.java
@@ -15,9 +15,9 @@
 package com.google.gerrit.server.account;
 
 import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.common.data.GroupDescription;
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupDescription;
 import com.google.gerrit.exceptions.NoSuchGroupException;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.server.CurrentUser;
diff --git a/java/com/google/gerrit/server/account/InternalAccountUpdate.java b/java/com/google/gerrit/server/account/InternalAccountUpdate.java
index bfbe917..4f9202f 100644
--- a/java/com/google/gerrit/server/account/InternalAccountUpdate.java
+++ b/java/com/google/gerrit/server/account/InternalAccountUpdate.java
@@ -20,10 +20,10 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.NotifyConfig.NotifyType;
 import com.google.gerrit.extensions.client.DiffPreferencesInfo;
 import com.google.gerrit.extensions.client.EditPreferencesInfo;
 import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
-import com.google.gerrit.server.account.ProjectWatches.NotifyType;
 import com.google.gerrit.server.account.ProjectWatches.ProjectWatchKey;
 import com.google.gerrit.server.account.externalids.DuplicateExternalIdKeyException;
 import com.google.gerrit.server.account.externalids.ExternalId;
diff --git a/java/com/google/gerrit/server/account/InternalGroupBackend.java b/java/com/google/gerrit/server/account/InternalGroupBackend.java
index ddd3da2..c520c96 100644
--- a/java/com/google/gerrit/server/account/InternalGroupBackend.java
+++ b/java/com/google/gerrit/server/account/InternalGroupBackend.java
@@ -17,9 +17,9 @@
 import static java.util.stream.Collectors.toList;
 
 import com.google.common.collect.ImmutableList;
-import com.google.gerrit.common.data.GroupDescription;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupDescription;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.group.InternalGroup;
 import com.google.gerrit.server.group.InternalGroupDescription;
diff --git a/java/com/google/gerrit/server/account/ProjectWatches.java b/java/com/google/gerrit/server/account/ProjectWatches.java
index 6d84f20..42137c1 100644
--- a/java/com/google/gerrit/server/account/ProjectWatches.java
+++ b/java/com/google/gerrit/server/account/ProjectWatches.java
@@ -31,6 +31,7 @@
 import com.google.common.collect.Sets;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.NotifyConfig;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.server.git.ValidationError;
 import java.util.ArrayList;
@@ -89,17 +90,6 @@
     public abstract @Nullable String filter();
   }
 
-  public enum NotifyType {
-    // sort by name, except 'ALL' which should stay last
-    ABANDONED_CHANGES,
-    ALL_COMMENTS,
-    NEW_CHANGES,
-    NEW_PATCHSETS,
-    SUBMITTED_CHANGES,
-
-    ALL
-  }
-
   public static final String FILTER_ALL = "*";
 
   public static final String WATCH_CONFIG = "watch.config";
@@ -110,7 +100,7 @@
   private final Config cfg;
   private final ValidationError.Sink validationErrorSink;
 
-  private ImmutableMap<ProjectWatchKey, ImmutableSet<NotifyType>> projectWatches;
+  private ImmutableMap<ProjectWatchKey, ImmutableSet<NotifyConfig.NotifyType>> projectWatches;
 
   ProjectWatches(Account.Id accountId, Config cfg, ValidationError.Sink validationErrorSink) {
     this.accountId = requireNonNull(accountId, "accountId");
@@ -118,7 +108,7 @@
     this.validationErrorSink = requireNonNull(validationErrorSink, "validationErrorSink");
   }
 
-  public ImmutableMap<ProjectWatchKey, ImmutableSet<NotifyType>> getProjectWatches() {
+  public ImmutableMap<ProjectWatchKey, ImmutableSet<NotifyConfig.NotifyType>> getProjectWatches() {
     if (projectWatches == null) {
       parse();
     }
@@ -152,9 +142,9 @@
    * @return the parsed project watches
    */
   @VisibleForTesting
-  public static ImmutableMap<ProjectWatchKey, ImmutableSet<NotifyType>> parse(
+  public static ImmutableMap<ProjectWatchKey, ImmutableSet<NotifyConfig.NotifyType>> parse(
       Account.Id accountId, Config cfg, ValidationError.Sink validationErrorSink) {
-    Map<ProjectWatchKey, Set<NotifyType>> projectWatches = new HashMap<>();
+    Map<ProjectWatchKey, Set<NotifyConfig.NotifyType>> projectWatches = new HashMap<>();
     for (String projectName : cfg.getSubsections(PROJECT)) {
       String[] notifyValues = cfg.getStringList(PROJECT, projectName, KEY_NOTIFY);
       for (String nv : notifyValues) {
@@ -171,7 +161,7 @@
         ProjectWatchKey key =
             ProjectWatchKey.create(Project.nameKey(projectName), notifyValue.filter());
         if (!projectWatches.containsKey(key)) {
-          projectWatches.put(key, EnumSet.noneOf(NotifyType.class));
+          projectWatches.put(key, EnumSet.noneOf(NotifyConfig.NotifyType.class));
         }
         projectWatches.get(key).addAll(notifyValue.notifyTypes());
       }
@@ -179,7 +169,7 @@
     return immutableCopyOf(projectWatches);
   }
 
-  public Config save(Map<ProjectWatchKey, Set<NotifyType>> projectWatches) {
+  public Config save(Map<ProjectWatchKey, Set<NotifyConfig.NotifyType>> projectWatches) {
     this.projectWatches = immutableCopyOf(projectWatches);
 
     for (String projectName : cfg.getSubsections(PROJECT)) {
@@ -188,7 +178,7 @@
 
     ListMultimap<String, String> notifyValuesByProject =
         MultimapBuilder.hashKeys().arrayListValues().build();
-    for (Map.Entry<ProjectWatchKey, Set<NotifyType>> e : projectWatches.entrySet()) {
+    for (Map.Entry<ProjectWatchKey, Set<NotifyConfig.NotifyType>> e : projectWatches.entrySet()) {
       NotifyValue notifyValue = NotifyValue.create(e.getKey().filter(), e.getValue());
       notifyValuesByProject.put(e.getKey().project().get(), notifyValue.toString());
     }
@@ -200,9 +190,10 @@
     return cfg;
   }
 
-  private static ImmutableMap<ProjectWatchKey, ImmutableSet<NotifyType>> immutableCopyOf(
-      Map<ProjectWatchKey, Set<NotifyType>> projectWatches) {
-    ImmutableMap.Builder<ProjectWatchKey, ImmutableSet<NotifyType>> b = ImmutableMap.builder();
+  private static ImmutableMap<ProjectWatchKey, ImmutableSet<NotifyConfig.NotifyType>>
+      immutableCopyOf(Map<ProjectWatchKey, Set<NotifyConfig.NotifyType>> projectWatches) {
+    ImmutableMap.Builder<ProjectWatchKey, ImmutableSet<NotifyConfig.NotifyType>> b =
+        ImmutableMap.builder();
     projectWatches.entrySet().stream()
         .forEach(e -> b.put(e.getKey(), ImmutableSet.copyOf(e.getValue())));
     return b.build();
@@ -231,13 +222,14 @@
         filter = null;
       }
 
-      Set<NotifyType> notifyTypes = EnumSet.noneOf(NotifyType.class);
+      Set<NotifyConfig.NotifyType> notifyTypes = EnumSet.noneOf(NotifyConfig.NotifyType.class);
       if (i + 1 < notifyValue.length() - 2) {
         for (String nt :
             Splitter.on(',')
                 .trimResults()
                 .splitToList(notifyValue.substring(i + 1, notifyValue.length() - 1))) {
-          NotifyType notifyType = Enums.getIfPresent(NotifyType.class, nt).orNull();
+          NotifyConfig.NotifyType notifyType =
+              Enums.getIfPresent(NotifyConfig.NotifyType.class, nt).orNull();
           if (notifyType == null) {
             validationErrorSink.error(
                 ValidationError.create(
@@ -254,18 +246,19 @@
       return create(filter, notifyTypes);
     }
 
-    public static NotifyValue create(@Nullable String filter, Collection<NotifyType> notifyTypes) {
+    public static NotifyValue create(
+        @Nullable String filter, Collection<NotifyConfig.NotifyType> notifyTypes) {
       return new AutoValue_ProjectWatches_NotifyValue(
           Strings.emptyToNull(filter), Sets.immutableEnumSet(notifyTypes));
     }
 
     public abstract @Nullable String filter();
 
-    public abstract ImmutableSet<NotifyType> notifyTypes();
+    public abstract ImmutableSet<NotifyConfig.NotifyType> notifyTypes();
 
     @Override
     public final String toString() {
-      List<NotifyType> notifyTypes = new ArrayList<>(notifyTypes());
+      List<NotifyConfig.NotifyType> notifyTypes = new ArrayList<>(notifyTypes());
       StringBuilder notifyValue = new StringBuilder();
       notifyValue.append(firstNonNull(filter(), FILTER_ALL)).append(" [");
       Joiner.on(", ").appendTo(notifyValue, notifyTypes);
diff --git a/java/com/google/gerrit/server/account/UniversalGroupBackend.java b/java/com/google/gerrit/server/account/UniversalGroupBackend.java
index fddbd2b..a35b0ac 100644
--- a/java/com/google/gerrit/server/account/UniversalGroupBackend.java
+++ b/java/com/google/gerrit/server/account/UniversalGroupBackend.java
@@ -24,9 +24,9 @@
 import com.google.common.collect.Sets;
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.Nullable;
-import com.google.gerrit.common.data.GroupDescription;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupDescription;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.StartupCheck;
 import com.google.gerrit.server.StartupException;
diff --git a/java/com/google/gerrit/server/args4j/AccountGroupUUIDHandler.java b/java/com/google/gerrit/server/args4j/AccountGroupUUIDHandler.java
index 20e8441..e88f6df 100644
--- a/java/com/google/gerrit/server/args4j/AccountGroupUUIDHandler.java
+++ b/java/com/google/gerrit/server/args4j/AccountGroupUUIDHandler.java
@@ -16,9 +16,9 @@
 
 import static com.google.gerrit.util.cli.Localizable.localizable;
 
-import com.google.gerrit.common.data.GroupDescription;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupDescription;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.server.account.GroupBackend;
 import com.google.gerrit.server.account.GroupBackends;
 import com.google.gerrit.server.account.GroupCache;
diff --git a/java/com/google/gerrit/server/auth/ldap/FakeLdapGroupBackend.java b/java/com/google/gerrit/server/auth/ldap/FakeLdapGroupBackend.java
index c2123cb..63cd426 100644
--- a/java/com/google/gerrit/server/auth/ldap/FakeLdapGroupBackend.java
+++ b/java/com/google/gerrit/server/auth/ldap/FakeLdapGroupBackend.java
@@ -17,9 +17,9 @@
 import static com.google.gerrit.server.auth.ldap.Helper.LDAP_UUID;
 
 import com.google.gerrit.common.Nullable;
-import com.google.gerrit.common.data.GroupDescription;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupDescription;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.account.GroupBackend;
 import com.google.gerrit.server.account.GroupMembership;
diff --git a/java/com/google/gerrit/server/auth/ldap/LdapGroupBackend.java b/java/com/google/gerrit/server/auth/ldap/LdapGroupBackend.java
index e86439a..180612c 100644
--- a/java/com/google/gerrit/server/auth/ldap/LdapGroupBackend.java
+++ b/java/com/google/gerrit/server/auth/ldap/LdapGroupBackend.java
@@ -24,10 +24,10 @@
 import com.google.common.collect.Sets;
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.Nullable;
-import com.google.gerrit.common.data.GroupDescription;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.common.data.ParameterizedString;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupDescription;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.account.GroupBackend;
diff --git a/java/com/google/gerrit/server/auth/ldap/LdapRealm.java b/java/com/google/gerrit/server/auth/ldap/LdapRealm.java
index 1421f17..b5972e2 100644
--- a/java/com/google/gerrit/server/auth/ldap/LdapRealm.java
+++ b/java/com/google/gerrit/server/auth/ldap/LdapRealm.java
@@ -20,10 +20,10 @@
 import com.google.common.cache.CacheLoader;
 import com.google.common.cache.LoadingCache;
 import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.common.data.ParameterizedString;
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.extensions.client.AccountFieldName;
 import com.google.gerrit.extensions.client.AuthType;
 import com.google.gerrit.server.account.AbstractRealm;
diff --git a/java/com/google/gerrit/server/cache/serialize/entities/AccessSectionSerializer.java b/java/com/google/gerrit/server/cache/serialize/entities/AccessSectionSerializer.java
new file mode 100644
index 0000000..af5fefd
--- /dev/null
+++ b/java/com/google/gerrit/server/cache/serialize/entities/AccessSectionSerializer.java
@@ -0,0 +1,45 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.cache.serialize.entities;
+
+import static com.google.common.collect.ImmutableList.toImmutableList;
+
+import com.google.gerrit.common.data.AccessSection;
+import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.server.cache.proto.Cache;
+
+/** Helper to (de)serialize values for caches. */
+public class AccessSectionSerializer {
+  public static AccessSection deserialize(Cache.AccessSectionProto proto) {
+    AccessSection.Builder builder = AccessSection.builder(proto.getName());
+    proto.getPermissionsList().stream()
+        .map(PermissionSerializer::deserialize)
+        .map(Permission::toBuilder)
+        .forEach(p -> builder.addPermission(p));
+    return builder.build();
+  }
+
+  public static Cache.AccessSectionProto serialize(AccessSection autoValue) {
+    return Cache.AccessSectionProto.newBuilder()
+        .setName(autoValue.getName())
+        .addAllPermissions(
+            autoValue.getPermissions().stream()
+                .map(PermissionSerializer::serialize)
+                .collect(toImmutableList()))
+        .build();
+  }
+
+  private AccessSectionSerializer() {}
+}
diff --git a/java/com/google/gerrit/server/cache/serialize/entities/AddressSerializer.java b/java/com/google/gerrit/server/cache/serialize/entities/AddressSerializer.java
new file mode 100644
index 0000000..cc86109
--- /dev/null
+++ b/java/com/google/gerrit/server/cache/serialize/entities/AddressSerializer.java
@@ -0,0 +1,37 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.cache.serialize.entities;
+
+import static com.google.common.base.Strings.emptyToNull;
+import static com.google.common.base.Strings.nullToEmpty;
+
+import com.google.gerrit.entities.Address;
+import com.google.gerrit.server.cache.proto.Cache;
+
+/** Helper to (de)serialize values for caches. */
+public class AddressSerializer {
+  public static Address deserialize(Cache.AddressProto proto) {
+    return Address.create(emptyToNull(proto.getName()), proto.getEmail());
+  }
+
+  public static Cache.AddressProto serialize(Address autoValue) {
+    return Cache.AddressProto.newBuilder()
+        .setName(nullToEmpty(autoValue.name()))
+        .setEmail(autoValue.email())
+        .build();
+  }
+
+  private AddressSerializer() {}
+}
diff --git a/java/com/google/gerrit/server/cache/serialize/entities/BranchOrderSectionSerializer.java b/java/com/google/gerrit/server/cache/serialize/entities/BranchOrderSectionSerializer.java
new file mode 100644
index 0000000..e86db74
--- /dev/null
+++ b/java/com/google/gerrit/server/cache/serialize/entities/BranchOrderSectionSerializer.java
@@ -0,0 +1,33 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.cache.serialize.entities;
+
+import com.google.gerrit.entities.BranchOrderSection;
+import com.google.gerrit.server.cache.proto.Cache;
+
+/** Helper to (de)serialize values for caches. */
+public class BranchOrderSectionSerializer {
+  public static BranchOrderSection deserialize(Cache.BranchOrderSectionProto proto) {
+    return BranchOrderSection.create(proto.getBranchesInOrderList());
+  }
+
+  public static Cache.BranchOrderSectionProto serialize(BranchOrderSection autoValue) {
+    return Cache.BranchOrderSectionProto.newBuilder()
+        .addAllBranchesInOrder(autoValue.order())
+        .build();
+  }
+
+  private BranchOrderSectionSerializer() {}
+}
diff --git a/java/com/google/gerrit/server/cache/serialize/entities/ConfiguredMimeTypeSerializer.java b/java/com/google/gerrit/server/cache/serialize/entities/ConfiguredMimeTypeSerializer.java
new file mode 100644
index 0000000..6e0c923
--- /dev/null
+++ b/java/com/google/gerrit/server/cache/serialize/entities/ConfiguredMimeTypeSerializer.java
@@ -0,0 +1,40 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.cache.serialize.entities;
+
+import com.google.gerrit.entities.ConfiguredMimeTypes;
+import com.google.gerrit.server.cache.proto.Cache;
+import java.util.regex.PatternSyntaxException;
+import org.eclipse.jgit.errors.InvalidPatternException;
+
+public class ConfiguredMimeTypeSerializer {
+  public static ConfiguredMimeTypes.TypeMatcher deserialize(Cache.ConfiguredMimeTypeProto proto) {
+    try {
+      return proto.getIsRegularExpression()
+          ? new ConfiguredMimeTypes.ReType(proto.getType(), proto.getPattern())
+          : new ConfiguredMimeTypes.FnType(proto.getType(), proto.getPattern());
+    } catch (PatternSyntaxException | InvalidPatternException e) {
+      throw new IllegalStateException(e);
+    }
+  }
+
+  public static Cache.ConfiguredMimeTypeProto serialize(ConfiguredMimeTypes.TypeMatcher value) {
+    return Cache.ConfiguredMimeTypeProto.newBuilder()
+        .setType(value.getType())
+        .setPattern(value.getPattern())
+        .setIsRegularExpression(value instanceof ConfiguredMimeTypes.ReType)
+        .build();
+  }
+}
diff --git a/java/com/google/gerrit/server/cache/serialize/entities/ContributorAgreementSerializer.java b/java/com/google/gerrit/server/cache/serialize/entities/ContributorAgreementSerializer.java
new file mode 100644
index 0000000..54d0703
--- /dev/null
+++ b/java/com/google/gerrit/server/cache/serialize/entities/ContributorAgreementSerializer.java
@@ -0,0 +1,62 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.cache.serialize.entities;
+
+import static com.google.common.base.Strings.emptyToNull;
+import static com.google.common.base.Strings.nullToEmpty;
+import static com.google.common.collect.ImmutableList.toImmutableList;
+
+import com.google.gerrit.common.data.ContributorAgreement;
+import com.google.gerrit.server.cache.proto.Cache;
+
+/** Helper to (de)serialize values for caches. */
+public class ContributorAgreementSerializer {
+  public static ContributorAgreement deserialize(Cache.ContributorAgreementProto proto) {
+    ContributorAgreement.Builder builder =
+        ContributorAgreement.builder(proto.getName())
+            .setDescription(emptyToNull(proto.getDescription()))
+            .setAccepted(
+                proto.getAcceptedList().stream()
+                    .map(PermissionRuleSerializer::deserialize)
+                    .collect(toImmutableList()))
+            .setAgreementUrl(emptyToNull(proto.getUrl()))
+            .setExcludeProjectsRegexes(proto.getExcludeRegularExpressionsList())
+            .setMatchProjectsRegexes(proto.getMatchRegularExpressionsList());
+    if (proto.hasAutoVerify()) {
+      builder.setAutoVerify(GroupReferenceSerializer.deserialize(proto.getAutoVerify()));
+    }
+    return builder.build();
+  }
+
+  public static Cache.ContributorAgreementProto serialize(ContributorAgreement autoValue) {
+    Cache.ContributorAgreementProto.Builder builder =
+        Cache.ContributorAgreementProto.newBuilder()
+            .setName(autoValue.getName())
+            .setDescription(nullToEmpty(autoValue.getDescription()))
+            .addAllAccepted(
+                autoValue.getAccepted().stream()
+                    .map(PermissionRuleSerializer::serialize)
+                    .collect(toImmutableList()))
+            .setUrl(nullToEmpty(autoValue.getAgreementUrl()))
+            .addAllExcludeRegularExpressions(autoValue.getExcludeProjectsRegexes())
+            .addAllMatchRegularExpressions(autoValue.getMatchProjectsRegexes());
+    if (autoValue.getAutoVerify() != null) {
+      builder.setAutoVerify(GroupReferenceSerializer.serialize(autoValue.getAutoVerify()));
+    }
+    return builder.build();
+  }
+
+  private ContributorAgreementSerializer() {}
+}
diff --git a/java/com/google/gerrit/server/cache/serialize/entities/GroupReferenceSerializer.java b/java/com/google/gerrit/server/cache/serialize/entities/GroupReferenceSerializer.java
index e6481d9..c5d4d07 100644
--- a/java/com/google/gerrit/server/cache/serialize/entities/GroupReferenceSerializer.java
+++ b/java/com/google/gerrit/server/cache/serialize/entities/GroupReferenceSerializer.java
@@ -14,8 +14,8 @@
 
 package com.google.gerrit.server.cache.serialize.entities;
 
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.server.cache.proto.Cache;
 
 /** Helper to (de)serialize values for caches. */
diff --git a/java/com/google/gerrit/server/cache/serialize/entities/LabelTypeSerializer.java b/java/com/google/gerrit/server/cache/serialize/entities/LabelTypeSerializer.java
new file mode 100644
index 0000000..1566e22
--- /dev/null
+++ b/java/com/google/gerrit/server/cache/serialize/entities/LabelTypeSerializer.java
@@ -0,0 +1,88 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.cache.serialize.entities;
+
+import static com.google.common.collect.ImmutableList.toImmutableList;
+
+import com.google.common.base.Converter;
+import com.google.common.base.Enums;
+import com.google.common.collect.ImmutableList;
+import com.google.common.primitives.Shorts;
+import com.google.gerrit.common.data.LabelFunction;
+import com.google.gerrit.common.data.LabelType;
+import com.google.gerrit.server.cache.proto.Cache;
+
+/** Helper to (de)serialize values for caches. */
+public class LabelTypeSerializer {
+  private static final Converter<String, LabelFunction> FUNCTION_CONVERTER =
+      Enums.stringConverter(LabelFunction.class);
+
+  public static LabelType deserialize(Cache.LabelTypeProto proto) {
+    return LabelType.builder(
+            proto.getName(),
+            proto.getValuesList().stream()
+                .map(LabelValueSerializer::deserialize)
+                .collect(toImmutableList()))
+        .setFunction(FUNCTION_CONVERTER.convert(proto.getFunction()))
+        .setAllowPostSubmit(proto.getAllowPostSubmit())
+        .setIgnoreSelfApproval(proto.getIgnoreSelfApproval())
+        .setDefaultValue(Shorts.saturatedCast(proto.getDefaultValue()))
+        .setCopyAnyScore(proto.getCopyAnyScore())
+        .setCopyMinScore(proto.getCopyMinScore())
+        .setCopyMaxScore(proto.getCopyMaxScore())
+        .setCopyAllScoresOnMergeFirstParentUpdate(proto.getCopyAllScoresOnMergeFirstParentUpdate())
+        .setCopyAllScoresOnTrivialRebase(proto.getCopyAllScoresOnTrivialRebase())
+        .setCopyAllScoresIfNoCodeChange(proto.getCopyAllScoresIfNoCodeChange())
+        .setCopyAllScoresIfNoChange(proto.getCopyAllScoresIfNoChange())
+        .setCopyValues(
+            proto.getCopyValuesList().stream()
+                .map(Shorts::saturatedCast)
+                .collect(toImmutableList()))
+        .setMaxNegative(Shorts.saturatedCast(proto.getMaxNegative()))
+        .setMaxPositive(Shorts.saturatedCast(proto.getMaxPositive()))
+        .setRefPatterns(proto.getRefPatternsList())
+        .build();
+  }
+
+  public static Cache.LabelTypeProto serialize(LabelType autoValue) {
+    return Cache.LabelTypeProto.newBuilder()
+        .setName(autoValue.getName())
+        .addAllValues(
+            autoValue.getValues().stream()
+                .map(LabelValueSerializer::serialize)
+                .collect(toImmutableList()))
+        .setFunction(FUNCTION_CONVERTER.reverse().convert(autoValue.getFunction()))
+        .setCopyAnyScore(autoValue.isCopyAnyScore())
+        .setCopyMinScore(autoValue.isCopyMinScore())
+        .setCopyMaxScore(autoValue.isCopyMaxScore())
+        .setCopyAllScoresOnMergeFirstParentUpdate(
+            autoValue.isCopyAllScoresOnMergeFirstParentUpdate())
+        .setCopyAllScoresOnTrivialRebase(autoValue.isCopyAllScoresOnTrivialRebase())
+        .setCopyAllScoresIfNoCodeChange(autoValue.isCopyAllScoresIfNoCodeChange())
+        .setCopyAllScoresIfNoChange(autoValue.isCopyAllScoresIfNoChange())
+        .addAllCopyValues(
+            autoValue.getCopyValues().stream().map(c -> (int) c).collect(toImmutableList()))
+        .setAllowPostSubmit(autoValue.isAllowPostSubmit())
+        .setIgnoreSelfApproval(autoValue.isIgnoreSelfApproval())
+        .setDefaultValue(Shorts.saturatedCast(autoValue.getDefaultValue()))
+        .setMaxNegative(Shorts.saturatedCast(autoValue.getMaxNegative()))
+        .setMaxPositive(Shorts.saturatedCast(autoValue.getMaxPositive()))
+        .addAllRefPatterns(
+            autoValue.getRefPatterns() == null ? ImmutableList.of() : autoValue.getRefPatterns())
+        .build();
+  }
+
+  private LabelTypeSerializer() {}
+}
diff --git a/java/com/google/gerrit/server/cache/serialize/entities/LabelValueSerializer.java b/java/com/google/gerrit/server/cache/serialize/entities/LabelValueSerializer.java
new file mode 100644
index 0000000..c1ca9a1
--- /dev/null
+++ b/java/com/google/gerrit/server/cache/serialize/entities/LabelValueSerializer.java
@@ -0,0 +1,35 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.cache.serialize.entities;
+
+import com.google.common.primitives.Shorts;
+import com.google.gerrit.entities.LabelValue;
+import com.google.gerrit.server.cache.proto.Cache;
+
+/** Helper to (de)serialize values for caches. */
+public class LabelValueSerializer {
+  public static LabelValue deserialize(Cache.LabelValueProto proto) {
+    return LabelValue.create(Shorts.saturatedCast(proto.getValue()), proto.getText());
+  }
+
+  public static Cache.LabelValueProto serialize(LabelValue autoValue) {
+    return Cache.LabelValueProto.newBuilder()
+        .setText(autoValue.getText())
+        .setValue(autoValue.getValue())
+        .build();
+  }
+
+  private LabelValueSerializer() {}
+}
diff --git a/java/com/google/gerrit/server/cache/serialize/entities/NotifyConfigSerializer.java b/java/com/google/gerrit/server/cache/serialize/entities/NotifyConfigSerializer.java
new file mode 100644
index 0000000..f0f7d905
--- /dev/null
+++ b/java/com/google/gerrit/server/cache/serialize/entities/NotifyConfigSerializer.java
@@ -0,0 +1,79 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.cache.serialize.entities;
+
+import static com.google.common.base.Strings.emptyToNull;
+import static com.google.common.base.Strings.nullToEmpty;
+import static com.google.common.collect.ImmutableList.toImmutableList;
+import static com.google.common.collect.ImmutableSet.toImmutableSet;
+
+import com.google.common.base.Converter;
+import com.google.common.base.Enums;
+import com.google.gerrit.entities.NotifyConfig;
+import com.google.gerrit.server.cache.proto.Cache;
+
+/** Helper to (de)serialize values for caches. */
+public class NotifyConfigSerializer {
+  private static final Converter<String, NotifyConfig.Header> HEADER_CONVERTER =
+      Enums.stringConverter(NotifyConfig.Header.class);
+
+  private static final Converter<String, NotifyConfig.NotifyType> NOTIFY_TYPE_CONVERTER =
+      Enums.stringConverter(NotifyConfig.NotifyType.class);
+
+  public static NotifyConfig deserialize(Cache.NotifyConfigProto proto) {
+    NotifyConfig.Builder builder =
+        NotifyConfig.builder()
+            .setName(emptyToNull(proto.getName()))
+            .setNotify(
+                proto.getTypeList().stream()
+                    .map(t -> NOTIFY_TYPE_CONVERTER.convert(t))
+                    .collect(toImmutableSet()))
+            .setFilter(emptyToNull(proto.getFilter()))
+            .setHeader(
+                proto.getHeader().isEmpty() ? null : HEADER_CONVERTER.convert(proto.getHeader()));
+    proto.getGroupsList().stream()
+        .map(GroupReferenceSerializer::deserialize)
+        .forEach(g -> builder.addGroup(g));
+    proto.getAddressesList().stream()
+        .map(AddressSerializer::deserialize)
+        .forEach(a -> builder.addAddress(a));
+    return builder.build();
+  }
+
+  public static Cache.NotifyConfigProto serialize(NotifyConfig autoValue) {
+    return Cache.NotifyConfigProto.newBuilder()
+        .setName(nullToEmpty(autoValue.getName()))
+        .addAllType(
+            autoValue.getNotify().stream()
+                .map(t -> NOTIFY_TYPE_CONVERTER.reverse().convert(t))
+                .collect(toImmutableSet()))
+        .setFilter(nullToEmpty(autoValue.getFilter()))
+        .setHeader(
+            autoValue.getHeader() == null
+                ? ""
+                : HEADER_CONVERTER.reverse().convert(autoValue.getHeader()))
+        .addAllGroups(
+            autoValue.getGroups().stream()
+                .map(GroupReferenceSerializer::serialize)
+                .collect(toImmutableSet()))
+        .addAllAddresses(
+            autoValue.getAddresses().stream()
+                .map(AddressSerializer::serialize)
+                .collect(toImmutableList()))
+        .build();
+  }
+
+  private NotifyConfigSerializer() {}
+}
diff --git a/java/com/google/gerrit/server/cache/serialize/entities/PermissionSerializer.java b/java/com/google/gerrit/server/cache/serialize/entities/PermissionSerializer.java
new file mode 100644
index 0000000..983d926
--- /dev/null
+++ b/java/com/google/gerrit/server/cache/serialize/entities/PermissionSerializer.java
@@ -0,0 +1,47 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.cache.serialize.entities;
+
+import static com.google.common.collect.ImmutableList.toImmutableList;
+
+import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.common.data.PermissionRule;
+import com.google.gerrit.server.cache.proto.Cache;
+
+/** Helper to (de)serialize values for caches. */
+public class PermissionSerializer {
+  public static Permission deserialize(Cache.PermissionProto proto) {
+    Permission.Builder builder =
+        Permission.builder(proto.getName()).setExclusiveGroup(proto.getExclusiveGroup());
+    proto.getRulesList().stream()
+        .map(PermissionRuleSerializer::deserialize)
+        .map(PermissionRule::toBuilder)
+        .forEach(rule -> builder.add(rule));
+    return builder.build();
+  }
+
+  public static Cache.PermissionProto serialize(Permission autoValue) {
+    return Cache.PermissionProto.newBuilder()
+        .setName(autoValue.getName())
+        .setExclusiveGroup(autoValue.getExclusiveGroup())
+        .addAllRules(
+            autoValue.getRules().stream()
+                .map(PermissionRuleSerializer::serialize)
+                .collect(toImmutableList()))
+        .build();
+  }
+
+  private PermissionSerializer() {}
+}
diff --git a/java/com/google/gerrit/server/cache/serialize/entities/StoredCommentLinkInfoSerializer.java b/java/com/google/gerrit/server/cache/serialize/entities/StoredCommentLinkInfoSerializer.java
new file mode 100644
index 0000000..d7bd373
--- /dev/null
+++ b/java/com/google/gerrit/server/cache/serialize/entities/StoredCommentLinkInfoSerializer.java
@@ -0,0 +1,47 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.cache.serialize.entities;
+
+import static com.google.common.base.Strings.emptyToNull;
+import static com.google.common.base.Strings.nullToEmpty;
+
+import com.google.gerrit.entities.StoredCommentLinkInfo;
+import com.google.gerrit.server.cache.proto.Cache;
+
+/** Helper to (de)serialize values for caches. */
+public class StoredCommentLinkInfoSerializer {
+  public static StoredCommentLinkInfo deserialize(Cache.StoredCommentLinkInfoProto proto) {
+    return StoredCommentLinkInfo.builder(proto.getName())
+        .setMatch(emptyToNull(proto.getMatch()))
+        .setLink(emptyToNull(proto.getLink()))
+        .setHtml(emptyToNull(proto.getHtml()))
+        .setEnabled(proto.getEnabled())
+        .setOverrideOnly(proto.getOverrideOnly())
+        .build();
+  }
+
+  public static Cache.StoredCommentLinkInfoProto serialize(StoredCommentLinkInfo autoValue) {
+    return Cache.StoredCommentLinkInfoProto.newBuilder()
+        .setName(autoValue.getName())
+        .setMatch(nullToEmpty(autoValue.getMatch()))
+        .setLink(nullToEmpty(autoValue.getLink()))
+        .setHtml(nullToEmpty(autoValue.getHtml()))
+        .setEnabled(autoValue.getEnabled())
+        .setOverrideOnly(autoValue.getOverrideOnly())
+        .build();
+  }
+
+  private StoredCommentLinkInfoSerializer() {}
+}
diff --git a/java/com/google/gerrit/server/cache/serialize/entities/SubscribeSectionSerializer.java b/java/com/google/gerrit/server/cache/serialize/entities/SubscribeSectionSerializer.java
new file mode 100644
index 0000000..6125818d
--- /dev/null
+++ b/java/com/google/gerrit/server/cache/serialize/entities/SubscribeSectionSerializer.java
@@ -0,0 +1,40 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.cache.serialize.entities;
+
+import com.google.gerrit.common.data.SubscribeSection;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.server.cache.proto.Cache;
+
+/** Helper to (de)serialize values for caches. */
+public class SubscribeSectionSerializer {
+  public static SubscribeSection deserialize(Cache.SubscribeSectionProto proto) {
+    SubscribeSection.Builder builder =
+        SubscribeSection.builder(Project.nameKey(proto.getProjectName()));
+    proto.getMatchingRefSpecsList().forEach(rs -> builder.addMatchingRefSpec(rs));
+    proto.getMultiMatchRefSpecsList().forEach(rs -> builder.addMultiMatchRefSpec(rs));
+    return builder.build();
+  }
+
+  public static Cache.SubscribeSectionProto serialize(SubscribeSection autoValue) {
+    Cache.SubscribeSectionProto.Builder builder =
+        Cache.SubscribeSectionProto.newBuilder().setProjectName(autoValue.project().get());
+    autoValue.multiMatchRefSpecsAsString().forEach(rs -> builder.addMultiMatchRefSpecs(rs));
+    autoValue.matchingRefSpecsAsString().forEach(rs -> builder.addMatchingRefSpecs(rs));
+    return builder.build();
+  }
+
+  private SubscribeSectionSerializer() {}
+}
diff --git a/java/com/google/gerrit/server/change/AddReviewersEmail.java b/java/com/google/gerrit/server/change/AddReviewersEmail.java
index 4fdcb9e..4a3f638 100644
--- a/java/com/google/gerrit/server/change/AddReviewersEmail.java
+++ b/java/com/google/gerrit/server/change/AddReviewersEmail.java
@@ -19,9 +19,9 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Address;
 import com.google.gerrit.entities.Change;
 import com.google.gerrit.entities.Project;
-import com.google.gerrit.mail.Address;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.config.SendEmailExecutor;
 import com.google.gerrit.server.mail.send.AddReviewerSender;
diff --git a/java/com/google/gerrit/server/change/AddReviewersOp.java b/java/com/google/gerrit/server/change/AddReviewersOp.java
index 7b87a29..ff8e5c6 100644
--- a/java/com/google/gerrit/server/change/AddReviewersOp.java
+++ b/java/com/google/gerrit/server/change/AddReviewersOp.java
@@ -29,12 +29,12 @@
 import com.google.common.collect.Lists;
 import com.google.common.collect.Streams;
 import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Address;
 import com.google.gerrit.entities.Change;
 import com.google.gerrit.entities.PatchSet;
 import com.google.gerrit.entities.PatchSetApproval;
 import com.google.gerrit.extensions.client.ReviewerState;
 import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.mail.Address;
 import com.google.gerrit.server.ApprovalsUtil;
 import com.google.gerrit.server.PatchSetUtil;
 import com.google.gerrit.server.account.AccountCache;
diff --git a/java/com/google/gerrit/server/change/ChangeJson.java b/java/com/google/gerrit/server/change/ChangeJson.java
index d0e9288..31df6a4 100644
--- a/java/com/google/gerrit/server/change/ChangeJson.java
+++ b/java/com/google/gerrit/server/change/ChangeJson.java
@@ -51,6 +51,7 @@
 import com.google.gerrit.common.data.SubmitRequirement;
 import com.google.gerrit.common.data.SubmitTypeRecord;
 import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Address;
 import com.google.gerrit.entities.Change;
 import com.google.gerrit.entities.ChangeMessage;
 import com.google.gerrit.entities.PatchSet;
@@ -73,7 +74,6 @@
 import com.google.gerrit.extensions.common.TrackingIdInfo;
 import com.google.gerrit.extensions.restapi.Url;
 import com.google.gerrit.index.query.QueryResult;
-import com.google.gerrit.mail.Address;
 import com.google.gerrit.metrics.Description;
 import com.google.gerrit.metrics.Description.Units;
 import com.google.gerrit.metrics.MetricMaker;
diff --git a/java/com/google/gerrit/server/change/DeleteReviewerByEmailOp.java b/java/com/google/gerrit/server/change/DeleteReviewerByEmailOp.java
index e2d661a..255e13a 100644
--- a/java/com/google/gerrit/server/change/DeleteReviewerByEmailOp.java
+++ b/java/com/google/gerrit/server/change/DeleteReviewerByEmailOp.java
@@ -15,10 +15,10 @@
 package com.google.gerrit.server.change;
 
 import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Address;
 import com.google.gerrit.entities.Change;
 import com.google.gerrit.entities.ChangeMessage;
 import com.google.gerrit.entities.PatchSet;
-import com.google.gerrit.mail.Address;
 import com.google.gerrit.server.ChangeUtil;
 import com.google.gerrit.server.mail.send.DeleteReviewerSender;
 import com.google.gerrit.server.mail.send.MessageIdGenerator;
diff --git a/java/com/google/gerrit/server/change/LabelNormalizer.java b/java/com/google/gerrit/server/change/LabelNormalizer.java
index 67cd0df..619b939 100644
--- a/java/com/google/gerrit/server/change/LabelNormalizer.java
+++ b/java/com/google/gerrit/server/change/LabelNormalizer.java
@@ -24,8 +24,8 @@
 import com.google.common.collect.Lists;
 import com.google.gerrit.common.data.LabelType;
 import com.google.gerrit.common.data.LabelTypes;
-import com.google.gerrit.common.data.LabelValue;
 import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.LabelValue;
 import com.google.gerrit.entities.PatchSetApproval;
 import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gerrit.server.project.ProjectCache;
diff --git a/java/com/google/gerrit/server/change/LabelsJson.java b/java/com/google/gerrit/server/change/LabelsJson.java
index 739e263..d9e81b1 100644
--- a/java/com/google/gerrit/server/change/LabelsJson.java
+++ b/java/com/google/gerrit/server/change/LabelsJson.java
@@ -34,9 +34,9 @@
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.common.data.LabelType;
 import com.google.gerrit.common.data.LabelTypes;
-import com.google.gerrit.common.data.LabelValue;
 import com.google.gerrit.common.data.SubmitRecord;
 import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.LabelValue;
 import com.google.gerrit.entities.PatchSetApproval;
 import com.google.gerrit.extensions.client.ListChangesOption;
 import com.google.gerrit.extensions.common.ApprovalInfo;
diff --git a/java/com/google/gerrit/server/change/ReviewerAdder.java b/java/com/google/gerrit/server/change/ReviewerAdder.java
index d9462bf..c271651 100644
--- a/java/com/google/gerrit/server/change/ReviewerAdder.java
+++ b/java/com/google/gerrit/server/change/ReviewerAdder.java
@@ -31,12 +31,13 @@
 import com.google.common.collect.Streams;
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.Nullable;
-import com.google.gerrit.common.data.GroupDescription;
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.Address;
 import com.google.gerrit.entities.BooleanProjectConfig;
 import com.google.gerrit.entities.BranchNameKey;
 import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.GroupDescription;
 import com.google.gerrit.entities.PatchSet;
 import com.google.gerrit.entities.PatchSetApproval;
 import com.google.gerrit.extensions.api.changes.AddReviewerInput;
@@ -48,7 +49,6 @@
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
-import com.google.gerrit.mail.Address;
 import com.google.gerrit.server.AnonymousUser;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.IdentifiedUser;
diff --git a/java/com/google/gerrit/server/change/ReviewerJson.java b/java/com/google/gerrit/server/change/ReviewerJson.java
index 39e5f74..d493fd0 100644
--- a/java/com/google/gerrit/server/change/ReviewerJson.java
+++ b/java/com/google/gerrit/server/change/ReviewerJson.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.server.change;
 
-import static com.google.gerrit.common.data.LabelValue.formatValue;
+import static com.google.gerrit.entities.LabelValue.formatValue;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
@@ -22,11 +22,11 @@
 import com.google.gerrit.common.data.LabelTypes;
 import com.google.gerrit.common.data.SubmitRecord;
 import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Address;
 import com.google.gerrit.entities.PatchSet;
 import com.google.gerrit.entities.PatchSetApproval;
 import com.google.gerrit.extensions.api.changes.ReviewerInfo;
 import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.mail.Address;
 import com.google.gerrit.server.ApprovalsUtil;
 import com.google.gerrit.server.account.AccountLoader;
 import com.google.gerrit.server.permissions.LabelPermission;
diff --git a/java/com/google/gerrit/server/change/ReviewerResource.java b/java/com/google/gerrit/server/change/ReviewerResource.java
index df0a03f..7a98f2b 100644
--- a/java/com/google/gerrit/server/change/ReviewerResource.java
+++ b/java/com/google/gerrit/server/change/ReviewerResource.java
@@ -18,10 +18,10 @@
 
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Address;
 import com.google.gerrit.entities.Change;
 import com.google.gerrit.extensions.restapi.RestResource;
 import com.google.gerrit.extensions.restapi.RestView;
-import com.google.gerrit.mail.Address;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.inject.TypeLiteral;
 import com.google.inject.assistedinject.Assisted;
diff --git a/java/com/google/gerrit/server/config/AdministrateServerGroupsProvider.java b/java/com/google/gerrit/server/config/AdministrateServerGroupsProvider.java
index d6e61c4..64937db 100644
--- a/java/com/google/gerrit/server/config/AdministrateServerGroupsProvider.java
+++ b/java/com/google/gerrit/server/config/AdministrateServerGroupsProvider.java
@@ -16,8 +16,8 @@
 
 import com.google.common.collect.ImmutableSet;
 import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.common.data.PermissionRule;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.server.account.GroupBackend;
 import com.google.gerrit.server.account.GroupBackends;
 import com.google.gerrit.server.util.RequestContext;
diff --git a/java/com/google/gerrit/server/config/GroupSetProvider.java b/java/com/google/gerrit/server/config/GroupSetProvider.java
index 7f487e1..025946d 100644
--- a/java/com/google/gerrit/server/config/GroupSetProvider.java
+++ b/java/com/google/gerrit/server/config/GroupSetProvider.java
@@ -16,8 +16,8 @@
 
 import com.google.common.collect.ImmutableSet;
 import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.server.account.GroupBackend;
 import com.google.gerrit.server.account.GroupBackends;
 import com.google.gerrit.server.util.RequestContext;
diff --git a/java/com/google/gerrit/server/config/PluginConfig.java b/java/com/google/gerrit/server/config/PluginConfig.java
index f41d5c2..0eebd98 100644
--- a/java/com/google/gerrit/server/config/PluginConfig.java
+++ b/java/com/google/gerrit/server/config/PluginConfig.java
@@ -17,7 +17,7 @@
 import com.google.common.base.MoreObjects;
 import com.google.common.base.Strings;
 import com.google.common.collect.Iterables;
-import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.server.project.ProjectConfig;
 import com.google.gerrit.server.project.ProjectState;
 import java.util.Arrays;
diff --git a/java/com/google/gerrit/server/group/GroupResolver.java b/java/com/google/gerrit/server/group/GroupResolver.java
index 1aa265b..50ec893 100644
--- a/java/com/google/gerrit/server/group/GroupResolver.java
+++ b/java/com/google/gerrit/server/group/GroupResolver.java
@@ -15,9 +15,9 @@
 package com.google.gerrit.server.group;
 
 import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.common.data.GroupDescription;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupDescription;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
 import com.google.gerrit.server.account.GroupBackend;
 import com.google.gerrit.server.account.GroupBackends;
diff --git a/java/com/google/gerrit/server/group/GroupResource.java b/java/com/google/gerrit/server/group/GroupResource.java
index 1050314..b0e81ec 100644
--- a/java/com/google/gerrit/server/group/GroupResource.java
+++ b/java/com/google/gerrit/server/group/GroupResource.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.server.group;
 
-import com.google.gerrit.common.data.GroupDescription;
+import com.google.gerrit.entities.GroupDescription;
 import com.google.gerrit.extensions.restapi.RestResource;
 import com.google.gerrit.extensions.restapi.RestView;
 import com.google.gerrit.server.account.GroupControl;
diff --git a/java/com/google/gerrit/server/group/InternalGroupDescription.java b/java/com/google/gerrit/server/group/InternalGroupDescription.java
index c70c8bf..740557a 100644
--- a/java/com/google/gerrit/server/group/InternalGroupDescription.java
+++ b/java/com/google/gerrit/server/group/InternalGroupDescription.java
@@ -19,9 +19,9 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.common.data.GroupDescription;
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupDescription;
 import java.sql.Timestamp;
 
 public class InternalGroupDescription implements GroupDescription.Internal {
diff --git a/java/com/google/gerrit/server/group/PeriodicGroupIndexer.java b/java/com/google/gerrit/server/group/PeriodicGroupIndexer.java
index b2d9632..cae213f 100644
--- a/java/com/google/gerrit/server/group/PeriodicGroupIndexer.java
+++ b/java/com/google/gerrit/server/group/PeriodicGroupIndexer.java
@@ -19,8 +19,8 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
 import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.extensions.events.LifecycleListener;
 import com.google.gerrit.lifecycle.LifecycleModule;
 import com.google.gerrit.server.config.AllUsersName;
diff --git a/java/com/google/gerrit/server/group/SubgroupResource.java b/java/com/google/gerrit/server/group/SubgroupResource.java
index ceea2dc..21356be 100644
--- a/java/com/google/gerrit/server/group/SubgroupResource.java
+++ b/java/com/google/gerrit/server/group/SubgroupResource.java
@@ -14,8 +14,8 @@
 
 package com.google.gerrit.server.group;
 
-import com.google.gerrit.common.data.GroupDescription;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupDescription;
 import com.google.gerrit.extensions.restapi.RestView;
 import com.google.inject.TypeLiteral;
 
diff --git a/java/com/google/gerrit/server/group/SystemGroupBackend.java b/java/com/google/gerrit/server/group/SystemGroupBackend.java
index a446718..b5ccb18 100644
--- a/java/com/google/gerrit/server/group/SystemGroupBackend.java
+++ b/java/com/google/gerrit/server/group/SystemGroupBackend.java
@@ -21,9 +21,9 @@
 import com.google.common.base.MoreObjects;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
-import com.google.gerrit.common.data.GroupDescription;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupDescription;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.StartupCheck;
 import com.google.gerrit.server.StartupException;
diff --git a/java/com/google/gerrit/server/group/db/AuditLogFormatter.java b/java/com/google/gerrit/server/group/db/AuditLogFormatter.java
index ec4c0fc..235ca4f 100644
--- a/java/com/google/gerrit/server/group/db/AuditLogFormatter.java
+++ b/java/com/google/gerrit/server/group/db/AuditLogFormatter.java
@@ -19,9 +19,9 @@
 
 import com.google.common.collect.ImmutableSet;
 import com.google.gerrit.common.Nullable;
-import com.google.gerrit.common.data.GroupDescription;
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupDescription;
 import com.google.gerrit.server.account.AccountCache;
 import com.google.gerrit.server.account.AccountState;
 import com.google.gerrit.server.account.GroupBackend;
diff --git a/java/com/google/gerrit/server/group/db/GroupNameNotes.java b/java/com/google/gerrit/server/group/db/GroupNameNotes.java
index b75670d..cdba81f 100644
--- a/java/com/google/gerrit/server/group/db/GroupNameNotes.java
+++ b/java/com/google/gerrit/server/group/db/GroupNameNotes.java
@@ -28,8 +28,8 @@
 import com.google.common.flogger.FluentLogger;
 import com.google.common.hash.Hashing;
 import com.google.gerrit.common.Nullable;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.entities.RefNames;
 import com.google.gerrit.exceptions.DuplicateKeyException;
diff --git a/java/com/google/gerrit/server/group/db/Groups.java b/java/com/google/gerrit/server/group/db/Groups.java
index 163b9c6..90a5a1f 100644
--- a/java/com/google/gerrit/server/group/db/Groups.java
+++ b/java/com/google/gerrit/server/group/db/Groups.java
@@ -17,10 +17,10 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.entities.AccountGroup;
 import com.google.gerrit.entities.AccountGroupByIdAudit;
 import com.google.gerrit.entities.AccountGroupMemberAudit;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.server.config.AllUsersName;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.group.InternalGroup;
diff --git a/java/com/google/gerrit/server/group/db/GroupsNoteDbConsistencyChecker.java b/java/com/google/gerrit/server/group/db/GroupsNoteDbConsistencyChecker.java
index 0414304..35f5dea 100644
--- a/java/com/google/gerrit/server/group/db/GroupsNoteDbConsistencyChecker.java
+++ b/java/com/google/gerrit/server/group/db/GroupsNoteDbConsistencyChecker.java
@@ -23,8 +23,8 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.Nullable;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.entities.RefNames;
 import com.google.gerrit.extensions.api.config.ConsistencyCheckInfo;
 import com.google.gerrit.extensions.api.config.ConsistencyCheckInfo.ConsistencyProblemInfo;
diff --git a/java/com/google/gerrit/server/group/db/RenameGroupOp.java b/java/com/google/gerrit/server/group/db/RenameGroupOp.java
index b7564e2..45dcdfc 100644
--- a/java/com/google/gerrit/server/group/db/RenameGroupOp.java
+++ b/java/com/google/gerrit/server/group/db/RenameGroupOp.java
@@ -17,8 +17,8 @@
 import static com.google.gerrit.server.project.ProjectCache.illegalState;
 
 import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.server.git.DefaultQueueOp;
 import com.google.gerrit.server.git.WorkQueue;
diff --git a/java/com/google/gerrit/server/group/testing/TestGroupBackend.java b/java/com/google/gerrit/server/group/testing/TestGroupBackend.java
index 51c7ca3..8b7055e 100644
--- a/java/com/google/gerrit/server/group/testing/TestGroupBackend.java
+++ b/java/com/google/gerrit/server/group/testing/TestGroupBackend.java
@@ -18,9 +18,9 @@
 import static java.util.Objects.requireNonNull;
 
 import com.google.common.collect.ImmutableList;
-import com.google.gerrit.common.data.GroupDescription;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupDescription;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.account.GroupBackend;
 import com.google.gerrit.server.account.GroupMembership;
diff --git a/java/com/google/gerrit/server/index/change/ChangeField.java b/java/com/google/gerrit/server/index/change/ChangeField.java
index a7c4016..160ac14 100644
--- a/java/com/google/gerrit/server/index/change/ChangeField.java
+++ b/java/com/google/gerrit/server/index/change/ChangeField.java
@@ -45,6 +45,7 @@
 import com.google.gerrit.common.data.SubmitRecord;
 import com.google.gerrit.common.data.SubmitRequirement;
 import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Address;
 import com.google.gerrit.entities.AttentionSetUpdate;
 import com.google.gerrit.entities.Change;
 import com.google.gerrit.entities.ChangeMessage;
@@ -59,7 +60,6 @@
 import com.google.gerrit.index.RefState;
 import com.google.gerrit.index.SchemaUtil;
 import com.google.gerrit.json.OutputFormat;
-import com.google.gerrit.mail.Address;
 import com.google.gerrit.proto.Protos;
 import com.google.gerrit.server.ReviewerByEmailSet;
 import com.google.gerrit.server.ReviewerSet;
diff --git a/java/com/google/gerrit/server/index/group/AllGroupsIndexer.java b/java/com/google/gerrit/server/index/group/AllGroupsIndexer.java
index 51c7730..2d77f61 100644
--- a/java/com/google/gerrit/server/index/group/AllGroupsIndexer.java
+++ b/java/com/google/gerrit/server/index/group/AllGroupsIndexer.java
@@ -22,8 +22,8 @@
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.ListeningExecutorService;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.index.SiteIndexer;
 import com.google.gerrit.server.account.GroupCache;
 import com.google.gerrit.server.group.InternalGroup;
diff --git a/java/com/google/gerrit/server/mail/send/AbandonedSender.java b/java/com/google/gerrit/server/mail/send/AbandonedSender.java
index 2b98a1b..3ac610d 100644
--- a/java/com/google/gerrit/server/mail/send/AbandonedSender.java
+++ b/java/com/google/gerrit/server/mail/send/AbandonedSender.java
@@ -15,9 +15,9 @@
 package com.google.gerrit.server.mail.send;
 
 import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.NotifyConfig.NotifyType;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.exceptions.EmailException;
-import com.google.gerrit.server.account.ProjectWatches.NotifyType;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 
diff --git a/java/com/google/gerrit/server/mail/send/AddKeySender.java b/java/com/google/gerrit/server/mail/send/AddKeySender.java
index e4b7d54..0d447ca 100644
--- a/java/com/google/gerrit/server/mail/send/AddKeySender.java
+++ b/java/com/google/gerrit/server/mail/send/AddKeySender.java
@@ -15,9 +15,9 @@
 package com.google.gerrit.server.mail.send;
 
 import com.google.common.base.Joiner;
+import com.google.gerrit.entities.Address;
 import com.google.gerrit.exceptions.EmailException;
 import com.google.gerrit.extensions.api.changes.RecipientType;
-import com.google.gerrit.mail.Address;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.account.AccountSshKey;
 import com.google.inject.assistedinject.Assisted;
diff --git a/java/com/google/gerrit/server/mail/send/ChangeEmail.java b/java/com/google/gerrit/server/mail/send/ChangeEmail.java
index 22d332a..1e984c1 100644
--- a/java/com/google/gerrit/server/mail/send/ChangeEmail.java
+++ b/java/com/google/gerrit/server/mail/send/ChangeEmail.java
@@ -22,6 +22,7 @@
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.NotifyConfig.NotifyType;
 import com.google.gerrit.entities.Patch;
 import com.google.gerrit.entities.PatchSet;
 import com.google.gerrit.entities.PatchSetInfo;
@@ -33,7 +34,6 @@
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.mail.MailHeader;
 import com.google.gerrit.server.StarredChangesUtil;
-import com.google.gerrit.server.account.ProjectWatches.NotifyType;
 import com.google.gerrit.server.mail.send.ProjectWatch.Watchers;
 import com.google.gerrit.server.notedb.ReviewerStateInternal;
 import com.google.gerrit.server.patch.PatchList;
diff --git a/java/com/google/gerrit/server/mail/send/CommentSender.java b/java/com/google/gerrit/server/mail/send/CommentSender.java
index 7cbe2c0..7d5f3fa 100644
--- a/java/com/google/gerrit/server/mail/send/CommentSender.java
+++ b/java/com/google/gerrit/server/mail/send/CommentSender.java
@@ -24,6 +24,7 @@
 import com.google.gerrit.entities.Comment;
 import com.google.gerrit.entities.HumanComment;
 import com.google.gerrit.entities.KeyUtil;
+import com.google.gerrit.entities.NotifyConfig.NotifyType;
 import com.google.gerrit.entities.Patch;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.entities.RobotComment;
@@ -34,7 +35,6 @@
 import com.google.gerrit.mail.MailHeader;
 import com.google.gerrit.mail.MailProcessingUtil;
 import com.google.gerrit.server.CommentsUtil;
-import com.google.gerrit.server.account.ProjectWatches.NotifyType;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.mail.receive.Protocol;
 import com.google.gerrit.server.patch.PatchFile;
diff --git a/java/com/google/gerrit/server/mail/send/CreateChangeSender.java b/java/com/google/gerrit/server/mail/send/CreateChangeSender.java
index 1f58abb..b78dc62 100644
--- a/java/com/google/gerrit/server/mail/send/CreateChangeSender.java
+++ b/java/com/google/gerrit/server/mail/send/CreateChangeSender.java
@@ -17,11 +17,11 @@
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.NotifyConfig.NotifyType;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.exceptions.EmailException;
 import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.api.changes.RecipientType;
-import com.google.gerrit.server.account.ProjectWatches.NotifyType;
 import com.google.gerrit.server.mail.send.ProjectWatch.Watchers;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.RefPermission;
diff --git a/java/com/google/gerrit/server/mail/send/DeleteKeySender.java b/java/com/google/gerrit/server/mail/send/DeleteKeySender.java
index f9c288c..46e7fd8 100644
--- a/java/com/google/gerrit/server/mail/send/DeleteKeySender.java
+++ b/java/com/google/gerrit/server/mail/send/DeleteKeySender.java
@@ -15,9 +15,9 @@
 package com.google.gerrit.server.mail.send;
 
 import com.google.common.base.Joiner;
+import com.google.gerrit.entities.Address;
 import com.google.gerrit.exceptions.EmailException;
 import com.google.gerrit.extensions.api.changes.RecipientType;
-import com.google.gerrit.mail.Address;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.account.AccountSshKey;
 import com.google.inject.assistedinject.Assisted;
diff --git a/java/com/google/gerrit/server/mail/send/DeleteReviewerSender.java b/java/com/google/gerrit/server/mail/send/DeleteReviewerSender.java
index 9da4bcc..d5863a6 100644
--- a/java/com/google/gerrit/server/mail/send/DeleteReviewerSender.java
+++ b/java/com/google/gerrit/server/mail/send/DeleteReviewerSender.java
@@ -15,12 +15,12 @@
 package com.google.gerrit.server.mail.send;
 
 import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Address;
 import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.NotifyConfig.NotifyType;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.exceptions.EmailException;
 import com.google.gerrit.extensions.api.changes.RecipientType;
-import com.google.gerrit.mail.Address;
-import com.google.gerrit.server.account.ProjectWatches.NotifyType;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 import java.util.ArrayList;
diff --git a/java/com/google/gerrit/server/mail/send/DeleteVoteSender.java b/java/com/google/gerrit/server/mail/send/DeleteVoteSender.java
index c0df0f6..77efbf8 100644
--- a/java/com/google/gerrit/server/mail/send/DeleteVoteSender.java
+++ b/java/com/google/gerrit/server/mail/send/DeleteVoteSender.java
@@ -15,9 +15,9 @@
 package com.google.gerrit.server.mail.send;
 
 import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.NotifyConfig.NotifyType;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.exceptions.EmailException;
-import com.google.gerrit.server.account.ProjectWatches.NotifyType;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 
diff --git a/java/com/google/gerrit/server/mail/send/EmailSender.java b/java/com/google/gerrit/server/mail/send/EmailSender.java
index 9b3a1f7..711ab1b 100644
--- a/java/com/google/gerrit/server/mail/send/EmailSender.java
+++ b/java/com/google/gerrit/server/mail/send/EmailSender.java
@@ -15,9 +15,9 @@
 package com.google.gerrit.server.mail.send;
 
 import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Address;
+import com.google.gerrit.entities.EmailHeader;
 import com.google.gerrit.exceptions.EmailException;
-import com.google.gerrit.mail.Address;
-import com.google.gerrit.mail.EmailHeader;
 import java.util.Collection;
 import java.util.Map;
 
diff --git a/java/com/google/gerrit/server/mail/send/FromAddressGenerator.java b/java/com/google/gerrit/server/mail/send/FromAddressGenerator.java
index 61fa50d..a6d4f6d 100644
--- a/java/com/google/gerrit/server/mail/send/FromAddressGenerator.java
+++ b/java/com/google/gerrit/server/mail/send/FromAddressGenerator.java
@@ -15,7 +15,7 @@
 package com.google.gerrit.server.mail.send;
 
 import com.google.gerrit.entities.Account;
-import com.google.gerrit.mail.Address;
+import com.google.gerrit.entities.Address;
 
 /** Constructs an address to send email from. */
 public interface FromAddressGenerator {
diff --git a/java/com/google/gerrit/server/mail/send/FromAddressGeneratorProvider.java b/java/com/google/gerrit/server/mail/send/FromAddressGeneratorProvider.java
index dfaabbe..ecf808d 100644
--- a/java/com/google/gerrit/server/mail/send/FromAddressGeneratorProvider.java
+++ b/java/com/google/gerrit/server/mail/send/FromAddressGeneratorProvider.java
@@ -19,7 +19,7 @@
 import com.google.common.io.BaseEncoding;
 import com.google.gerrit.common.data.ParameterizedString;
 import com.google.gerrit.entities.Account;
-import com.google.gerrit.mail.Address;
+import com.google.gerrit.entities.Address;
 import com.google.gerrit.server.GerritPersonIdent;
 import com.google.gerrit.server.account.AccountCache;
 import com.google.gerrit.server.account.AccountState;
diff --git a/java/com/google/gerrit/server/mail/send/HttpPasswordUpdateSender.java b/java/com/google/gerrit/server/mail/send/HttpPasswordUpdateSender.java
index 1d42484..c1c2f31 100644
--- a/java/com/google/gerrit/server/mail/send/HttpPasswordUpdateSender.java
+++ b/java/com/google/gerrit/server/mail/send/HttpPasswordUpdateSender.java
@@ -14,9 +14,9 @@
 
 package com.google.gerrit.server.mail.send;
 
+import com.google.gerrit.entities.Address;
 import com.google.gerrit.exceptions.EmailException;
 import com.google.gerrit.extensions.api.changes.RecipientType;
-import com.google.gerrit.mail.Address;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.util.time.TimeUtil;
 import com.google.inject.assistedinject.Assisted;
diff --git a/java/com/google/gerrit/server/mail/send/InboundEmailRejectionSender.java b/java/com/google/gerrit/server/mail/send/InboundEmailRejectionSender.java
index 73fcf10..709bf61 100644
--- a/java/com/google/gerrit/server/mail/send/InboundEmailRejectionSender.java
+++ b/java/com/google/gerrit/server/mail/send/InboundEmailRejectionSender.java
@@ -16,9 +16,9 @@
 
 import static java.util.Objects.requireNonNull;
 
+import com.google.gerrit.entities.Address;
 import com.google.gerrit.exceptions.EmailException;
 import com.google.gerrit.extensions.api.changes.RecipientType;
-import com.google.gerrit.mail.Address;
 import com.google.gerrit.mail.MailHeader;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
diff --git a/java/com/google/gerrit/server/mail/send/MergedSender.java b/java/com/google/gerrit/server/mail/send/MergedSender.java
index e5efe80..6ee6c68 100644
--- a/java/com/google/gerrit/server/mail/send/MergedSender.java
+++ b/java/com/google/gerrit/server/mail/send/MergedSender.java
@@ -18,14 +18,14 @@
 import com.google.common.collect.Table;
 import com.google.gerrit.common.data.LabelType;
 import com.google.gerrit.common.data.LabelTypes;
-import com.google.gerrit.common.data.LabelValue;
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.LabelValue;
+import com.google.gerrit.entities.NotifyConfig.NotifyType;
 import com.google.gerrit.entities.PatchSetApproval;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.exceptions.EmailException;
 import com.google.gerrit.exceptions.StorageException;
-import com.google.gerrit.server.account.ProjectWatches.NotifyType;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 
diff --git a/java/com/google/gerrit/server/mail/send/NewChangeSender.java b/java/com/google/gerrit/server/mail/send/NewChangeSender.java
index e318804..0e97f7e 100644
--- a/java/com/google/gerrit/server/mail/send/NewChangeSender.java
+++ b/java/com/google/gerrit/server/mail/send/NewChangeSender.java
@@ -15,9 +15,9 @@
 package com.google.gerrit.server.mail.send;
 
 import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Address;
 import com.google.gerrit.exceptions.EmailException;
 import com.google.gerrit.extensions.api.changes.RecipientType;
-import com.google.gerrit.mail.Address;
 import com.google.gerrit.server.query.change.ChangeData;
 import java.util.ArrayList;
 import java.util.Collection;
diff --git a/java/com/google/gerrit/server/mail/send/NotificationEmail.java b/java/com/google/gerrit/server/mail/send/NotificationEmail.java
index 0fb5c6f..5ffd928 100644
--- a/java/com/google/gerrit/server/mail/send/NotificationEmail.java
+++ b/java/com/google/gerrit/server/mail/send/NotificationEmail.java
@@ -18,13 +18,13 @@
 import com.google.common.collect.Iterables;
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Address;
 import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.NotifyConfig.NotifyType;
 import com.google.gerrit.exceptions.EmailException;
 import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.api.changes.RecipientType;
-import com.google.gerrit.mail.Address;
 import com.google.gerrit.mail.MailHeader;
-import com.google.gerrit.server.account.ProjectWatches.NotifyType;
 import com.google.gerrit.server.mail.send.ProjectWatch.Watchers;
 import java.util.HashMap;
 import java.util.Map;
diff --git a/java/com/google/gerrit/server/mail/send/OutgoingEmail.java b/java/com/google/gerrit/server/mail/send/OutgoingEmail.java
index 62ec939..1eb274b 100644
--- a/java/com/google/gerrit/server/mail/send/OutgoingEmail.java
+++ b/java/com/google/gerrit/server/mail/send/OutgoingEmail.java
@@ -22,14 +22,14 @@
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Address;
+import com.google.gerrit.entities.EmailHeader;
+import com.google.gerrit.entities.EmailHeader.AddressList;
 import com.google.gerrit.entities.UserIdentity;
 import com.google.gerrit.exceptions.EmailException;
 import com.google.gerrit.extensions.api.changes.RecipientType;
 import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
 import com.google.gerrit.extensions.client.GeneralPreferencesInfo.EmailFormat;
-import com.google.gerrit.mail.Address;
-import com.google.gerrit.mail.EmailHeader;
-import com.google.gerrit.mail.EmailHeader.AddressList;
 import com.google.gerrit.mail.MailHeader;
 import com.google.gerrit.server.account.AccountState;
 import com.google.gerrit.server.change.NotifyResolver;
diff --git a/java/com/google/gerrit/server/mail/send/ProjectWatch.java b/java/com/google/gerrit/server/mail/send/ProjectWatch.java
index e67ff01..0514337 100644
--- a/java/com/google/gerrit/server/mail/send/ProjectWatch.java
+++ b/java/com/google/gerrit/server/mail/send/ProjectWatch.java
@@ -17,20 +17,19 @@
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.common.data.GroupDescription;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.Address;
+import com.google.gerrit.entities.GroupDescription;
+import com.google.gerrit.entities.GroupReference;
+import com.google.gerrit.entities.NotifyConfig;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.index.query.Predicate;
 import com.google.gerrit.index.query.QueryParseException;
-import com.google.gerrit.mail.Address;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.account.AccountState;
-import com.google.gerrit.server.account.ProjectWatches.NotifyType;
 import com.google.gerrit.server.account.ProjectWatches.ProjectWatchKey;
-import com.google.gerrit.server.git.NotifyConfig;
 import com.google.gerrit.server.project.ProjectState;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.query.change.ChangeQueryBuilder;
@@ -61,13 +60,15 @@
   }
 
   /** Returns all watchers that are relevant */
-  public final Watchers getWatchers(NotifyType type, boolean includeWatchersFromNotifyConfig) {
+  public final Watchers getWatchers(
+      NotifyConfig.NotifyType type, boolean includeWatchersFromNotifyConfig) {
     Watchers matching = new Watchers();
     Set<Account.Id> projectWatchers = new HashSet<>();
 
     for (AccountState a : args.accountQueryProvider.get().byWatchedProject(project)) {
       Account.Id accountId = a.account().id();
-      for (Map.Entry<ProjectWatchKey, ImmutableSet<NotifyType>> e : a.projectWatches().entrySet()) {
+      for (Map.Entry<ProjectWatchKey, ImmutableSet<NotifyConfig.NotifyType>> e :
+          a.projectWatches().entrySet()) {
         if (project.equals(e.getKey().project())
             && add(matching, accountId, e.getKey(), e.getValue(), type)) {
           // We only want to prevent matching All-Projects if this filter hits
@@ -77,7 +78,8 @@
     }
 
     for (AccountState a : args.accountQueryProvider.get().byWatchedProject(args.allProjectsName)) {
-      for (Map.Entry<ProjectWatchKey, ImmutableSet<NotifyType>> e : a.projectWatches().entrySet()) {
+      for (Map.Entry<ProjectWatchKey, ImmutableSet<NotifyConfig.NotifyType>> e :
+          a.projectWatches().entrySet()) {
         if (args.allProjectsName.equals(e.getKey().project())) {
           Account.Id accountId = a.account().id();
           if (!projectWatchers.contains(accountId)) {
@@ -212,8 +214,8 @@
       Watchers matching,
       Account.Id accountId,
       ProjectWatchKey key,
-      Set<NotifyType> watchedTypes,
-      NotifyType type) {
+      Set<NotifyConfig.NotifyType> watchedTypes,
+      NotifyConfig.NotifyType type) {
     logger.atFine().log("Checking project watch %s of account %s", key, accountId);
 
     IdentifiedUser user = args.identifiedUserFactory.create(accountId);
diff --git a/java/com/google/gerrit/server/mail/send/RegisterNewEmailSender.java b/java/com/google/gerrit/server/mail/send/RegisterNewEmailSender.java
index fc738a5..a54a652 100644
--- a/java/com/google/gerrit/server/mail/send/RegisterNewEmailSender.java
+++ b/java/com/google/gerrit/server/mail/send/RegisterNewEmailSender.java
@@ -16,9 +16,9 @@
 
 import static java.util.Objects.requireNonNull;
 
+import com.google.gerrit.entities.Address;
 import com.google.gerrit.exceptions.EmailException;
 import com.google.gerrit.extensions.api.changes.RecipientType;
-import com.google.gerrit.mail.Address;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.mail.EmailTokenVerifier;
 import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/server/mail/send/ReplacePatchSetSender.java b/java/com/google/gerrit/server/mail/send/ReplacePatchSetSender.java
index 8e6c9ae..274e664 100644
--- a/java/com/google/gerrit/server/mail/send/ReplacePatchSetSender.java
+++ b/java/com/google/gerrit/server/mail/send/ReplacePatchSetSender.java
@@ -16,11 +16,11 @@
 
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.NotifyConfig.NotifyType;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.exceptions.EmailException;
 import com.google.gerrit.extensions.api.changes.NotifyHandling;
 import com.google.gerrit.extensions.api.changes.RecipientType;
-import com.google.gerrit.server.account.ProjectWatches.NotifyType;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 import java.util.ArrayList;
diff --git a/java/com/google/gerrit/server/mail/send/RestoredSender.java b/java/com/google/gerrit/server/mail/send/RestoredSender.java
index b4db60e..ffe70cf 100644
--- a/java/com/google/gerrit/server/mail/send/RestoredSender.java
+++ b/java/com/google/gerrit/server/mail/send/RestoredSender.java
@@ -15,9 +15,9 @@
 package com.google.gerrit.server.mail.send;
 
 import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.NotifyConfig.NotifyType;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.exceptions.EmailException;
-import com.google.gerrit.server.account.ProjectWatches.NotifyType;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 
diff --git a/java/com/google/gerrit/server/mail/send/RevertedSender.java b/java/com/google/gerrit/server/mail/send/RevertedSender.java
index 40fdb60..c11529b 100644
--- a/java/com/google/gerrit/server/mail/send/RevertedSender.java
+++ b/java/com/google/gerrit/server/mail/send/RevertedSender.java
@@ -15,9 +15,9 @@
 package com.google.gerrit.server.mail.send;
 
 import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.NotifyConfig.NotifyType;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.exceptions.EmailException;
-import com.google.gerrit.server.account.ProjectWatches.NotifyType;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 
diff --git a/java/com/google/gerrit/server/mail/send/SmtpEmailSender.java b/java/com/google/gerrit/server/mail/send/SmtpEmailSender.java
index 8e53558..af00b20 100644
--- a/java/com/google/gerrit/server/mail/send/SmtpEmailSender.java
+++ b/java/com/google/gerrit/server/mail/send/SmtpEmailSender.java
@@ -21,9 +21,9 @@
 import com.google.common.primitives.Ints;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.common.Version;
+import com.google.gerrit.entities.Address;
+import com.google.gerrit.entities.EmailHeader;
 import com.google.gerrit.exceptions.EmailException;
-import com.google.gerrit.mail.Address;
-import com.google.gerrit.mail.EmailHeader;
 import com.google.gerrit.server.config.ConfigUtil;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.mail.Encryption;
diff --git a/java/com/google/gerrit/server/notedb/ChangeNotesParser.java b/java/com/google/gerrit/server/notedb/ChangeNotesParser.java
index f639f49..10a8d8b 100644
--- a/java/com/google/gerrit/server/notedb/ChangeNotesParser.java
+++ b/java/com/google/gerrit/server/notedb/ChangeNotesParser.java
@@ -58,6 +58,7 @@
 import com.google.gerrit.common.data.LabelType;
 import com.google.gerrit.common.data.SubmitRecord;
 import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Address;
 import com.google.gerrit.entities.AttentionSetUpdate;
 import com.google.gerrit.entities.Change;
 import com.google.gerrit.entities.ChangeMessage;
@@ -66,7 +67,6 @@
 import com.google.gerrit.entities.PatchSet;
 import com.google.gerrit.entities.PatchSetApproval;
 import com.google.gerrit.entities.RefNames;
-import com.google.gerrit.mail.Address;
 import com.google.gerrit.metrics.Timer0;
 import com.google.gerrit.server.AssigneeStatusUpdate;
 import com.google.gerrit.server.ReviewerByEmailSet;
diff --git a/java/com/google/gerrit/server/notedb/ChangeNotesState.java b/java/com/google/gerrit/server/notedb/ChangeNotesState.java
index a2ca066..0b03a07 100644
--- a/java/com/google/gerrit/server/notedb/ChangeNotesState.java
+++ b/java/com/google/gerrit/server/notedb/ChangeNotesState.java
@@ -35,6 +35,7 @@
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.common.data.SubmitRecord;
 import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Address;
 import com.google.gerrit.entities.AttentionSetUpdate;
 import com.google.gerrit.entities.BranchNameKey;
 import com.google.gerrit.entities.Change;
@@ -48,7 +49,6 @@
 import com.google.gerrit.entities.converter.PatchSetProtoConverter;
 import com.google.gerrit.entities.converter.ProtoConverter;
 import com.google.gerrit.json.OutputFormat;
-import com.google.gerrit.mail.Address;
 import com.google.gerrit.proto.Protos;
 import com.google.gerrit.server.AssigneeStatusUpdate;
 import com.google.gerrit.server.ReviewerByEmailSet;
diff --git a/java/com/google/gerrit/server/notedb/ChangeUpdate.java b/java/com/google/gerrit/server/notedb/ChangeUpdate.java
index 1c81694..1956154 100644
--- a/java/com/google/gerrit/server/notedb/ChangeUpdate.java
+++ b/java/com/google/gerrit/server/notedb/ChangeUpdate.java
@@ -55,6 +55,7 @@
 import com.google.common.collect.TreeBasedTable;
 import com.google.gerrit.common.data.SubmitRecord;
 import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Address;
 import com.google.gerrit.entities.AttentionSetUpdate;
 import com.google.gerrit.entities.Change;
 import com.google.gerrit.entities.Comment;
@@ -63,7 +64,6 @@
 import com.google.gerrit.entities.RobotComment;
 import com.google.gerrit.entities.SubmissionId;
 import com.google.gerrit.exceptions.StorageException;
-import com.google.gerrit.mail.Address;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.GerritPersonIdent;
 import com.google.gerrit.server.project.ProjectCache;
diff --git a/java/com/google/gerrit/server/permissions/LabelPermission.java b/java/com/google/gerrit/server/permissions/LabelPermission.java
index 7cce9c4..64eecfe 100644
--- a/java/com/google/gerrit/server/permissions/LabelPermission.java
+++ b/java/com/google/gerrit/server/permissions/LabelPermission.java
@@ -19,7 +19,7 @@
 import static java.util.Objects.requireNonNull;
 
 import com.google.gerrit.common.data.LabelType;
-import com.google.gerrit.common.data.LabelValue;
+import com.google.gerrit.entities.LabelValue;
 import com.google.gerrit.server.util.LabelVote;
 
 /** Permission representing a label. */
diff --git a/java/com/google/gerrit/server/project/AccessControlModule.java b/java/com/google/gerrit/server/project/AccessControlModule.java
index 89ab8ee..ecad4e1 100644
--- a/java/com/google/gerrit/server/project/AccessControlModule.java
+++ b/java/com/google/gerrit/server/project/AccessControlModule.java
@@ -17,8 +17,8 @@
 import static com.google.inject.Scopes.SINGLETON;
 
 import com.google.common.collect.ImmutableSet;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.extensions.config.FactoryModule;
 import com.google.gerrit.server.config.AdministrateServerGroups;
 import com.google.gerrit.server.config.AdministrateServerGroupsProvider;
diff --git a/java/com/google/gerrit/server/project/CachedProjectConfig.java b/java/com/google/gerrit/server/project/CachedProjectConfig.java
index 455a462..8af2f80 100644
--- a/java/com/google/gerrit/server/project/CachedProjectConfig.java
+++ b/java/com/google/gerrit/server/project/CachedProjectConfig.java
@@ -20,14 +20,16 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.gerrit.common.data.AccessSection;
 import com.google.gerrit.common.data.ContributorAgreement;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.common.data.LabelType;
 import com.google.gerrit.common.data.SubscribeSection;
 import com.google.gerrit.entities.AccountGroup;
 import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.BranchOrderSection;
+import com.google.gerrit.entities.ConfiguredMimeTypes;
+import com.google.gerrit.entities.GroupReference;
+import com.google.gerrit.entities.NotifyConfig;
 import com.google.gerrit.entities.Project;
-import com.google.gerrit.server.git.BranchOrderSection;
-import com.google.gerrit.server.git.NotifyConfig;
+import com.google.gerrit.entities.StoredCommentLinkInfo;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
diff --git a/java/com/google/gerrit/server/project/CommentLinkProvider.java b/java/com/google/gerrit/server/project/CommentLinkProvider.java
index 500e163..1b9dc37 100644
--- a/java/com/google/gerrit/server/project/CommentLinkProvider.java
+++ b/java/com/google/gerrit/server/project/CommentLinkProvider.java
@@ -18,6 +18,7 @@
 import com.google.common.collect.Lists;
 import com.google.common.collect.Multimap;
 import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.StoredCommentLinkInfo;
 import com.google.gerrit.extensions.api.projects.CommentLinkInfo;
 import com.google.gerrit.server.config.ConfigUpdatedEvent;
 import com.google.gerrit.server.config.ConfigUpdatedEvent.ConfigUpdateEntry;
diff --git a/java/com/google/gerrit/server/project/GroupList.java b/java/com/google/gerrit/server/project/GroupList.java
index 33c73e8..98dc44a 100644
--- a/java/com/google/gerrit/server/project/GroupList.java
+++ b/java/com/google/gerrit/server/project/GroupList.java
@@ -16,8 +16,8 @@
 
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.Nullable;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.server.git.ValidationError;
 import com.google.gerrit.server.git.meta.TabFile;
diff --git a/java/com/google/gerrit/server/project/LabelDefinitionJson.java b/java/com/google/gerrit/server/project/LabelDefinitionJson.java
index 9ff079f..569cb54 100644
--- a/java/com/google/gerrit/server/project/LabelDefinitionJson.java
+++ b/java/com/google/gerrit/server/project/LabelDefinitionJson.java
@@ -17,7 +17,7 @@
 import static java.util.stream.Collectors.toMap;
 
 import com.google.gerrit.common.data.LabelType;
-import com.google.gerrit.common.data.LabelValue;
+import com.google.gerrit.entities.LabelValue;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.extensions.common.LabelDefinitionInfo;
 
diff --git a/java/com/google/gerrit/server/project/ProjectConfig.java b/java/com/google/gerrit/server/project/ProjectConfig.java
index 0dbdd16..0c69722 100644
--- a/java/com/google/gerrit/server/project/ProjectConfig.java
+++ b/java/com/google/gerrit/server/project/ProjectConfig.java
@@ -30,37 +30,40 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
 import com.google.common.primitives.Shorts;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.common.UsedAt;
 import com.google.gerrit.common.data.AccessSection;
 import com.google.gerrit.common.data.ContributorAgreement;
 import com.google.gerrit.common.data.GlobalCapability;
-import com.google.gerrit.common.data.GroupDescription;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.common.data.LabelFunction;
 import com.google.gerrit.common.data.LabelType;
-import com.google.gerrit.common.data.LabelValue;
 import com.google.gerrit.common.data.Permission;
 import com.google.gerrit.common.data.PermissionRule;
 import com.google.gerrit.common.data.PermissionRule.Action;
 import com.google.gerrit.common.data.SubscribeSection;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.Address;
 import com.google.gerrit.entities.BooleanProjectConfig;
+import com.google.gerrit.entities.BranchOrderSection;
+import com.google.gerrit.entities.ConfiguredMimeTypes;
+import com.google.gerrit.entities.GroupDescription;
+import com.google.gerrit.entities.GroupReference;
+import com.google.gerrit.entities.LabelValue;
+import com.google.gerrit.entities.NotifyConfig;
+import com.google.gerrit.entities.NotifyConfig.NotifyType;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.entities.RefNames;
+import com.google.gerrit.entities.StoredCommentLinkInfo;
 import com.google.gerrit.exceptions.InvalidNameException;
 import com.google.gerrit.extensions.client.InheritableBoolean;
 import com.google.gerrit.extensions.client.ProjectState;
-import com.google.gerrit.mail.Address;
 import com.google.gerrit.server.account.GroupBackend;
-import com.google.gerrit.server.account.ProjectWatches.NotifyType;
 import com.google.gerrit.server.config.AllProjectsName;
 import com.google.gerrit.server.config.ConfigUtil;
 import com.google.gerrit.server.config.PluginConfig;
 import com.google.gerrit.server.config.SitePaths;
-import com.google.gerrit.server.git.BranchOrderSection;
-import com.google.gerrit.server.git.NotifyConfig;
 import com.google.gerrit.server.git.ValidationError;
 import com.google.gerrit.server.git.meta.MetaDataUpdate;
 import com.google.gerrit.server.git.meta.VersionedMetaData;
@@ -1337,7 +1340,7 @@
         rc.setStringList(NOTIFY, nc.getName(), KEY_EMAIL, email);
       }
 
-      if (nc.getNotify().equals(EnumSet.of(NotifyType.ALL))) {
+      if (nc.getNotify().equals(Sets.immutableEnumSet(NotifyType.ALL))) {
         rc.unset(NOTIFY, nc.getName(), KEY_TYPE);
       } else {
         List<String> types = new ArrayList<>(4);
diff --git a/java/com/google/gerrit/server/project/ProjectCreator.java b/java/com/google/gerrit/server/project/ProjectCreator.java
index fa0a262..19c3afb 100644
--- a/java/com/google/gerrit/server/project/ProjectCreator.java
+++ b/java/com/google/gerrit/server/project/ProjectCreator.java
@@ -20,12 +20,12 @@
 import com.google.common.base.Strings;
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.data.AccessSection;
-import com.google.gerrit.common.data.GroupDescription;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.common.data.Permission;
 import com.google.gerrit.common.data.PermissionRule;
 import com.google.gerrit.entities.AccountGroup;
 import com.google.gerrit.entities.BooleanProjectConfig;
+import com.google.gerrit.entities.GroupDescription;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.entities.RefNames;
 import com.google.gerrit.extensions.events.NewProjectCreatedListener;
diff --git a/java/com/google/gerrit/server/project/ProjectJson.java b/java/com/google/gerrit/server/project/ProjectJson.java
index f00df53..de55a12 100644
--- a/java/com/google/gerrit/server/project/ProjectJson.java
+++ b/java/com/google/gerrit/server/project/ProjectJson.java
@@ -20,7 +20,7 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.data.LabelType;
-import com.google.gerrit.common.data.LabelValue;
+import com.google.gerrit.entities.LabelValue;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.extensions.common.LabelTypeInfo;
 import com.google.gerrit.extensions.common.ProjectInfo;
diff --git a/java/com/google/gerrit/server/project/ProjectState.java b/java/com/google/gerrit/server/project/ProjectState.java
index 616809b..42e09d3 100644
--- a/java/com/google/gerrit/server/project/ProjectState.java
+++ b/java/com/google/gerrit/server/project/ProjectState.java
@@ -22,7 +22,6 @@
 import com.google.common.collect.Lists;
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.data.AccessSection;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.common.data.LabelType;
 import com.google.gerrit.common.data.LabelTypes;
 import com.google.gerrit.common.data.Permission;
@@ -31,7 +30,10 @@
 import com.google.gerrit.entities.AccountGroup;
 import com.google.gerrit.entities.BooleanProjectConfig;
 import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.BranchOrderSection;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.StoredCommentLinkInfo;
 import com.google.gerrit.extensions.api.projects.CommentLinkInfo;
 import com.google.gerrit.extensions.client.SubmitType;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
@@ -39,7 +41,6 @@
 import com.google.gerrit.server.account.CapabilityCollection;
 import com.google.gerrit.server.config.AllProjectsName;
 import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.git.BranchOrderSection;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.git.TransferConfig;
 import com.google.gerrit.server.notedb.ChangeNotes;
diff --git a/java/com/google/gerrit/server/project/testing/BUILD b/java/com/google/gerrit/server/project/testing/BUILD
index 988a89f..3112b5a 100644
--- a/java/com/google/gerrit/server/project/testing/BUILD
+++ b/java/com/google/gerrit/server/project/testing/BUILD
@@ -5,5 +5,8 @@
     testonly = True,
     srcs = glob(["*.java"]),
     visibility = ["//visibility:public"],
-    deps = ["//java/com/google/gerrit/common:server"],
+    deps = [
+        "//java/com/google/gerrit/common:server",
+        "//java/com/google/gerrit/entities",
+    ],
 )
diff --git a/java/com/google/gerrit/server/project/testing/TestLabels.java b/java/com/google/gerrit/server/project/testing/TestLabels.java
index 2c0b23c..8629757 100644
--- a/java/com/google/gerrit/server/project/testing/TestLabels.java
+++ b/java/com/google/gerrit/server/project/testing/TestLabels.java
@@ -16,7 +16,7 @@
 
 import com.google.gerrit.common.data.LabelFunction;
 import com.google.gerrit.common.data.LabelType;
-import com.google.gerrit.common.data.LabelValue;
+import com.google.gerrit.entities.LabelValue;
 import java.util.Arrays;
 
 public class TestLabels {
diff --git a/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java b/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
index 886d0ee..2681b6d 100644
--- a/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
+++ b/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
@@ -28,13 +28,14 @@
 import com.google.common.collect.Lists;
 import com.google.common.flogger.FluentLogger;
 import com.google.common.primitives.Ints;
-import com.google.gerrit.common.data.GroupDescription;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.common.data.SubmitRecord;
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.Address;
 import com.google.gerrit.entities.BranchNameKey;
 import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.GroupDescription;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.entities.PatchSet;
 import com.google.gerrit.entities.RefNames;
 import com.google.gerrit.exceptions.NotSignedInException;
@@ -48,7 +49,6 @@
 import com.google.gerrit.index.query.QueryBuilder;
 import com.google.gerrit.index.query.QueryParseException;
 import com.google.gerrit.index.query.QueryRequiresAuthException;
-import com.google.gerrit.mail.Address;
 import com.google.gerrit.server.CommentsUtil;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.IdentifiedUser;
diff --git a/java/com/google/gerrit/server/query/change/ReviewerByEmailPredicate.java b/java/com/google/gerrit/server/query/change/ReviewerByEmailPredicate.java
index 070f800..62fe9e8 100644
--- a/java/com/google/gerrit/server/query/change/ReviewerByEmailPredicate.java
+++ b/java/com/google/gerrit/server/query/change/ReviewerByEmailPredicate.java
@@ -16,8 +16,8 @@
 
 import static com.google.common.base.Preconditions.checkArgument;
 
+import com.google.gerrit.entities.Address;
 import com.google.gerrit.index.query.Predicate;
-import com.google.gerrit.mail.Address;
 import com.google.gerrit.server.index.change.ChangeField;
 import com.google.gerrit.server.notedb.ReviewerStateInternal;
 
diff --git a/java/com/google/gerrit/server/query/group/GroupQueryBuilder.java b/java/com/google/gerrit/server/query/group/GroupQueryBuilder.java
index 4e60db5..fbc8d0e 100644
--- a/java/com/google/gerrit/server/query/group/GroupQueryBuilder.java
+++ b/java/com/google/gerrit/server/query/group/GroupQueryBuilder.java
@@ -19,9 +19,9 @@
 import com.google.common.base.Strings;
 import com.google.common.collect.Lists;
 import com.google.common.primitives.Ints;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.index.query.LimitPredicate;
 import com.google.gerrit.index.query.Predicate;
 import com.google.gerrit.index.query.QueryBuilder;
diff --git a/java/com/google/gerrit/server/restapi/account/CreateAccount.java b/java/com/google/gerrit/server/restapi/account/CreateAccount.java
index 907dd18..015b235 100644
--- a/java/com/google/gerrit/server/restapi/account/CreateAccount.java
+++ b/java/com/google/gerrit/server/restapi/account/CreateAccount.java
@@ -21,9 +21,9 @@
 import com.google.common.collect.Sets;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.common.data.GlobalCapability;
-import com.google.gerrit.common.data.GroupDescription;
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupDescription;
 import com.google.gerrit.exceptions.InvalidSshKeyException;
 import com.google.gerrit.exceptions.NoSuchGroupException;
 import com.google.gerrit.extensions.annotations.RequiresCapability;
diff --git a/java/com/google/gerrit/server/restapi/account/GetWatchedProjects.java b/java/com/google/gerrit/server/restapi/account/GetWatchedProjects.java
index beb5e8f..8d65aac 100644
--- a/java/com/google/gerrit/server/restapi/account/GetWatchedProjects.java
+++ b/java/com/google/gerrit/server/restapi/account/GetWatchedProjects.java
@@ -20,6 +20,7 @@
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableSet;
 import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.NotifyConfig.NotifyType;
 import com.google.gerrit.extensions.client.ProjectWatchInfo;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
@@ -29,7 +30,6 @@
 import com.google.gerrit.server.account.AccountResource;
 import com.google.gerrit.server.account.AccountState;
 import com.google.gerrit.server.account.Accounts;
-import com.google.gerrit.server.account.ProjectWatches.NotifyType;
 import com.google.gerrit.server.account.ProjectWatches.ProjectWatchKey;
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
diff --git a/java/com/google/gerrit/server/restapi/account/PostWatchedProjects.java b/java/com/google/gerrit/server/restapi/account/PostWatchedProjects.java
index b2859e6..c80bf57 100644
--- a/java/com/google/gerrit/server/restapi/account/PostWatchedProjects.java
+++ b/java/com/google/gerrit/server/restapi/account/PostWatchedProjects.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.server.restapi.account;
 
+import com.google.gerrit.entities.NotifyConfig.NotifyType;
 import com.google.gerrit.extensions.client.ProjectWatchInfo;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.Response;
@@ -24,7 +25,6 @@
 import com.google.gerrit.server.account.AccountResource;
 import com.google.gerrit.server.account.AccountsUpdate;
 import com.google.gerrit.server.account.ProjectWatches;
-import com.google.gerrit.server.account.ProjectWatches.NotifyType;
 import com.google.gerrit.server.account.ProjectWatches.ProjectWatchKey;
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
diff --git a/java/com/google/gerrit/server/restapi/change/ListReviewers.java b/java/com/google/gerrit/server/restapi/change/ListReviewers.java
index 25ef480..3d07d43 100644
--- a/java/com/google/gerrit/server/restapi/change/ListReviewers.java
+++ b/java/com/google/gerrit/server/restapi/change/ListReviewers.java
@@ -15,10 +15,10 @@
 package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Address;
 import com.google.gerrit.extensions.api.changes.ReviewerInfo;
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gerrit.mail.Address;
 import com.google.gerrit.server.ApprovalsUtil;
 import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.change.ReviewerJson;
diff --git a/java/com/google/gerrit/server/restapi/change/ListRevisionReviewers.java b/java/com/google/gerrit/server/restapi/change/ListRevisionReviewers.java
index 73b1f59..b44f637 100644
--- a/java/com/google/gerrit/server/restapi/change/ListRevisionReviewers.java
+++ b/java/com/google/gerrit/server/restapi/change/ListRevisionReviewers.java
@@ -15,11 +15,11 @@
 package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Address;
 import com.google.gerrit.extensions.api.changes.ReviewerInfo;
 import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gerrit.mail.Address;
 import com.google.gerrit.server.ApprovalsUtil;
 import com.google.gerrit.server.change.ReviewerJson;
 import com.google.gerrit.server.change.ReviewerResource;
diff --git a/java/com/google/gerrit/server/restapi/change/Mergeable.java b/java/com/google/gerrit/server/restapi/change/Mergeable.java
index 3338543..5d65663 100644
--- a/java/com/google/gerrit/server/restapi/change/Mergeable.java
+++ b/java/com/google/gerrit/server/restapi/change/Mergeable.java
@@ -17,6 +17,7 @@
 import static com.google.gerrit.server.project.ProjectCache.illegalState;
 
 import com.google.gerrit.common.data.SubmitTypeRecord;
+import com.google.gerrit.entities.BranchOrderSection;
 import com.google.gerrit.entities.Change;
 import com.google.gerrit.entities.PatchSet;
 import com.google.gerrit.extensions.client.SubmitType;
@@ -29,7 +30,6 @@
 import com.google.gerrit.server.ChangeUtil;
 import com.google.gerrit.server.change.MergeabilityCache;
 import com.google.gerrit.server.change.RevisionResource;
-import com.google.gerrit.server.git.BranchOrderSection;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.git.MergeUtil;
 import com.google.gerrit.server.index.change.ChangeIndexer;
diff --git a/java/com/google/gerrit/server/restapi/change/PostReview.java b/java/com/google/gerrit/server/restapi/change/PostReview.java
index 85079e2..902986c 100644
--- a/java/com/google/gerrit/server/restapi/change/PostReview.java
+++ b/java/com/google/gerrit/server/restapi/change/PostReview.java
@@ -44,6 +44,7 @@
 import com.google.gerrit.common.data.LabelType;
 import com.google.gerrit.common.data.LabelTypes;
 import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Address;
 import com.google.gerrit.entities.Change;
 import com.google.gerrit.entities.ChangeMessage;
 import com.google.gerrit.entities.Comment;
@@ -81,7 +82,6 @@
 import com.google.gerrit.extensions.validators.CommentValidationContext;
 import com.google.gerrit.extensions.validators.CommentValidationFailure;
 import com.google.gerrit.extensions.validators.CommentValidator;
-import com.google.gerrit.mail.Address;
 import com.google.gerrit.server.ApprovalsUtil;
 import com.google.gerrit.server.ChangeMessagesUtil;
 import com.google.gerrit.server.ChangeUtil;
diff --git a/java/com/google/gerrit/server/restapi/change/Reviewers.java b/java/com/google/gerrit/server/restapi/change/Reviewers.java
index d702142..4bfcf14 100644
--- a/java/com/google/gerrit/server/restapi/change/Reviewers.java
+++ b/java/com/google/gerrit/server/restapi/change/Reviewers.java
@@ -15,13 +15,13 @@
 package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Address;
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.ChildCollection;
 import com.google.gerrit.extensions.restapi.IdString;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.RestView;
-import com.google.gerrit.mail.Address;
 import com.google.gerrit.server.ApprovalsUtil;
 import com.google.gerrit.server.account.AccountResolver;
 import com.google.gerrit.server.change.ChangeResource;
diff --git a/java/com/google/gerrit/server/restapi/change/ReviewersUtil.java b/java/com/google/gerrit/server/restapi/change/ReviewersUtil.java
index 676cc07..2a55e41 100644
--- a/java/com/google/gerrit/server/restapi/change/ReviewersUtil.java
+++ b/java/com/google/gerrit/server/restapi/change/ReviewersUtil.java
@@ -23,8 +23,8 @@
 import com.google.common.collect.Sets;
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.Nullable;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.client.ReviewerState;
diff --git a/java/com/google/gerrit/server/restapi/change/RevisionReviewers.java b/java/com/google/gerrit/server/restapi/change/RevisionReviewers.java
index ac0945d..2651ab5 100644
--- a/java/com/google/gerrit/server/restapi/change/RevisionReviewers.java
+++ b/java/com/google/gerrit/server/restapi/change/RevisionReviewers.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Address;
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.ChildCollection;
@@ -23,7 +24,6 @@
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.RestView;
 import com.google.gerrit.extensions.restapi.TopLevelResource;
-import com.google.gerrit.mail.Address;
 import com.google.gerrit.server.ApprovalsUtil;
 import com.google.gerrit.server.change.ReviewerResource;
 import com.google.gerrit.server.change.RevisionResource;
diff --git a/java/com/google/gerrit/server/restapi/config/AgreementJson.java b/java/com/google/gerrit/server/restapi/config/AgreementJson.java
index d5c085b..e1e8e96 100644
--- a/java/com/google/gerrit/server/restapi/config/AgreementJson.java
+++ b/java/com/google/gerrit/server/restapi/config/AgreementJson.java
@@ -16,7 +16,7 @@
 
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.data.ContributorAgreement;
-import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.exceptions.NoSuchGroupException;
 import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.common.AgreementInfo;
diff --git a/java/com/google/gerrit/server/restapi/group/AddMembers.java b/java/com/google/gerrit/server/restapi/group/AddMembers.java
index 93d095d..700a2ab 100644
--- a/java/com/google/gerrit/server/restapi/group/AddMembers.java
+++ b/java/com/google/gerrit/server/restapi/group/AddMembers.java
@@ -17,9 +17,9 @@
 import com.google.common.base.Strings;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
-import com.google.gerrit.common.data.GroupDescription;
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupDescription;
 import com.google.gerrit.exceptions.NoSuchGroupException;
 import com.google.gerrit.extensions.client.AuthType;
 import com.google.gerrit.extensions.common.AccountInfo;
diff --git a/java/com/google/gerrit/server/restapi/group/AddSubgroups.java b/java/com/google/gerrit/server/restapi/group/AddSubgroups.java
index 3fd3f29..23fa73d 100644
--- a/java/com/google/gerrit/server/restapi/group/AddSubgroups.java
+++ b/java/com/google/gerrit/server/restapi/group/AddSubgroups.java
@@ -18,8 +18,8 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
-import com.google.gerrit.common.data.GroupDescription;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupDescription;
 import com.google.gerrit.exceptions.NoSuchGroupException;
 import com.google.gerrit.extensions.common.GroupInfo;
 import com.google.gerrit.extensions.restapi.AuthException;
diff --git a/java/com/google/gerrit/server/restapi/group/CreateGroup.java b/java/com/google/gerrit/server/restapi/group/CreateGroup.java
index e5a1478..74ca721 100644
--- a/java/com/google/gerrit/server/restapi/group/CreateGroup.java
+++ b/java/com/google/gerrit/server/restapi/group/CreateGroup.java
@@ -18,9 +18,9 @@
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableSet;
 import com.google.gerrit.common.data.GlobalCapability;
-import com.google.gerrit.common.data.GroupDescription;
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupDescription;
 import com.google.gerrit.exceptions.DuplicateKeyException;
 import com.google.gerrit.extensions.annotations.RequiresCapability;
 import com.google.gerrit.extensions.api.groups.GroupInput;
diff --git a/java/com/google/gerrit/server/restapi/group/DeleteMembers.java b/java/com/google/gerrit/server/restapi/group/DeleteMembers.java
index a7b2e2d..fa52a79 100644
--- a/java/com/google/gerrit/server/restapi/group/DeleteMembers.java
+++ b/java/com/google/gerrit/server/restapi/group/DeleteMembers.java
@@ -16,9 +16,9 @@
 
 import com.google.common.base.Strings;
 import com.google.common.collect.Sets;
-import com.google.gerrit.common.data.GroupDescription;
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupDescription;
 import com.google.gerrit.exceptions.NoSuchGroupException;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
diff --git a/java/com/google/gerrit/server/restapi/group/DeleteSubgroups.java b/java/com/google/gerrit/server/restapi/group/DeleteSubgroups.java
index b9d6ca8..fe67635 100644
--- a/java/com/google/gerrit/server/restapi/group/DeleteSubgroups.java
+++ b/java/com/google/gerrit/server/restapi/group/DeleteSubgroups.java
@@ -16,8 +16,8 @@
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Sets;
-import com.google.gerrit.common.data.GroupDescription;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupDescription;
 import com.google.gerrit.exceptions.NoSuchGroupException;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
diff --git a/java/com/google/gerrit/server/restapi/group/GetAuditLog.java b/java/com/google/gerrit/server/restapi/group/GetAuditLog.java
index 508547d..8a469f1 100644
--- a/java/com/google/gerrit/server/restapi/group/GetAuditLog.java
+++ b/java/com/google/gerrit/server/restapi/group/GetAuditLog.java
@@ -16,10 +16,10 @@
 
 import static java.util.Comparator.comparing;
 
-import com.google.gerrit.common.data.GroupDescription;
 import com.google.gerrit.entities.AccountGroup;
 import com.google.gerrit.entities.AccountGroupByIdAudit;
 import com.google.gerrit.entities.AccountGroupMemberAudit;
+import com.google.gerrit.entities.GroupDescription;
 import com.google.gerrit.extensions.common.AccountInfo;
 import com.google.gerrit.extensions.common.GroupAuditEventInfo;
 import com.google.gerrit.extensions.common.GroupInfo;
diff --git a/java/com/google/gerrit/server/restapi/group/GetDescription.java b/java/com/google/gerrit/server/restapi/group/GetDescription.java
index b770281..f65b5e0 100644
--- a/java/com/google/gerrit/server/restapi/group/GetDescription.java
+++ b/java/com/google/gerrit/server/restapi/group/GetDescription.java
@@ -15,7 +15,7 @@
 package com.google.gerrit.server.restapi.group;
 
 import com.google.common.base.Strings;
-import com.google.gerrit.common.data.GroupDescription;
+import com.google.gerrit.entities.GroupDescription;
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.group.GroupResource;
diff --git a/java/com/google/gerrit/server/restapi/group/GetOwner.java b/java/com/google/gerrit/server/restapi/group/GetOwner.java
index e8bdfaa..2ab9a69c 100644
--- a/java/com/google/gerrit/server/restapi/group/GetOwner.java
+++ b/java/com/google/gerrit/server/restapi/group/GetOwner.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.server.restapi.group;
 
-import com.google.gerrit.common.data.GroupDescription;
+import com.google.gerrit.entities.GroupDescription;
 import com.google.gerrit.exceptions.NoSuchGroupException;
 import com.google.gerrit.extensions.common.GroupInfo;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
diff --git a/java/com/google/gerrit/server/restapi/group/GroupJson.java b/java/com/google/gerrit/server/restapi/group/GroupJson.java
index 99c9df7..e1459c3 100644
--- a/java/com/google/gerrit/server/restapi/group/GroupJson.java
+++ b/java/com/google/gerrit/server/restapi/group/GroupJson.java
@@ -19,8 +19,8 @@
 
 import com.google.common.base.Strings;
 import com.google.common.base.Suppliers;
-import com.google.gerrit.common.data.GroupDescription;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupDescription;
 import com.google.gerrit.extensions.client.ListGroupsOption;
 import com.google.gerrit.extensions.common.GroupInfo;
 import com.google.gerrit.extensions.common.GroupOptionsInfo;
diff --git a/java/com/google/gerrit/server/restapi/group/GroupsCollection.java b/java/com/google/gerrit/server/restapi/group/GroupsCollection.java
index 65a7f4f..e0cfb1e 100644
--- a/java/com/google/gerrit/server/restapi/group/GroupsCollection.java
+++ b/java/com/google/gerrit/server/restapi/group/GroupsCollection.java
@@ -15,7 +15,7 @@
 package com.google.gerrit.server.restapi.group;
 
 import com.google.common.collect.ListMultimap;
-import com.google.gerrit.common.data.GroupDescription;
+import com.google.gerrit.entities.GroupDescription;
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
diff --git a/java/com/google/gerrit/server/restapi/group/ListGroups.java b/java/com/google/gerrit/server/restapi/group/ListGroups.java
index bcb199f..3e2a577 100644
--- a/java/com/google/gerrit/server/restapi/group/ListGroups.java
+++ b/java/com/google/gerrit/server/restapi/group/ListGroups.java
@@ -21,10 +21,10 @@
 import com.google.common.base.Strings;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Streams;
-import com.google.gerrit.common.data.GroupDescription;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupDescription;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.exceptions.NoSuchGroupException;
 import com.google.gerrit.extensions.client.ListGroupsOption;
 import com.google.gerrit.extensions.client.ListOption;
diff --git a/java/com/google/gerrit/server/restapi/group/ListMembers.java b/java/com/google/gerrit/server/restapi/group/ListMembers.java
index 23f0aa7..5b3e8dc 100644
--- a/java/com/google/gerrit/server/restapi/group/ListMembers.java
+++ b/java/com/google/gerrit/server/restapi/group/ListMembers.java
@@ -20,9 +20,9 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
-import com.google.gerrit.common.data.GroupDescription;
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupDescription;
 import com.google.gerrit.extensions.common.AccountInfo;
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
diff --git a/java/com/google/gerrit/server/restapi/group/ListSubgroups.java b/java/com/google/gerrit/server/restapi/group/ListSubgroups.java
index 540718f..776c17c 100644
--- a/java/com/google/gerrit/server/restapi/group/ListSubgroups.java
+++ b/java/com/google/gerrit/server/restapi/group/ListSubgroups.java
@@ -18,8 +18,8 @@
 import static java.util.Comparator.comparing;
 
 import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.common.data.GroupDescription;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupDescription;
 import com.google.gerrit.exceptions.NoSuchGroupException;
 import com.google.gerrit.extensions.common.GroupInfo;
 import com.google.gerrit.extensions.restapi.Response;
diff --git a/java/com/google/gerrit/server/restapi/group/MembersCollection.java b/java/com/google/gerrit/server/restapi/group/MembersCollection.java
index 6dfb2b6..79f3d6a 100644
--- a/java/com/google/gerrit/server/restapi/group/MembersCollection.java
+++ b/java/com/google/gerrit/server/restapi/group/MembersCollection.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.server.restapi.group;
 
-import com.google.gerrit.common.data.GroupDescription;
+import com.google.gerrit.entities.GroupDescription;
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.ChildCollection;
diff --git a/java/com/google/gerrit/server/restapi/group/PutDescription.java b/java/com/google/gerrit/server/restapi/group/PutDescription.java
index 8fe4b20..942e680 100644
--- a/java/com/google/gerrit/server/restapi/group/PutDescription.java
+++ b/java/com/google/gerrit/server/restapi/group/PutDescription.java
@@ -15,8 +15,8 @@
 package com.google.gerrit.server.restapi.group;
 
 import com.google.common.base.Strings;
-import com.google.gerrit.common.data.GroupDescription;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupDescription;
 import com.google.gerrit.exceptions.NoSuchGroupException;
 import com.google.gerrit.extensions.common.DescriptionInput;
 import com.google.gerrit.extensions.restapi.AuthException;
diff --git a/java/com/google/gerrit/server/restapi/group/PutName.java b/java/com/google/gerrit/server/restapi/group/PutName.java
index 9a3c87d..acdae33 100644
--- a/java/com/google/gerrit/server/restapi/group/PutName.java
+++ b/java/com/google/gerrit/server/restapi/group/PutName.java
@@ -15,8 +15,8 @@
 package com.google.gerrit.server.restapi.group;
 
 import com.google.common.base.Strings;
-import com.google.gerrit.common.data.GroupDescription;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupDescription;
 import com.google.gerrit.exceptions.DuplicateKeyException;
 import com.google.gerrit.exceptions.NoSuchGroupException;
 import com.google.gerrit.extensions.common.NameInput;
diff --git a/java/com/google/gerrit/server/restapi/group/PutOptions.java b/java/com/google/gerrit/server/restapi/group/PutOptions.java
index 53bf571..748861e 100644
--- a/java/com/google/gerrit/server/restapi/group/PutOptions.java
+++ b/java/com/google/gerrit/server/restapi/group/PutOptions.java
@@ -14,8 +14,8 @@
 
 package com.google.gerrit.server.restapi.group;
 
-import com.google.gerrit.common.data.GroupDescription;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupDescription;
 import com.google.gerrit.exceptions.NoSuchGroupException;
 import com.google.gerrit.extensions.common.GroupOptionsInfo;
 import com.google.gerrit.extensions.restapi.AuthException;
diff --git a/java/com/google/gerrit/server/restapi/group/PutOwner.java b/java/com/google/gerrit/server/restapi/group/PutOwner.java
index 04129af..96ce9e4 100644
--- a/java/com/google/gerrit/server/restapi/group/PutOwner.java
+++ b/java/com/google/gerrit/server/restapi/group/PutOwner.java
@@ -15,8 +15,8 @@
 package com.google.gerrit.server.restapi.group;
 
 import com.google.common.base.Strings;
-import com.google.gerrit.common.data.GroupDescription;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupDescription;
 import com.google.gerrit.exceptions.NoSuchGroupException;
 import com.google.gerrit.extensions.api.groups.OwnerInput;
 import com.google.gerrit.extensions.common.GroupInfo;
diff --git a/java/com/google/gerrit/server/restapi/group/SubgroupsCollection.java b/java/com/google/gerrit/server/restapi/group/SubgroupsCollection.java
index cebc27a..c7f6473 100644
--- a/java/com/google/gerrit/server/restapi/group/SubgroupsCollection.java
+++ b/java/com/google/gerrit/server/restapi/group/SubgroupsCollection.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.server.restapi.group;
 
-import com.google.gerrit.common.data.GroupDescription;
+import com.google.gerrit.entities.GroupDescription;
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.ChildCollection;
diff --git a/java/com/google/gerrit/server/restapi/project/CreateLabel.java b/java/com/google/gerrit/server/restapi/project/CreateLabel.java
index 1c19eb0..416eeb3 100644
--- a/java/com/google/gerrit/server/restapi/project/CreateLabel.java
+++ b/java/com/google/gerrit/server/restapi/project/CreateLabel.java
@@ -17,7 +17,7 @@
 import com.google.common.base.Strings;
 import com.google.gerrit.common.data.LabelFunction;
 import com.google.gerrit.common.data.LabelType;
-import com.google.gerrit.common.data.LabelValue;
+import com.google.gerrit.entities.LabelValue;
 import com.google.gerrit.extensions.common.LabelDefinitionInfo;
 import com.google.gerrit.extensions.common.LabelDefinitionInput;
 import com.google.gerrit.extensions.restapi.AuthException;
diff --git a/java/com/google/gerrit/server/restapi/project/GetAccess.java b/java/com/google/gerrit/server/restapi/project/GetAccess.java
index 2d1191f..f60601e 100644
--- a/java/com/google/gerrit/server/restapi/project/GetAccess.java
+++ b/java/com/google/gerrit/server/restapi/project/GetAccess.java
@@ -27,10 +27,10 @@
 import com.google.common.collect.Iterables;
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.data.AccessSection;
-import com.google.gerrit.common.data.GroupDescription;
 import com.google.gerrit.common.data.Permission;
 import com.google.gerrit.common.data.PermissionRule;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupDescription;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.entities.RefNames;
 import com.google.gerrit.extensions.api.access.AccessSectionInfo;
diff --git a/java/com/google/gerrit/server/restapi/project/LabelDefinitionInputParser.java b/java/com/google/gerrit/server/restapi/project/LabelDefinitionInputParser.java
index ccc216d..82e34eb 100644
--- a/java/com/google/gerrit/server/restapi/project/LabelDefinitionInputParser.java
+++ b/java/com/google/gerrit/server/restapi/project/LabelDefinitionInputParser.java
@@ -18,8 +18,8 @@
 import com.google.common.primitives.Shorts;
 import com.google.gerrit.common.data.LabelFunction;
 import com.google.gerrit.common.data.LabelType;
-import com.google.gerrit.common.data.LabelValue;
 import com.google.gerrit.common.data.PermissionRule;
+import com.google.gerrit.entities.LabelValue;
 import com.google.gerrit.entities.RefNames;
 import com.google.gerrit.exceptions.InvalidNameException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
diff --git a/java/com/google/gerrit/server/restapi/project/ListProjects.java b/java/com/google/gerrit/server/restapi/project/ListProjects.java
index c56e8c6..0c16822 100644
--- a/java/com/google/gerrit/server/restapi/project/ListProjects.java
+++ b/java/com/google/gerrit/server/restapi/project/ListProjects.java
@@ -26,8 +26,8 @@
 import com.google.common.collect.Iterables;
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.Nullable;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.entities.RefNames;
 import com.google.gerrit.exceptions.NoSuchGroupException;
diff --git a/java/com/google/gerrit/server/restapi/project/SetAccessUtil.java b/java/com/google/gerrit/server/restapi/project/SetAccessUtil.java
index d1511c1..572b798 100644
--- a/java/com/google/gerrit/server/restapi/project/SetAccessUtil.java
+++ b/java/com/google/gerrit/server/restapi/project/SetAccessUtil.java
@@ -17,11 +17,11 @@
 import com.google.common.collect.Iterables;
 import com.google.gerrit.common.data.AccessSection;
 import com.google.gerrit.common.data.GlobalCapability;
-import com.google.gerrit.common.data.GroupDescription;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.common.data.LabelType;
 import com.google.gerrit.common.data.Permission;
 import com.google.gerrit.common.data.PermissionRule;
+import com.google.gerrit.entities.GroupDescription;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.exceptions.InvalidNameException;
 import com.google.gerrit.extensions.api.access.AccessSectionInfo;
diff --git a/java/com/google/gerrit/server/schema/AclUtil.java b/java/com/google/gerrit/server/schema/AclUtil.java
index e65568f..6db93397 100644
--- a/java/com/google/gerrit/server/schema/AclUtil.java
+++ b/java/com/google/gerrit/server/schema/AclUtil.java
@@ -15,10 +15,10 @@
 package com.google.gerrit.server.schema;
 
 import com.google.gerrit.common.data.AccessSection;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.common.data.LabelType;
 import com.google.gerrit.common.data.Permission;
 import com.google.gerrit.common.data.PermissionRule;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.server.project.ProjectConfig;
 
 /**
diff --git a/java/com/google/gerrit/server/schema/AllProjectsCreator.java b/java/com/google/gerrit/server/schema/AllProjectsCreator.java
index 0fb282e..cd3c945 100644
--- a/java/com/google/gerrit/server/schema/AllProjectsCreator.java
+++ b/java/com/google/gerrit/server/schema/AllProjectsCreator.java
@@ -25,10 +25,10 @@
 import com.google.gerrit.common.Version;
 import com.google.gerrit.common.data.AccessSection;
 import com.google.gerrit.common.data.GlobalCapability;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.common.data.LabelType;
 import com.google.gerrit.common.data.Permission;
 import com.google.gerrit.common.data.PermissionRule.Action;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.entities.RefNames;
 import com.google.gerrit.server.GerritPersonIdent;
 import com.google.gerrit.server.config.AllProjectsName;
diff --git a/java/com/google/gerrit/server/schema/AllProjectsInput.java b/java/com/google/gerrit/server/schema/AllProjectsInput.java
index c91695f..bd405f7 100644
--- a/java/com/google/gerrit/server/schema/AllProjectsInput.java
+++ b/java/com/google/gerrit/server/schema/AllProjectsInput.java
@@ -18,10 +18,10 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.gerrit.common.UsedAt;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.common.data.LabelType;
-import com.google.gerrit.common.data.LabelValue;
 import com.google.gerrit.entities.BooleanProjectConfig;
+import com.google.gerrit.entities.GroupReference;
+import com.google.gerrit.entities.LabelValue;
 import com.google.gerrit.extensions.client.InheritableBoolean;
 import com.google.gerrit.server.notedb.Sequences;
 import java.util.Optional;
diff --git a/java/com/google/gerrit/server/schema/AllUsersCreator.java b/java/com/google/gerrit/server/schema/AllUsersCreator.java
index 1ac8e69..89fd3654d 100644
--- a/java/com/google/gerrit/server/schema/AllUsersCreator.java
+++ b/java/com/google/gerrit/server/schema/AllUsersCreator.java
@@ -22,9 +22,9 @@
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.common.UsedAt;
 import com.google.gerrit.common.Version;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.common.data.LabelType;
 import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.entities.RefNames;
 import com.google.gerrit.server.GerritPersonIdent;
 import com.google.gerrit.server.config.AllUsersName;
diff --git a/java/com/google/gerrit/server/schema/GrantRevertPermission.java b/java/com/google/gerrit/server/schema/GrantRevertPermission.java
index d4ba29b..77513d3 100644
--- a/java/com/google/gerrit/server/schema/GrantRevertPermission.java
+++ b/java/com/google/gerrit/server/schema/GrantRevertPermission.java
@@ -19,8 +19,8 @@
 import static com.google.gerrit.server.schema.AclUtil.remove;
 
 import com.google.gerrit.common.data.AccessSection;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.server.GerritPersonIdent;
 import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
diff --git a/java/com/google/gerrit/server/schema/SchemaCreatorImpl.java b/java/com/google/gerrit/server/schema/SchemaCreatorImpl.java
index 1279218..f53f9a6 100644
--- a/java/com/google/gerrit/server/schema/SchemaCreatorImpl.java
+++ b/java/com/google/gerrit/server/schema/SchemaCreatorImpl.java
@@ -16,8 +16,8 @@
 
 import com.google.common.collect.ImmutableSet;
 import com.google.gerrit.common.Nullable;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.exceptions.DuplicateKeyException;
 import com.google.gerrit.git.RefUpdateUtil;
 import com.google.gerrit.metrics.MetricMaker;
diff --git a/java/com/google/gerrit/server/validators/OutgoingEmailValidationListener.java b/java/com/google/gerrit/server/validators/OutgoingEmailValidationListener.java
index 996ad87..76034ce 100644
--- a/java/com/google/gerrit/server/validators/OutgoingEmailValidationListener.java
+++ b/java/com/google/gerrit/server/validators/OutgoingEmailValidationListener.java
@@ -15,9 +15,9 @@
 package com.google.gerrit.server.validators;
 
 import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Address;
+import com.google.gerrit.entities.EmailHeader;
 import com.google.gerrit.extensions.annotations.ExtensionPoint;
-import com.google.gerrit.mail.Address;
-import com.google.gerrit.mail.EmailHeader;
 import java.util.Map;
 import java.util.Set;
 
diff --git a/java/com/google/gerrit/sshd/commands/ReviewCommand.java b/java/com/google/gerrit/sshd/commands/ReviewCommand.java
index 81964be..42d781f 100644
--- a/java/com/google/gerrit/sshd/commands/ReviewCommand.java
+++ b/java/com/google/gerrit/sshd/commands/ReviewCommand.java
@@ -23,7 +23,7 @@
 import com.google.common.flogger.FluentLogger;
 import com.google.common.io.CharStreams;
 import com.google.gerrit.common.data.LabelType;
-import com.google.gerrit.common.data.LabelValue;
+import com.google.gerrit.entities.LabelValue;
 import com.google.gerrit.entities.PatchSet;
 import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.api.GerritApi;
diff --git a/java/com/google/gerrit/testing/FakeEmailSender.java b/java/com/google/gerrit/testing/FakeEmailSender.java
index a60995b..fec9b27 100644
--- a/java/com/google/gerrit/testing/FakeEmailSender.java
+++ b/java/com/google/gerrit/testing/FakeEmailSender.java
@@ -21,9 +21,9 @@
 import com.google.common.collect.ImmutableMap;
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Address;
+import com.google.gerrit.entities.EmailHeader;
 import com.google.gerrit.exceptions.EmailException;
-import com.google.gerrit.mail.Address;
-import com.google.gerrit.mail.EmailHeader;
 import com.google.gerrit.mail.MailHeader;
 import com.google.gerrit.server.git.WorkQueue;
 import com.google.gerrit.server.mail.send.EmailSender;
diff --git a/java/gerrit/PRED_get_legacy_label_types_1.java b/java/gerrit/PRED_get_legacy_label_types_1.java
index 2f0c1ea..956e821 100644
--- a/java/gerrit/PRED_get_legacy_label_types_1.java
+++ b/java/gerrit/PRED_get_legacy_label_types_1.java
@@ -15,7 +15,7 @@
 package gerrit;
 
 import com.google.gerrit.common.data.LabelType;
-import com.google.gerrit.common.data.LabelValue;
+import com.google.gerrit.entities.LabelValue;
 import com.google.gerrit.server.rules.StoredValues;
 import com.googlecode.prolog_cafe.exceptions.PrologException;
 import com.googlecode.prolog_cafe.lang.IntegerTerm;
diff --git a/javatests/com/google/gerrit/acceptance/OutgoingEmailIT.java b/javatests/com/google/gerrit/acceptance/OutgoingEmailIT.java
index 8e08b1c..30f1dcb 100644
--- a/javatests/com/google/gerrit/acceptance/OutgoingEmailIT.java
+++ b/javatests/com/google/gerrit/acceptance/OutgoingEmailIT.java
@@ -18,13 +18,13 @@
 
 import com.google.common.collect.Iterables;
 import com.google.gerrit.acceptance.config.GerritConfig;
+import com.google.gerrit.entities.Address;
+import com.google.gerrit.entities.EmailHeader;
 import com.google.gerrit.entities.RefNames;
 import com.google.gerrit.extensions.api.accounts.EmailInput;
 import com.google.gerrit.extensions.api.changes.AddReviewerInput;
 import com.google.gerrit.extensions.api.changes.ReviewInput;
 import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
-import com.google.gerrit.mail.Address;
-import com.google.gerrit.mail.EmailHeader;
 import com.google.gerrit.testing.FakeEmailSender;
 import java.net.URL;
 import org.eclipse.jgit.lib.Repository;
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
index aaa1eaa..60c2543 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
@@ -78,13 +78,14 @@
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.common.data.AccessSection;
 import com.google.gerrit.common.data.GlobalCapability;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.common.data.Permission;
 import com.google.gerrit.common.data.PermissionRule.Action;
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.Address;
 import com.google.gerrit.entities.BranchNameKey;
 import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.entities.RefNames;
 import com.google.gerrit.exceptions.StorageException;
@@ -122,7 +123,6 @@
 import com.google.gerrit.gpg.PublicKeyStore;
 import com.google.gerrit.gpg.testing.TestKey;
 import com.google.gerrit.httpd.CacheBasedWebSession;
-import com.google.gerrit.mail.Address;
 import com.google.gerrit.server.ExceptionHook;
 import com.google.gerrit.server.ServerInitiated;
 import com.google.gerrit.server.account.AccountProperties;
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/AgreementsIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/AgreementsIT.java
index 62a1ad2..c4bb47a 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/AgreementsIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/AgreementsIT.java
@@ -29,10 +29,10 @@
 import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
 import com.google.gerrit.common.RawInputUtil;
 import com.google.gerrit.common.data.ContributorAgreement;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.common.data.PermissionRule;
 import com.google.gerrit.entities.AccountGroup;
 import com.google.gerrit.entities.BooleanProjectConfig;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.extensions.api.changes.CherryPickInput;
 import com.google.gerrit.extensions.api.changes.ReviewInput;
 import com.google.gerrit.extensions.api.changes.SubmitInput;
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/GeneralPreferencesIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/GeneralPreferencesIT.java
index 746e6fe..f66bc8d 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/GeneralPreferencesIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/GeneralPreferencesIT.java
@@ -29,6 +29,7 @@
 import com.google.gerrit.extensions.client.GeneralPreferencesInfo.DiffView;
 import com.google.gerrit.extensions.client.GeneralPreferencesInfo.EmailFormat;
 import com.google.gerrit.extensions.client.GeneralPreferencesInfo.EmailStrategy;
+import com.google.gerrit.extensions.client.GeneralPreferencesInfo.Theme;
 import com.google.gerrit.extensions.client.GeneralPreferencesInfo.TimeFormat;
 import com.google.gerrit.extensions.client.MenuItem;
 import com.google.gerrit.extensions.config.DownloadScheme;
@@ -68,6 +69,7 @@
 
     // change all default values
     i.changesPerPage *= -1;
+    i.theme = Theme.DARK;
     i.dateFormat = DateFormat.US;
     i.timeFormat = TimeFormat.HHMM_24;
     i.emailStrategy = EmailStrategy.DISABLED;
@@ -90,6 +92,7 @@
     assertPrefs(o, i, "my");
     assertThat(o.my).containsExactlyElementsIn(i.my);
     assertThat(o.changeTable).containsExactlyElementsIn(i.changeTable);
+    assertThat(o.theme).isEqualTo(i.theme);
   }
 
   @Test
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/MessageIdGeneratorIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/MessageIdGeneratorIT.java
index a8fd834..0309646 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/MessageIdGeneratorIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/MessageIdGeneratorIT.java
@@ -18,9 +18,9 @@
 
 import com.google.gerrit.acceptance.AbstractDaemonTest;
 import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.entities.Address;
 import com.google.gerrit.entities.PatchSet;
 import com.google.gerrit.entities.RefNames;
-import com.google.gerrit.mail.Address;
 import com.google.gerrit.mail.MailMessage;
 import com.google.gerrit.server.mail.send.MessageIdGenerator;
 import com.google.gerrit.server.util.time.TimeUtil;
diff --git a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
index 42c09c7..e7c0f89 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
@@ -94,6 +94,7 @@
 import com.google.gerrit.common.data.Permission;
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.Address;
 import com.google.gerrit.entities.BranchNameKey;
 import com.google.gerrit.entities.Change;
 import com.google.gerrit.entities.PatchSet;
@@ -156,7 +157,6 @@
 import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
 import com.google.gerrit.index.IndexConfig;
 import com.google.gerrit.index.query.PostFilterPredicate;
-import com.google.gerrit.mail.Address;
 import com.google.gerrit.server.ChangeMessagesUtil;
 import com.google.gerrit.server.StarredChangesUtil;
 import com.google.gerrit.server.change.ChangeResource;
diff --git a/javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java b/javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java
index 8bc9cd1..acebe67 100644
--- a/javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java
@@ -56,10 +56,10 @@
 import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.common.data.GlobalCapability;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.common.data.Permission;
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.entities.RefNames;
 import com.google.gerrit.extensions.api.changes.ReviewInput;
diff --git a/javatests/com/google/gerrit/acceptance/api/group/GroupsUpdateIT.java b/javatests/com/google/gerrit/acceptance/api/group/GroupsUpdateIT.java
index 6fcca8c..c977d43 100644
--- a/javatests/com/google/gerrit/acceptance/api/group/GroupsUpdateIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/group/GroupsUpdateIT.java
@@ -18,9 +18,9 @@
 import static com.google.gerrit.testing.GerritJUnit.assertThrows;
 
 import com.google.common.collect.ImmutableSet;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.exceptions.NoSuchGroupException;
 import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.server.ServerInitiated;
diff --git a/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java b/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java
index da92381..d9d2f65 100644
--- a/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java
@@ -53,6 +53,7 @@
 import com.google.gerrit.common.data.Permission;
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.BranchOrderSection;
 import com.google.gerrit.entities.Change;
 import com.google.gerrit.entities.PatchSetApproval;
 import com.google.gerrit.entities.RefNames;
@@ -93,7 +94,6 @@
 import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
 import com.google.gerrit.extensions.webui.PatchSetWebLink;
 import com.google.gerrit.server.change.RevisionResource;
-import com.google.gerrit.server.git.BranchOrderSection;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.restapi.change.GetRevisionActions;
 import com.google.inject.Inject;
diff --git a/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java b/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java
index b01b195..4c3c9d3 100644
--- a/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java
+++ b/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java
@@ -66,6 +66,7 @@
 import com.google.gerrit.common.data.LabelType;
 import com.google.gerrit.common.data.Permission;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.Address;
 import com.google.gerrit.entities.BooleanProjectConfig;
 import com.google.gerrit.entities.Change;
 import com.google.gerrit.entities.ChangeMessage;
@@ -93,7 +94,6 @@
 import com.google.gerrit.extensions.common.LabelInfo;
 import com.google.gerrit.extensions.common.RevisionInfo;
 import com.google.gerrit.git.ObjectIds;
-import com.google.gerrit.mail.Address;
 import com.google.gerrit.server.ChangeMessagesUtil;
 import com.google.gerrit.server.events.CommitReceivedEvent;
 import com.google.gerrit.server.git.receive.NoteDbPushOption;
diff --git a/javatests/com/google/gerrit/acceptance/git/AbstractSubmitOnPush.java b/javatests/com/google/gerrit/acceptance/git/AbstractSubmitOnPush.java
index dcee118..f2accd4 100644
--- a/javatests/com/google/gerrit/acceptance/git/AbstractSubmitOnPush.java
+++ b/javatests/com/google/gerrit/acceptance/git/AbstractSubmitOnPush.java
@@ -27,15 +27,15 @@
 import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.entities.Address;
 import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.EmailHeader;
 import com.google.gerrit.entities.PatchSet;
 import com.google.gerrit.entities.PatchSetApproval;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.entities.RefNames;
 import com.google.gerrit.extensions.api.changes.NotifyHandling;
 import com.google.gerrit.extensions.api.changes.RecipientType;
-import com.google.gerrit.mail.Address;
-import com.google.gerrit.mail.EmailHeader;
 import com.google.gerrit.server.ApprovalsUtil;
 import com.google.gerrit.server.events.ChangeMergedEvent;
 import com.google.gerrit.server.notedb.ChangeNotes;
diff --git a/javatests/com/google/gerrit/acceptance/git/PushAccountIT.java b/javatests/com/google/gerrit/acceptance/git/PushAccountIT.java
index 6f7a4c3..86fce9c 100644
--- a/javatests/com/google/gerrit/acceptance/git/PushAccountIT.java
+++ b/javatests/com/google/gerrit/acceptance/git/PushAccountIT.java
@@ -35,6 +35,7 @@
 import com.google.gerrit.common.data.GlobalCapability;
 import com.google.gerrit.common.data.Permission;
 import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.NotifyConfig.NotifyType;
 import com.google.gerrit.entities.RefNames;
 import com.google.gerrit.extensions.api.changes.ReviewInput;
 import com.google.gerrit.extensions.common.AccountInfo;
@@ -43,7 +44,6 @@
 import com.google.gerrit.server.account.AccountProperties;
 import com.google.gerrit.server.account.AccountsUpdate;
 import com.google.gerrit.server.account.ProjectWatches;
-import com.google.gerrit.server.account.ProjectWatches.NotifyType;
 import com.google.gerrit.server.notedb.Sequences;
 import com.google.gerrit.server.util.MagicBranch;
 import com.google.gerrit.testing.ConfigSuite;
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersByEmailIT.java b/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersByEmailIT.java
index 843ecc6..012e98d 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersByEmailIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersByEmailIT.java
@@ -24,6 +24,7 @@
 import com.google.gerrit.acceptance.PushOneCommit;
 import com.google.gerrit.acceptance.RestResponse;
 import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
+import com.google.gerrit.entities.Address;
 import com.google.gerrit.extensions.api.changes.AddReviewerInput;
 import com.google.gerrit.extensions.api.changes.AddReviewerResult;
 import com.google.gerrit.extensions.api.changes.ReviewInput;
@@ -33,7 +34,6 @@
 import com.google.gerrit.extensions.client.ReviewerState;
 import com.google.gerrit.extensions.common.AccountInfo;
 import com.google.gerrit.extensions.common.ChangeInfo;
-import com.google.gerrit.mail.Address;
 import com.google.gerrit.testing.FakeEmailSender.Message;
 import com.google.gson.reflect.TypeToken;
 import com.google.inject.Inject;
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersIT.java b/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersIT.java
index 94357b9..40b6da4 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersIT.java
@@ -37,6 +37,7 @@
 import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
 import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
 import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.entities.Address;
 import com.google.gerrit.entities.RefNames;
 import com.google.gerrit.extensions.api.changes.AddReviewerInput;
 import com.google.gerrit.extensions.api.changes.AddReviewerResult;
@@ -53,7 +54,6 @@
 import com.google.gerrit.extensions.common.LabelInfo;
 import com.google.gerrit.extensions.common.ReviewerUpdateInfo;
 import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.mail.Address;
 import com.google.gerrit.server.change.ReviewerAdder;
 import com.google.gerrit.testing.FakeEmailSender.Message;
 import com.google.gson.stream.JsonReader;
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/AccessIT.java b/javatests/com/google/gerrit/acceptance/rest/project/AccessIT.java
index 46054ec..5f17e87 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/AccessIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/AccessIT.java
@@ -33,8 +33,8 @@
 import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
 import com.google.gerrit.common.data.AccessSection;
 import com.google.gerrit.common.data.GlobalCapability;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.entities.RefNames;
 import com.google.gerrit.extensions.api.access.AccessSectionInfo;
diff --git a/javatests/com/google/gerrit/acceptance/server/mail/ChangeNotificationsIT.java b/javatests/com/google/gerrit/acceptance/server/mail/ChangeNotificationsIT.java
index 76514ec..70d8335 100644
--- a/javatests/com/google/gerrit/acceptance/server/mail/ChangeNotificationsIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/mail/ChangeNotificationsIT.java
@@ -17,17 +17,17 @@
 import static com.google.common.truth.Truth.assertWithMessage;
 import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
 import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowLabel;
+import static com.google.gerrit.entities.NotifyConfig.NotifyType.ABANDONED_CHANGES;
+import static com.google.gerrit.entities.NotifyConfig.NotifyType.ALL_COMMENTS;
+import static com.google.gerrit.entities.NotifyConfig.NotifyType.NEW_CHANGES;
+import static com.google.gerrit.entities.NotifyConfig.NotifyType.NEW_PATCHSETS;
+import static com.google.gerrit.entities.NotifyConfig.NotifyType.SUBMITTED_CHANGES;
 import static com.google.gerrit.extensions.api.changes.NotifyHandling.ALL;
 import static com.google.gerrit.extensions.api.changes.NotifyHandling.NONE;
 import static com.google.gerrit.extensions.api.changes.NotifyHandling.OWNER;
 import static com.google.gerrit.extensions.api.changes.NotifyHandling.OWNER_REVIEWERS;
 import static com.google.gerrit.extensions.client.GeneralPreferencesInfo.EmailStrategy.CC_ON_OWN_COMMENTS;
 import static com.google.gerrit.extensions.client.GeneralPreferencesInfo.EmailStrategy.ENABLED;
-import static com.google.gerrit.server.account.ProjectWatches.NotifyType.ABANDONED_CHANGES;
-import static com.google.gerrit.server.account.ProjectWatches.NotifyType.ALL_COMMENTS;
-import static com.google.gerrit.server.account.ProjectWatches.NotifyType.NEW_CHANGES;
-import static com.google.gerrit.server.account.ProjectWatches.NotifyType.NEW_PATCHSETS;
-import static com.google.gerrit.server.account.ProjectWatches.NotifyType.SUBMITTED_CHANGES;
 import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
 
 import com.google.common.collect.ImmutableList;
diff --git a/javatests/com/google/gerrit/acceptance/server/mail/MailMetadataIT.java b/javatests/com/google/gerrit/acceptance/server/mail/MailMetadataIT.java
index 0826c166..6dd2f32 100644
--- a/javatests/com/google/gerrit/acceptance/server/mail/MailMetadataIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/mail/MailMetadataIT.java
@@ -22,9 +22,9 @@
 import com.google.gerrit.acceptance.UseClockStep;
 import com.google.gerrit.acceptance.UseTimezone;
 import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
+import com.google.gerrit.entities.EmailHeader;
 import com.google.gerrit.extensions.api.changes.ReviewInput;
 import com.google.gerrit.extensions.common.ChangeMessageInfo;
-import com.google.gerrit.mail.EmailHeader;
 import com.google.gerrit.mail.MailProcessingUtil;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.testing.FakeEmailSender;
diff --git a/javatests/com/google/gerrit/acceptance/server/mail/MailProcessorIT.java b/javatests/com/google/gerrit/acceptance/server/mail/MailProcessorIT.java
index d7d67b8..4f79e09 100644
--- a/javatests/com/google/gerrit/acceptance/server/mail/MailProcessorIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/mail/MailProcessorIT.java
@@ -28,6 +28,7 @@
 import com.google.common.collect.Streams;
 import com.google.gerrit.acceptance.config.GerritConfig;
 import com.google.gerrit.acceptance.testsuite.account.AccountOperations;
+import com.google.gerrit.entities.EmailHeader;
 import com.google.gerrit.extensions.annotations.Exports;
 import com.google.gerrit.extensions.api.changes.ReviewInput;
 import com.google.gerrit.extensions.api.changes.ReviewInput.CommentInput;
@@ -40,7 +41,6 @@
 import com.google.gerrit.extensions.validators.CommentForValidation;
 import com.google.gerrit.extensions.validators.CommentValidationContext;
 import com.google.gerrit.extensions.validators.CommentValidator;
-import com.google.gerrit.mail.EmailHeader;
 import com.google.gerrit.mail.MailMessage;
 import com.google.gerrit.mail.MailProcessingUtil;
 import com.google.gerrit.server.mail.receive.MailProcessor;
diff --git a/javatests/com/google/gerrit/acceptance/server/mail/MailSenderIT.java b/javatests/com/google/gerrit/acceptance/server/mail/MailSenderIT.java
index 0ae9ad2..1c916a3 100644
--- a/javatests/com/google/gerrit/acceptance/server/mail/MailSenderIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/mail/MailSenderIT.java
@@ -17,7 +17,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import com.google.gerrit.acceptance.config.GerritConfig;
-import com.google.gerrit.mail.EmailHeader;
+import com.google.gerrit.entities.EmailHeader;
 import java.net.URI;
 import java.util.Map;
 import org.junit.Test;
diff --git a/javatests/com/google/gerrit/acceptance/server/project/ProjectWatchIT.java b/javatests/com/google/gerrit/acceptance/server/project/ProjectWatchIT.java
index ff26fec..b04ae33 100644
--- a/javatests/com/google/gerrit/acceptance/server/project/ProjectWatchIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/project/ProjectWatchIT.java
@@ -27,13 +27,13 @@
 import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
 import com.google.gerrit.common.data.Permission;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.Address;
+import com.google.gerrit.entities.NotifyConfig;
+import com.google.gerrit.entities.NotifyConfig.NotifyType;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.extensions.api.changes.ReviewInput;
 import com.google.gerrit.extensions.api.changes.StarsInput;
 import com.google.gerrit.extensions.common.GroupInfo;
-import com.google.gerrit.mail.Address;
-import com.google.gerrit.server.account.ProjectWatches.NotifyType;
-import com.google.gerrit.server.git.NotifyConfig;
 import com.google.gerrit.testing.FakeEmailSender.Message;
 import com.google.inject.Inject;
 import java.util.EnumSet;
diff --git a/javatests/com/google/gerrit/common/data/GroupReferenceTest.java b/javatests/com/google/gerrit/common/data/GroupReferenceTest.java
index 113bd77..593b635 100644
--- a/javatests/com/google/gerrit/common/data/GroupReferenceTest.java
+++ b/javatests/com/google/gerrit/common/data/GroupReferenceTest.java
@@ -19,6 +19,8 @@
 
 import com.google.gerrit.entities.AccountGroup;
 import com.google.gerrit.entities.AccountGroup.UUID;
+import com.google.gerrit.entities.GroupDescription;
+import com.google.gerrit.entities.GroupReference;
 import org.junit.Test;
 
 public class GroupReferenceTest {
diff --git a/javatests/com/google/gerrit/common/data/LabelFunctionTest.java b/javatests/com/google/gerrit/common/data/LabelFunctionTest.java
index 8fea072..298ce1e 100644
--- a/javatests/com/google/gerrit/common/data/LabelFunctionTest.java
+++ b/javatests/com/google/gerrit/common/data/LabelFunctionTest.java
@@ -20,6 +20,7 @@
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.entities.Change;
 import com.google.gerrit.entities.LabelId;
+import com.google.gerrit.entities.LabelValue;
 import com.google.gerrit.entities.PatchSet;
 import com.google.gerrit.entities.PatchSetApproval;
 import java.time.Instant;
diff --git a/javatests/com/google/gerrit/common/data/LabelTypeTest.java b/javatests/com/google/gerrit/common/data/LabelTypeTest.java
index 2f81fa9..4810f58 100644
--- a/javatests/com/google/gerrit/common/data/LabelTypeTest.java
+++ b/javatests/com/google/gerrit/common/data/LabelTypeTest.java
@@ -17,6 +17,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import com.google.common.collect.ImmutableList;
+import com.google.gerrit.entities.LabelValue;
 import org.junit.Test;
 
 public class LabelTypeTest {
diff --git a/javatests/com/google/gerrit/common/data/PermissionRuleTest.java b/javatests/com/google/gerrit/common/data/PermissionRuleTest.java
index d193b09..ee6590a 100644
--- a/javatests/com/google/gerrit/common/data/PermissionRuleTest.java
+++ b/javatests/com/google/gerrit/common/data/PermissionRuleTest.java
@@ -18,6 +18,7 @@
 
 import com.google.gerrit.common.data.PermissionRule.Action;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupReference;
 import org.junit.Before;
 import org.junit.Test;
 
diff --git a/javatests/com/google/gerrit/common/data/PermissionTest.java b/javatests/com/google/gerrit/common/data/PermissionTest.java
index 9dd71ca..ac3e2c5 100644
--- a/javatests/com/google/gerrit/common/data/PermissionTest.java
+++ b/javatests/com/google/gerrit/common/data/PermissionTest.java
@@ -17,6 +17,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupReference;
 import org.junit.Before;
 import org.junit.Test;
 
diff --git a/javatests/com/google/gerrit/mail/AbstractParserTest.java b/javatests/com/google/gerrit/mail/AbstractParserTest.java
index 2306449..b22b8ad 100644
--- a/javatests/com/google/gerrit/mail/AbstractParserTest.java
+++ b/javatests/com/google/gerrit/mail/AbstractParserTest.java
@@ -17,6 +17,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Address;
 import com.google.gerrit.entities.Comment;
 import com.google.gerrit.entities.HumanComment;
 import java.sql.Timestamp;
diff --git a/javatests/com/google/gerrit/mail/AddressTest.java b/javatests/com/google/gerrit/mail/AddressTest.java
index 8addcf8..232b8d1 100644
--- a/javatests/com/google/gerrit/mail/AddressTest.java
+++ b/javatests/com/google/gerrit/mail/AddressTest.java
@@ -17,6 +17,7 @@
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.gerrit.testing.GerritJUnit.assertThrows;
 
+import com.google.gerrit.entities.Address;
 import org.junit.Test;
 
 public class AddressTest {
diff --git a/javatests/com/google/gerrit/mail/MailHeaderParserTest.java b/javatests/com/google/gerrit/mail/MailHeaderParserTest.java
index 296d1a1..7e3edab 100644
--- a/javatests/com/google/gerrit/mail/MailHeaderParserTest.java
+++ b/javatests/com/google/gerrit/mail/MailHeaderParserTest.java
@@ -16,6 +16,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import com.google.gerrit.entities.Address;
 import java.time.Instant;
 import java.time.LocalDateTime;
 import java.time.Month;
diff --git a/javatests/com/google/gerrit/mail/data/AttachmentMessage.java b/javatests/com/google/gerrit/mail/data/AttachmentMessage.java
index aea59ba..b39e3be 100644
--- a/javatests/com/google/gerrit/mail/data/AttachmentMessage.java
+++ b/javatests/com/google/gerrit/mail/data/AttachmentMessage.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.mail.data;
 
-import com.google.gerrit.mail.Address;
+import com.google.gerrit.entities.Address;
 import com.google.gerrit.mail.MailMessage;
 import java.time.LocalDateTime;
 import java.time.Month;
diff --git a/javatests/com/google/gerrit/mail/data/Base64HeaderMessage.java b/javatests/com/google/gerrit/mail/data/Base64HeaderMessage.java
index 957ee6e..92ba97c 100644
--- a/javatests/com/google/gerrit/mail/data/Base64HeaderMessage.java
+++ b/javatests/com/google/gerrit/mail/data/Base64HeaderMessage.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.mail.data;
 
-import com.google.gerrit.mail.Address;
+import com.google.gerrit.entities.Address;
 import com.google.gerrit.mail.MailMessage;
 import java.time.LocalDateTime;
 import java.time.Month;
diff --git a/javatests/com/google/gerrit/mail/data/HtmlMimeMessage.java b/javatests/com/google/gerrit/mail/data/HtmlMimeMessage.java
index e5e2ed8..7cbf9c0 100644
--- a/javatests/com/google/gerrit/mail/data/HtmlMimeMessage.java
+++ b/javatests/com/google/gerrit/mail/data/HtmlMimeMessage.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.mail.data;
 
-import com.google.gerrit.mail.Address;
+import com.google.gerrit.entities.Address;
 import com.google.gerrit.mail.MailMessage;
 import java.time.LocalDateTime;
 import java.time.Month;
diff --git a/javatests/com/google/gerrit/mail/data/NonUTF8Message.java b/javatests/com/google/gerrit/mail/data/NonUTF8Message.java
index e183a37..60368eb 100644
--- a/javatests/com/google/gerrit/mail/data/NonUTF8Message.java
+++ b/javatests/com/google/gerrit/mail/data/NonUTF8Message.java
@@ -13,7 +13,7 @@
 // limitations under the License.
 package com.google.gerrit.mail.data;
 
-import com.google.gerrit.mail.Address;
+import com.google.gerrit.entities.Address;
 import com.google.gerrit.mail.MailMessage;
 import java.time.LocalDateTime;
 import java.time.Month;
diff --git a/javatests/com/google/gerrit/mail/data/QuotedPrintableHeaderMessage.java b/javatests/com/google/gerrit/mail/data/QuotedPrintableHeaderMessage.java
index ac739c8..94c9d42 100644
--- a/javatests/com/google/gerrit/mail/data/QuotedPrintableHeaderMessage.java
+++ b/javatests/com/google/gerrit/mail/data/QuotedPrintableHeaderMessage.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.mail.data;
 
-import com.google.gerrit.mail.Address;
+import com.google.gerrit.entities.Address;
 import com.google.gerrit.mail.MailMessage;
 import java.time.LocalDateTime;
 import java.time.Month;
diff --git a/javatests/com/google/gerrit/mail/data/SimpleTextMessage.java b/javatests/com/google/gerrit/mail/data/SimpleTextMessage.java
index 3f8e62f..20d8076 100644
--- a/javatests/com/google/gerrit/mail/data/SimpleTextMessage.java
+++ b/javatests/com/google/gerrit/mail/data/SimpleTextMessage.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.mail.data;
 
-import com.google.gerrit.mail.Address;
+import com.google.gerrit.entities.Address;
 import com.google.gerrit.mail.MailMessage;
 import java.time.LocalDateTime;
 import java.time.Month;
diff --git a/javatests/com/google/gerrit/server/account/AccountCacheTest.java b/javatests/com/google/gerrit/server/account/AccountCacheTest.java
index 5e75fe5..a2aa40b 100644
--- a/javatests/com/google/gerrit/server/account/AccountCacheTest.java
+++ b/javatests/com/google/gerrit/server/account/AccountCacheTest.java
@@ -19,6 +19,7 @@
 import com.google.common.truth.Truth;
 import com.google.common.truth.extensions.proto.ProtoTruth;
 import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.NotifyConfig;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.server.cache.proto.Cache;
 import com.google.gerrit.server.config.CachedPreferences;
@@ -103,7 +104,7 @@
     CachedAccountDetails original =
         CachedAccountDetails.create(
             ACCOUNT,
-            ImmutableMap.of(key, ImmutableSet.of(ProjectWatches.NotifyType.ALL_COMMENTS)),
+            ImmutableMap.of(key, ImmutableSet.of(NotifyConfig.NotifyType.ALL_COMMENTS)),
             CachedPreferences.fromString(""));
 
     byte[] serialized = SERIALIZER.serialize(original);
@@ -127,7 +128,7 @@
     CachedAccountDetails original =
         CachedAccountDetails.create(
             ACCOUNT,
-            ImmutableMap.of(key, ImmutableSet.of(ProjectWatches.NotifyType.ALL_COMMENTS)),
+            ImmutableMap.of(key, ImmutableSet.of(NotifyConfig.NotifyType.ALL_COMMENTS)),
             CachedPreferences.fromString(""));
 
     byte[] serialized = SERIALIZER.serialize(original);
diff --git a/javatests/com/google/gerrit/server/account/WatchConfigTest.java b/javatests/com/google/gerrit/server/account/WatchConfigTest.java
index 95dbbde..7d36b94 100644
--- a/javatests/com/google/gerrit/server/account/WatchConfigTest.java
+++ b/javatests/com/google/gerrit/server/account/WatchConfigTest.java
@@ -19,8 +19,8 @@
 
 import com.google.common.collect.ImmutableSet;
 import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.NotifyConfig.NotifyType;
 import com.google.gerrit.entities.Project;
-import com.google.gerrit.server.account.ProjectWatches.NotifyType;
 import com.google.gerrit.server.account.ProjectWatches.NotifyValue;
 import com.google.gerrit.server.account.ProjectWatches.ProjectWatchKey;
 import com.google.gerrit.server.git.ValidationError;
diff --git a/javatests/com/google/gerrit/server/cache/serialize/entities/AccessSectionSerializerTest.java b/javatests/com/google/gerrit/server/cache/serialize/entities/AccessSectionSerializerTest.java
new file mode 100644
index 0000000..660b9e6
--- /dev/null
+++ b/javatests/com/google/gerrit/server/cache/serialize/entities/AccessSectionSerializerTest.java
@@ -0,0 +1,39 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.cache.serialize.entities;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.server.cache.serialize.entities.AccessSectionSerializer.deserialize;
+import static com.google.gerrit.server.cache.serialize.entities.AccessSectionSerializer.serialize;
+
+import com.google.gerrit.common.data.AccessSection;
+import org.junit.Test;
+
+public class AccessSectionSerializerTest {
+  @Test
+  public void roundTrip() {
+    AccessSection autoValue =
+        AccessSection.builder("refs/test")
+            .addPermission(PermissionSerializerTest.ALL_VALUES_SET.toBuilder())
+            .build();
+    assertThat(deserialize(serialize(autoValue))).isEqualTo(autoValue);
+  }
+
+  @Test
+  public void roundTripWithMinimalValues() {
+    AccessSection autoValue = AccessSection.builder("refs/test").build();
+    assertThat(deserialize(serialize(autoValue))).isEqualTo(autoValue);
+  }
+}
diff --git a/javatests/com/google/gerrit/server/cache/serialize/entities/AddressSerializerTest.java b/javatests/com/google/gerrit/server/cache/serialize/entities/AddressSerializerTest.java
new file mode 100644
index 0000000..4f080a7
--- /dev/null
+++ b/javatests/com/google/gerrit/server/cache/serialize/entities/AddressSerializerTest.java
@@ -0,0 +1,36 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.cache.serialize.entities;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.server.cache.serialize.entities.AddressSerializer.deserialize;
+import static com.google.gerrit.server.cache.serialize.entities.AddressSerializer.serialize;
+
+import com.google.gerrit.entities.Address;
+import org.junit.Test;
+
+public class AddressSerializerTest {
+  @Test
+  public void roundTrip() {
+    Address autoValue = Address.create("Jane Doe", "jdoe@example.com");
+    assertThat(deserialize(serialize(autoValue))).isEqualTo(autoValue);
+  }
+
+  @Test
+  public void roundTripWithMinimalValues() {
+    Address autoValue = Address.create("jdoe@example.com");
+    assertThat(deserialize(serialize(autoValue))).isEqualTo(autoValue);
+  }
+}
diff --git a/javatests/com/google/gerrit/server/cache/serialize/entities/BranchOrderSectionSerializerTest.java b/javatests/com/google/gerrit/server/cache/serialize/entities/BranchOrderSectionSerializerTest.java
new file mode 100644
index 0000000..f3a0445
--- /dev/null
+++ b/javatests/com/google/gerrit/server/cache/serialize/entities/BranchOrderSectionSerializerTest.java
@@ -0,0 +1,31 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.cache.serialize.entities;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.server.cache.serialize.entities.BranchOrderSectionSerializer.deserialize;
+import static com.google.gerrit.server.cache.serialize.entities.BranchOrderSectionSerializer.serialize;
+
+import com.google.common.collect.ImmutableList;
+import com.google.gerrit.entities.BranchOrderSection;
+import org.junit.Test;
+
+public class BranchOrderSectionSerializerTest {
+  @Test
+  public void roundTrip() {
+    BranchOrderSection autoValue = BranchOrderSection.create(ImmutableList.of("master", "stable"));
+    assertThat(deserialize(serialize(autoValue))).isEqualTo(autoValue);
+  }
+}
diff --git a/javatests/com/google/gerrit/server/cache/serialize/entities/ConfiguredMimeTypeSerializerTest.java b/javatests/com/google/gerrit/server/cache/serialize/entities/ConfiguredMimeTypeSerializerTest.java
new file mode 100644
index 0000000..f0e4932
--- /dev/null
+++ b/javatests/com/google/gerrit/server/cache/serialize/entities/ConfiguredMimeTypeSerializerTest.java
@@ -0,0 +1,36 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.cache.serialize.entities;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.server.cache.serialize.entities.ConfiguredMimeTypeSerializer.deserialize;
+import static com.google.gerrit.server.cache.serialize.entities.ConfiguredMimeTypeSerializer.serialize;
+
+import com.google.gerrit.entities.ConfiguredMimeTypes;
+import org.junit.Test;
+
+public class ConfiguredMimeTypeSerializerTest {
+  @Test
+  public void reType_roundTrip() {
+    ConfiguredMimeTypes.ReType value = new ConfiguredMimeTypes.ReType("type", "pattern");
+    assertThat(deserialize(serialize(value))).isEqualTo(value);
+  }
+
+  @Test
+  public void fnType_roundTrip() throws Exception {
+    ConfiguredMimeTypes.FnType value = new ConfiguredMimeTypes.FnType("type", "pattern");
+    assertThat(deserialize(serialize(value))).isEqualTo(value);
+  }
+}
diff --git a/javatests/com/google/gerrit/server/cache/serialize/entities/ContributorAgreementSerializerTest.java b/javatests/com/google/gerrit/server/cache/serialize/entities/ContributorAgreementSerializerTest.java
new file mode 100644
index 0000000..81372d5
--- /dev/null
+++ b/javatests/com/google/gerrit/server/cache/serialize/entities/ContributorAgreementSerializerTest.java
@@ -0,0 +1,56 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.cache.serialize.entities;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.server.cache.serialize.entities.ContributorAgreementSerializer.deserialize;
+import static com.google.gerrit.server.cache.serialize.entities.ContributorAgreementSerializer.serialize;
+
+import com.google.common.collect.ImmutableList;
+import com.google.gerrit.common.data.ContributorAgreement;
+import com.google.gerrit.common.data.PermissionRule;
+import com.google.gerrit.entities.GroupReference;
+import org.junit.Test;
+
+public class ContributorAgreementSerializerTest {
+  @Test
+  public void roundTrip() {
+    ContributorAgreement autoValue =
+        ContributorAgreement.builder("name")
+            .setDescription("desc")
+            .setAgreementUrl("url")
+            .setAutoVerify(GroupReference.create("auto-verify"))
+            .setAccepted(
+                ImmutableList.of(
+                    PermissionRule.create(GroupReference.create("accepted1")),
+                    PermissionRule.create(GroupReference.create("accepted2"))))
+            .setExcludeProjectsRegexes(ImmutableList.of("refs/*"))
+            .setMatchProjectsRegexes(ImmutableList.of("refs/heads/*"))
+            .build();
+    assertThat(deserialize(serialize(autoValue))).isEqualTo(autoValue);
+  }
+
+  @Test
+  public void roundTripWithMinimalValues() {
+    ContributorAgreement autoValue =
+        ContributorAgreement.builder("name")
+            .setAccepted(
+                ImmutableList.of(
+                    PermissionRule.create(GroupReference.create("accepted1")),
+                    PermissionRule.create(GroupReference.create("accepted2"))))
+            .build();
+    assertThat(deserialize(serialize(autoValue))).isEqualTo(autoValue);
+  }
+}
diff --git a/javatests/com/google/gerrit/server/cache/serialize/entities/GroupReferenceSerializerTest.java b/javatests/com/google/gerrit/server/cache/serialize/entities/GroupReferenceSerializerTest.java
index b52f4ea..a5092e0 100644
--- a/javatests/com/google/gerrit/server/cache/serialize/entities/GroupReferenceSerializerTest.java
+++ b/javatests/com/google/gerrit/server/cache/serialize/entities/GroupReferenceSerializerTest.java
@@ -18,8 +18,8 @@
 import static com.google.gerrit.server.cache.serialize.entities.GroupReferenceSerializer.deserialize;
 import static com.google.gerrit.server.cache.serialize.entities.GroupReferenceSerializer.serialize;
 
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupReference;
 import org.junit.Test;
 
 public class GroupReferenceSerializerTest {
diff --git a/javatests/com/google/gerrit/server/cache/serialize/entities/LabelTypeSerializerTest.java b/javatests/com/google/gerrit/server/cache/serialize/entities/LabelTypeSerializerTest.java
new file mode 100644
index 0000000..fac662d
--- /dev/null
+++ b/javatests/com/google/gerrit/server/cache/serialize/entities/LabelTypeSerializerTest.java
@@ -0,0 +1,61 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.cache.serialize.entities;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.server.cache.serialize.entities.LabelTypeSerializer.deserialize;
+import static com.google.gerrit.server.cache.serialize.entities.LabelTypeSerializer.serialize;
+
+import com.google.common.collect.ImmutableList;
+import com.google.gerrit.common.data.LabelType;
+import com.google.gerrit.entities.LabelValue;
+import org.junit.Test;
+
+public class LabelTypeSerializerTest {
+  static final LabelType ALL_VALUES_SET =
+      LabelType.builder(
+              "name",
+              ImmutableList.of(
+                  LabelValue.create((short) 0, "no vote"),
+                  LabelValue.create((short) 1, "approved")))
+          .setCanOverride(true)
+          .setAllowPostSubmit(true)
+          .setIgnoreSelfApproval(true)
+          .setRefPatterns(ImmutableList.of("refs/heads/*", "refs/tags/*"))
+          .setDefaultValue((short) 1)
+          .setCopyAnyScore(true)
+          .setCopyMaxScore(true)
+          .setCopyMinScore(true)
+          .setCopyAllScoresOnMergeFirstParentUpdate(true)
+          .setCopyAllScoresOnTrivialRebase(true)
+          .setCopyAllScoresIfNoCodeChange(true)
+          .setCopyAllScoresIfNoChange(true)
+          .setCopyValues(ImmutableList.of((short) 0, (short) 1))
+          .setMaxNegative((short) -1)
+          .setMaxPositive((short) 1)
+          .setCanOverride(true)
+          .build();
+
+  @Test
+  public void roundTrip() {
+    assertThat(deserialize(serialize(ALL_VALUES_SET))).isEqualTo(ALL_VALUES_SET);
+  }
+
+  @Test
+  public void roundTripWithMinimalValues() {
+    LabelType autoValue = ALL_VALUES_SET.toBuilder().setRefPatterns(null).build();
+    assertThat(deserialize(serialize(autoValue))).isEqualTo(autoValue);
+  }
+}
diff --git a/javatests/com/google/gerrit/server/cache/serialize/entities/LabelValueSerializerTest.java b/javatests/com/google/gerrit/server/cache/serialize/entities/LabelValueSerializerTest.java
new file mode 100644
index 0000000..7e3abbd
--- /dev/null
+++ b/javatests/com/google/gerrit/server/cache/serialize/entities/LabelValueSerializerTest.java
@@ -0,0 +1,30 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.cache.serialize.entities;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.server.cache.serialize.entities.LabelValueSerializer.deserialize;
+import static com.google.gerrit.server.cache.serialize.entities.LabelValueSerializer.serialize;
+
+import com.google.gerrit.entities.LabelValue;
+import org.junit.Test;
+
+public class LabelValueSerializerTest {
+  @Test
+  public void roundTrip() {
+    LabelValue autoValue = LabelValue.create((short) 123, "Approved!");
+    assertThat(deserialize(serialize(autoValue))).isEqualTo(autoValue);
+  }
+}
diff --git a/javatests/com/google/gerrit/server/cache/serialize/entities/NotifyConfigSerializerTest.java b/javatests/com/google/gerrit/server/cache/serialize/entities/NotifyConfigSerializerTest.java
new file mode 100644
index 0000000..3447ae3
--- /dev/null
+++ b/javatests/com/google/gerrit/server/cache/serialize/entities/NotifyConfigSerializerTest.java
@@ -0,0 +1,47 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.cache.serialize.entities;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.server.cache.serialize.entities.NotifyConfigSerializer.deserialize;
+import static com.google.gerrit.server.cache.serialize.entities.NotifyConfigSerializer.serialize;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.gerrit.entities.Address;
+import com.google.gerrit.entities.GroupReference;
+import com.google.gerrit.entities.NotifyConfig;
+import org.junit.Test;
+
+public class NotifyConfigSerializerTest {
+  @Test
+  public void roundTrip() {
+    NotifyConfig autoValue =
+        NotifyConfig.builder()
+            .setName("foo-bar")
+            .addAddress(Address.create("address@example.com"))
+            .addGroup(GroupReference.create("group-uuid"))
+            .setHeader(NotifyConfig.Header.CC)
+            .setFilter("filter")
+            .setNotify(ImmutableSet.of(NotifyConfig.NotifyType.ALL_COMMENTS))
+            .build();
+    assertThat(deserialize(serialize(autoValue))).isEqualTo(autoValue);
+  }
+
+  @Test
+  public void roundTripWithMinimalValues() {
+    NotifyConfig autoValue = NotifyConfig.builder().setName("foo-bar").build();
+    assertThat(deserialize(serialize(autoValue))).isEqualTo(autoValue);
+  }
+}
diff --git a/javatests/com/google/gerrit/server/cache/serialize/entities/PermissionRuleSerializerTest.java b/javatests/com/google/gerrit/server/cache/serialize/entities/PermissionRuleSerializerTest.java
index b447d5e..3ce3549 100644
--- a/javatests/com/google/gerrit/server/cache/serialize/entities/PermissionRuleSerializerTest.java
+++ b/javatests/com/google/gerrit/server/cache/serialize/entities/PermissionRuleSerializerTest.java
@@ -18,8 +18,8 @@
 import static com.google.gerrit.server.cache.serialize.entities.PermissionRuleSerializer.deserialize;
 import static com.google.gerrit.server.cache.serialize.entities.PermissionRuleSerializer.serialize;
 
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.common.data.PermissionRule;
+import com.google.gerrit.entities.GroupReference;
 import org.junit.Test;
 
 public class PermissionRuleSerializerTest {
diff --git a/javatests/com/google/gerrit/server/cache/serialize/entities/PermissionSerializerTest.java b/javatests/com/google/gerrit/server/cache/serialize/entities/PermissionSerializerTest.java
new file mode 100644
index 0000000..ae399eb
--- /dev/null
+++ b/javatests/com/google/gerrit/server/cache/serialize/entities/PermissionSerializerTest.java
@@ -0,0 +1,43 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.cache.serialize.entities;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.server.cache.serialize.entities.PermissionSerializer.deserialize;
+import static com.google.gerrit.server.cache.serialize.entities.PermissionSerializer.serialize;
+
+import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.common.data.PermissionRule;
+import com.google.gerrit.entities.GroupReference;
+import org.junit.Test;
+
+public class PermissionSerializerTest {
+  static final Permission ALL_VALUES_SET =
+      Permission.builder(Permission.ABANDON)
+          .setExclusiveGroup(true)
+          .add(PermissionRule.builder(GroupReference.create("group")))
+          .build();
+
+  @Test
+  public void roundTrip() {
+    assertThat(deserialize(serialize(ALL_VALUES_SET))).isEqualTo(ALL_VALUES_SET);
+  }
+
+  @Test
+  public void roundTripWithMinimalValues() {
+    Permission permission = Permission.builder(Permission.ABANDON).build();
+    assertThat(deserialize(serialize(permission))).isEqualTo(permission);
+  }
+}
diff --git a/javatests/com/google/gerrit/server/cache/serialize/entities/StoredCommentLinkInfoSerializerTest.java b/javatests/com/google/gerrit/server/cache/serialize/entities/StoredCommentLinkInfoSerializerTest.java
new file mode 100644
index 0000000..ccd2378
--- /dev/null
+++ b/javatests/com/google/gerrit/server/cache/serialize/entities/StoredCommentLinkInfoSerializerTest.java
@@ -0,0 +1,58 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.cache.serialize.entities;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.server.cache.serialize.entities.StoredCommentLinkInfoSerializer.deserialize;
+import static com.google.gerrit.server.cache.serialize.entities.StoredCommentLinkInfoSerializer.serialize;
+
+import com.google.gerrit.entities.StoredCommentLinkInfo;
+import org.junit.Test;
+
+public class StoredCommentLinkInfoSerializerTest {
+  @Test
+  public void htmlOnly_roundTrip() {
+    StoredCommentLinkInfo autoValue =
+        StoredCommentLinkInfo.builder("name")
+            .setEnabled(true)
+            .setHtml("<p>html")
+            .setMatch("*")
+            .build();
+    assertThat(deserialize(serialize(autoValue))).isEqualTo(autoValue);
+  }
+
+  @Test
+  public void linkOnly_roundTrip() {
+    StoredCommentLinkInfo autoValue =
+        StoredCommentLinkInfo.builder("name")
+            .setEnabled(true)
+            .setLink("<p>html")
+            .setMatch("*")
+            .build();
+    assertThat(deserialize(serialize(autoValue))).isEqualTo(autoValue);
+  }
+
+  @Test
+  public void overrideOnly_roundTrip() {
+    StoredCommentLinkInfo autoValue =
+        StoredCommentLinkInfo.builder("name")
+            .setEnabled(true)
+            .setOverrideOnly(true)
+            .setLink("<p>html")
+            .setMatch("*")
+            .build();
+    assertThat(deserialize(serialize(autoValue))).isEqualTo(autoValue);
+  }
+}
diff --git a/javatests/com/google/gerrit/server/cache/serialize/entities/SubscribeSectionSerializerTest.java b/javatests/com/google/gerrit/server/cache/serialize/entities/SubscribeSectionSerializerTest.java
new file mode 100644
index 0000000..fc96932
--- /dev/null
+++ b/javatests/com/google/gerrit/server/cache/serialize/entities/SubscribeSectionSerializerTest.java
@@ -0,0 +1,37 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.cache.serialize.entities;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.server.cache.serialize.entities.SubscribeSectionSerializer.deserialize;
+import static com.google.gerrit.server.cache.serialize.entities.SubscribeSectionSerializer.serialize;
+
+import com.google.gerrit.common.data.SubscribeSection;
+import com.google.gerrit.entities.Project;
+import org.junit.Test;
+
+public class SubscribeSectionSerializerTest {
+  @Test
+  public void roundTrip() {
+    SubscribeSection autoValue =
+        SubscribeSection.builder(Project.nameKey("project"))
+            .addMultiMatchRefSpec("multi")
+            .addMultiMatchRefSpec("multi2")
+            .addMatchingRefSpec("matching1")
+            .addMatchingRefSpec("matching2")
+            .build();
+    assertThat(deserialize(serialize(autoValue))).isEqualTo(autoValue);
+  }
+}
diff --git a/javatests/com/google/gerrit/server/group/db/AbstractGroupTest.java b/javatests/com/google/gerrit/server/group/db/AbstractGroupTest.java
index c749b77..20fe387 100644
--- a/javatests/com/google/gerrit/server/group/db/AbstractGroupTest.java
+++ b/javatests/com/google/gerrit/server/group/db/AbstractGroupTest.java
@@ -17,9 +17,9 @@
 import static com.google.gerrit.extensions.common.testing.CommitInfoSubject.assertThat;
 
 import com.google.gerrit.common.Nullable;
-import com.google.gerrit.common.data.GroupDescription;
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupDescription;
 import com.google.gerrit.entities.RefNames;
 import com.google.gerrit.extensions.common.CommitInfo;
 import com.google.gerrit.server.config.AllUsersName;
diff --git a/javatests/com/google/gerrit/server/group/db/GroupConfigTest.java b/javatests/com/google/gerrit/server/group/db/GroupConfigTest.java
index b7fe23d..c1f3615 100644
--- a/javatests/com/google/gerrit/server/group/db/GroupConfigTest.java
+++ b/javatests/com/google/gerrit/server/group/db/GroupConfigTest.java
@@ -25,9 +25,9 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
 import com.google.gerrit.common.Nullable;
-import com.google.gerrit.common.data.GroupDescription;
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupDescription;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.entities.RefNames;
 import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
diff --git a/javatests/com/google/gerrit/server/group/db/GroupNameNotesTest.java b/javatests/com/google/gerrit/server/group/db/GroupNameNotesTest.java
index 278f617..3b7beb9 100644
--- a/javatests/com/google/gerrit/server/group/db/GroupNameNotesTest.java
+++ b/javatests/com/google/gerrit/server/group/db/GroupNameNotesTest.java
@@ -25,9 +25,9 @@
 import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
 
 import com.google.common.collect.ImmutableList;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.common.data.testing.GroupReferenceSubject;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.entities.RefNames;
 import com.google.gerrit.exceptions.DuplicateKeyException;
diff --git a/javatests/com/google/gerrit/server/mail/AutoReplyMailFilterTest.java b/javatests/com/google/gerrit/server/mail/AutoReplyMailFilterTest.java
index 805c542..d8e29f9 100644
--- a/javatests/com/google/gerrit/server/mail/AutoReplyMailFilterTest.java
+++ b/javatests/com/google/gerrit/server/mail/AutoReplyMailFilterTest.java
@@ -16,7 +16,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import com.google.gerrit.mail.Address;
+import com.google.gerrit.entities.Address;
 import com.google.gerrit.mail.MailMessage;
 import java.time.Instant;
 import org.junit.Test;
diff --git a/javatests/com/google/gerrit/server/mail/send/FromAddressGeneratorProviderTest.java b/javatests/com/google/gerrit/server/mail/send/FromAddressGeneratorProviderTest.java
index 03129ae..f10a281 100644
--- a/javatests/com/google/gerrit/server/mail/send/FromAddressGeneratorProviderTest.java
+++ b/javatests/com/google/gerrit/server/mail/send/FromAddressGeneratorProviderTest.java
@@ -22,7 +22,7 @@
 import static org.mockito.Mockito.when;
 
 import com.google.gerrit.entities.Account;
-import com.google.gerrit.mail.Address;
+import com.google.gerrit.entities.Address;
 import com.google.gerrit.server.account.AccountCache;
 import com.google.gerrit.server.account.AccountState;
 import com.google.gerrit.server.util.time.TimeUtil;
diff --git a/javatests/com/google/gerrit/server/notedb/ChangeNotesStateTest.java b/javatests/com/google/gerrit/server/notedb/ChangeNotesStateTest.java
index a5e7dce..f1b7198 100644
--- a/javatests/com/google/gerrit/server/notedb/ChangeNotesStateTest.java
+++ b/javatests/com/google/gerrit/server/notedb/ChangeNotesStateTest.java
@@ -28,6 +28,7 @@
 import com.google.gerrit.common.data.SubmitRecord;
 import com.google.gerrit.common.data.SubmitRequirement;
 import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Address;
 import com.google.gerrit.entities.AttentionSetUpdate;
 import com.google.gerrit.entities.Change;
 import com.google.gerrit.entities.ChangeMessage;
@@ -39,7 +40,6 @@
 import com.google.gerrit.entities.converter.ChangeMessageProtoConverter;
 import com.google.gerrit.entities.converter.PatchSetApprovalProtoConverter;
 import com.google.gerrit.entities.converter.PatchSetProtoConverter;
-import com.google.gerrit.mail.Address;
 import com.google.gerrit.server.AssigneeStatusUpdate;
 import com.google.gerrit.server.ReviewerByEmailSet;
 import com.google.gerrit.server.ReviewerSet;
diff --git a/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java b/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java
index df5903f..b0b1c1e 100644
--- a/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java
+++ b/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java
@@ -38,6 +38,7 @@
 import com.google.common.collect.Lists;
 import com.google.gerrit.common.data.SubmitRecord;
 import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Address;
 import com.google.gerrit.entities.AttentionSetUpdate;
 import com.google.gerrit.entities.AttentionSetUpdate.Operation;
 import com.google.gerrit.entities.BranchNameKey;
@@ -49,7 +50,6 @@
 import com.google.gerrit.entities.PatchSetApproval;
 import com.google.gerrit.entities.SubmissionId;
 import com.google.gerrit.exceptions.StorageException;
-import com.google.gerrit.mail.Address;
 import com.google.gerrit.server.AssigneeStatusUpdate;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.IdentifiedUser;
diff --git a/javatests/com/google/gerrit/server/notedb/CommitMessageOutputTest.java b/javatests/com/google/gerrit/server/notedb/CommitMessageOutputTest.java
index 8818d81..6cfd9f2d 100644
--- a/javatests/com/google/gerrit/server/notedb/CommitMessageOutputTest.java
+++ b/javatests/com/google/gerrit/server/notedb/CommitMessageOutputTest.java
@@ -20,9 +20,9 @@
 
 import com.google.common.collect.ImmutableList;
 import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Address;
 import com.google.gerrit.entities.Change;
 import com.google.gerrit.entities.SubmissionId;
-import com.google.gerrit.mail.Address;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.util.time.TimeUtil;
 import com.google.gerrit.testing.TestChanges;
diff --git a/javatests/com/google/gerrit/server/project/CommitsCollectionTest.java b/javatests/com/google/gerrit/server/project/CommitsCollectionTest.java
index 5e57551..5af8a1e 100644
--- a/javatests/com/google/gerrit/server/project/CommitsCollectionTest.java
+++ b/javatests/com/google/gerrit/server/project/CommitsCollectionTest.java
@@ -28,11 +28,11 @@
 import com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate;
 import com.google.gerrit.common.data.AccessSection;
 import com.google.gerrit.common.data.GlobalCapability;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.common.data.Permission;
 import com.google.gerrit.common.data.PermissionRule;
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.server.account.AccountManager;
 import com.google.gerrit.server.account.AuthRequest;
diff --git a/javatests/com/google/gerrit/server/project/GroupListTest.java b/javatests/com/google/gerrit/server/project/GroupListTest.java
index 18e1631..f3295f8 100644
--- a/javatests/com/google/gerrit/server/project/GroupListTest.java
+++ b/javatests/com/google/gerrit/server/project/GroupListTest.java
@@ -23,8 +23,8 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.server.git.ValidationError;
 import java.io.IOException;
diff --git a/javatests/com/google/gerrit/server/project/ProjectConfigTest.java b/javatests/com/google/gerrit/server/project/ProjectConfigTest.java
index 2d31acf..6ea6a33 100644
--- a/javatests/com/google/gerrit/server/project/ProjectConfigTest.java
+++ b/javatests/com/google/gerrit/server/project/ProjectConfigTest.java
@@ -22,19 +22,20 @@
 import com.google.common.collect.Iterables;
 import com.google.gerrit.common.data.AccessSection;
 import com.google.gerrit.common.data.ContributorAgreement;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.common.data.LabelType;
 import com.google.gerrit.common.data.Permission;
 import com.google.gerrit.common.data.PermissionRule;
 import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.BranchOrderSection;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.entities.RefNames;
+import com.google.gerrit.entities.StoredCommentLinkInfo;
 import com.google.gerrit.extensions.client.InheritableBoolean;
 import com.google.gerrit.server.config.AllProjectsName;
 import com.google.gerrit.server.config.PluginConfig;
 import com.google.gerrit.server.config.SitePaths;
 import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
-import com.google.gerrit.server.git.BranchOrderSection;
 import com.google.gerrit.server.git.ValidationError;
 import com.google.gerrit.server.git.meta.MetaDataUpdate;
 import com.google.gerrit.server.project.testing.TestLabels;
@@ -597,6 +598,27 @@
   }
 
   @Test
+  public void readCommentLinksNoHtmlOrLinkAndMissingEnabled() throws Exception {
+    RevCommit rev =
+        tr.commit()
+            .add(
+                "project.config",
+                "[commentlink \"bugzilla\"]\n \tlink = http://bugs.example.com/show_bug.cgi?id=$2"
+                    + "\n \tmatch = \"(bug\\\\s+#?)(\\\\d+)\"\n")
+            .create();
+    ProjectConfig cfg = read(rev);
+    assertThat(cfg.getCommentLinkSections())
+        .containsExactly(
+            StoredCommentLinkInfo.builder("bugzilla")
+                .setMatch("(bug\\s+#?)(\\d+)")
+                .setLink("http://bugs.example.com/show_bug.cgi?id=$2")
+                .build());
+    StoredCommentLinkInfo stored = Iterables.getOnlyElement(cfg.getCommentLinkSections());
+    assertThat(StoredCommentLinkInfo.fromInfo(stored.toInfo(), stored.getEnabled()))
+        .isEqualTo(stored);
+  }
+
+  @Test
   public void readCommentLinkInvalidPattern() throws Exception {
     RevCommit rev =
         tr.commit()
diff --git a/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java b/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
index 2eb25da..a013145 100644
--- a/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
+++ b/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
@@ -44,7 +44,6 @@
 import com.google.gerrit.acceptance.config.GerritConfig;
 import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
 import com.google.gerrit.common.Nullable;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.common.data.LabelType;
 import com.google.gerrit.common.data.Permission;
 import com.google.gerrit.common.data.PermissionRule;
@@ -52,6 +51,7 @@
 import com.google.gerrit.entities.AccountGroup;
 import com.google.gerrit.entities.BranchNameKey;
 import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.entities.Patch;
 import com.google.gerrit.entities.PatchSet;
 import com.google.gerrit.entities.Project;
diff --git a/javatests/com/google/gerrit/server/rules/IgnoreSelfApprovalRuleTest.java b/javatests/com/google/gerrit/server/rules/IgnoreSelfApprovalRuleTest.java
index 85a3207..858f6a2 100644
--- a/javatests/com/google/gerrit/server/rules/IgnoreSelfApprovalRuleTest.java
+++ b/javatests/com/google/gerrit/server/rules/IgnoreSelfApprovalRuleTest.java
@@ -18,10 +18,10 @@
 
 import com.google.common.collect.ImmutableList;
 import com.google.gerrit.common.data.LabelType;
-import com.google.gerrit.common.data.LabelValue;
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.entities.Change;
 import com.google.gerrit.entities.LabelId;
+import com.google.gerrit.entities.LabelValue;
 import com.google.gerrit.entities.PatchSet;
 import com.google.gerrit.entities.PatchSetApproval;
 import java.time.Instant;
diff --git a/javatests/com/google/gerrit/server/schema/AllProjectsCreatorTest.java b/javatests/com/google/gerrit/server/schema/AllProjectsCreatorTest.java
index 9cf4896..18d279f 100644
--- a/javatests/com/google/gerrit/server/schema/AllProjectsCreatorTest.java
+++ b/javatests/com/google/gerrit/server/schema/AllProjectsCreatorTest.java
@@ -23,11 +23,11 @@
 import static com.google.gerrit.truth.ConfigSubject.assertThat;
 
 import com.google.common.collect.ImmutableList;
-import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.common.data.LabelType;
-import com.google.gerrit.common.data.LabelValue;
 import com.google.gerrit.entities.AccountGroup;
 import com.google.gerrit.entities.BooleanProjectConfig;
+import com.google.gerrit.entities.GroupReference;
+import com.google.gerrit.entities.LabelValue;
 import com.google.gerrit.extensions.client.InheritableBoolean;
 import com.google.gerrit.server.GerritPersonIdent;
 import com.google.gerrit.server.account.GroupUuid;
diff --git a/javatests/com/google/gerrit/server/schema/SchemaCreatorImplTest.java b/javatests/com/google/gerrit/server/schema/SchemaCreatorImplTest.java
index c92a8e0..d58713a 100644
--- a/javatests/com/google/gerrit/server/schema/SchemaCreatorImplTest.java
+++ b/javatests/com/google/gerrit/server/schema/SchemaCreatorImplTest.java
@@ -21,7 +21,7 @@
 import com.google.gerrit.common.data.LabelFunction;
 import com.google.gerrit.common.data.LabelType;
 import com.google.gerrit.common.data.LabelTypes;
-import com.google.gerrit.common.data.LabelValue;
+import com.google.gerrit.entities.LabelValue;
 import com.google.gerrit.server.config.AllProjectsName;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.project.ProjectConfig;
diff --git a/package.json b/package.json
index 329e3cb..a6641b9 100644
--- a/package.json
+++ b/package.json
@@ -4,16 +4,15 @@
   "description": "Gerrit Code Review",
   "dependencies": {},
   "devDependencies": {
-    "@bazel/rollup": "^1.6.1",
-    "@bazel/terser": "^1.7.0",
-    "@bazel/typescript": "^1.6.1",
+    "@bazel/rollup": "^2.0.0",
+    "@bazel/terser": "^2.0.0",
+    "@bazel/typescript": "^2.0.0",
     "eslint": "^6.6.0",
     "eslint-config-google": "^0.13.0",
     "eslint-plugin-html": "^6.0.0",
     "eslint-plugin-import": "^2.20.1",
     "eslint-plugin-jsdoc": "^19.2.0",
     "eslint-plugin-prettier": "^3.1.3",
-    "fried-twinkie": "^0.2.2",
     "gts": "^2.0.2",
     "polymer-cli": "^1.9.11",
     "prettier": "2.0.5",
@@ -29,7 +28,6 @@
     "safe_bazelisk": "if which bazelisk >/dev/null; then bazel_bin=bazelisk; else bazel_bin=bazel; fi && $bazel_bin",
     "eslint": "npm run safe_bazelisk test polygerrit-ui/app:lint_test",
     "eslintfix": "npm run safe_bazelisk run polygerrit-ui/app:lint_bin -- -- --fix $(pwd)/polygerrit-ui/app",
-    "test-template": "./polygerrit-ui/app/run_template_test.sh",
     "polylint": "npm run safe_bazelisk test polygerrit-ui/app:polylint_test",
     "test:debug": "npm run safe_bazelisk run //polygerrit-ui:karma_bin -- -- start $(pwd)/polygerrit-ui/karma.conf.js --browsers ChromeDev --no-single-run --testFiles",
     "test:single": "npm run safe_bazelisk run //polygerrit-ui:karma_bin -- -- start $(pwd)/polygerrit-ui/karma.conf.js --testFiles"
diff --git a/plugins/delete-project b/plugins/delete-project
index 7cb59ec..64db8df 160000
--- a/plugins/delete-project
+++ b/plugins/delete-project
@@ -1 +1 @@
-Subproject commit 7cb59ecacbbe7bc995873ae112e48cf0ff521d2a
+Subproject commit 64db8df08e855d8367c146bc5071f68f70df5171
diff --git a/plugins/download-commands b/plugins/download-commands
index ea7b331..fd650ca 160000
--- a/plugins/download-commands
+++ b/plugins/download-commands
@@ -1 +1 @@
-Subproject commit ea7b33154997f4eae6481fbb3d5f3e47a63187a4
+Subproject commit fd650ca386c382b42d30e7ad72279bfeb311aee4
diff --git a/plugins/replication b/plugins/replication
index ced7fc3..9d4d19a 160000
--- a/plugins/replication
+++ b/plugins/replication
@@ -1 +1 @@
-Subproject commit ced7fc318feb76e2fc6d549669c5f5d8d905add5
+Subproject commit 9d4d19a579fc4962ab7f85f6b5cb12501ed048ad
diff --git a/plugins/reviewnotes b/plugins/reviewnotes
index e952b92..05f0ddd 160000
--- a/plugins/reviewnotes
+++ b/plugins/reviewnotes
@@ -1 +1 @@
-Subproject commit e952b920ecbee5225f1098a02d4a39b19aa7e234
+Subproject commit 05f0ddd30928d0d050696f3d269dea0899334513
diff --git a/plugins/singleusergroup b/plugins/singleusergroup
index 9eb6334..58ee52a 160000
--- a/plugins/singleusergroup
+++ b/plugins/singleusergroup
@@ -1 +1 @@
-Subproject commit 9eb63345a129533aa88235af3ba9308c53cee1d2
+Subproject commit 58ee52a8670e38f30785bfbb648ba27c61c3a202
diff --git a/polygerrit-ui/README.md b/polygerrit-ui/README.md
index 3a66e97..3e95e42 100644
--- a/polygerrit-ui/README.md
+++ b/polygerrit-ui/README.md
@@ -279,47 +279,6 @@
 npm run polylint
 ```
 
-## Template Type Safety
-
-> **Warning**: This feature is temporary disabled, because it doesn't work with Polymer 2 and Polymer 3. Some of the checks are made by polymer linter.
-
-Polymer elements are not type checked against the element definition, making it
-trivial to break the display when refactoring or moving code. We now run
-additional tests to help ensure that template types are checked.
-
-A few notes to ensure that these tests pass
-- Any functions with optional parameters will need closure annotations.
-- Any Polymer parameters that are nullable or can be multiple types (other than
-  the one explicitly delared) will need type annotations.
-
-These tests require the `typescript` and `fried-twinkie` npm packages.
-
-To run on all files, execute the following command:
-
-```sh
-./polygerrit-ui/app/run_template_test.sh
-```
-
-or
-
-```sh
-npm run test-template
-```
-
-To run on a specific top level directory (ex: change-list)
-```sh
-TEMPLATE_NO_DEFAULT=true ./polygerrit-ui/app/run_template_test.sh //polygerrit-ui/app:template_test_change-list
-```
-
-To run on a specific file (ex: gr-change-list-view), execute the following command:
-```sh
-TEMPLATE_NO_DEFAULT=true ./polygerrit-ui/app/run_template_test.sh //polygerrit-ui/app:template_test_<TOP_LEVEL_DIRECTORY> --test_arg=<VIEW_NAME>
-```
-
-```sh
-TEMPLATE_NO_DEFAULT=true ./polygerrit-ui/app/run_template_test.sh //polygerrit-ui/app:template_test_change-list --test_arg=gr-change-list-view
-```
-
 ## Contributing
 
 Our users report bugs / feature requests related to the UI through [Monorail Issues - PolyGerrit](https://bugs.chromium.org/p/gerrit/issues/list?q=component%3APolyGerrit).
diff --git a/polygerrit-ui/app/.eslintrc.js b/polygerrit-ui/app/.eslintrc.js
index 6669762..c402297 100644
--- a/polygerrit-ui/app/.eslintrc.js
+++ b/polygerrit-ui/app/.eslintrc.js
@@ -70,10 +70,11 @@
       "capIsNewExceptions": ["Polymer", "GestureEventListeners"],
       "capIsNewExceptionPattern": "^.*Mixin$"
     }],
-    "no-console": "off",
+    "no-console": ["error", { allow: ["warn", "error", "info", "assert", "group", "groupEnd"] }],
     "no-multiple-empty-lines": ["error", {"max": 1}],
     "no-prototype-builtins": "off",
     "no-redeclare": "off",
+    'array-callback-return': ['error', { allowImplicit: true }],
     "no-restricted-syntax": [
       "error",
       {
@@ -109,6 +110,7 @@
     "prefer-const": "error",
     "prefer-promise-reject-errors": "error",
     "prefer-spread": "error",
+    "prefer-object-spread": "error",
     "quote-props": ["error", "consistent-as-needed"],
     "semi": ["error", "always"],
     "template-curly-spacing": "error",
@@ -216,7 +218,7 @@
       }
     },
     {
-      "files": ["*.html", "test.js", "test-infra.js", "template_test.js"],
+      "files": ["*.html", "test.js", "test-infra.js"],
       "rules": {
         "jsdoc/require-file-overview": "off"
       },
@@ -270,7 +272,7 @@
       }
     },
     {
-      "files": ["test/functional/**/*.js", "template_test.js"],
+      "files": ["test/functional/**/*.js"],
       // Settings for functional tests. These scripts are node scripts.
       // Turn off "no-undef" to allow any global variable
       "env": {
diff --git a/polygerrit-ui/app/BUILD b/polygerrit-ui/app/BUILD
index ada507c..41c3f17 100644
--- a/polygerrit-ui/app/BUILD
+++ b/polygerrit-ui/app/BUILD
@@ -163,40 +163,3 @@
         "manual",
     ],
 )
-
-DIRECTORIES = [
-    "admin",
-    "change",
-    "change-list",
-    "core",
-    "diff",
-    "edit",
-    "plugins",
-    "settings",
-    "shared",
-    "gr-app",
-]
-
-[sh_test(
-    name = "template_test_" + directory,
-    size = "enormous",
-    srcs = ["template_test.sh"],
-    args = [directory],
-    data = [
-        ":pg_code",
-        ":template_test_srcs",
-    ],
-    tags = [
-        # Should not run sandboxed.
-        "local",
-        "template",
-    ],
-) for directory in DIRECTORIES]
-
-filegroup(
-    name = "template_test_srcs",
-    srcs = [
-        "template_test_srcs/convert_for_template_tests.py",
-        "template_test_srcs/template_test.js",
-    ],
-)
diff --git a/polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section_html.js b/polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section_html.ts
similarity index 98%
rename from polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section_html.js
rename to polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section_html.ts
index 7bbd599..7c9f28b 100644
--- a/polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section_html.js
+++ b/polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list_html.js b/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list_html.ts
similarity index 97%
rename from polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list_html.js
rename to polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list_html.ts
index 4548a45..e22dc66 100644
--- a/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list_html.js
+++ b/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view_html.js b/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view_html.ts
similarity index 98%
rename from polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view_html.js
rename to polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view_html.ts
index b62a41b..5e85a93 100644
--- a/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view_html.js
+++ b/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog_html.js b/polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog_html.ts
similarity index 94%
rename from polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog_html.js
rename to polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog_html.ts
index 3810d32..ce9ac9c 100644
--- a/polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog_html.js
+++ b/polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog_html.js b/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog_html.ts
similarity index 97%
rename from polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog_html.js
rename to polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog_html.ts
index f18da81..77e2c3b 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog_html.js
+++ b/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/admin/gr-create-group-dialog/gr-create-group-dialog_html.js b/polygerrit-ui/app/elements/admin/gr-create-group-dialog/gr-create-group-dialog_html.ts
similarity index 94%
rename from polygerrit-ui/app/elements/admin/gr-create-group-dialog/gr-create-group-dialog_html.js
rename to polygerrit-ui/app/elements/admin/gr-create-group-dialog/gr-create-group-dialog_html.ts
index bc1f24c..d4ecc5d 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-group-dialog/gr-create-group-dialog_html.js
+++ b/polygerrit-ui/app/elements/admin/gr-create-group-dialog/gr-create-group-dialog_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog_html.js b/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog_html.ts
similarity index 97%
rename from polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog_html.js
rename to polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog_html.ts
index 62a2e0f..0b3d81ae 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog_html.js
+++ b/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog_html.js b/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog_html.ts
similarity index 97%
rename from polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog_html.js
rename to polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog_html.ts
index 680986c..02aabfe 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog_html.js
+++ b/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log_html.js b/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log_html.ts
similarity index 97%
rename from polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log_html.js
rename to polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log_html.ts
index 130efbb..1212685 100644
--- a/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log_html.js
+++ b/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members_html.js b/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members_html.ts
similarity index 98%
rename from polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members_html.js
rename to polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members_html.ts
index 47657ac..2d3f8fc 100644
--- a/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members_html.js
+++ b/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="gr-form-styles">
diff --git a/polygerrit-ui/app/elements/admin/gr-group/gr-group_html.js b/polygerrit-ui/app/elements/admin/gr-group/gr-group_html.ts
similarity index 98%
rename from polygerrit-ui/app/elements/admin/gr-group/gr-group_html.js
rename to polygerrit-ui/app/elements/admin/gr-group/gr-group_html.ts
index 7a843dc..aed73bf 100644
--- a/polygerrit-ui/app/elements/admin/gr-group/gr-group_html.js
+++ b/polygerrit-ui/app/elements/admin/gr-group/gr-group_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/admin/gr-group/gr-group_test.js b/polygerrit-ui/app/elements/admin/gr-group/gr-group_test.js
index c6054b2..479f2b1 100644
--- a/polygerrit-ui/app/elements/admin/gr-group/gr-group_test.js
+++ b/polygerrit-ui/app/elements/admin/gr-group/gr-group_test.js
@@ -68,7 +68,7 @@
   });
 
   test('default values with external group', done => {
-    const groupExternal = Object.assign({}, group);
+    const groupExternal = {...group};
     groupExternal.id = 'external-group-id';
     groupStub.restore();
     groupStub = sinon.stub(
diff --git a/polygerrit-ui/app/elements/admin/gr-permission/gr-permission_html.js b/polygerrit-ui/app/elements/admin/gr-permission/gr-permission_html.ts
similarity index 98%
rename from polygerrit-ui/app/elements/admin/gr-permission/gr-permission_html.js
rename to polygerrit-ui/app/elements/admin/gr-permission/gr-permission_html.ts
index ed4f64a..9795c92 100644
--- a/polygerrit-ui/app/elements/admin/gr-permission/gr-permission_html.js
+++ b/polygerrit-ui/app/elements/admin/gr-permission/gr-permission_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/admin/gr-plugin-config-array-editor/gr-plugin-config-array-editor_html.js b/polygerrit-ui/app/elements/admin/gr-plugin-config-array-editor/gr-plugin-config-array-editor_html.ts
similarity index 97%
rename from polygerrit-ui/app/elements/admin/gr-plugin-config-array-editor/gr-plugin-config-array-editor_html.js
rename to polygerrit-ui/app/elements/admin/gr-plugin-config-array-editor/gr-plugin-config-array-editor_html.ts
index be35035..c96b86c 100644
--- a/polygerrit-ui/app/elements/admin/gr-plugin-config-array-editor/gr-plugin-config-array-editor_html.js
+++ b/polygerrit-ui/app/elements/admin/gr-plugin-config-array-editor/gr-plugin-config-array-editor_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list_html.js b/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list_html.ts
similarity index 97%
rename from polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list_html.js
rename to polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list_html.ts
index bd2bea3..d5318b5 100644
--- a/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list_html.js
+++ b/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.js b/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.js
index 8bdc737..8f48382 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.js
@@ -182,10 +182,10 @@
           // Keep a copy of the original inherit from values separate from
           // the ones data bound to gr-autocomplete, so the original value
           // can be restored if the user cancels.
-          this._inheritsFrom = res.inherits_from ? Object.assign({},
-              res.inherits_from) : null;
-          this._originalInheritsFrom = res.inherits_from ? Object.assign({},
-              res.inherits_from) : null;
+          this._inheritsFrom = res.inherits_from ? ({
+            ...res.inherits_from}) : null;
+          this._originalInheritsFrom = res.inherits_from ? ({
+            ...res.inherits_from}) : null;
           // Initialize the filter value so when the user clicks edit, the
           // current value appears. If there is no parent repo, it is
           // initialized as an empty string.
@@ -282,7 +282,7 @@
     }
     // Restore inheritFrom.
     if (this._inheritsFrom) {
-      this._inheritsFrom = Object.assign({}, this._originalInheritsFrom);
+      this._inheritsFrom = {...this._originalInheritsFrom};
       this._inheritFromFilter = this._inheritsFrom.name;
     }
     for (const key of Object.keys(this._local)) {
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access_html.js b/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access_html.ts
similarity index 98%
rename from polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access_html.js
rename to polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access_html.ts
index 8148884..4e76360 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access_html.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-commands/gr-repo-commands_html.js b/polygerrit-ui/app/elements/admin/gr-repo-commands/gr-repo-commands_html.ts
similarity index 97%
rename from polygerrit-ui/app/elements/admin/gr-repo-commands/gr-repo-commands_html.js
rename to polygerrit-ui/app/elements/admin/gr-repo-commands/gr-repo-commands_html.ts
index 3ae5b29..3880e4a 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-commands/gr-repo-commands_html.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo-commands/gr-repo-commands_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards_html.js b/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards_html.ts
similarity index 97%
rename from polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards_html.js
rename to polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards_html.ts
index 8ce69df..7cdd10e 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards_html.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list_html.js b/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list_html.ts
similarity index 98%
rename from polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list_html.js
rename to polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list_html.ts
index 21971e7..8955092 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list_html.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="gr-form-styles">
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list_html.js b/polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list_html.ts
similarity index 97%
rename from polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list_html.js
rename to polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list_html.ts
index 3681399..f61adce 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list_html.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config_html.js b/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config_html.ts
similarity index 98%
rename from polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config_html.js
rename to polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config_html.ts
index ee633463..3045108 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config_html.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/admin/gr-repo/gr-repo_html.js b/polygerrit-ui/app/elements/admin/gr-repo/gr-repo_html.ts
similarity index 99%
rename from polygerrit-ui/app/elements/admin/gr-repo/gr-repo_html.js
rename to polygerrit-ui/app/elements/admin/gr-repo/gr-repo_html.ts
index ea8a8b9..26d05c3 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo/gr-repo_html.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo/gr-repo_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor.js b/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor.js
index a1f10de..a23614d 100644
--- a/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor.js
+++ b/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor.js
@@ -259,7 +259,7 @@
     // gr-permission will take care of removing rules that were added but
     // unsaved. We need to keep the added bit for the filter.
     if (this.rule.value.added) { return; }
-    this.set('rule.value', Object.assign({}, this._originalRuleValues));
+    this.set('rule.value', {...this._originalRuleValues});
     this._deleted = false;
     delete this.rule.value.deleted;
     delete this.rule.value.modified;
@@ -274,7 +274,7 @@
   }
 
   _setOriginalRuleValues(value) {
-    this._originalRuleValues = Object.assign({}, value);
+    this._originalRuleValues = {...value};
   }
 }
 
diff --git a/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor_html.js b/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor_html.ts
similarity index 98%
rename from polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor_html.js
rename to polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor_html.ts
index 3e4f9d4..98403e0 100644
--- a/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor_html.js
+++ b/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item_html.js b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item_html.ts
similarity index 98%
rename from polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item_html.js
rename to polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item_html.ts
index 0fd3030..7fa59d4 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item_html.js
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view_html.js b/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view_html.ts
similarity index 97%
rename from polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view_html.js
rename to polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view_html.ts
index b322f38..0e8f843 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view_html.js
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list_html.js b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list_html.ts
similarity index 98%
rename from polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list_html.js
rename to polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list_html.ts
index 623838b..404456c 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list_html.js
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/change-list/gr-create-change-help/gr-create-change-help_html.js b/polygerrit-ui/app/elements/change-list/gr-create-change-help/gr-create-change-help_html.ts
similarity index 96%
rename from polygerrit-ui/app/elements/change-list/gr-create-change-help/gr-create-change-help_html.js
rename to polygerrit-ui/app/elements/change-list/gr-create-change-help/gr-create-change-help_html.ts
index cc6223d..c2f97a6 100644
--- a/polygerrit-ui/app/elements/change-list/gr-create-change-help/gr-create-change-help_html.js
+++ b/polygerrit-ui/app/elements/change-list/gr-create-change-help/gr-create-change-help_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/change-list/gr-create-commands-dialog/gr-create-commands-dialog_html.js b/polygerrit-ui/app/elements/change-list/gr-create-commands-dialog/gr-create-commands-dialog_html.ts
similarity index 97%
rename from polygerrit-ui/app/elements/change-list/gr-create-commands-dialog/gr-create-commands-dialog_html.js
rename to polygerrit-ui/app/elements/change-list/gr-create-commands-dialog/gr-create-commands-dialog_html.ts
index d2a1af9..9031738 100644
--- a/polygerrit-ui/app/elements/change-list/gr-create-commands-dialog/gr-create-commands-dialog_html.js
+++ b/polygerrit-ui/app/elements/change-list/gr-create-commands-dialog/gr-create-commands-dialog_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/change-list/gr-create-destination-dialog/gr-create-destination-dialog_html.js b/polygerrit-ui/app/elements/change-list/gr-create-destination-dialog/gr-create-destination-dialog_html.ts
similarity index 95%
rename from polygerrit-ui/app/elements/change-list/gr-create-destination-dialog/gr-create-destination-dialog_html.js
rename to polygerrit-ui/app/elements/change-list/gr-create-destination-dialog/gr-create-destination-dialog_html.ts
index c7cd647..9155d9a 100644
--- a/polygerrit-ui/app/elements/change-list/gr-create-destination-dialog/gr-create-destination-dialog_html.js
+++ b/polygerrit-ui/app/elements/change-list/gr-create-destination-dialog/gr-create-destination-dialog_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles"></style>
diff --git a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.js b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.js
index ac53b74..3b44381 100644
--- a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.js
+++ b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.js
@@ -107,6 +107,10 @@
   attached() {
     super.attached();
     this._loadPreferences();
+    this.addEventListener('reload', e => {
+      e.stopPropagation();
+      this._reload();
+    });
   }
 
   _loadPreferences() {
diff --git a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_html.js b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_html.ts
similarity index 97%
rename from polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_html.js
rename to polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_html.ts
index cf1036f..ea04c5a 100644
--- a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_html.js
+++ b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/change-list/gr-repo-header/gr-repo-header_html.js b/polygerrit-ui/app/elements/change-list/gr-repo-header/gr-repo-header_html.ts
similarity index 94%
rename from polygerrit-ui/app/elements/change-list/gr-repo-header/gr-repo-header_html.js
rename to polygerrit-ui/app/elements/change-list/gr-repo-header/gr-repo-header_html.ts
index 75af51e..9fd27c6 100644
--- a/polygerrit-ui/app/elements/change-list/gr-repo-header/gr-repo-header_html.js
+++ b/polygerrit-ui/app/elements/change-list/gr-repo-header/gr-repo-header_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/change-list/gr-user-header/gr-user-header_html.js b/polygerrit-ui/app/elements/change-list/gr-user-header/gr-user-header_html.ts
similarity index 96%
rename from polygerrit-ui/app/elements/change-list/gr-user-header/gr-user-header_html.js
rename to polygerrit-ui/app/elements/change-list/gr-user-header/gr-user-header_html.ts
index 8207284..72bdca6 100644
--- a/polygerrit-ui/app/elements/change-list/gr-user-header/gr-user-header_html.js
+++ b/polygerrit-ui/app/elements/change-list/gr-user-header/gr-user-header_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js
index f816ee9..259fea8 100644
--- a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js
+++ b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js
@@ -858,7 +858,7 @@
     if (!approval) {
       return null;
     }
-    const action = Object.assign({}, QUICK_APPROVE_ACTION);
+    const action = {...QUICK_APPROVE_ACTION};
     action.label = approval.label + approval.score;
     const review = {
       drafts: 'PUBLISH_ALL_REVISIONS',
@@ -899,7 +899,7 @@
       actions[a].label = this._getActionLabel(actions[a]);
 
       // Triggers a re-render by ensuring object inequality.
-      result.push(Object.assign({}, actions[a]));
+      result.push({...actions[a]});
     });
 
     let additionalActions = (additionalActionsChangeRecord &&
@@ -909,7 +909,7 @@
         .map(a => {
           a.__primary = primaryActionKeys.includes(a.__key);
           // Triggers a re-render by ensuring object inequality.
-          return Object.assign({}, a);
+          return {...a};
         });
     return result.concat(additionalActions).concat(pluginActions);
   }
diff --git a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_html.js b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_html.ts
similarity index 98%
rename from polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_html.js
rename to polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_html.ts
index 200ffc9..4e315af 100644
--- a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_html.js
+++ b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.js b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.js
index a2aa8d7..0655931 100644
--- a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.js
+++ b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.js
@@ -175,9 +175,10 @@
     });
 
     test('getActionDetails', () => {
-      element.revisionActions = Object.assign({
+      element.revisionActions = {
         'plugin~action': {},
-      }, element.revisionActions);
+        ...element.revisionActions,
+      };
       assert.isUndefined(element.getActionDetails('rubbish'));
       assert.strictEqual(element.revisionActions['plugin~action'],
           element.getActionDetails('plugin~action'));
diff --git a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.js b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.js
index bee7721..4f86390 100644
--- a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.js
+++ b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.js
@@ -173,7 +173,7 @@
   }
 
   _labelsChanged(labels) {
-    this.labels = Object.assign({}, labels) || null;
+    this.labels = ({...labels}) || null;
   }
 
   _changeChanged(change) {
diff --git a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_html.js b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_html.ts
similarity index 98%
rename from polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_html.js
rename to polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_html.ts
index 7d84835..e96912d 100644
--- a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_html.js
+++ b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="gr-change-metadata-shared-styles">
diff --git a/polygerrit-ui/app/elements/change/gr-change-requirements/gr-change-requirements_html.js b/polygerrit-ui/app/elements/change/gr-change-requirements/gr-change-requirements_html.ts
similarity index 98%
rename from polygerrit-ui/app/elements/change/gr-change-requirements/gr-change-requirements_html.js
rename to polygerrit-ui/app/elements/change/gr-change-requirements/gr-change-requirements_html.ts
index 2314f6b..fc2346a 100644
--- a/polygerrit-ui/app/elements/change/gr-change-requirements/gr-change-requirements_html.js
+++ b/polygerrit-ui/app/elements/change/gr-change-requirements/gr-change-requirements_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
index 1cd9f3f..9547ce4 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
@@ -532,6 +532,10 @@
         e => this._setActivePrimaryTab(e));
     this.addEventListener('show-secondary-tab',
         e => this._setActiveSecondaryTab(e));
+    this.addEventListener('reload', e => {
+      e.stopPropagation();
+      this._reload();
+    });
   }
 
   /** @override */
@@ -865,7 +869,7 @@
     // because the paths could contain dots in them. A new object must be
     // created to satisfy Polymer’s dirty checking.
     // https://github.com/Polymer/polymer/issues/3127
-    const diffDrafts = Object.assign({}, this._diffDrafts);
+    const diffDrafts = {...this._diffDrafts};
     if (!diffDrafts[draft.path]) {
       diffDrafts[draft.path] = [draft];
       this._diffDrafts = diffDrafts;
@@ -913,7 +917,7 @@
     // because the paths could contain dots in them. A new object must be
     // created to satisfy Polymer’s dirty checking.
     // https://github.com/Polymer/polymer/issues/3127
-    const diffDrafts = Object.assign({}, this._diffDrafts);
+    const diffDrafts = {...this._diffDrafts};
     diffDrafts[draft.path].splice(index, 1);
     if (diffDrafts[draft.path].length === 0) {
       delete diffDrafts[draft.path];
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_html.js b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_html.ts
similarity index 99%
rename from polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_html.js
rename to polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_html.ts
index eaa13a4..04a810f 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_html.js
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.js b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.js
index ff8cbac..c853449 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.js
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.js
@@ -457,11 +457,9 @@
       const queryMap = new Map();
       queryMap.set('tab', PrimaryTab.FINDINGS);
       // view is required
-      element.params = Object.assign(
-          {
-            view: GerritNav.View.CHANGE,
-          },
-          element.params, {queryMap});
+      element.params = {
+        view: GerritNav.View.CHANGE,
+        ...element.params, queryMap};
       flush(() => {
         assert.equal(element._activeTabs[0], PrimaryTab.FINDINGS);
         done();
@@ -473,11 +471,9 @@
       const queryMap = new Map();
       queryMap.set('tab', 'random');
       // view is required
-      element.params = Object.assign(
-          {
-            view: GerritNav.View.CHANGE,
-          },
-          element.params, {queryMap});
+      element.params = {
+        view: GerritNav.View.CHANGE,
+        ...element.params, queryMap};
       flush(() => {
         assert.equal(element._activeTabs[0], PrimaryTab.FILES);
         done();
diff --git a/polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info_html.js b/polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info_html.ts
similarity index 95%
rename from polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info_html.js
rename to polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info_html.ts
index 608d12b..e350593 100644
--- a/polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info_html.js
+++ b/polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog_html.js b/polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog_html.ts
similarity index 96%
rename from polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog_html.js
rename to polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog_html.ts
index 050df25..7c1b725 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog_html.js
+++ b/polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-conflict-dialog/gr-confirm-cherrypick-conflict-dialog_html.js b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-conflict-dialog/gr-confirm-cherrypick-conflict-dialog_html.ts
similarity index 95%
rename from polygerrit-ui/app/elements/change/gr-confirm-cherrypick-conflict-dialog/gr-confirm-cherrypick-conflict-dialog_html.js
rename to polygerrit-ui/app/elements/change/gr-confirm-cherrypick-conflict-dialog/gr-confirm-cherrypick-conflict-dialog_html.ts
index c7fb70c..5cf56b54 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-conflict-dialog/gr-confirm-cherrypick-conflict-dialog_html.js
+++ b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-conflict-dialog/gr-confirm-cherrypick-conflict-dialog_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog.js b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog.js
index e6d529c..cb15d97 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog.js
+++ b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog.js
@@ -133,7 +133,7 @@
   }
 
   updateStatus(change, status) {
-    this._statuses = Object.assign({}, this._statuses, {[change.id]: status});
+    this._statuses = {...this._statuses, [change.id]: status};
   }
 
   _computeStatus(change, statuses) {
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog_html.js b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog_html.ts
similarity index 98%
rename from polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog_html.js
rename to polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog_html.ts
index eaf9cc8..072f110 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog_html.js
+++ b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog_html.js b/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog_html.ts
similarity index 96%
rename from polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog_html.js
rename to polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog_html.ts
index f5ddf41..b5b46d6 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog_html.js
+++ b/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog_html.js b/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog_html.ts
similarity index 97%
rename from polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog_html.js
rename to polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog_html.ts
index e9a8424..687d31f 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog_html.js
+++ b/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog_html.js b/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog_html.ts
similarity index 97%
rename from polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog_html.js
rename to polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog_html.ts
index 7875fa7..f5561fc 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog_html.js
+++ b/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-revert-submission-dialog/gr-confirm-revert-submission-dialog_html.js b/polygerrit-ui/app/elements/change/gr-confirm-revert-submission-dialog/gr-confirm-revert-submission-dialog_html.ts
similarity index 96%
rename from polygerrit-ui/app/elements/change/gr-confirm-revert-submission-dialog/gr-confirm-revert-submission-dialog_html.js
rename to polygerrit-ui/app/elements/change/gr-confirm-revert-submission-dialog/gr-confirm-revert-submission-dialog_html.ts
index 48051a0..cae4e1f 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-revert-submission-dialog/gr-confirm-revert-submission-dialog_html.js
+++ b/polygerrit-ui/app/elements/change/gr-confirm-revert-submission-dialog/gr-confirm-revert-submission-dialog_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <!-- TODO(taoalpha): move all shared styles to a style module. -->
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog_html.js b/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog_html.ts
similarity index 97%
rename from polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog_html.js
rename to polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog_html.ts
index cf1a332..84668ed 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog_html.js
+++ b/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog_html.js b/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog_html.ts
similarity index 97%
rename from polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog_html.js
rename to polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog_html.ts
index 0446e4e..90185a6 100644
--- a/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog_html.js
+++ b/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header_html.js b/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header_html.ts
similarity index 98%
rename from polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header_html.js
rename to polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header_html.ts
index bb04114..beabeef 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header_html.js
+++ b/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
index 2ee8173..93b2cce 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
@@ -1073,7 +1073,7 @@
     if (loading || !changeComments) { return; }
 
     const commentedPaths = changeComments.getPaths(patchRange);
-    const files = Object.assign({}, filesByPath);
+    const files = {...filesByPath};
     addUnmodifiedFiles(files, commentedPaths);
     const reviewedSet = new Set(reviewed || []);
     for (const filePath in files) {
@@ -1294,7 +1294,7 @@
       this._cancelForEachDiff = cancel;
 
       iter++;
-      console.log('Expanding diff', iter, 'of', initialCount, ':',
+      console.info('Expanding diff', iter, 'of', initialCount, ':',
           path);
       const diffElem = this._findDiffByPath(path, diffElements);
       if (!diffElem) {
@@ -1311,7 +1311,7 @@
     }).then(() => {
       this._cancelForEachDiff = null;
       this._nextRenderParams = null;
-      console.log('Finished expanding', initialCount, 'diff(s)');
+      console.info('Finished expanding', initialCount, 'diff(s)');
       this.reporting.timeEndWithAverage(EXPAND_ALL_TIMING_LABEL,
           EXPAND_ALL_AVG_TIMING_LABEL, initialCount);
       /* Block diff cursor from auto scrolling after files are done rendering.
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_html.js b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_html.ts
similarity index 99%
rename from polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_html.js
rename to polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_html.ts
index 46497a1..e141b70 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_html.js
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/change/gr-included-in-dialog/gr-included-in-dialog_html.js b/polygerrit-ui/app/elements/change/gr-included-in-dialog/gr-included-in-dialog_html.ts
similarity index 97%
rename from polygerrit-ui/app/elements/change/gr-included-in-dialog/gr-included-in-dialog_html.js
rename to polygerrit-ui/app/elements/change/gr-included-in-dialog/gr-included-in-dialog_html.ts
index 4670e6a..8e90f3b 100644
--- a/polygerrit-ui/app/elements/change/gr-included-in-dialog/gr-included-in-dialog_html.js
+++ b/polygerrit-ui/app/elements/change/gr-included-in-dialog/gr-included-in-dialog_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row_html.js b/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row_html.ts
similarity index 98%
rename from polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row_html.js
rename to polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row_html.ts
index fb0f9e8..312532e 100644
--- a/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row_html.js
+++ b/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="gr-voting-styles">
diff --git a/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores_html.js b/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores_html.ts
similarity index 96%
rename from polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores_html.js
rename to polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores_html.ts
index 1e3e7ec..974e55d 100644
--- a/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores_html.js
+++ b/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/change/gr-message/gr-message_html.js b/polygerrit-ui/app/elements/change/gr-message/gr-message_html.ts
similarity index 98%
rename from polygerrit-ui/app/elements/change/gr-message/gr-message_html.js
rename to polygerrit-ui/app/elements/change/gr-message/gr-message_html.ts
index af57782..22d495b 100644
--- a/polygerrit-ui/app/elements/change/gr-message/gr-message_html.js
+++ b/polygerrit-ui/app/elements/change/gr-message/gr-message_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="gr-voting-styles">
diff --git a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list_html.js b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list_html.ts
similarity index 97%
rename from polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list_html.js
rename to polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list_html.ts
index c696c8c..9c4ef04 100644
--- a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list_html.js
+++ b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list_html.js b/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list_html.ts
similarity index 98%
rename from polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list_html.js
rename to polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list_html.ts
index 2721b2e2..6fe8cea 100644
--- a/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list_html.js
+++ b/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js
index f4beeea..aa032cc 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js
@@ -457,7 +457,7 @@
     for (const splice of indexSplices) {
       for (const account of splice.removed) {
         if (!this._reviewersPendingRemove[type]) {
-          console.err('Invalid type ' + type + ' for reviewer.');
+          console.error('Invalid type ' + type + ' for reviewer.');
           return;
         }
         this._reviewersPendingRemove[type].push(account);
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_html.js b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_html.ts
similarity index 96%
rename from polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_html.js
rename to polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_html.ts
index bebeb7f..3cc32b2 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_html.js
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
@@ -286,38 +286,6 @@
       <div id="pluginMessage">[[_pluginMessage]]</div>
     </section>
     <section
-      class="draftsContainer"
-      hidden$="[[_computeHideDraftList(draftCommentThreads)]]"
-    >
-      <div class="includeComments">
-        <input
-          type="checkbox"
-          id="includeComments"
-          checked="{{_includeComments::change}}"
-        />
-        <label for="includeComments"
-          >Publish [[_computeDraftsTitle(draftCommentThreads)]]</label
-        >
-      </div>
-      <gr-thread-list
-        id="commentList"
-        hidden$="[[!_includeComments]]"
-        threads="[[draftCommentThreads]]"
-        change="[[change]]"
-        change-num="[[change._number]]"
-        logged-in="true"
-        hide-toggle-buttons=""
-        on-thread-list-modified="_onThreadListModified"
-      >
-      </gr-thread-list>
-      <span
-        id="savingLabel"
-        class$="[[_computeSavingLabelClass(_savingComments)]]"
-      >
-        Saving comments...
-      </span>
-    </section>
-    <section
       hidden$="[[!_showAttentionSummary(serverConfig, _attentionModified)]]"
       class="attention"
     >
@@ -353,13 +321,13 @@
       <div class="attentionDetailsTitle">
         <iron-icon class="attention-icon" icon="gr-icons:attention"></iron-icon>
         <span>Bring to attention of ...</span>
-        <span class="selectUsers">(select users)</span>
+        <span class="selectUsers">(click chips to select users)</span>
       </div>
       <div class="peopleList">
         <div class="peopleListLabel">Owner</div>
         <gr-account-label
           account="[[_owner]]"
-          show-attention="[[_computeHasNewAttention(_owner, _newAttentionSet)]]"
+          force-attention="[[_computeHasNewAttention(_owner, _newAttentionSet)]]"
           blurred="[[!_computeHasNewAttention(_owner, _newAttentionSet)]]"
           hide-hovercard=""
           on-click="_handleAttentionClick"
@@ -371,7 +339,7 @@
         <template is="dom-repeat" items="[[_reviewers]]" as="account">
           <gr-account-label
             account="[[account]]"
-            show-attention="[[_computeHasNewAttention(account, _newAttentionSet)]]"
+            force-attention="[[_computeHasNewAttention(account, _newAttentionSet)]]"
             blurred="[[!_computeHasNewAttention(account, _newAttentionSet)]]"
             hide-hovercard=""
             on-click="_handleAttentionClick"
@@ -384,7 +352,7 @@
         <template is="dom-repeat" items="[[_ccs]]" as="account">
           <gr-account-label
             account="[[account]]"
-            show-attention="[[_computeHasNewAttention(account, _newAttentionSet)]]"
+            force-attention="[[_computeHasNewAttention(account, _newAttentionSet)]]"
             blurred="[[!_computeHasNewAttention(account, _newAttentionSet)]]"
             hide-hovercard=""
             on-click="_handleAttentionClick"
@@ -393,6 +361,38 @@
         </template>
       </div>
     </section>
+    <section
+      class="draftsContainer"
+      hidden$="[[_computeHideDraftList(draftCommentThreads)]]"
+    >
+      <div class="includeComments">
+        <input
+          type="checkbox"
+          id="includeComments"
+          checked="{{_includeComments::change}}"
+        />
+        <label for="includeComments"
+          >Publish [[_computeDraftsTitle(draftCommentThreads)]]</label
+        >
+      </div>
+      <gr-thread-list
+        id="commentList"
+        hidden$="[[!_includeComments]]"
+        threads="[[draftCommentThreads]]"
+        change="[[change]]"
+        change-num="[[change._number]]"
+        logged-in="true"
+        hide-toggle-buttons=""
+        on-thread-list-modified="_onThreadListModified"
+      >
+      </gr-thread-list>
+      <span
+        id="savingLabel"
+        class$="[[_computeSavingLabelClass(_savingComments)]]"
+      >
+        Saving comments...
+      </span>
+    </section>
     <section class="actions">
       <div class="left">
         <span
diff --git a/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list_html.js b/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list_html.ts
similarity index 96%
rename from polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list_html.js
rename to polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list_html.ts
index 13a41ed..616a7db 100644
--- a/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list_html.js
+++ b/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list_html.js b/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list_html.ts
similarity index 97%
rename from polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list_html.js
rename to polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list_html.ts
index 80983ca..d74c985 100644
--- a/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list_html.js
+++ b/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/change/gr-upload-help-dialog/gr-upload-help-dialog_html.js b/polygerrit-ui/app/elements/change/gr-upload-help-dialog/gr-upload-help-dialog_html.ts
similarity index 96%
rename from polygerrit-ui/app/elements/change/gr-upload-help-dialog/gr-upload-help-dialog_html.js
rename to polygerrit-ui/app/elements/change/gr-upload-help-dialog/gr-upload-help-dialog_html.ts
index ec010a1..1ee3a3a 100644
--- a/polygerrit-ui/app/elements/change/gr-upload-help-dialog/gr-upload-help-dialog_html.js
+++ b/polygerrit-ui/app/elements/change/gr-upload-help-dialog/gr-upload-help-dialog_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/change/gr-upload-help-dialog/gr-upload-help-dialog_test.js b/polygerrit-ui/app/elements/change/gr-upload-help-dialog/gr-upload-help-dialog_test.js
index d1af425..2d1da92 100644
--- a/polygerrit-ui/app/elements/change/gr-upload-help-dialog/gr-upload-help-dialog_test.js
+++ b/polygerrit-ui/app/elements/change/gr-upload-help-dialog/gr-upload-help-dialog_test.js
@@ -73,8 +73,8 @@
       assert.isUndefined(
           element._computeFetchCommand(testRev, '', 'badscheme'));
 
-      const rev = Object.assign({}, testRev);
-      rev.fetch = Object.assign({}, testRev.fetch, {nocmds: {commands: {}}});
+      const rev = {...testRev};
+      rev.fetch = {...testRev.fetch, nocmds: {commands: {}}};
       assert.isUndefined(
           element._computeFetchCommand(rev, '', 'nocmds'));
 
diff --git a/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown_html.js b/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown_html.ts
similarity index 95%
rename from polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown_html.js
rename to polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown_html.ts
index 5db7923..b67e1e8 100644
--- a/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown_html.js
+++ b/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/core/gr-error-dialog/gr-error-dialog_html.js b/polygerrit-ui/app/elements/core/gr-error-dialog/gr-error-dialog_html.ts
similarity index 95%
rename from polygerrit-ui/app/elements/core/gr-error-dialog/gr-error-dialog_html.js
rename to polygerrit-ui/app/elements/core/gr-error-dialog/gr-error-dialog_html.ts
index 39d4f2d..10476cd 100644
--- a/polygerrit-ui/app/elements/core/gr-error-dialog/gr-error-dialog_html.js
+++ b/polygerrit-ui/app/elements/core/gr-error-dialog/gr-error-dialog_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.js b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.js
index 7e4e568..e619eab 100644
--- a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.js
+++ b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.js
@@ -26,7 +26,6 @@
 import {PolymerElement} from '@polymer/polymer/polymer-element.js';
 import {htmlTemplate} from './gr-error-manager_html.js';
 import {getBaseUrl} from '../../../utils/url-util.js';
-import {authService} from '../../shared/gr-rest-api-interface/gr-auth.js';
 import {appContext} from '../../../services/app-context.js';
 
 const HIDE_ALERT_TIMEOUT_MS = 5000;
@@ -37,6 +36,21 @@
 const TOO_MANY_FILES = 'too many files to find conflicts';
 const AUTHENTICATION_REQUIRED = 'Authentication required\n';
 
+const ErrorType = {
+  AUTH: 'AUTH',
+  NETWORK: 'NETWORK',
+  GENERIC: 'GENERIC',
+};
+
+// Bigger number has higher priority
+const ErrorTypePriority = {
+  [ErrorType.AUTH]: 3,
+  [ErrorType.NETWORK]: 2,
+  [ErrorType.GENERIC]: 1,
+};
+
+export const __testOnly_ErrorType = ErrorType;
+
 /**
  * @extends PolymerElement
  */
@@ -83,7 +97,7 @@
     super();
 
     /** @type {!Auth} */
-    this._authService = authService;
+    this._authService = appContext.authService;
 
     /** @type {?Function} */
     this._authErrorHandlerDeregistrationHook;
@@ -98,6 +112,7 @@
     this.listen(document, 'server-error', '_handleServerError');
     this.listen(document, 'network-error', '_handleNetworkError');
     this.listen(document, 'show-alert', '_handleShowAlert');
+    this.listen(document, 'hide-alert', '_hideAlert');
     this.listen(document, 'show-error', '_handleShowErrorDialog');
     this.listen(document, 'visibilitychange', '_handleVisibilityChange');
     this.listen(document, 'show-auth-required', '_handleAuthRequired');
@@ -116,6 +131,7 @@
     this.unlisten(document, 'server-error', '_handleServerError');
     this.unlisten(document, 'network-error', '_handleNetworkError');
     this.unlisten(document, 'show-alert', '_handleShowAlert');
+    this.unlisten(document, 'hide-alert', '_hideAlert');
     this.unlisten(document, 'show-error', '_handleShowErrorDialog');
     this.unlisten(document, 'visibilitychange', '_handleVisibilityChange');
     this.unlisten(document, 'show-auth-required', '_handleAuthRequired');
@@ -178,7 +194,7 @@
           }));
         }
       }
-      console.log(`server error: ${errorText}`);
+      console.info(`server error: ${errorText}`);
     });
   }
 
@@ -224,17 +240,24 @@
     console.error(e.detail.error.message);
   }
 
+  // TODO(dhruvsr): allow less priority alerts to override high priority alerts
+  // In some use cases we may want generic alerts to show along/over errors
+  _canOverride(incoming = ErrorType.GENERIC, existing = ErrorType.GENERIC) {
+    return ErrorTypePriority[incoming] >= ErrorTypePriority[existing];
+  }
+
   /**
    * @param {string} text
    * @param {?string=} opt_actionText
    * @param {?Function=} opt_actionCallback
    * @param {?boolean=} opt_dismissOnNavigation
+   * @param {?string=} opt_type
    */
   _showAlert(text, opt_actionText, opt_actionCallback,
-      opt_dismissOnNavigation) {
+      opt_dismissOnNavigation, opt_type) {
     if (this._alertElement) {
-      // do not override auth alerts
-      if (this._alertElement.type === 'AUTH') return;
+      // check priority before hiding
+      if (!this._canOverride(opt_type, this._alertElement.type)) return;
       this._hideAlert();
     }
 
@@ -276,7 +299,7 @@
     }
 
     this._alertElement = this._createToastAlert();
-    this._alertElement.type = 'AUTH';
+    this._alertElement.type = ErrorType.AUTH;
     this._alertElement.show(errorText, actionText,
         this._createLoginPopup.bind(this));
 
diff --git a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager_html.js b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager_html.ts
similarity index 94%
rename from polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager_html.js
rename to polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager_html.ts
index 2fa9b95..1cefb78 100644
--- a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager_html.js
+++ b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <gr-overlay with-backdrop="" id="errorOverlay">
diff --git a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager_test.js b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager_test.js
index 934f244..b527786 100644
--- a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager_test.js
+++ b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager_test.js
@@ -19,6 +19,7 @@
 import './gr-error-manager.js';
 import {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';
 import {_testOnly_initGerritPluginApi} from '../../shared/gr-js-api-interface/gr-gerrit.js';
+import {__testOnly_ErrorType} from './gr-error-manager.js';
 
 const basicFixture = fixtureFromElement('gr-error-manager');
 
@@ -219,6 +220,26 @@
       });
     });
 
+    test('_canOverride alerts', () => {
+      assert.isFalse(element._canOverride(undefined,
+          __testOnly_ErrorType.AUTH));
+      assert.isFalse(element._canOverride(undefined,
+          __testOnly_ErrorType.NETWORK));
+      assert.isTrue(element._canOverride(undefined,
+          __testOnly_ErrorType.GENERIC));
+      assert.isTrue(element._canOverride(undefined, undefined));
+
+      assert.isTrue(element._canOverride(__testOnly_ErrorType.NETWORK,
+          undefined));
+      assert.isTrue(element._canOverride(__testOnly_ErrorType.AUTH,
+          undefined));
+      assert.isFalse(element._canOverride(__testOnly_ErrorType.NETWORK,
+          __testOnly_ErrorType.AUTH));
+
+      assert.isTrue(element._canOverride(__testOnly_ErrorType.AUTH,
+          __testOnly_ErrorType.NETWORK));
+    });
+
     test('show auth refresh toast', async () => {
       // starts with authed state
       element.$.restAPI.getLoggedIn();
diff --git a/polygerrit-ui/app/elements/core/gr-key-binding-display/gr-key-binding-display_html.js b/polygerrit-ui/app/elements/core/gr-key-binding-display/gr-key-binding-display_html.ts
similarity index 95%
rename from polygerrit-ui/app/elements/core/gr-key-binding-display/gr-key-binding-display_html.js
rename to polygerrit-ui/app/elements/core/gr-key-binding-display/gr-key-binding-display_html.ts
index 334a40a..0a75104 100644
--- a/polygerrit-ui/app/elements/core/gr-key-binding-display/gr-key-binding-display_html.js
+++ b/polygerrit-ui/app/elements/core/gr-key-binding-display/gr-key-binding-display_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog_html.js b/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog_html.ts
similarity index 97%
rename from polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog_html.js
rename to polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog_html.ts
index e850fbe..1860f38 100644
--- a/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog_html.js
+++ b/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_html.js b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_html.ts
similarity index 98%
rename from polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_html.js
rename to polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_html.ts
index e50b408..8ef54d6 100644
--- a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_html.js
+++ b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.js b/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.js
index 1142d8e..1f3fad9 100644
--- a/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.js
+++ b/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.js
@@ -758,10 +758,10 @@
         .filter(section => (attentionEnabled || !section.attentionSetOnly))
         .filter(section => (assigneeEnabled || !section.assigneeOnly))
         .filter(section => (user === 'self' || !section.selfOnly))
-        .map(section => Object.assign({}, section, {
-          name: section.name,
-          query: section.query.replace(USER_PLACEHOLDER_PATTERN, user),
-        }));
+        .map(section => {
+          return {...section, name: section.name,
+            query: section.query.replace(USER_PLACEHOLDER_PATTERN, user)};
+        });
     return {title, sections};
   },
 };
diff --git a/polygerrit-ui/app/elements/core/gr-router/gr-router.js b/polygerrit-ui/app/elements/core/gr-router/gr-router.js
index 792a751..60722d3 100644
--- a/polygerrit-ui/app/elements/core/gr-router/gr-router.js
+++ b/polygerrit-ui/app/elements/core/gr-router/gr-router.js
@@ -201,7 +201,7 @@
 // gr-router and gr-app in the PolyGerritIndexHtml.soy file if needed
 const app = document.querySelector('#app');
 if (!app) {
-  console.log('No gr-app found (running tests)');
+  console.info('No gr-app found (running tests)');
 }
 
 // Setup listeners outside of the router component initialization.
diff --git a/polygerrit-ui/app/elements/core/gr-router/gr-router_html.js b/polygerrit-ui/app/elements/core/gr-router/gr-router_html.ts
similarity index 91%
rename from polygerrit-ui/app/elements/core/gr-router/gr-router_html.js
rename to polygerrit-ui/app/elements/core/gr-router/gr-router_html.ts
index 8aa0835..91d8b41 100644
--- a/polygerrit-ui/app/elements/core/gr-router/gr-router_html.js
+++ b/polygerrit-ui/app/elements/core/gr-router/gr-router_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
diff --git a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar_html.js b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar_html.ts
similarity index 95%
rename from polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar_html.js
rename to polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar_html.ts
index b0ef7af..a4d5e69 100644
--- a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar_html.js
+++ b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search_html.js b/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search_html.ts
similarity index 94%
rename from polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search_html.js
rename to polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search_html.ts
index d490308..7088937 100644
--- a/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search_html.js
+++ b/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles"></style>
diff --git a/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog.js b/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog.js
index 6394bff..d051d30 100644
--- a/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog.js
+++ b/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog.js
@@ -141,7 +141,7 @@
 
   overridePartialPrefs(prefs) {
     // generate a smaller gr-diff than fullscreen for dialog
-    return Object.assign({}, prefs, {line_length: 50});
+    return {...prefs, line_length: 50};
   }
 
   onCancel(e) {
diff --git a/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog_html.js b/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog_html.ts
similarity index 97%
rename from polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog_html.js
rename to polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog_html.ts
index a5a6ff2..057fd01 100644
--- a/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog_html.js
+++ b/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api.js b/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api.js
index 89821fd..165d28c 100644
--- a/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api.js
+++ b/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api.js
@@ -380,7 +380,7 @@
     for (const file of Object.keys(comments)) {
       const commentsForFile = [];
       for (const comment of comments[file]) {
-        commentsForFile.push(Object.assign({__path: file}, comment));
+        commentsForFile.push({__path: file, ...comment});
       }
       commentArr = commentArr.concat(commentsForFile);
     }
diff --git a/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api_html.js b/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api_html.js
deleted file mode 100644
index 8aa0835..0000000
--- a/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api_html.js
+++ /dev/null
@@ -1,21 +0,0 @@
-/**
- * @license
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
-
-export const htmlTemplate = html`
-  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
-`;
diff --git a/polygerrit-ui/app/elements/core/gr-router/gr-router_html.js b/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api_html.ts
similarity index 91%
copy from polygerrit-ui/app/elements/core/gr-router/gr-router_html.js
copy to polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api_html.ts
index 8aa0835..91d8b41 100644
--- a/polygerrit-ui/app/elements/core/gr-router/gr-router_html.js
+++ b/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
diff --git a/polygerrit-ui/app/elements/diff/gr-coverage-layer/gr-coverage-layer_html.js b/polygerrit-ui/app/elements/diff/gr-coverage-layer/gr-coverage-layer_html.ts
similarity index 91%
rename from polygerrit-ui/app/elements/diff/gr-coverage-layer/gr-coverage-layer_html.js
rename to polygerrit-ui/app/elements/diff/gr-coverage-layer/gr-coverage-layer_html.ts
index 3ed33d1..1489006 100644
--- a/polygerrit-ui/app/elements/diff/gr-coverage-layer/gr-coverage-layer_html.js
+++ b/polygerrit-ui/app/elements/diff/gr-coverage-layer/gr-coverage-layer_html.ts
@@ -14,6 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html``;
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-element.js b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-element.js
index bb617f0..ef75da0 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-element.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-element.js
@@ -312,7 +312,7 @@
       return;
     }
 
-    const localPrefs = Object.assign({}, prefs);
+    const localPrefs = {...prefs};
     if (this.path === COMMIT_MSG_PATH) {
       // override line_length for commit msg the same way as
       // in gr-diff
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-element_html.js b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-element_html.ts
similarity index 94%
rename from polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-element_html.js
rename to polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-element_html.ts
index 4d6b890..573f559 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-element_html.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-element_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <div class="contentWrapper">
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor_html.js b/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor_html.ts
similarity index 93%
rename from polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor_html.js
rename to polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor_html.ts
index e400792..712a93d 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor_html.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <gr-cursor-manager
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight_html.js b/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight_html.ts
similarity index 93%
rename from polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight_html.js
rename to polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight_html.ts
index afd4ac5..5a6cb1c 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight_html.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.js b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.js
index 6afa9df..d90221a 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.js
@@ -729,7 +729,7 @@
         isOnParent: comment.side === 'PARENT',
       };
       if (comment.range) {
-        newThread.range = Object.assign({}, comment.range);
+        newThread.range = {...comment.range};
       }
       threads.push(newThread);
     }
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_html.js b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_html.ts
similarity index 96%
rename from polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_html.js
rename to polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_html.ts
index aa05bb5..38b0d6d 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_html.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <gr-diff
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector_html.js b/polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector_html.ts
similarity index 95%
rename from polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector_html.js
rename to polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector_html.ts
index b5393ea..40f6a32 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector_html.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-preferences-dialog/gr-diff-preferences-dialog_html.js b/polygerrit-ui/app/elements/diff/gr-diff-preferences-dialog/gr-diff-preferences-dialog_html.ts
similarity index 96%
rename from polygerrit-ui/app/elements/diff/gr-diff-preferences-dialog/gr-diff-preferences-dialog_html.js
rename to polygerrit-ui/app/elements/diff/gr-diff-preferences-dialog/gr-diff-preferences-dialog_html.ts
index d65ba1f..9c942a3 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-preferences-dialog/gr-diff-preferences-dialog_html.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-preferences-dialog/gr-diff-preferences-dialog_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor.js b/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor.js
index b9c97a9..a4954a1 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor.js
@@ -509,14 +509,20 @@
 
       if (chunk.ab) {
         result.push(...this._splitAtChunkEnds(chunk.ab, chunkEnds)
-            .map(({lines, keyLocation}) =>
-              Object.assign({}, chunk, {ab: lines, keyLocation})));
+            .map(({lines, keyLocation}) => {
+              return {
+                ...chunk,
+                ab: lines,
+                keyLocation,
+              };
+            }));
       } else if (chunk.common) {
         const aChunks = this._splitAtChunkEnds(chunk.a, chunkEnds);
         const bChunks = this._splitAtChunkEnds(chunk.b, chunkEnds);
-        result.push(...aChunks.map(({lines, keyLocation}, i) =>
-          Object.assign(
-              {}, chunk, {a: lines, b: bChunks[i].lines, keyLocation})));
+        result.push(...aChunks.map(({lines, keyLocation}, i) => {
+          return {
+            ...chunk, a: lines, b: bChunks[i].lines, keyLocation};
+        }));
       }
     }
 
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection_html.js b/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection_html.ts
similarity index 91%
rename from polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection_html.js
rename to polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection_html.ts
index 620ef02..bd0e034 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection_html.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <div class="contentWrapper">
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js
index 854ac63..6823156 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js
@@ -390,7 +390,7 @@
         changeNum, patchRange).then(changeFiles => {
       if (!changeFiles) return;
       const commentedPaths = changeComments.getPaths(patchRange);
-      const files = Object.assign({}, changeFiles);
+      const files = {...changeFiles};
       addUnmodifiedFiles(files, commentedPaths);
       this._files = {
         sortedFileList: Object.keys(files).sort(specialFilePathCompare),
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_html.js b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_html.ts
similarity index 99%
rename from polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_html.js
rename to polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_html.ts
index a6e6591..e2cb880 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_html.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js
index 8e45b62..2c5787a 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js
@@ -880,7 +880,7 @@
    */
   _getBypassPrefs() {
     if (this._safetyBypass !== null) {
-      return Object.assign({}, this.prefs, {context: this._safetyBypass});
+      return {...this.prefs, context: this._safetyBypass};
     }
     return this.prefs;
   }
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_html.js b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_html.ts
similarity index 99%
rename from polygerrit-ui/app/elements/diff/gr-diff/gr-diff_html.js
rename to polygerrit-ui/app/elements/diff/gr-diff/gr-diff_html.ts
index 94a3701..f18af23 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_html.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.js b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.js
index 86a2b65..02f09a8 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.js
@@ -76,14 +76,14 @@
 
   test('line limit with line_wrapping', () => {
     element = basicFixture.instantiate();
-    element.prefs = Object.assign({}, MINIMAL_PREFS, {line_wrapping: true});
+    element.prefs = {...MINIMAL_PREFS, line_wrapping: true};
     flushAsynchronousOperations();
     assert.equal(getComputedStyleValue('--line-limit', element), '80ch');
   });
 
   test('line limit without line_wrapping', () => {
     element = basicFixture.instantiate();
-    element.prefs = Object.assign({}, MINIMAL_PREFS, {line_wrapping: false});
+    element.prefs = {...MINIMAL_PREFS, line_wrapping: false};
     flushAsynchronousOperations();
     assert.isNotOk(getComputedStyleValue('--line-limit', element));
   });
@@ -225,7 +225,7 @@
       element.path = 'file.txt';
 
       element.$.diffBuilder._builder = element.$.diffBuilder._getDiffBuilder(
-          getMockDiffResponse(), Object.assign({}, MINIMAL_PREFS));
+          getMockDiffResponse(), {...MINIMAL_PREFS});
 
       // No thread groups.
       assert.isNotOk(element._getThreadGroupForLine(contentEl));
@@ -693,22 +693,22 @@
 
       test('change in preferences re-renders diff', () => {
         sinon.stub(element, '_renderDiffTable');
-        element.prefs = Object.assign(
-            {}, MINIMAL_PREFS, {time_format: 'HHMM_12'});
+        element.prefs = {
+          ...MINIMAL_PREFS, time_format: 'HHMM_12'};
         element.flushDebouncer('renderDiffTable');
         assert.isTrue(element._renderDiffTable.called);
       });
 
       test('adding/removing property in preferences re-renders diff', () => {
         const stub = sinon.stub(element, '_renderDiffTable');
-        const newPrefs1 = Object.assign({}, MINIMAL_PREFS,
-            {line_wrapping: true});
+        const newPrefs1 = {...MINIMAL_PREFS,
+          line_wrapping: true};
         element.prefs = newPrefs1;
         element.flushDebouncer('renderDiffTable');
         assert.isTrue(element._renderDiffTable.called);
         stub.reset();
 
-        const newPrefs2 = Object.assign({}, newPrefs1);
+        const newPrefs2 = {...newPrefs1};
         delete newPrefs2.line_wrapping;
         element.prefs = newPrefs2;
         element.flushDebouncer('renderDiffTable');
@@ -719,8 +719,8 @@
           'noRenderOnPrefsChange', () => {
         sinon.stub(element, '_renderDiffTable');
         element.noRenderOnPrefsChange = true;
-        element.prefs = Object.assign(
-            {}, MINIMAL_PREFS, {time_format: 'HHMM_12'});
+        element.prefs = {
+          ...MINIMAL_PREFS, time_format: 'HHMM_12'};
         element.flushDebouncer('renderDiffTable');
         assert.isFalse(element._renderDiffTable.called);
       });
@@ -787,7 +787,7 @@
     });
 
     test('large render w/ context = 10', done => {
-      element.prefs = Object.assign({}, MINIMAL_PREFS, {context: 10});
+      element.prefs = {...MINIMAL_PREFS, context: 10};
       function rendered() {
         assert.isTrue(renderStub.called);
         assert.isFalse(element._showWarning);
@@ -799,7 +799,7 @@
     });
 
     test('large render w/ whole file and bypass', done => {
-      element.prefs = Object.assign({}, MINIMAL_PREFS, {context: -1});
+      element.prefs = {...MINIMAL_PREFS, context: -1};
       element._safetyBypass = 10;
       function rendered() {
         assert.isTrue(renderStub.called);
@@ -812,7 +812,7 @@
     });
 
     test('large render w/ whole file and no bypass', done => {
-      element.prefs = Object.assign({}, MINIMAL_PREFS, {context: -1});
+      element.prefs = {...MINIMAL_PREFS, context: -1};
       function rendered() {
         assert.isFalse(renderStub.called);
         assert.isTrue(element._showWarning);
@@ -1028,7 +1028,7 @@
     }
     setupSampleDiff({content});
     assertDiffTableWithContent();
-    element.diff = Object.assign({}, element.diff);
+    element.diff = {...element.diff};
     // immediately cleaned up
     assert.equal(element.$.diffTable.innerHTML, '');
     element._renderDiffTable();
diff --git a/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select.js b/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select.js
index 8ec2058..0744d0d 100644
--- a/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select.js
+++ b/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select.js
@@ -115,10 +115,8 @@
       const basePatchNum = basePatch.num;
       const entry = this._createDropdownEntry(basePatchNum, 'Patchset ',
           _sortedRevisions, changeComments, this._getShaForPatch(basePatch));
-      dropdownContent.push(Object.assign({}, entry, {
-        disabled: this._computeLeftDisabled(
-            basePatch.num, patchNum, _sortedRevisions),
-      }));
+      dropdownContent.push({...entry, disabled: this._computeLeftDisabled(
+          basePatch.num, patchNum, _sortedRevisions)});
     }
 
     dropdownContent.push({
@@ -163,10 +161,11 @@
       const entry = this._createDropdownEntry(
           patchNum, patchNum === 'edit' ? '' : 'Patchset ', _sortedRevisions,
           changeComments, this._getShaForPatch(patch));
-      dropdownContent.push(Object.assign({}, entry, {
-        disabled: this._computeRightDisabled(basePatchNum, patchNum,
-            _sortedRevisions),
-      }));
+      dropdownContent.push({
+        ...entry,
+        disabled: this._computeRightDisabled(
+            basePatchNum, patchNum, _sortedRevisions),
+      });
     }
     return dropdownContent;
   }
diff --git a/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select_html.js b/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select_html.ts
similarity index 97%
rename from polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select_html.js
rename to polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select_html.ts
index cc11cfa..415ef4b 100644
--- a/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select_html.js
+++ b/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer.js b/polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer.js
index 231e4b5..830a91a 100644
--- a/polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer.js
+++ b/polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer.js
@@ -223,7 +223,7 @@
         .map(range => {
           // Make a copy, so that the normalization below does not mess with
           // our map.
-          range = Object.assign({}, range);
+          range = {...range};
           range.end = range.end === -1 ? line.text.length : range.end;
 
           // Normalize invalid ranges where the start is after the end but the
diff --git a/polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer_html.js b/polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer_html.ts
similarity index 91%
rename from polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer_html.js
rename to polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer_html.ts
index 3ed33d1..1489006 100644
--- a/polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer_html.js
+++ b/polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer_html.ts
@@ -14,6 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html``;
diff --git a/polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box_html.js b/polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box_html.ts
similarity index 93%
rename from polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box_html.js
rename to polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box_html.ts
index e7795b9..24d63b3 100644
--- a/polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box_html.js
+++ b/polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer_html.js b/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer_html.ts
similarity index 91%
rename from polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer_html.js
rename to polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer_html.ts
index 433f814..ac59f4f 100644
--- a/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer_html.js
+++ b/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <gr-lib-loader id="libLoader"></gr-lib-loader>
diff --git a/polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search_html.js b/polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search_html.ts
similarity index 96%
rename from polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search_html.js
rename to polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search_html.ts
index b637b75..de0a990 100644
--- a/polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search_html.js
+++ b/polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/edit/gr-default-editor/gr-default-editor_html.js b/polygerrit-ui/app/elements/edit/gr-default-editor/gr-default-editor_html.ts
similarity index 94%
rename from polygerrit-ui/app/elements/edit/gr-default-editor/gr-default-editor_html.js
rename to polygerrit-ui/app/elements/edit/gr-default-editor/gr-default-editor_html.ts
index 7368289..ba8af3c 100644
--- a/polygerrit-ui/app/elements/edit/gr-default-editor/gr-default-editor_html.js
+++ b/polygerrit-ui/app/elements/edit/gr-default-editor/gr-default-editor_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls_html.js b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls_html.ts
similarity index 98%
rename from polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls_html.js
rename to polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls_html.ts
index 02639c0..b67f6cd 100644
--- a/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls_html.js
+++ b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/edit/gr-edit-file-controls/gr-edit-file-controls_html.js b/polygerrit-ui/app/elements/edit/gr-edit-file-controls/gr-edit-file-controls_html.ts
similarity index 95%
rename from polygerrit-ui/app/elements/edit/gr-edit-file-controls/gr-edit-file-controls_html.js
rename to polygerrit-ui/app/elements/edit/gr-edit-file-controls/gr-edit-file-controls_html.ts
index ec0b8b4..c6a6de7 100644
--- a/polygerrit-ui/app/elements/edit/gr-edit-file-controls/gr-edit-file-controls_html.js
+++ b/polygerrit-ui/app/elements/edit/gr-edit-file-controls/gr-edit-file-controls_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view_html.js b/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view_html.ts
similarity index 97%
rename from polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view_html.js
rename to polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view_html.ts
index 9dc35b2..bd8304f 100644
--- a/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view_html.js
+++ b/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view_test.js b/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view_test.js
index 21a61aa..a673955 100644
--- a/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view_test.js
+++ b/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view_test.js
@@ -51,7 +51,7 @@
   suite('_paramsChanged', () => {
     test('incorrect view returns immediately', () => {
       element._paramsChanged(
-          Object.assign({}, mockParams, {view: GerritNav.View.DIFF}));
+          {...mockParams, view: GerritNav.View.DIFF});
       assert.notOk(element._changeNum);
     });
 
@@ -64,7 +64,7 @@
       });
 
       const promises = element._paramsChanged(
-          Object.assign({}, mockParams, {view: GerritNav.View.EDIT}));
+          {...mockParams, view: GerritNav.View.EDIT});
 
       flushAsynchronousOperations();
       assert.equal(element._changeNum, mockParams.changeNum);
diff --git a/polygerrit-ui/app/elements/gr-app-element.js b/polygerrit-ui/app/elements/gr-app-element.js
index 3ee67ae..394d929 100644
--- a/polygerrit-ui/app/elements/gr-app-element.js
+++ b/polygerrit-ui/app/elements/gr-app-element.js
@@ -176,6 +176,10 @@
         e => this._handleRpcLog(e));
     this.addEventListener('shortcut-triggered',
         e => this._handleShortcutTriggered(e));
+    // Ideally individual views should handle this event and respond with a soft
+    // reload. This is a catch-all for all views that cannot or have not
+    // implemented that.
+    this.addEventListener('reload', e => window.location.reload());
   }
 
   /** @override */
@@ -571,13 +575,13 @@
 
   _logWelcome() {
     console.group('Runtime Info');
-    console.log('Gerrit UI (PolyGerrit)');
-    console.log(`Gerrit Server Version: ${this._version}`);
+    console.info('Gerrit UI (PolyGerrit)');
+    console.info(`Gerrit Server Version: ${this._version}`);
     if (window.VERSION_INFO) {
-      console.log(`UI Version Info: ${window.VERSION_INFO}`);
+      console.info(`UI Version Info: ${window.VERSION_INFO}`);
     }
     if (this._feedbackUrl) {
-      console.log(`Please file bugs and feedback at: ${this._feedbackUrl}`);
+      console.info(`Please file bugs and feedback at: ${this._feedbackUrl}`);
     }
     console.groupEnd();
   }
diff --git a/polygerrit-ui/app/elements/gr-app-element_html.js b/polygerrit-ui/app/elements/gr-app-element_html.ts
similarity index 98%
rename from polygerrit-ui/app/elements/gr-app-element_html.js
rename to polygerrit-ui/app/elements/gr-app-element_html.ts
index 75295bc..66624e3 100644
--- a/polygerrit-ui/app/elements/gr-app-element_html.js
+++ b/polygerrit-ui/app/elements/gr-app-element_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/gr-app-global-var-init.js b/polygerrit-ui/app/elements/gr-app-global-var-init.js
index 70fc1af..402dff2 100644
--- a/polygerrit-ui/app/elements/gr-app-global-var-init.js
+++ b/polygerrit-ui/app/elements/gr-app-global-var-init.js
@@ -49,7 +49,6 @@
 import {GrReviewerSuggestionsProvider, SUGGESTIONS_PROVIDERS_USERS_TYPES} from '../scripts/gr-reviewer-suggestions-provider/gr-reviewer-suggestions-provider.js';
 import {util} from '../scripts/util.js';
 import page from 'page/page.mjs';
-import {Auth} from './shared/gr-rest-api-interface/gr-auth.js';
 import {appContext} from '../services/app-context.js';
 import {GrAdminApi} from './plugins/gr-admin-api/gr-admin-api.js';
 import {GrAnnotationActionsContext} from './shared/gr-js-api-interface/gr-annotation-actions-context.js';
@@ -109,7 +108,7 @@
   window.GrReviewerSuggestionsProvider = GrReviewerSuggestionsProvider;
   window.util = util;
   window.page = page;
-  window.Auth = Auth;
+  window.Auth = appContext.authService;
   window.EventEmitter = appContext.eventEmitter;
   window.GrAdminApi = GrAdminApi;
   window.GrAnnotationActionsContext = GrAnnotationActionsContext;
@@ -137,6 +136,7 @@
   window.Gerrit = window.Gerrit || {};
   window.Gerrit.Nav = GerritNav;
   window.Gerrit.getRootElement = getRootElement;
+  window.Gerrit.Auth = appContext.authService;
 
   window.Gerrit._pluginLoader = pluginLoader;
   window.Gerrit._endpoints = pluginEndpoints;
diff --git a/polygerrit-ui/app/elements/gr-app_html.js b/polygerrit-ui/app/elements/gr-app_html.ts
similarity index 91%
rename from polygerrit-ui/app/elements/gr-app_html.js
rename to polygerrit-ui/app/elements/gr-app_html.ts
index 3da1b69..f6172c9 100644
--- a/polygerrit-ui/app/elements/gr-app_html.js
+++ b/polygerrit-ui/app/elements/gr-app_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <gr-app-element id="app-element"></gr-app-element>
diff --git a/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator_html.js b/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator_html.ts
similarity index 91%
rename from polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator_html.js
rename to polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator_html.ts
index c4310fc..94196df 100644
--- a/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator_html.js
+++ b/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator_html.ts
@@ -14,6 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html` <slot></slot> `;
diff --git a/polygerrit-ui/app/elements/plugins/gr-external-style/gr-external-style_html.js b/polygerrit-ui/app/elements/plugins/gr-external-style/gr-external-style_html.js
deleted file mode 100644
index c4310fc..0000000
--- a/polygerrit-ui/app/elements/plugins/gr-external-style/gr-external-style_html.js
+++ /dev/null
@@ -1,19 +0,0 @@
-/**
- * @license
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
-
-export const htmlTemplate = html` <slot></slot> `;
diff --git a/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator_html.js b/polygerrit-ui/app/elements/plugins/gr-external-style/gr-external-style_html.ts
similarity index 91%
copy from polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator_html.js
copy to polygerrit-ui/app/elements/plugins/gr-external-style/gr-external-style_html.ts
index c4310fc..94196df 100644
--- a/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator_html.js
+++ b/polygerrit-ui/app/elements/plugins/gr-external-style/gr-external-style_html.ts
@@ -14,6 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html` <slot></slot> `;
diff --git a/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-plugin-popup_html.js b/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-plugin-popup_html.ts
similarity index 93%
rename from polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-plugin-popup_html.js
rename to polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-plugin-popup_html.ts
index 5d2cae7..aa7a92d 100644
--- a/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-plugin-popup_html.js
+++ b/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-plugin-popup_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/plugins/gr-repo-api/gr-plugin-repo-command_html.js b/polygerrit-ui/app/elements/plugins/gr-repo-api/gr-plugin-repo-command_html.ts
similarity index 93%
rename from polygerrit-ui/app/elements/plugins/gr-repo-api/gr-plugin-repo-command_html.js
rename to polygerrit-ui/app/elements/plugins/gr-repo-api/gr-plugin-repo-command_html.ts
index 1e5088b3..6eee643 100644
--- a/polygerrit-ui/app/elements/plugins/gr-repo-api/gr-plugin-repo-command_html.js
+++ b/polygerrit-ui/app/elements/plugins/gr-repo-api/gr-plugin-repo-command_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info_html.js b/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info_html.ts
similarity index 98%
rename from polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info_html.js
rename to polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info_html.ts
index fd94821..d69a279 100644
--- a/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info_html.js
+++ b/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/settings/gr-agreements-list/gr-agreements-list_html.js b/polygerrit-ui/app/elements/settings/gr-agreements-list/gr-agreements-list_html.ts
similarity index 96%
rename from polygerrit-ui/app/elements/settings/gr-agreements-list/gr-agreements-list_html.js
rename to polygerrit-ui/app/elements/settings/gr-agreements-list/gr-agreements-list_html.ts
index 1cd9ce2..194ca2b 100644
--- a/polygerrit-ui/app/elements/settings/gr-agreements-list/gr-agreements-list_html.js
+++ b/polygerrit-ui/app/elements/settings/gr-agreements-list/gr-agreements-list_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/settings/gr-change-table-editor/gr-change-table-editor_html.js b/polygerrit-ui/app/elements/settings/gr-change-table-editor/gr-change-table-editor_html.ts
similarity index 96%
rename from polygerrit-ui/app/elements/settings/gr-change-table-editor/gr-change-table-editor_html.js
rename to polygerrit-ui/app/elements/settings/gr-change-table-editor/gr-change-table-editor_html.ts
index d63e627..1233cf1 100644
--- a/polygerrit-ui/app/elements/settings/gr-change-table-editor/gr-change-table-editor_html.js
+++ b/polygerrit-ui/app/elements/settings/gr-change-table-editor/gr-change-table-editor_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view_html.js b/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view_html.ts
similarity index 97%
rename from polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view_html.js
rename to polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view_html.ts
index b1a951b7..c461718 100644
--- a/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view_html.js
+++ b/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/settings/gr-edit-preferences/gr-edit-preferences_html.js b/polygerrit-ui/app/elements/settings/gr-edit-preferences/gr-edit-preferences_html.ts
similarity index 98%
rename from polygerrit-ui/app/elements/settings/gr-edit-preferences/gr-edit-preferences_html.js
rename to polygerrit-ui/app/elements/settings/gr-edit-preferences/gr-edit-preferences_html.ts
index f2c476a..47e4592 100644
--- a/polygerrit-ui/app/elements/settings/gr-edit-preferences/gr-edit-preferences_html.js
+++ b/polygerrit-ui/app/elements/settings/gr-edit-preferences/gr-edit-preferences_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor_html.js b/polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor_html.ts
similarity index 97%
rename from polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor_html.js
rename to polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor_html.ts
index 977e95d..525fca6 100644
--- a/polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor_html.js
+++ b/polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/settings/gr-gpg-editor/gr-gpg-editor.js b/polygerrit-ui/app/elements/settings/gr-gpg-editor/gr-gpg-editor.js
index 6c6ad01..d1bc7eb 100644
--- a/polygerrit-ui/app/elements/settings/gr-gpg-editor/gr-gpg-editor.js
+++ b/polygerrit-ui/app/elements/settings/gr-gpg-editor/gr-gpg-editor.js
@@ -72,9 +72,8 @@
   }
 
   save() {
-    const promises = this._keysToRemove.map(key => {
-      this.$.restAPI.deleteAccountGPGKey(key.id);
-    });
+    const promises = this._keysToRemove
+        .map(key => this.$.restAPI.deleteAccountGPGKey(key.id));
 
     return Promise.all(promises).then(() => {
       this._keysToRemove = [];
diff --git a/polygerrit-ui/app/elements/settings/gr-gpg-editor/gr-gpg-editor_html.js b/polygerrit-ui/app/elements/settings/gr-gpg-editor/gr-gpg-editor_html.ts
similarity index 98%
rename from polygerrit-ui/app/elements/settings/gr-gpg-editor/gr-gpg-editor_html.js
rename to polygerrit-ui/app/elements/settings/gr-gpg-editor/gr-gpg-editor_html.ts
index 19b8d0c..432bc4f 100644
--- a/polygerrit-ui/app/elements/settings/gr-gpg-editor/gr-gpg-editor_html.js
+++ b/polygerrit-ui/app/elements/settings/gr-gpg-editor/gr-gpg-editor_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/settings/gr-group-list/gr-group-list_html.js b/polygerrit-ui/app/elements/settings/gr-group-list/gr-group-list_html.ts
similarity index 96%
rename from polygerrit-ui/app/elements/settings/gr-group-list/gr-group-list_html.js
rename to polygerrit-ui/app/elements/settings/gr-group-list/gr-group-list_html.ts
index d5350aa..e52583d 100644
--- a/polygerrit-ui/app/elements/settings/gr-group-list/gr-group-list_html.js
+++ b/polygerrit-ui/app/elements/settings/gr-group-list/gr-group-list_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/settings/gr-http-password/gr-http-password_html.js b/polygerrit-ui/app/elements/settings/gr-http-password/gr-http-password_html.ts
similarity index 97%
rename from polygerrit-ui/app/elements/settings/gr-http-password/gr-http-password_html.js
rename to polygerrit-ui/app/elements/settings/gr-http-password/gr-http-password_html.ts
index 0474b99..41084b5 100644
--- a/polygerrit-ui/app/elements/settings/gr-http-password/gr-http-password_html.js
+++ b/polygerrit-ui/app/elements/settings/gr-http-password/gr-http-password_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/settings/gr-identities/gr-identities_html.js b/polygerrit-ui/app/elements/settings/gr-identities/gr-identities_html.ts
similarity index 97%
rename from polygerrit-ui/app/elements/settings/gr-identities/gr-identities_html.js
rename to polygerrit-ui/app/elements/settings/gr-identities/gr-identities_html.ts
index bf50124..1472103 100644
--- a/polygerrit-ui/app/elements/settings/gr-identities/gr-identities_html.js
+++ b/polygerrit-ui/app/elements/settings/gr-identities/gr-identities_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor_html.js b/polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor_html.ts
similarity index 97%
rename from polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor_html.js
rename to polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor_html.ts
index ceb8958..e4d66e2 100644
--- a/polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor_html.js
+++ b/polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.js b/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.js
index fe4a61c..0f92428 100644
--- a/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.js
+++ b/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.js
@@ -86,7 +86,7 @@
       // Using Object.assign here allows preservation of the default values
       // supplied in the value generating function of this._account, unless
       // they are overridden by properties in the account from the response.
-      this._account = Object.assign({}, this._account, account);
+      this._account = {...this._account, ...account};
     });
 
     const loadConfig = this.$.restAPI.getConfig().then(config => {
diff --git a/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog_html.js b/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog_html.ts
similarity index 97%
rename from polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog_html.js
rename to polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog_html.ts
index 3559ba6..11fbbc9 100644
--- a/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog_html.js
+++ b/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="gr-form-styles">
diff --git a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-item_html.js b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-item_html.ts
similarity index 92%
rename from polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-item_html.js
rename to polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-item_html.ts
index 26200d4..786abc0 100644
--- a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-item_html.js
+++ b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-item_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style>
diff --git a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-menu-item_html.js b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-menu-item_html.ts
similarity index 93%
rename from polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-menu-item_html.js
rename to polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-menu-item_html.ts
index 95433ac..fc3edcd 100644
--- a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-menu-item_html.js
+++ b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-menu-item_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_html.js b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_html.ts
similarity index 99%
rename from polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_html.js
rename to polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_html.ts
index 0e0f86c..e80e646 100644
--- a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_html.js
+++ b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor.js b/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor.js
index d869128..9c819e9 100644
--- a/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor.js
+++ b/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor.js
@@ -63,9 +63,8 @@
   }
 
   save() {
-    const promises = this._keysToRemove.map(key => {
-      this.$.restAPI.deleteAccountSSHKey(key.seq);
-    });
+    const promises = this._keysToRemove
+        .map(key => this.$.restAPI.deleteAccountSSHKey(key.seq));
 
     return Promise.all(promises).then(() => {
       this._keysToRemove = [];
diff --git a/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor_html.js b/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor_html.ts
similarity index 98%
rename from polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor_html.js
rename to polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor_html.ts
index 1f3c793..96f770a 100644
--- a/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor_html.js
+++ b/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor_html.js b/polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor_html.ts
similarity index 97%
rename from polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor_html.js
rename to polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor_html.ts
index b1ca653..e3a90a2 100644
--- a/polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor_html.js
+++ b/polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip_html.js b/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip_html.ts
similarity index 97%
rename from polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip_html.js
rename to polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip_html.ts
index 7c75488..f77b8cc 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry_html.js b/polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry_html.ts
similarity index 94%
rename from polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry_html.js
rename to polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry_html.ts
index afd427a..c6c2b7f 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label.js b/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label.js
index efb7c0e..a6c2bd7 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label.js
+++ b/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label.js
@@ -49,6 +49,15 @@
       change: Object,
       voteableText: String,
       /**
+       * Should this user be considered to be in the attention set, regardless
+       * of the current state of the change object? This can be used in a widget
+       * that allows the user to make adjustments to the attention set.
+       */
+      forceAttention: {
+        type: Boolean,
+        value: false,
+      },
+      /**
        * Should attention set related features be shown in the component? Note
        * that the information whether the user is in the attention set or not is
        * part of the ChangeInfo object in the change property.
@@ -90,19 +99,17 @@
     this.$.restAPI.getConfig().then(config => { this._config = config; });
   }
 
-  get isAttentionSetEnabled() {
-    return !!this._config && !!this._config.change
-        && !!this._config.change.enable_attention_set
-        && !!this.highlightAttention && !!this.change && !!this.account;
+  _isAttentionSetEnabled(config, highlight, account, change) {
+    return !!config && !!config.change
+        && !!config.change.enable_attention_set
+        && !!highlight && !!change && !!account;
   }
 
-  get hasAttention() {
-    if (!this.isAttentionSetEnabled || !this.change.attention_set) return false;
-    return this.change.attention_set.hasOwnProperty(this.account._account_id);
-  }
-
-  _computeShowAttentionIcon(config, highlightAttention, account, change) {
-    return this.isAttentionSetEnabled && this.hasAttention;
+  _hasAttention(config, highlight, account, change, force) {
+    if (force) return true;
+    return this._isAttentionSetEnabled(config, highlight, account, change)
+        && change.attention_set
+        && change.attention_set.hasOwnProperty(account._account_id);
   }
 
   _computeName(account, config) {
diff --git a/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label_html.js b/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label_html.ts
similarity index 94%
rename from polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label_html.js
rename to polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label_html.ts
index c090416..b338c4c 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
@@ -78,7 +78,7 @@
     </template>
     <template
       is="dom-if"
-      if="[[_computeShowAttentionIcon(_config, highlightAttention, account, change)]]"
+      if="[[_hasAttention(_config, highlightAttention, account, change, forceAttention)]]"
     >
       <iron-icon class="attention" icon="gr-icons:attention"></iron-icon>
     </template>
diff --git a/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link_html.js b/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link_html.ts
similarity index 96%
rename from polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link_html.js
rename to polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link_html.ts
index 44afb84..dfcef4e 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list.js b/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list.js
index 6f5f9e4..0cf9af2 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list.js
+++ b/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list.js
@@ -168,7 +168,7 @@
     let itemTypeAdded = 'unknown';
     if (item.account) {
       const account =
-          Object.assign({}, item.account, {_pendingAdd: true});
+          {...item.account, _pendingAdd: true};
       this.push('accounts', account);
       itemTypeAdded = 'account';
     } else if (item.group) {
@@ -176,8 +176,8 @@
         this.pendingConfirmation = item;
         return;
       }
-      const group = Object.assign({}, item.group,
-          {_pendingAdd: true, _group: true});
+      const group = {...item.group,
+        _pendingAdd: true, _group: true};
       this.push('accounts', group);
       itemTypeAdded = 'group';
     } else if (this.allowAnyInput) {
@@ -204,8 +204,8 @@
   }
 
   confirmGroup(group) {
-    group = Object.assign(
-        {}, group, {confirmed: true, _pendingAdd: true, _group: true});
+    group = {
+      ...group, confirmed: true, _pendingAdd: true, _group: true};
     this.push('accounts', group);
     this.pendingConfirmation = null;
   }
diff --git a/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list_html.js b/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list_html.ts
similarity index 96%
rename from polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list_html.js
rename to polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list_html.ts
index 6fee9f3..2824bb5 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/shared/gr-alert/gr-alert_html.js b/polygerrit-ui/app/elements/shared/gr-alert/gr-alert_html.ts
similarity index 97%
rename from polygerrit-ui/app/elements/shared/gr-alert/gr-alert_html.js
rename to polygerrit-ui/app/elements/shared/gr-alert/gr-alert_html.ts
index e9f386d..d2aed40 100644
--- a/polygerrit-ui/app/elements/shared/gr-alert/gr-alert_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-alert/gr-alert_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown_html.js b/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown_html.ts
similarity index 97%
rename from polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown_html.js
rename to polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown_html.ts
index fecaa73..d3d2481 100644
--- a/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete_html.js b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete_html.ts
similarity index 97%
rename from polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete_html.js
rename to polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete_html.ts
index c0e8abf..8ab4828 100644
--- a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_html.js b/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_html.ts
similarity index 93%
rename from polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_html.js
rename to polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_html.ts
index cc8a42f..0d8e78f 100644
--- a/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/shared/gr-button/gr-button_html.js b/polygerrit-ui/app/elements/shared/gr-button/gr-button_html.ts
similarity index 98%
rename from polygerrit-ui/app/elements/shared/gr-button/gr-button_html.js
rename to polygerrit-ui/app/elements/shared/gr-button/gr-button_html.ts
index e992ffc..b272951 100644
--- a/polygerrit-ui/app/elements/shared/gr-button/gr-button_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-button/gr-button_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/shared/gr-change-star/gr-change-star_html.js b/polygerrit-ui/app/elements/shared/gr-change-star/gr-change-star_html.ts
similarity index 95%
rename from polygerrit-ui/app/elements/shared/gr-change-star/gr-change-star_html.js
rename to polygerrit-ui/app/elements/shared/gr-change-star/gr-change-star_html.ts
index 3b7b1af..c7930c0 100644
--- a/polygerrit-ui/app/elements/shared/gr-change-star/gr-change-star_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-change-star/gr-change-star_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status_html.js b/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status_html.ts
similarity index 96%
rename from polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status_html.js
rename to polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status_html.ts
index 38e620f..542d8be 100644
--- a/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread_html.js b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread_html.ts
similarity index 98%
rename from polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread_html.js
rename to polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread_html.ts
index 837a58f..4c15383 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.js b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.js
index 4026fa3..93f3fed 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.js
+++ b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.js
@@ -46,6 +46,7 @@
 const DRAFT_SINGULAR = 'draft...';
 const DRAFT_PLURAL = 'drafts...';
 const SAVED_MESSAGE = 'All changes saved';
+const UNSAVED_MESSAGE = 'Unable to save draft';
 
 const REPORT_CREATE_DRAFT = 'CreateDraftComment';
 const REPORT_UPDATE_DRAFT = 'UpdateDraftComment';
@@ -53,6 +54,8 @@
 
 const FILE = 'FILE';
 
+export const __testOnly_UNSAVED_MESSAGE = UNSAVED_MESSAGE;
+
 /**
  * All candidates tips to show, will pick randomly.
  */
@@ -219,6 +222,10 @@
         value: false,
       },
       _serverConfig: Object,
+      _unableToSave: {
+        type: Boolean,
+        value: false,
+      },
     };
   }
 
@@ -373,6 +380,16 @@
     return this.$.restAPI.getIsAdmin();
   }
 
+  _computeDraftTooltip(unableToSave) {
+    return unableToSave ? `Unable to save draft. Please try to save again.` :
+      `This draft is only visible to you. To publish drafts, click the 'Reply'`
+    + `or 'Start review' button at the top of the change or press the 'A' key.`;
+  }
+
+  _computeDraftText(unableToSave) {
+    return 'DRAFT' + (unableToSave ? '(Failed to save)' : '');
+  }
+
   /**
    * @param {*=} opt_comment
    */
@@ -453,10 +470,8 @@
    * @return {!Object}
    */
   _getEventPayload(opt_mixin) {
-    return Object.assign({}, opt_mixin, {
-      comment: this.comment,
-      patchNum: this.patchNum,
-    });
+    return {...opt_mixin, comment: this.comment,
+      patchNum: this.patchNum};
   }
 
   _fireSave() {
@@ -710,7 +725,10 @@
     this._closeOverlay(this.confirmDiscardOverlay);
   }
 
-  _getSavingMessage(numPending) {
+  _getSavingMessage(numPending, requestFailed) {
+    if (requestFailed) {
+      return UNSAVED_MESSAGE;
+    }
     if (numPending === 0) {
       return SAVED_MESSAGE;
     }
@@ -737,10 +755,12 @@
     // Cancel the debouncer so that error toasts from the error-manager will
     // not be overridden.
     this.cancelDebouncer('draft-toast');
+    this._updateRequestToast(this._numPendingDraftRequests.number,
+        /* requestFailed=*/true);
   }
 
-  _updateRequestToast(numPending) {
-    const message = this._getSavingMessage(numPending);
+  _updateRequestToast(numPending, requestFailed) {
+    const message = this._getSavingMessage(numPending, requestFailed);
     this.debounce('draft-toast', () => {
       // Note: the event is fired on the body rather than this element because
       // this element may not be attached by the time this executes, in which
@@ -754,9 +774,13 @@
     this._showStartRequest();
     return this.$.restAPI.saveDiffDraft(this.changeNum, this.patchNum, draft)
         .then(result => {
-          if (result.ok) {
+          if (result.ok) { // remove
+            this._unableToSave = false;
+            this.$.container.classList.remove('unableToSave');
             this._showEndRequest();
           } else {
+            this.$.container.classList.add('unableToSave');
+            this._unableToSave = true;
             this._handleFailedDraftRequest();
           }
           return result;
diff --git a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment_html.js b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment_html.ts
similarity index 97%
rename from polygerrit-ui/app/elements/shared/gr-comment/gr-comment_html.js
rename to polygerrit-ui/app/elements/shared/gr-comment/gr-comment_html.ts
index 47ac8a6..5087977 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
@@ -112,7 +112,7 @@
     .draft .draftTooltip {
       display: inline;
     }
-    .draft:not(.editing) .save,
+    .draft:not(.editing):not(.unableToSave) .save,
     .draft:not(.editing) .cancel {
       display: none;
     }
@@ -245,11 +245,11 @@
         <gr-tooltip-content
           class="draftTooltip"
           has-tooltip=""
-          title="This draft is only visible to you. To publish drafts, click the 'Reply' or 'Start review' button at the top of the change or press the 'A' key."
+          title="[[_computeDraftTooltip(_unableToSave)]]"
           max-width="20em"
           show-icon=""
         >
-          <span class="draftLabel">DRAFT</span>
+          <span class="draftLabel">[[_computeDraftText(_unableToSave)]]</span>
         </gr-tooltip-content>
       </div>
       <div class="headerMiddle">
diff --git a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment_test.js b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment_test.js
index eb82daa..16664cb 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment_test.js
@@ -18,6 +18,7 @@
 import '../../../test/common-test-setup-karma.js';
 import './gr-comment.js';
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {__testOnly_UNSAVED_MESSAGE} from './gr-comment.js';
 
 const basicFixture = fixtureFromElement('gr-comment');
 
@@ -354,6 +355,40 @@
           .querySelector('.discard'));
       assert.isTrue(reportStub.calledOnce);
     });
+
+    test('failed save draft request', done => {
+      element.draft = true;
+      const updateRequestStub = sinon.stub(element, '_updateRequestToast');
+      const diffDraftStub =
+        sinon.stub(element.$.restAPI, 'saveDiffDraft').returns(
+            Promise.resolve({ok: false}));
+      element._saveDraft();
+      flush(() => {
+        let args = updateRequestStub.lastCall.args;
+        assert.deepEqual(args, [0, true]);
+        assert.equal(element._getSavingMessage(...args),
+            __testOnly_UNSAVED_MESSAGE);
+        assert.equal(element.shadowRoot.querySelector('.draftLabel').innerText,
+            'DRAFT(Failed to save)');
+        assert.isTrue(isVisible(element.shadowRoot
+            .querySelector('.save')), 'save is visible');
+        diffDraftStub.returns(
+            Promise.resolve({ok: true}));
+        element._saveDraft();
+        flush(() => {
+          args = updateRequestStub.lastCall.args;
+          assert.deepEqual(args, [0]);
+          assert.equal(element._getSavingMessage(...args),
+              'All changes saved');
+          assert.equal(element.shadowRoot.querySelector('.draftLabel')
+              .innerText, 'DRAFT');
+          assert.isFalse(isVisible(element.shadowRoot
+              .querySelector('.save')), 'save is not visible');
+          assert.isFalse(element._unableToSave);
+          done();
+        });
+      });
+    });
   });
 
   suite('gr-comment draft tests', () => {
@@ -590,13 +625,11 @@
     });
 
     test('robot comment layout', done => {
-      const comment = Object.assign({
-        robot_id: 'happy_robot_id',
+      const comment = {robot_id: 'happy_robot_id',
         url: '/robot/comment',
         author: {
           name: 'Happy Robot',
-        },
-      }, element.comment);
+        }, ...element.comment};
       element.comment = comment;
       element.collapsed = false;
       flush(() => {
@@ -627,12 +660,10 @@
     });
 
     test('author name fallback to email', done => {
-      const comment = Object.assign({
-        url: '/robot/comment',
+      const comment = {url: '/robot/comment',
         author: {
           email: 'test@test.com',
-        },
-      }, element.comment);
+        }, ...element.comment};
       element.comment = comment;
       element.collapsed = false;
       flush(() => {
diff --git a/polygerrit-ui/app/elements/shared/gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog_html.js b/polygerrit-ui/app/elements/shared/gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog_html.ts
similarity index 96%
rename from polygerrit-ui/app/elements/shared/gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog_html.js
rename to polygerrit-ui/app/elements/shared/gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog_html.ts
index 2d0fa6f..6876c1a 100644
--- a/polygerrit-ui/app/elements/shared/gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard_html.js b/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard_html.ts
similarity index 96%
rename from polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard_html.js
rename to polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard_html.ts
index 9fc83c8..197c94c 100644
--- a/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager_html.js b/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager_html.ts
similarity index 91%
rename from polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager_html.js
rename to polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager_html.ts
index 3ed33d1..1489006 100644
--- a/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager_html.ts
@@ -14,6 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html``;
diff --git a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter_html.js b/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter_html.ts
similarity index 93%
rename from polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter_html.js
rename to polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter_html.ts
index 2571065..a5dd6d0 100644
--- a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog_html.js b/polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog_html.ts
similarity index 96%
rename from polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog_html.js
rename to polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog_html.ts
index 54b404d..f8ddcfd 100644
--- a/polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences_html.js b/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences_html.ts
similarity index 98%
rename from polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences_html.js
rename to polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences_html.ts
index 3ea40d9..f9d971d 100644
--- a/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands.js b/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands.js
index 4ecc99d..e3ae8e2 100644
--- a/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands.js
+++ b/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands.js
@@ -93,6 +93,11 @@
   _computeShowTabs(schemes) {
     return schemes.length > 1 ? '' : 'hidden';
   }
+
+  _computeClass(title) {
+    // Only retain [a-z] chars, so "Cherry Pick" becomes "cherrypick".
+    return '_label_' + title.replace(/[^a-z]+/gi, '').toLowerCase();
+  }
 }
 
 customElements.define(GrDownloadCommands.is, GrDownloadCommands);
diff --git a/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands_html.js b/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands_html.ts
similarity index 94%
rename from polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands_html.js
rename to polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands_html.ts
index 7248e65..35385fa 100644
--- a/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
@@ -66,6 +66,7 @@
   <div class="commands" hidden$="[[!schemes.length]]" hidden="">
     <template is="dom-repeat" items="[[commands]]" as="command">
       <gr-shell-command
+        class$="[[_computeClass(command.title)]]"
         label="[[command.title]]"
         command="[[command.command]]"
       ></gr-shell-command>
diff --git a/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list_html.js b/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list_html.ts
similarity index 98%
rename from polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list_html.js
rename to polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list_html.ts
index 0f80af2..629b0ad 100644
--- a/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown_html.js b/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown_html.ts
similarity index 98%
rename from polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown_html.js
rename to polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown_html.ts
index 6617a0e..8ea0d21 100644
--- a/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content_html.js b/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content_html.ts
similarity index 96%
rename from polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content_html.js
rename to polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content_html.ts
index 24eb0b0..81a2c2f 100644
--- a/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label_html.js b/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label_html.ts
similarity index 97%
rename from polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label_html.js
rename to polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label_html.ts
index a226e30..5e36166 100644
--- a/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel_html.js b/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel_html.ts
similarity index 95%
rename from polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel_html.js
rename to polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel_html.ts
index 61e8b24..ce475c6 100644
--- a/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text.js b/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text.js
index 039b95d..862c48c 100644
--- a/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text.js
+++ b/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text.js
@@ -83,7 +83,7 @@
 
     // Add new content.
     for (const node of this._computeNodes(this._computeBlocks(content))) {
-      container.appendChild(node);
+      if (node) container.appendChild(node);
     }
   }
 
@@ -276,7 +276,7 @@
       if (block.type === 'quote') {
         const bq = document.createElement('blockquote');
         for (const node of this._computeNodes(block.blocks)) {
-          bq.appendChild(node);
+          if (node) bq.appendChild(node);
         }
         return bq;
       }
@@ -300,6 +300,9 @@
         }
         return ul;
       }
+
+      console.warn('Unrecognized type.');
+      return;
     });
   }
 }
diff --git a/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text_html.js b/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text_html.ts
similarity index 96%
rename from polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text_html.js
rename to polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text_html.ts
index 5cb8670..468bbee 100644
--- a/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account.js b/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account.js
index 717a28e..a92ae4d 100644
--- a/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account.js
+++ b/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account.js
@@ -21,7 +21,6 @@
 import '../gr-button/gr-button.js';
 import '../gr-rest-api-interface/gr-rest-api-interface.js';
 import {hovercardBehaviorMixin} from '../gr-hovercard/gr-hovercard-behavior.js';
-import {GerritNav} from '../../core/gr-navigation/gr-navigation.js';
 import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
 import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin.js';
 import {PolymerElement} from '@polymer/polymer/polymer-element.js';
@@ -104,6 +103,20 @@
     return this.change.attention_set.hasOwnProperty(this.account._account_id);
   }
 
+  _computeReason(change) {
+    if (!change || !change.attention_set) return '';
+    const entry = change.attention_set[this.account._account_id];
+    if (!entry || !entry.reason) return '';
+    return entry.reason;
+  }
+
+  _computeLastUpdate(change) {
+    if (!change || !change.attention_set) return '';
+    const entry = change.attention_set[this.account._account_id];
+    if (!entry || !entry.last_update) return '';
+    return entry.last_update;
+  }
+
   _computeShowLabelNeedsAttention(config, highlightAttention, account, change) {
     return this.isAttentionSetEnabled && this.hasAttention;
   }
@@ -130,7 +143,8 @@
         this._reportingDetails());
     this.$.restAPI.addToAttentionSet(this.change._number,
         this.account._account_id, 'manually added').then(obj => {
-      GerritNav.navigateToChange(this.change);
+      this.dispatchEventThroughTarget('hide-alert');
+      this.dispatchEventThroughTarget('reload');
     });
     this.hide();
   }
@@ -148,7 +162,8 @@
         this._reportingDetails());
     this.$.restAPI.removeFromAttentionSet(this.change._number,
         this.account._account_id, 'manually removed').then(obj => {
-      GerritNav.navigateToChange(this.change);
+      this.dispatchEventThroughTarget('hide-alert');
+      this.dispatchEventThroughTarget('reload');
     });
     this.hide();
   }
diff --git a/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account_html.js b/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account_html.ts
similarity index 82%
rename from polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account_html.js
rename to polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account_html.ts
index 262b089..0075881 100644
--- a/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account_html.ts
@@ -14,8 +14,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../gr-hovercard/gr-hovercard-shared-style.js';
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import '../gr-hovercard/gr-hovercard-shared-style';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="gr-hovercard-shared-style">
@@ -63,6 +63,9 @@
       position: relative;
       top: 3px;
     }
+    .reason {
+      padding-top: var(--spacing-s);
+    }
   </style>
   <div id="container" role="tooltip" tabindex="-1">
     <template is="dom-if" if="[[_isShowing]]">
@@ -95,10 +98,20 @@
         if="[[_computeShowLabelNeedsAttention(_config, highlightAttention, account, change)]]"
       >
         <div class="attention">
-          <iron-icon icon="gr-icons:attention"></iron-icon>
-          <span>
-            [[_computeText(account, _selfAccount)]] turn to take action.
-          </span>
+          <div>
+            <iron-icon icon="gr-icons:attention"></iron-icon>
+            <span>
+              [[_computeText(account, _selfAccount)]] turn to take action.
+            </span>
+          </div>
+          <div class="reason">
+            <span class="title">Reason:</span>
+            <span class="value">[[_computeReason(change)]]</span>,
+            <gr-date-formatter
+              has-tooltip
+              date-str="[[_computeLastUpdate(change)]]"
+            ></gr-date-formatter>
+          </div>
         </div>
       </template>
       <template
@@ -107,6 +120,7 @@
       >
         <div class="action">
           <gr-button
+            class="addToAttentionSet"
             link=""
             no-uppercase=""
             on-click="_handleClickAddToAttentionSet"
@@ -121,6 +135,7 @@
       >
         <div class="action">
           <gr-button
+            class="removeFromAttentionSet"
             link=""
             no-uppercase=""
             on-click="_handleClickRemoveFromAttentionSet"
diff --git a/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account_test.js b/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account_test.js
index ea7eb87..6e611d5 100644
--- a/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account_test.js
@@ -39,7 +39,13 @@
         new Promise(resolve => { '2'; })
     );
 
-    element.account = Object.assign({}, ACCOUNT);
+    element.account = {...ACCOUNT};
+    element._config = {
+      change: {enable_attention_set: true},
+    };
+    element.change = {
+      attention_set: {},
+    };
     element.show({});
     flushAsynchronousOperations();
   });
@@ -53,6 +59,29 @@
         'Kermit The Frog');
   });
 
+  test('_computeReason', () => {
+    const change = {
+      attention_set: {
+        31415926535: {
+          reason: 'a good reason',
+        },
+      },
+    };
+    assert.equal(element._computeReason(change), 'a good reason');
+  });
+
+  test('_computeLastUpdate', () => {
+    const last_update = '2019-07-17 19:39:02.000000000';
+    const change = {
+      attention_set: {
+        31415926535: {
+          last_update,
+        },
+      },
+    };
+    assert.equal(element._computeLastUpdate(change), last_update);
+  });
+
   test('_computeText', () => {
     let account = {_account_id: '1'};
     const selfAccount = {_account_id: '1'};
@@ -66,7 +95,7 @@
   });
 
   test('account status is displayed', () => {
-    element.account = Object.assign({status: 'OOO'}, ACCOUNT);
+    element.account = {status: 'OOO', ...ACCOUNT};
     flushAsynchronousOperations();
     assert.equal(element.shadowRoot.querySelector('.status .value').innerText,
         'OOO');
@@ -82,5 +111,72 @@
     assert.equal(element.shadowRoot.querySelector('.voteable .value').innerText,
         element.voteableText);
   });
+
+  test('add to attention set', done => {
+    let apiResolve;
+    const apiPromise = new Promise(r => {
+      apiResolve = r;
+    });
+    sinon.stub(element.$.restAPI, 'addToAttentionSet')
+        .callsFake(() => apiPromise);
+    element.highlightAttention = true;
+    element._target = document.createElement('div');
+    flushAsynchronousOperations();
+    const showAlertListener = sinon.spy();
+    const hideAlertListener = sinon.spy();
+    const reloadListener = sinon.spy();
+    element.addEventListener('show-alert', showAlertListener);
+    element._target.addEventListener('hide-alert', hideAlertListener);
+    element._target.addEventListener('reload', reloadListener);
+
+    const button = element.shadowRoot.querySelector('.addToAttentionSet');
+    assert.isOk(button);
+    assert.isTrue(element._isShowing, 'hovercard is showing');
+    MockInteractions.tap(button);
+
+    assert.isTrue(showAlertListener.called, 'showAlertListener was called');
+    assert.isFalse(element._isShowing, 'hovercard is hidden');
+
+    apiResolve({});
+    flush(() => {
+      assert.isTrue(hideAlertListener.called, 'hideAlertListener was called');
+      assert.isTrue(reloadListener.called, 'reloadListener was called');
+      done();
+    });
+  });
+
+  test('remove from attention set', done => {
+    let apiResolve;
+    const apiPromise = new Promise(r => {
+      apiResolve = r;
+    });
+    sinon.stub(element.$.restAPI, 'removeFromAttentionSet')
+        .callsFake(() => apiPromise);
+    element.highlightAttention = true;
+    element.change = {attention_set: {31415926535: {}}};
+    element._target = document.createElement('div');
+    flushAsynchronousOperations();
+    const showAlertListener = sinon.spy();
+    const hideAlertListener = sinon.spy();
+    const reloadListener = sinon.spy();
+    element.addEventListener('show-alert', showAlertListener);
+    element._target.addEventListener('hide-alert', hideAlertListener);
+    element._target.addEventListener('reload', reloadListener);
+
+    const button = element.shadowRoot.querySelector('.removeFromAttentionSet');
+    assert.isOk(button);
+    assert.isTrue(element._isShowing, 'hovercard is showing');
+    MockInteractions.tap(button);
+
+    assert.isTrue(showAlertListener.called, 'showAlertListener was called');
+    assert.isFalse(element._isShowing, 'hovercard is hidden');
+
+    apiResolve({});
+    flush(() => {
+      assert.isTrue(hideAlertListener.called, 'hideAlertListener was called');
+      assert.isTrue(reloadListener.called, 'reloadListener was called');
+      done();
+    });
+  });
 });
 
diff --git a/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard-behavior.js b/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard-behavior.js
index 0d351f6..04c3166 100644
--- a/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard-behavior.js
+++ b/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard-behavior.js
@@ -161,6 +161,17 @@
   }
 
   /**
+   * Hovercard elements are created outside of <gr-app>, so if you want to fire
+   * events, then you probably want to do that through the target element.
+   */
+  dispatchEventThroughTarget(eventName) {
+    this._target.dispatchEvent(new CustomEvent(eventName, {
+      bubbles: true,
+      composed: true,
+    }));
+  }
+
+  /**
    * Returns the target element that the hovercard is anchored to (the `id` of
    * the `for` property).
    *
diff --git a/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard-shared-style.js b/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard-shared-style.ts
similarity index 84%
rename from polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard-shared-style.js
rename to polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard-shared-style.ts
index 5fb1add..4133fd1 100644
--- a/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard-shared-style.js
+++ b/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard-shared-style.ts
@@ -15,10 +15,14 @@
  * limitations under the License.
  */
 
+// Mark the file as a module. Otherwise typescript assumes this is a script
+// and $_documentContainer is a global variable.
+// See: https://www.typescriptlang.org/docs/handbook/modules.html
+export {};
+
 /** The shared styles for all hover cards. */
 const GrHoverCardSharedStyle = document.createElement('dom-module');
-GrHoverCardSharedStyle.innerHTML =
-  `<template>
+GrHoverCardSharedStyle.innerHTML = `<template>
     <style include="shared-styles">
       :host {
         position: absolute;
diff --git a/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard_html.js b/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard_html.ts
similarity index 92%
rename from polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard_html.js
rename to polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard_html.ts
index 67a3545..830cbd878 100644
--- a/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="gr-hovercard-shared-style">
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-reply-js-api.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-reply-js-api.js
index da0157f..2fa9acc 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-reply-js-api.js
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-reply-js-api.js
@@ -19,14 +19,14 @@
  * GrChangeReplyInterface, provides a set of handy methods on reply dialog.
  */
 export class GrChangeReplyInterface {
-  constructor(plugin) {
+  constructor(plugin, sharedApiElement) {
     this.plugin = plugin;
-    this._sharedApiEl = Plugin._sharedAPIElement;
+    this.sharedApiElement = sharedApiElement;
   }
 
   get _el() {
-    return this._sharedApiEl.getElement(
-        this._sharedApiEl.Element.REPLY_DIALOG);
+    return this.sharedApiElement.getElement(
+        this.sharedApiElement.Element.REPLY_DIALOG);
   }
 
   getLabelValue(label) {
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface-element.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface-element.js
index e215bc9..6c785d4 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface-element.js
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface-element.js
@@ -155,13 +155,11 @@
     //
     // assign on getter with existing property will report error
     // see Issue: 12286
-    const change = Object.assign({}, detail.change, {
-      get mergeable() {
-        console.warn('Accessing change.mergeable from SHOW_CHANGE is ' +
+    const change = {...detail.change, get mergeable() {
+      console.warn('Accessing change.mergeable from SHOW_CHANGE is ' +
             'deprecated! Use info.mergeable instead.');
-        return detail.info && detail.info.mergeable;
-      },
-    });
+      return detail.info && detail.info.mergeable;
+    }};
     const patchNum = detail.patchNum;
     const info = detail.info;
 
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.js
index 946325f..2a11f62 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.js
@@ -158,8 +158,11 @@
     const response = {status: 204};
     sendStub.returns(Promise.resolve(response));
     return plugin.delete('/url', r => {
-      assert.isTrue(sendStub.calledWithExactly(
-          'DELETE', 'http://test.com/plugins/testplugin/url'));
+      assert.equal(sendStub.lastCall.args[0], 'DELETE');
+      assert.equal(
+          sendStub.lastCall.args[1],
+          'http://test.com/plugins/testplugin/url'
+      );
       assert.strictEqual(r, response);
     });
   });
@@ -170,8 +173,11 @@
     return plugin.delete('/url', r => {
       throw new Error('Should not resolve');
     }).catch(err => {
-      assert.isTrue(sendStub.calledWith(
-          'DELETE', 'http://test.com/plugins/testplugin/url'));
+      assert.equal(sendStub.lastCall.args[0], 'DELETE');
+      assert.equal(
+          sendStub.lastCall.args[1],
+          'http://test.com/plugins/testplugin/url'
+      );
       assert.equal('text', err.message);
     });
   });
@@ -192,7 +198,7 @@
       _number: 42,
       revisions: {def: {_number: 2}, abc: {_number: 1}},
     };
-    const expectedChange = Object.assign({mergeable: false}, testChange);
+    const expectedChange = {mergeable: false, ...testChange};
     plugin.on(element.EventType.SHOW_CHANGE, throwErrFn);
     plugin.on(element.EventType.SHOW_CHANGE, (change, revision, info) => {
       assert.deepEqual(change, expectedChange);
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-endpoints.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-endpoints.js
index 2c97df0..dae8d3e 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-endpoints.js
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-endpoints.js
@@ -15,7 +15,6 @@
  * limitations under the License.
  */
 
-import {pluginLoader} from './gr-plugin-loader.js';
 import {importHref} from '../../../scripts/import-href.js';
 
 /** @constructor */
@@ -25,6 +24,11 @@
     this._callbacks = {};
     this._dynamicPlugins = {};
     this._importedUrls = new Set();
+    this._pluginLoaded = false;
+  }
+
+  setPluginsReady() {
+    this._pluginLoaded = true;
   }
 
   onNewEndpoint(endpoint, callback) {
@@ -89,7 +93,12 @@
       this._endpoints[endpoint] = [];
     }
     const moduleInfo = this._getOrCreateModuleInfo(plugin, opts);
-    if (pluginLoader.arePluginsLoaded() && this._callbacks[endpoint]) {
+    // TODO: the logic below seems wrong when:
+    // multiple plugins register to the same endpoint
+    // one register before plugins ready
+    // the other done after, then only the later one will have the callbacks
+    // invoked.
+    if (this._pluginLoaded && this._callbacks[endpoint]) {
       this._callbacks[endpoint].forEach(callback => callback(moduleInfo));
     }
   }
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-endpoints_test.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-endpoints_test.js
index f1af433..5b931b4 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-endpoints_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-endpoints_test.js
@@ -19,7 +19,6 @@
 import {resetPlugins} from '../../../test/test-utils.js';
 import './gr-js-api-interface.js';
 import {GrPluginEndpoints} from './gr-plugin-endpoints.js';
-import {pluginLoader} from './gr-plugin-loader.js';
 import {_testOnly_initGerritPluginApi} from './gr-gerrit.js';
 
 const pluginApi = _testOnly_initGerritPluginApi();
@@ -55,7 +54,6 @@
           domHook,
         }
     );
-    sinon.stub(pluginLoader, 'arePluginsLoaded').returns(true);
     sinon.spy(instance, 'importUrl');
   });
 
@@ -132,6 +130,7 @@
 
   test('onNewEndpoint', () => {
     const newModuleStub = sinon.stub();
+    instance.setPluginsReady();
     instance.onNewEndpoint('a-place', newModuleStub);
     instance.registerModule(
         pluginFoo,
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-loader.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-loader.js
index 0bf49c3..37aecf9 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-loader.js
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-loader.js
@@ -16,16 +16,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import './gr-api-utils.js';
 import {importHref} from '../../../scripts/import-href.js';
-
 import {
   PLUGIN_LOADING_TIMEOUT_MS,
   PRELOADED_PROTOCOL,
   getPluginNameFromUrl,
 } from './gr-api-utils.js';
-
+import {Plugin} from './gr-public-js-api.js';
 import {getBaseUrl} from '../../../utils/url-util.js';
+import {pluginEndpoints} from './gr-plugin-endpoints.js';
 
 /**
  * @enum {string}
@@ -210,10 +209,13 @@
   }
 
   _checkIfCompleted() {
-    if (this.arePluginsLoaded() && this._loadingResolver) {
-      this._loadingResolver();
-      this._loadingResolver = null;
-      this._loadingPromise = null;
+    if (this.arePluginsLoaded()) {
+      pluginEndpoints.setPluginsReady();
+      if (this._loadingResolver) {
+        this._loadingResolver();
+        this._loadingResolver = null;
+        this._loadingPromise = null;
+      }
     }
   }
 
@@ -261,7 +263,7 @@
     const pluginObj = this._updatePluginState(url, PluginState.LOADED);
     pluginObj.plugin = plugin;
     this._getReporting().pluginLoaded(plugin.getPluginName() || url);
-    console.log(`Plugin ${plugin.getPluginName() || url} installed.`);
+    console.info(`Plugin ${plugin.getPluginName() || url} installed.`);
     this._checkIfCompleted();
   }
 
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js
index 446ceb5..3611e8a 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js
@@ -16,6 +16,7 @@
  */
 
 import {getBaseUrl} from '../../../utils/url-util.js';
+import {getSharedApiEl} from '../../../utils/dom-util.js';
 import {GrAttributeHelper} from '../../plugins/gr-attribute-helper/gr-attribute-helper.js';
 import {GrChangeActionsInterface} from './gr-change-actions-js-api.js';
 import {GrChangeReplyInterface} from './gr-change-reply-js-api.js';
@@ -33,43 +34,42 @@
 import {GrPluginActionContext} from './gr-plugin-action-context.js';
 import {pluginEndpoints} from './gr-plugin-endpoints.js';
 
-import {PRELOADED_PROTOCOL, getPluginNameFromUrl, send} from './gr-api-utils.js';
-import {deprecatedDelete} from './gr-gerrit.js';
+import {
+  PRELOADED_PROTOCOL,
+  getPluginNameFromUrl,
+  send,
+} from './gr-api-utils.js';
 
-(function(window) {
-  'use strict';
+const PANEL_ENDPOINTS_MAPPING = {
+  CHANGE_SCREEN_BELOW_COMMIT_INFO_BLOCK: 'change-view-integration',
+  CHANGE_SCREEN_BELOW_CHANGE_INFO_BLOCK: 'change-metadata-item',
+};
 
-  const PANEL_ENDPOINTS_MAPPING = {
-    CHANGE_SCREEN_BELOW_COMMIT_INFO_BLOCK: 'change-view-integration',
-    CHANGE_SCREEN_BELOW_CHANGE_INFO_BLOCK: 'change-metadata-item',
-  };
+/**
+ * Plugin-provided custom components can affect content in extension
+ * points using one of following methods:
+ * - DECORATE: custom component is set with `content` attribute and may
+ *   decorate (e.g. style) DOM element.
+ * - REPLACE: contents of extension point are replaced with the custom
+ *   component.
+ * - STYLE: custom component is a shared styles module that is inserted
+ *   into the extension point.
+ */
+const EndpointType = {
+  DECORATE: 'decorate',
+  REPLACE: 'replace',
+  STYLE: 'style',
+};
 
-  /**
-   * Plugin-provided custom components can affect content in extension
-   * points using one of following methods:
-   * - DECORATE: custom component is set with `content` attribute and may
-   *   decorate (e.g. style) DOM element.
-   * - REPLACE: contents of extension point are replaced with the custom
-   *   component.
-   * - STYLE: custom component is a shared styles module that is inserted
-   *   into the extension point.
-   */
-  const EndpointType = {
-    DECORATE: 'decorate',
-    REPLACE: 'replace',
-    STYLE: 'style',
-  };
-
-  /**
-   * @constructor
-   * @param {string=} opt_url
-   */
-  function Plugin(opt_url) {
+export class Plugin {
+  constructor(opt_url) {
     this._domHooks = new GrDomHooksManager(this);
 
     if (!opt_url) {
-      console.warn('Plugin not being loaded from /plugins base path.',
-          'Unable to determine name.');
+      console.warn(
+          'Plugin not being loaded from /plugins base path.',
+          'Unable to determine name.'
+      );
       return this;
     }
     this.deprecated = {
@@ -84,29 +84,31 @@
 
     this._url = new URL(opt_url);
     this._name = getPluginNameFromUrl(this._url);
+    this.sharedApiElement = getSharedApiEl();
   }
 
-  Plugin._sharedAPIElement = document.createElement('gr-js-api-interface');
-
-  Plugin.prototype._name = '';
-
-  Plugin.prototype.getPluginName = function() {
+  getPluginName() {
     return this._name;
-  };
+  }
 
-  Plugin.prototype.registerStyleModule = function(endpoint, moduleName) {
-    pluginEndpoints.registerModule(
-        this, {endpoint, type: EndpointType.STYLE, moduleName});
-  };
+  registerStyleModule(endpoint, moduleName) {
+    pluginEndpoints.registerModule(this, {
+      endpoint,
+      type: EndpointType.STYLE,
+      moduleName,
+    });
+  }
 
   /**
    * Registers an endpoint for the plugin.
    */
-  Plugin.prototype.registerCustomComponent = function(
-      endpointName, opt_moduleName, opt_options) {
-    return this._registerCustomComponent(endpointName, opt_moduleName,
-        opt_options);
-  };
+  registerCustomComponent(endpointName, opt_moduleName, opt_options) {
+    return this._registerCustomComponent(
+        endpointName,
+        opt_moduleName,
+        opt_options
+    );
+  }
 
   /**
    * Registers a dynamic endpoint for the plugin.
@@ -114,125 +116,151 @@
    * Dynamic plugins are registered by specific prefix, such as
    * 'change-list-header'.
    */
-  Plugin.prototype.registerDynamicCustomComponent = function(
-      endpointName, opt_moduleName, opt_options) {
+  registerDynamicCustomComponent(endpointName, opt_moduleName, opt_options) {
     const fullEndpointName = `${endpointName}-${this.getPluginName()}`;
-    return this._registerCustomComponent(fullEndpointName, opt_moduleName,
-        opt_options, endpointName);
-  };
+    return this._registerCustomComponent(
+        fullEndpointName,
+        opt_moduleName,
+        opt_options,
+        endpointName
+    );
+  }
 
-  Plugin.prototype._registerCustomComponent = function(
-      endpoint, opt_moduleName, opt_options, dynamicEndpoint) {
-    const type = opt_options && opt_options.replace ?
-      EndpointType.REPLACE : EndpointType.DECORATE;
-    const slot = opt_options && opt_options.slot || '';
+  _registerCustomComponent(
+      endpoint,
+      opt_moduleName,
+      opt_options,
+      dynamicEndpoint
+  ) {
+    const type =
+      opt_options && opt_options.replace
+        ? EndpointType.REPLACE
+        : EndpointType.DECORATE;
+    const slot = (opt_options && opt_options.slot) || '';
     const domHook = this._domHooks.getDomHook(endpoint, opt_moduleName);
     const moduleName = opt_moduleName || domHook.getModuleName();
-    pluginEndpoints.registerModule(
-        this, {slot, endpoint, type, moduleName, domHook, dynamicEndpoint});
+    pluginEndpoints.registerModule(this, {
+      slot,
+      endpoint,
+      type,
+      moduleName,
+      domHook,
+      dynamicEndpoint,
+    });
     return domHook.getPublicAPI();
-  };
+  }
 
   /**
    * Returns instance of DOM hook API for endpoint. Creates a placeholder
    * element for the first call.
    */
-  Plugin.prototype.hook = function(endpointName, opt_options) {
+  hook(endpointName, opt_options) {
     return this.registerCustomComponent(endpointName, undefined, opt_options);
-  };
+  }
 
-  Plugin.prototype.getServerInfo = function() {
+  getServerInfo() {
     return document.createElement('gr-rest-api-interface').getConfig();
-  };
+  }
 
-  Plugin.prototype.on = function(eventName, callback) {
-    Plugin._sharedAPIElement.addEventCallback(eventName, callback);
-  };
+  on(eventName, callback) {
+    this.sharedApiElement.addEventCallback(eventName, callback);
+  }
 
-  Plugin.prototype.url = function(opt_path) {
+  url(opt_path) {
     const relPath = '/plugins/' + this._name + (opt_path || '/');
-    const sameOriginPath = window.location.origin +
-      `${getBaseUrl()}${relPath}`;
+    const sameOriginPath = window.location.origin + `${getBaseUrl()}${relPath}`;
     if (window.location.origin === this._url.origin) {
       // Plugin loaded from the same origin as gr-app, getBaseUrl in effect.
       return sameOriginPath;
     } else if (this._url.protocol === PRELOADED_PROTOCOL) {
       // Plugin is preloaded, load plugin with ASSETS_PATH or location.origin
-      return window.ASSETS_PATH ? `${window.ASSETS_PATH}${relPath}` :
-        sameOriginPath;
+      return window.ASSETS_PATH
+        ? `${window.ASSETS_PATH}${relPath}`
+        : sameOriginPath;
     } else {
       // Plugin loaded from assets bundle, expect assets placed along with it.
       return this._url.href.split('/plugins/' + this._name)[0] + relPath;
     }
-  };
+  }
 
-  Plugin.prototype.screenUrl = function(opt_screenName) {
+  screenUrl(opt_screenName) {
     const origin = location.origin;
     const base = getBaseUrl();
     const tokenPart = opt_screenName ? '/' + opt_screenName : '';
     return `${origin}${base}/x/${this.getPluginName()}${tokenPart}`;
-  };
+  }
 
-  Plugin.prototype._send = function(method, url, opt_callback, opt_payload) {
+  _send(method, url, opt_callback, opt_payload) {
     return send(method, this.url(url), opt_callback, opt_payload);
-  };
+  }
 
-  Plugin.prototype.get = function(url, opt_callback) {
+  get(url, opt_callback) {
     console.warn('.get() is deprecated! Use .restApi().get()');
     return this._send('GET', url, opt_callback);
-  };
+  }
 
-  Plugin.prototype.post = function(url, payload, opt_callback) {
+  post(url, payload, opt_callback) {
     console.warn('.post() is deprecated! Use .restApi().post()');
     return this._send('POST', url, opt_callback, payload);
-  };
+  }
 
-  Plugin.prototype.put = function(url, payload, opt_callback) {
+  put(url, payload, opt_callback) {
     console.warn('.put() is deprecated! Use .restApi().put()');
     return this._send('PUT', url, opt_callback, payload);
-  };
+  }
 
-  Plugin.prototype.delete = function(url, opt_callback) {
-    return deprecatedDelete(this.url(url), opt_callback);
-  };
+  delete(url, opt_callback) {
+    console.warn('.delete() is deprecated! Use plugin.restApi().delete()');
+    return this.restApi()
+        .delete(this.url(url))
+        .then(res => {
+          if (opt_callback) {
+            opt_callback(res);
+          }
+          return res;
+        });
+  }
 
-  Plugin.prototype.annotationApi = function() {
+  annotationApi() {
     return new GrAnnotationActionsInterface(this);
-  };
+  }
 
-  Plugin.prototype.changeActions = function() {
-    return new GrChangeActionsInterface(this,
-        Plugin._sharedAPIElement.getElement(
-            Plugin._sharedAPIElement.Element.CHANGE_ACTIONS));
-  };
+  changeActions() {
+    return new GrChangeActionsInterface(
+        this,
+        this.sharedApiElement.getElement(
+            this.sharedApiElement.Element.CHANGE_ACTIONS
+        )
+    );
+  }
 
-  Plugin.prototype.changeReply = function() {
-    return new GrChangeReplyInterface(this);
-  };
+  changeReply() {
+    return new GrChangeReplyInterface(this, this.sharedApiElement);
+  }
 
-  Plugin.prototype.theme = function() {
+  theme() {
     return new GrThemeApi(this);
-  };
+  }
 
-  Plugin.prototype.project = function() {
+  project() {
     return new GrRepoApi(this);
-  };
+  }
 
-  Plugin.prototype.changeMetadata = function() {
+  changeMetadata() {
     return new GrChangeMetadataApi(this);
-  };
+  }
 
-  Plugin.prototype.admin = function() {
+  admin() {
     return new GrAdminApi(this);
-  };
+  }
 
-  Plugin.prototype.settings = function() {
+  settings() {
     return new GrSettingsApi(this);
-  };
+  }
 
-  Plugin.prototype.styles = function() {
+  styles() {
     return new GrStylesApi();
-  };
+  }
 
   /**
    * To make REST requests for plugin-provided endpoints, use
@@ -242,158 +270,172 @@
    *
    * @param {string=} opt_prefix url for subsequent .get(), .post() etc requests.
    */
-  Plugin.prototype.restApi = function(opt_prefix) {
+  restApi(opt_prefix) {
     return new GrPluginRestApi(opt_prefix);
-  };
+  }
 
-  Plugin.prototype.attributeHelper = function(element) {
+  attributeHelper(element) {
     return new GrAttributeHelper(element);
-  };
+  }
 
-  Plugin.prototype.eventHelper = function(element) {
+  eventHelper(element) {
     return new GrEventHelper(element);
-  };
+  }
 
-  Plugin.prototype.popup = function(moduleName) {
+  popup(moduleName) {
     if (typeof moduleName !== 'string') {
       console.error('.popup(element) deprecated, use .popup(moduleName)!');
       return;
     }
     const api = new GrPopupInterface(this, moduleName);
     return api.open();
-  };
+  }
 
-  Plugin.prototype.panel = function() {
-    console.error('.panel() is deprecated! ' +
-        'Use registerCustomComponent() instead.');
-  };
+  panel() {
+    console.error(
+        '.panel() is deprecated! ' + 'Use registerCustomComponent() instead.'
+    );
+  }
 
-  Plugin.prototype.settingsScreen = function() {
-    console.error('.settingsScreen() is deprecated! ' +
-        'Use .settings() instead.');
-  };
+  settingsScreen() {
+    console.error(
+        '.settingsScreen() is deprecated! ' + 'Use .settings() instead.'
+    );
+  }
 
-  Plugin.prototype.screen = function(screenName, opt_moduleName) {
+  screen(screenName, opt_moduleName) {
     if (opt_moduleName && typeof opt_moduleName !== 'string') {
-      console.error('.screen(pattern, callback) deprecated, use ' +
-          '.screen(screenName, opt_moduleName)!');
+      console.error(
+          '.screen(pattern, callback) deprecated, use ' +
+          '.screen(screenName, opt_moduleName)!'
+      );
       return;
     }
     return this.registerCustomComponent(
         this._getScreenName(screenName),
-        opt_moduleName);
-  };
+        opt_moduleName
+    );
+  }
 
-  Plugin.prototype._getScreenName = function(screenName) {
+  _getScreenName(screenName) {
     return `${this.getPluginName()}-screen-${screenName}`;
-  };
+  }
+}
 
-  const deprecatedAPI = {
-    _loadedGwt: ()=> {},
+// TODO: should be removed soon after all core plugins moved away from it.
+const deprecatedAPI = {
+  _loadedGwt: () => {},
 
-    install() {
-      console.log('Installing deprecated APIs is deprecated!');
-      for (const method in this.deprecated) {
-        if (method === 'install') continue;
-        this[method] = this.deprecated[method];
-      }
-    },
+  install() {
+    console.info('Installing deprecated APIs is deprecated!');
+    for (const method in this.deprecated) {
+      if (method === 'install') continue;
+      this[method] = this.deprecated[method];
+    }
+  },
 
-    popup(el) {
-      console.warn('plugin.deprecated.popup() is deprecated, ' +
-          'use plugin.popup() insted!');
-      if (!el) {
-        throw new Error('Popup contents not found');
-      }
-      const api = new GrPopupInterface(this);
-      api.open().then(api => api._getElement().appendChild(el));
-      return api;
-    },
+  popup(el) {
+    console.warn(
+        'plugin.deprecated.popup() is deprecated, '
+        + 'use plugin.popup() insted!'
+    );
+    if (!el) {
+      throw new Error('Popup contents not found');
+    }
+    const api = new GrPopupInterface(this);
+    api.open().then(api => api._getElement().appendChild(el));
+    return api;
+  },
 
-    onAction(type, action, callback) {
-      console.warn('plugin.deprecated.onAction() is deprecated,' +
-          ' use plugin.changeActions() instead!');
-      if (type !== 'change' && type !== 'revision') {
-        console.warn(`${type} actions are not supported.`);
+  onAction(type, action, callback) {
+    console.warn(
+        'plugin.deprecated.onAction() is deprecated,' +
+        ' use plugin.changeActions() instead!'
+    );
+    if (type !== 'change' && type !== 'revision') {
+      console.warn(`${type} actions are not supported.`);
+      return;
+    }
+    this.on('showchange', (change, revision) => {
+      const details = this.changeActions().getActionDetails(action);
+      if (!details) {
+        console.warn(
+            `${this.getPluginName()} onAction error: ${action} not found!`
+        );
         return;
       }
-      this.on('showchange', (change, revision) => {
-        const details = this.changeActions().getActionDetails(action);
-        if (!details) {
-          console.warn(
-              `${this.getPluginName()} onAction error: ${action} not found!`);
-          return;
-        }
-        this.changeActions().addTapListener(details.__key, () => {
-          callback(new GrPluginActionContext(this, details, change, revision));
-        });
+      this.changeActions().addTapListener(details.__key, () => {
+        callback(new GrPluginActionContext(this, details, change, revision));
       });
-    },
+    });
+  },
 
-    screen(pattern, callback) {
-      console.warn('plugin.deprecated.screen is deprecated,' +
-          ' use plugin.screen instead!');
-      if (pattern instanceof RegExp) {
-        console.error('deprecated.screen() does not support RegExp. ' +
-            'Please use strings for patterns.');
-        return;
-      }
-      this.hook(this._getScreenName(pattern))
-          .onAttached(el => {
-            el.style.display = 'none';
-            callback({
-              body: el,
-              token: el.token,
-              onUnload: () => {},
-              setTitle: () => {},
-              setWindowTitle: () => {},
-              show: () => {
-                el.style.display = 'initial';
-              },
-            });
-          });
-    },
-
-    settingsScreen(path, menu, callback) {
-      console.warn('.settingsScreen() is deprecated! Use .settings() instead.');
-      const hook = this.settings()
-          .title(menu)
-          .token(path)
-          .module('div')
-          .build();
-      hook.onAttached(el => {
-        el.style.display = 'none';
-        const body = el.querySelector('div');
-        callback({
-          body,
-          onUnload: () => {},
-          setTitle: () => {},
-          setWindowTitle: () => {},
-          show: () => {
-            el.style.display = 'initial';
-          },
-        });
+  screen(pattern, callback) {
+    console.warn(
+        'plugin.deprecated.screen is deprecated,'
+        + ' use plugin.screen instead!'
+    );
+    if (pattern instanceof RegExp) {
+      console.error(
+          'deprecated.screen() does not support RegExp. ' +
+          'Please use strings for patterns.'
+      );
+      return;
+    }
+    this.hook(this._getScreenName(pattern)).onAttached(el => {
+      el.style.display = 'none';
+      callback({
+        body: el,
+        token: el.token,
+        onUnload: () => {},
+        setTitle: () => {},
+        setWindowTitle: () => {},
+        show: () => {
+          el.style.display = 'initial';
+        },
       });
-    },
+    });
+  },
 
-    panel(extensionpoint, callback) {
-      console.warn('.panel() is deprecated! ' +
-          'Use registerCustomComponent() instead.');
-      const endpoint = PANEL_ENDPOINTS_MAPPING[extensionpoint];
-      if (!endpoint) {
-        console.warn(`.panel ${extensionpoint} not supported!`);
-        return;
-      }
-      this.hook(endpoint).onAttached(el => callback({
+  settingsScreen(path, menu, callback) {
+    console.warn('.settingsScreen() is deprecated! Use .settings() instead.');
+    const hook = this.settings().title(menu)
+        .token(path)
+        .module('div')
+        .build();
+    hook.onAttached(el => {
+      el.style.display = 'none';
+      const body = el.querySelector('div');
+      callback({
+        body,
+        onUnload: () => {},
+        setTitle: () => {},
+        setWindowTitle: () => {},
+        show: () => {
+          el.style.display = 'initial';
+        },
+      });
+    });
+  },
+
+  panel(extensionpoint, callback) {
+    console.warn(
+        '.panel() is deprecated! ' + 'Use registerCustomComponent() instead.'
+    );
+    const endpoint = PANEL_ENDPOINTS_MAPPING[extensionpoint];
+    if (!endpoint) {
+      console.warn(`.panel ${extensionpoint} not supported!`);
+      return;
+    }
+    this.hook(endpoint).onAttached(el =>
+      callback({
         body: el,
         p: {
           CHANGE_INFO: el.change,
           REVISION_INFO: el.revision,
         },
         onUnload: () => {},
-      }));
-    },
-  };
-
-  window.Plugin = Plugin;
-})(window);
+      })
+    );
+  },
+};
\ No newline at end of file
diff --git a/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info_html.js b/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info_html.ts
similarity index 97%
rename from polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info_html.js
rename to polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info_html.ts
index 1722d02..dbc3b5e 100644
--- a/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="gr-voting-styles">
diff --git a/polygerrit-ui/app/elements/shared/gr-label/gr-label_html.js b/polygerrit-ui/app/elements/shared/gr-label/gr-label_html.ts
similarity index 91%
rename from polygerrit-ui/app/elements/shared/gr-label/gr-label_html.js
rename to polygerrit-ui/app/elements/shared/gr-label/gr-label_html.ts
index c4310fc..94196df 100644
--- a/polygerrit-ui/app/elements/shared/gr-label/gr-label_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-label/gr-label_html.ts
@@ -14,6 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html` <slot></slot> `;
diff --git a/polygerrit-ui/app/elements/shared/gr-labeled-autocomplete/gr-labeled-autocomplete_html.js b/polygerrit-ui/app/elements/shared/gr-labeled-autocomplete/gr-labeled-autocomplete_html.ts
similarity index 96%
rename from polygerrit-ui/app/elements/shared/gr-labeled-autocomplete/gr-labeled-autocomplete_html.js
rename to polygerrit-ui/app/elements/shared/gr-labeled-autocomplete/gr-labeled-autocomplete_html.ts
index 615a525..fa50624 100644
--- a/polygerrit-ui/app/elements/shared/gr-labeled-autocomplete/gr-labeled-autocomplete_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-labeled-autocomplete/gr-labeled-autocomplete_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader_html.js b/polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader_html.ts
similarity index 91%
rename from polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader_html.js
rename to polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader_html.ts
index 204aa87..f34f99e 100644
--- a/polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <gr-js-api-interface id="jsAPI"></gr-js-api-interface>
diff --git a/polygerrit-ui/app/elements/shared/gr-limited-text/gr-limited-text_html.js b/polygerrit-ui/app/elements/shared/gr-limited-text/gr-limited-text_html.ts
similarity index 91%
rename from polygerrit-ui/app/elements/shared/gr-limited-text/gr-limited-text_html.js
rename to polygerrit-ui/app/elements/shared/gr-limited-text/gr-limited-text_html.ts
index 6bcce8c..b942d07 100644
--- a/polygerrit-ui/app/elements/shared/gr-limited-text/gr-limited-text_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-limited-text/gr-limited-text_html.ts
@@ -14,6 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html` [[_computeDisplayText(text, limit)]] `;
diff --git a/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip_html.js b/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip_html.ts
similarity index 96%
rename from polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip_html.js
rename to polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip_html.ts
index f1f5f46..a335db7 100644
--- a/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text_html.js b/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text_html.ts
similarity index 94%
rename from polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text_html.js
rename to polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text_html.ts
index 59bed1e..4bdc1ab 100644
--- a/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/shared/gr-list-view/gr-list-view_html.js b/polygerrit-ui/app/elements/shared/gr-list-view/gr-list-view_html.ts
similarity index 97%
rename from polygerrit-ui/app/elements/shared/gr-list-view/gr-list-view_html.js
rename to polygerrit-ui/app/elements/shared/gr-list-view/gr-list-view_html.ts
index ff73d4d8..75ee667 100644
--- a/polygerrit-ui/app/elements/shared/gr-list-view/gr-list-view_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-list-view/gr-list-view_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay_html.js b/polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay_html.ts
similarity index 94%
rename from polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay_html.js
rename to polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay_html.ts
index 7123adb..730eeac 100644
--- a/polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/shared/gr-page-nav/gr-page-nav_html.js b/polygerrit-ui/app/elements/shared/gr-page-nav/gr-page-nav_html.ts
similarity index 94%
rename from polygerrit-ui/app/elements/shared/gr-page-nav/gr-page-nav_html.js
rename to polygerrit-ui/app/elements/shared/gr-page-nav/gr-page-nav_html.ts
index c5e9142..facc4f8 100644
--- a/polygerrit-ui/app/elements/shared/gr-page-nav/gr-page-nav_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-page-nav/gr-page-nav_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/shared/gr-repo-branch-picker/gr-repo-branch-picker_html.js b/polygerrit-ui/app/elements/shared/gr-repo-branch-picker/gr-repo-branch-picker_html.ts
similarity index 95%
rename from polygerrit-ui/app/elements/shared/gr-repo-branch-picker/gr-repo-branch-picker_html.js
rename to polygerrit-ui/app/elements/shared/gr-repo-branch-picker/gr-repo-branch-picker_html.ts
index 0ce885a..934b3cb 100644
--- a/polygerrit-ui/app/elements/shared/gr-repo-branch-picker/gr-repo-branch-picker_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-repo-branch-picker/gr-repo-branch-picker_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-etag-decorator.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-etag-decorator.js
index a4adbac..b364caf 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-etag-decorator.js
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-etag-decorator.js
@@ -38,7 +38,7 @@
   if (!etag) {
     return opt_options;
   }
-  const options = Object.assign({}, opt_options);
+  const options = {...opt_options};
   options.headers = options.headers || new Headers();
   options.headers.set('If-None-Match', this._etags.get(url));
   return options;
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
index 5f8bb9b..58d53bb 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
@@ -23,8 +23,8 @@
 import {SiteBasedCache, FetchPromisesCache, GrRestApiHelper} from './gr-rest-apis/gr-rest-api-helper.js';
 import {GrReviewerUpdatesParser} from './gr-reviewer-updates-parser.js';
 import {parseDate} from '../../../utils/date-util.js';
-import {authService} from './gr-auth.js';
 import {getBaseUrl} from '../../../utils/url-util.js';
+import {appContext} from '../../../services/app-context.js';
 import {
   getParentIndex,
   isMergeParent,
@@ -83,7 +83,7 @@
   pendingRequest = {};
   grEtagDecorator = new GrEtagDecorator;
   projectLookup = {};
-  authService.clearCache();
+  appContext.authService.clearCache();
 }
 
 /**
@@ -146,7 +146,7 @@
   /** @override */
   created() {
     super.created();
-    this._auth = authService;
+    this.authService = appContext.authService;
     this._initRestApiHelper();
   }
 
@@ -154,8 +154,8 @@
     if (this._restApiHelper) {
       return;
     }
-    if (this._cache && this._auth && this._sharedFetchPromises) {
-      this._restApiHelper = new GrRestApiHelper(this._cache, this._auth,
+    if (this._cache && this.authService && this._sharedFetchPromises) {
+      this._restApiHelper = new GrRestApiHelper(this._cache, this.authService,
           this._sharedFetchPromises, this);
     }
   }
@@ -748,7 +748,7 @@
     if (cachedAccount) {
       // Replace object in cache with new object to force UI updates.
       this._cache.set('/accounts/self/detail',
-          Object.assign({}, cachedAccount, obj));
+          {...cachedAccount, ...obj});
     }
   }
 
@@ -867,7 +867,7 @@
   }
 
   getLoggedIn() {
-    return this._auth.authCheck();
+    return this.authService.authCheck();
   }
 
   getIsAdmin() {
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.js
index c7f46912..aadde88 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.js
@@ -19,8 +19,8 @@
 import './gr-rest-api-interface.js';
 import {mockPromise} from '../../../test/test-utils.js';
 import {GrReviewerUpdatesParser} from './gr-reviewer-updates-parser.js';
-import {authService} from './gr-auth.js';
 import {ListChangesOption} from '../../../utils/change-util.js';
+import {appContext} from '../../../services/app-context.js';
 
 const basicFixture = fixtureFromElement('gr-rest-api-interface');
 
@@ -44,7 +44,8 @@
       },
     }));
     // fake auth
-    sinon.stub(authService, 'authCheck').returns(Promise.resolve(true));
+    sinon.stub(appContext.authService, 'authCheck')
+        .returns(Promise.resolve(true));
     element = basicFixture.instantiate();
     element._projectLookup = {};
   });
@@ -871,9 +872,9 @@
   });
 
   test('gerrit auth is used', () => {
-    sinon.stub(authService, 'fetch').returns(Promise.resolve());
+    sinon.stub(appContext.authService, 'fetch').returns(Promise.resolve());
     element._restApiHelper.fetchJSON({url: 'foo'});
-    assert(authService.fetch.called);
+    assert(appContext.authService.fetch.called);
   });
 
   test('getSuggestedAccounts does not return _fetchJSON', () => {
@@ -1295,7 +1296,7 @@
       done();
     };
     element.addEventListener('server-error', handler);
-    sinon.stub(authService, 'fetch').returns(Promise.resolve(res));
+    sinon.stub(appContext.authService, 'fetch').returns(Promise.resolve(res));
     sinon.stub(element, '_changeBaseURL').returns(Promise.resolve(''));
     element.getFileContent('1', 'tst/path', '1').then(() => {
       flushAsynchronousOperations();
@@ -1327,7 +1328,7 @@
     const response = {status: 404, text: sinon.stub()};
     const url = 'my url';
     const fetchOptions = {method: 'DELETE'};
-    sinon.stub(element._auth, 'fetch').returns(Promise.resolve(response));
+    sinon.stub(element.authService, 'fetch').returns(Promise.resolve(response));
     const startTime = 123;
     sinon.stub(Date, 'now').returns(startTime);
     const req = {url, fetchOptions};
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper.js
index d54d342..f0932b6 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper.js
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper.js
@@ -150,7 +150,7 @@
     const elapsed = (endTime - startTime);
     const startAt = new Date(startTime);
     const endAt = new Date(endTime);
-    console.log([
+    console.info([
       'HTTP',
       status,
       method,
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper_test.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper_test.js
index 9cac375..a50a9e7 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper_test.js
@@ -18,7 +18,7 @@
 import '../../../../test/common-test-setup-karma.js';
 import {SiteBasedCache} from './gr-rest-api-helper.js';
 import {FetchPromisesCache, GrRestApiHelper} from './gr-rest-api-helper.js';
-import {authService} from '../gr-auth.js';
+import {appContext} from '../../../../services/app-context.js';
 
 suite('gr-rest-api-helper tests', () => {
   let helper;
@@ -46,8 +46,8 @@
       },
     }));
 
-    helper = new GrRestApiHelper(cache, authService, fetchPromisesCache,
-        mockRestApiInterface);
+    helper = new GrRestApiHelper(cache, appContext.authService,
+        fetchPromisesCache, mockRestApiInterface);
   });
 
   teardown(() => {
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser.js
index ed474d8..49887240 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser.js
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser.js
@@ -18,212 +18,220 @@
 import {parseDate} from '../../../utils/date-util.js';
 import {MessageTag} from '../../../constants/constants.js';
 
-/** @constructor */
-export function GrReviewerUpdatesParser(change) {
-  this.result = Object.assign({}, change);
-  this._lastState = {};
-}
+const MESSAGE_REVIEWERS_THRESHOLD_MILLIS = 500;
+const REVIEWER_UPDATE_THRESHOLD_MILLIS = 6000;
 
-GrReviewerUpdatesParser.parse = function(change) {
-  if (!change ||
-      !change.messages ||
-      !change.reviewer_updates ||
-      !change.reviewer_updates.length) {
-    return change;
+export class GrReviewerUpdatesParser {
+  constructor(change) {
+    this.result = {...change};
+    this._lastState = {};
+    this._batch = null;
+    this._updateItems = null;
   }
-  const parser = new GrReviewerUpdatesParser(change);
-  parser._filterRemovedMessages();
-  parser._groupUpdates();
-  parser._formatUpdates();
-  parser._advanceUpdates();
-  return parser.result;
-};
 
-GrReviewerUpdatesParser.MESSAGE_REVIEWERS_THRESHOLD_MILLIS = 500;
-GrReviewerUpdatesParser.REVIEWER_UPDATE_THRESHOLD_MILLIS = 6000;
-
-GrReviewerUpdatesParser.prototype.result = null;
-GrReviewerUpdatesParser.prototype._batch = null;
-GrReviewerUpdatesParser.prototype._updateItems = null;
-GrReviewerUpdatesParser.prototype._lastState = null;
-
-/**
- * Removes messages that describe removed reviewers, since reviewer_updates
- * are used.
- */
-GrReviewerUpdatesParser.prototype._filterRemovedMessages = function() {
-  this.result.messages = this.result.messages
-      .filter(message => message.tag !== MessageTag.TAG_DELETE_REVIEWER);
-};
-
-/**
- * Is a part of _groupUpdates(). Creates a new batch of updates.
- *
- * @param {Object} update instance of ReviewerUpdateInfo
- */
-GrReviewerUpdatesParser.prototype._startBatch = function(update) {
-  this._updateItems = [];
-  return {
-    author: update.updated_by,
-    date: update.updated,
-    type: 'REVIEWER_UPDATE',
-    tag: MessageTag.TAG_REVIEWER_UPDATE,
-  };
-};
-
-/**
- * Is a part of _groupUpdates(). Validates current batch:
- * - filters out updates that don't change reviewer state.
- * - updates current reviewer state.
- *
- * @param {Object} update instance of ReviewerUpdateInfo
- */
-GrReviewerUpdatesParser.prototype._completeBatch = function(update) {
-  const items = [];
-  for (const accountId in this._updateItems) {
-    if (!this._updateItems.hasOwnProperty(accountId)) continue;
-    const updateItem = this._updateItems[accountId];
-    if (this._lastState[accountId] !== updateItem.state) {
-      this._lastState[accountId] = updateItem.state;
-      items.push(updateItem);
-    }
+  /**
+   * Removes messages that describe removed reviewers, since reviewer_updates
+   * are used.
+   */
+  _filterRemovedMessages() {
+    this.result.messages = this.result.messages.filter(
+        message => message.tag !== MessageTag.TAG_DELETE_REVIEWER
+    );
   }
-  if (items.length) {
-    this._batch.updates = items;
-  }
-};
 
-/**
- * Groups reviewer updates. Sequential updates are grouped if:
- * - They were performed within short timeframe (6 seconds)
- * - Made by the same person
- * - Non-change updates are discarded within a group
- * - Groups with no-change updates are discarded (eg CC -> CC)
- */
-GrReviewerUpdatesParser.prototype._groupUpdates = function() {
-  const updates = this.result.reviewer_updates;
-  const newUpdates = updates.reduce((newUpdates, update) => {
-    if (!this._batch) {
-      this._batch = this._startBatch(update);
-    }
-    const updateDate = parseDate(update.updated).getTime();
-    const batchUpdateDate = parseDate(this._batch.date).getTime();
-    const reviewerId = update.reviewer._account_id.toString();
-    if (updateDate - batchUpdateDate >
-        GrReviewerUpdatesParser.REVIEWER_UPDATE_THRESHOLD_MILLIS ||
-        update.updated_by._account_id !== this._batch.author._account_id) {
-      // Next sequential update should form new group.
-      this._completeBatch();
-      if (this._batch.updates && this._batch.updates.length) {
-        newUpdates.push(this._batch);
-      }
-      this._batch = this._startBatch(update);
-    }
-    this._updateItems[reviewerId] = {
-      reviewer: update.reviewer,
-      state: update.state,
+  /**
+   * Is a part of _groupUpdates(). Creates a new batch of updates.
+   *
+   * @param {Object} update instance of ReviewerUpdateInfo
+   */
+  _startBatch(update) {
+    this._updateItems = [];
+    return {
+      author: update.updated_by,
+      date: update.updated,
+      type: 'REVIEWER_UPDATE',
+      tag: MessageTag.TAG_REVIEWER_UPDATE,
     };
-    if (this._lastState[reviewerId]) {
-      this._updateItems[reviewerId].prev_state = this._lastState[reviewerId];
-    }
-    return newUpdates;
-  }, []);
-  this._completeBatch();
-  if (this._batch.updates && this._batch.updates.length) {
-    newUpdates.push(this._batch);
   }
-  this.result.reviewer_updates = newUpdates;
-};
 
-/**
- * Generates update message for reviewer state change.
- *
- * @param {string} prev previous reviewer state.
- * @param {string} state current reviewer state.
- * @return {string}
- */
-GrReviewerUpdatesParser.prototype._getUpdateMessage = function(prev, state) {
-  if (prev === 'REMOVED' || !prev) {
-    return 'Added to ' + state.toLowerCase() + ': ';
-  } else if (state === 'REMOVED') {
-    if (prev) {
-      return 'Removed from ' + prev.toLowerCase() + ': ';
+  /**
+   * Is a part of _groupUpdates(). Validates current batch:
+   * - filters out updates that don't change reviewer state.
+   * - updates current reviewer state.
+   *
+   * @param {Object} update instance of ReviewerUpdateInfo
+   */
+  _completeBatch(update) {
+    const items = [];
+    for (const accountId in this._updateItems) {
+      if (!this._updateItems.hasOwnProperty(accountId)) continue;
+      const updateItem = this._updateItems[accountId];
+      if (this._lastState[accountId] !== updateItem.state) {
+        this._lastState[accountId] = updateItem.state;
+        items.push(updateItem);
+      }
+    }
+    if (items.length) {
+      this._batch.updates = items;
+    }
+  }
+
+  /**
+   * Groups reviewer updates. Sequential updates are grouped if:
+   * - They were performed within short timeframe (6 seconds)
+   * - Made by the same person
+   * - Non-change updates are discarded within a group
+   * - Groups with no-change updates are discarded (eg CC -> CC)
+   */
+  _groupUpdates() {
+    const updates = this.result.reviewer_updates;
+    const newUpdates = updates.reduce((newUpdates, update) => {
+      if (!this._batch) {
+        this._batch = this._startBatch(update);
+      }
+      const updateDate = parseDate(update.updated).getTime();
+      const batchUpdateDate = parseDate(this._batch.date).getTime();
+      const reviewerId = update.reviewer._account_id.toString();
+      if (
+        updateDate - batchUpdateDate >
+          REVIEWER_UPDATE_THRESHOLD_MILLIS ||
+        update.updated_by._account_id !== this._batch.author._account_id
+      ) {
+        // Next sequential update should form new group.
+        this._completeBatch();
+        if (this._batch.updates && this._batch.updates.length) {
+          newUpdates.push(this._batch);
+        }
+        this._batch = this._startBatch(update);
+      }
+      this._updateItems[reviewerId] = {
+        reviewer: update.reviewer,
+        state: update.state,
+      };
+      if (this._lastState[reviewerId]) {
+        this._updateItems[reviewerId].prev_state = this._lastState[reviewerId];
+      }
+      return newUpdates;
+    }, []);
+    this._completeBatch();
+    if (this._batch.updates && this._batch.updates.length) {
+      newUpdates.push(this._batch);
+    }
+    this.result.reviewer_updates = newUpdates;
+  }
+
+  /**
+   * Generates update message for reviewer state change.
+   *
+   * @param {string} prev previous reviewer state.
+   * @param {string} state current reviewer state.
+   * @return {string}
+   */
+  _getUpdateMessage(prev, state) {
+    if (prev === 'REMOVED' || !prev) {
+      return 'Added to ' + state.toLowerCase() + ': ';
+    } else if (state === 'REMOVED') {
+      if (prev) {
+        return 'Removed from ' + prev.toLowerCase() + ': ';
+      } else {
+        return 'Removed : ';
+      }
     } else {
-      return 'Removed : ';
+      return (
+        'Moved from ' + prev.toLowerCase() + ' to ' + state.toLowerCase() + ': '
+      );
     }
-  } else {
-    return 'Moved from ' + prev.toLowerCase() + ' to ' + state.toLowerCase() +
-        ': ';
   }
-};
 
-/**
- * Groups updates for same category (eg CC->CC) into a hash arrays of
- * reviewers.
- *
- * @param {!Array<!Object>} updates Array of ReviewerUpdateItemInfo.
- * @return {!Object} Hash of arrays of AccountInfo, message as key.
- */
-GrReviewerUpdatesParser.prototype._groupUpdatesByMessage = function(updates) {
-  return updates.reduce((result, item) => {
-    const message = this._getUpdateMessage(item.prev_state, item.state);
-    if (!result[message]) {
-      result[message] = [];
-    }
-    result[message].push(item.reviewer);
-    return result;
-  }, {});
-};
-
-/**
- * Generates text messages for grouped reviewer updates.
- * Formats reviewer updates to a (not yet implemented) EventInfo instance.
- *
- * @see https://gerrit-review.googlesource.com/c/94490/
- */
-GrReviewerUpdatesParser.prototype._formatUpdates = function() {
-  for (const update of this.result.reviewer_updates) {
-    const grouppedReviewers = this._groupUpdatesByMessage(update.updates);
-    const newUpdates = [];
-    for (const message in grouppedReviewers) {
-      if (grouppedReviewers.hasOwnProperty(message)) {
-        newUpdates.push({
-          message,
-          reviewers: grouppedReviewers[message],
-        });
+  /**
+   * Groups updates for same category (eg CC->CC) into a hash arrays of
+   * reviewers.
+   *
+   * @param {!Array<!Object>} updates Array of ReviewerUpdateItemInfo.
+   * @return {!Object} Hash of arrays of AccountInfo, message as key.
+   */
+  _groupUpdatesByMessage(updates) {
+    return updates.reduce((result, item) => {
+      const message = this._getUpdateMessage(item.prev_state, item.state);
+      if (!result[message]) {
+        result[message] = [];
       }
-    }
-    update.updates = newUpdates;
+      result[message].push(item.reviewer);
+      return result;
+    }, {});
   }
-};
 
-/**
- * Moves reviewer updates that are within short time frame of change messages
- * back in time so they would come before change messages.
- * TODO(viktard): Remove when server-side serves reviewer updates like so.
- */
-GrReviewerUpdatesParser.prototype._advanceUpdates = function() {
-  const updates = this.result.reviewer_updates;
-  const messages = this.result.messages;
-  messages.forEach((message, index) => {
-    const messageDate = parseDate(message.date).getTime();
-    const nextMessageDate = index === messages.length - 1 ? null :
-      parseDate(messages[index + 1].date).getTime();
-    for (const update of updates) {
-      const date = parseDate(update.date).getTime();
-      if (date >= messageDate &&
-          (!nextMessageDate || date < nextMessageDate)) {
-        const timestamp = parseDate(update.date).getTime() -
-            GrReviewerUpdatesParser.MESSAGE_REVIEWERS_THRESHOLD_MILLIS;
-        update.date = new Date(timestamp)
-            .toISOString()
-            .replace('T', ' ')
-            .replace('Z', '000000');
+  /**
+   * Generates text messages for grouped reviewer updates.
+   * Formats reviewer updates to a (not yet implemented) EventInfo instance.
+   *
+   * @see https://gerrit-review.googlesource.com/c/94490/
+   */
+  _formatUpdates() {
+    for (const update of this.result.reviewer_updates) {
+      const grouppedReviewers = this._groupUpdatesByMessage(update.updates);
+      const newUpdates = [];
+      for (const message in grouppedReviewers) {
+        if (grouppedReviewers.hasOwnProperty(message)) {
+          newUpdates.push({
+            message,
+            reviewers: grouppedReviewers[message],
+          });
+        }
       }
-      if (nextMessageDate && date > nextMessageDate) {
-        break;
-      }
+      update.updates = newUpdates;
     }
-  });
-};
+  }
 
+  /**
+   * Moves reviewer updates that are within short time frame of change messages
+   * back in time so they would come before change messages.
+   * TODO(viktard): Remove when server-side serves reviewer updates like so.
+   */
+  _advanceUpdates() {
+    const updates = this.result.reviewer_updates;
+    const messages = this.result.messages;
+    messages.forEach((message, index) => {
+      const messageDate = parseDate(message.date).getTime();
+      const nextMessageDate =
+        index === messages.length - 1
+          ? null
+          : parseDate(messages[index + 1].date).getTime();
+      for (const update of updates) {
+        const date = parseDate(update.date).getTime();
+        if (
+          date >= messageDate &&
+          (!nextMessageDate || date < nextMessageDate)
+        ) {
+          const timestamp =
+            parseDate(update.date).getTime() -
+            MESSAGE_REVIEWERS_THRESHOLD_MILLIS;
+          update.date = new Date(timestamp)
+              .toISOString()
+              .replace('T', ' ')
+              .replace('Z', '000000');
+        }
+        if (nextMessageDate && date > nextMessageDate) {
+          break;
+        }
+      }
+    });
+  }
+
+  static parse(change) {
+    if (
+      !change ||
+    !change.messages ||
+    !change.reviewer_updates ||
+    !change.reviewer_updates.length
+    ) {
+      return change;
+    }
+    const parser = new GrReviewerUpdatesParser(change);
+    parser._filterRemovedMessages();
+    parser._groupUpdates();
+    parser._formatUpdates();
+    parser._advanceUpdates();
+    return parser.result;
+  }
+}
\ No newline at end of file
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser_test.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser_test.js
index f408a97..34fb709 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser_test.js
@@ -22,10 +22,6 @@
 suite('gr-reviewer-updates-parser tests', () => {
   let instance;
 
-  setup(() => {
-
-  });
-
   test('ignores changes without messages', () => {
     const change = {};
     sinon.stub(
diff --git a/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command_html.js b/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command_html.ts
similarity index 96%
rename from polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command_html.js
rename to polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command_html.ts
index 4a4480e..ef76999 100644
--- a/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea_html.js b/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea_html.ts
similarity index 97%
rename from polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea_html.js
rename to polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea_html.ts
index 8454f62..1f777aa 100644
--- a/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/elements/shared/gr-tooltip-content/gr-tooltip-content_html.js b/polygerrit-ui/app/elements/shared/gr-tooltip-content/gr-tooltip-content_html.ts
similarity index 93%
rename from polygerrit-ui/app/elements/shared/gr-tooltip-content/gr-tooltip-content_html.js
rename to polygerrit-ui/app/elements/shared/gr-tooltip-content/gr-tooltip-content_html.ts
index e5a2813..952420d 100644
--- a/polygerrit-ui/app/elements/shared/gr-tooltip-content/gr-tooltip-content_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-tooltip-content/gr-tooltip-content_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style>
diff --git a/polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip_html.js b/polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip_html.ts
similarity index 96%
rename from polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip_html.js
rename to polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip_html.ts
index 3f02fc5..d59a6c3 100644
--- a/polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip_html.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {html} from '@polymer/polymer/lib/utils/html-tag';
 
 export const htmlTemplate = html`
   <style include="shared-styles">
diff --git a/polygerrit-ui/app/embed/gr-diff-app-context-init.js b/polygerrit-ui/app/embed/gr-diff-app-context-init.js
index 90110f0..933edba 100644
--- a/polygerrit-ui/app/embed/gr-diff-app-context-init.js
+++ b/polygerrit-ui/app/embed/gr-diff-app-context-init.js
@@ -31,6 +31,20 @@
   }
 }
 
+class MockAuthService {
+  clearCache() {
+
+  }
+
+  get isAuthed() {
+    return false;
+  }
+
+  authCheck() {
+    return Promise.resolve(false);
+  }
+}
+
 // Setup mocks for appContext.
 // This is a temporary solution
 // TODO(dmfilippov): find a better solution for gr-diff
@@ -44,4 +58,5 @@
   }
   setMock('flagsService', new MockFlagsService);
   setMock('reportingService', grReportingMock);
+  setMock('authService', new MockAuthService);
 }
diff --git a/polygerrit-ui/app/mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin.js b/polygerrit-ui/app/mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin.js
index 1408260..75b0b00 100644
--- a/polygerrit-ui/app/mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin.js
+++ b/polygerrit-ui/app/mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin.js
@@ -539,6 +539,10 @@
           type: Number,
           value: null,
         },
+        _shortcut_v_key_last_pressed: {
+          type: Number,
+          value: null,
+        },
         _shortcut_go_table: {
           type: Array,
           value() {
@@ -555,6 +559,10 @@
     }
 
     modifierPressed(e) {
+      /* We are checking for g/v as modifiers pressed. There are cases such as
+       * pressing v and then /, where we want the handler for / to be triggered.
+       * TODO(dhruvsri): find a way to support that keyboard combination
+       */
       e = getKeyboardEvent(e);
       return e.altKey || e.ctrlKey || e.metaKey || e.shiftKey ||
         !!this._inGoKeyMode() || !!this._inVKeyMode();
@@ -686,6 +694,7 @@
     }
 
     _handleVKeyDown(e) {
+      if (this.shouldSuppressKeyboardShortcut(e)) return;
       this._shortcut_v_key_last_pressed = Date.now();
     }
 
@@ -713,6 +722,7 @@
     }
 
     _handleGoKeyDown(e) {
+      if (this.shouldSuppressKeyboardShortcut(e)) return;
       this._shortcut_go_key_last_pressed = Date.now();
     }
 
diff --git a/polygerrit-ui/app/node_modules_licenses/BUILD b/polygerrit-ui/app/node_modules_licenses/BUILD
index 92a3db8..7652ddc 100644
--- a/polygerrit-ui/app/node_modules_licenses/BUILD
+++ b/polygerrit-ui/app/node_modules_licenses/BUILD
@@ -1,4 +1,4 @@
-load("@npm_bazel_typescript//:index.bzl", "ts_library")
+load("@npm//@bazel/typescript:index.bzl", "ts_library")
 load("//tools/node_tools/node_modules_licenses:node_modules_licenses.bzl", "node_modules_licenses")
 
 filegroup(
diff --git a/polygerrit-ui/app/rollup.config.js b/polygerrit-ui/app/rollup.config.js
index d83f24f..db0e2f7 100644
--- a/polygerrit-ui/app/rollup.config.js
+++ b/polygerrit-ui/app/rollup.config.js
@@ -60,12 +60,6 @@
 export default {
   treeshake: false,
   onwarn: warning => {
-    if(warning.code === 'CIRCULAR_DEPENDENCY') {
-      // Temporary allow CIRCULAR_DEPENDENCY.
-      // See https://bugs.chromium.org/p/gerrit/issues/detail?id=12090
-      // Delete this code after bug is fixed.
-      return;
-    }
     // No warnings from rollupjs are allowed.
     // Most of the warnings are real error in our code (for example,
     // if some import couldn't be resolved we can't continue, but rollup
diff --git a/polygerrit-ui/app/rules.bzl b/polygerrit-ui/app/rules.bzl
index 74b9ac1..8d9be62 100644
--- a/polygerrit-ui/app/rules.bzl
+++ b/polygerrit-ui/app/rules.bzl
@@ -1,5 +1,5 @@
 load("//tools/bzl:genrule2.bzl", "genrule2")
-load("@npm_bazel_rollup//:index.bzl", "rollup_bundle")
+load("@npm//@bazel/rollup:index.bzl", "rollup_bundle")
 
 def _get_ts_compiled_path(outdir, file_name):
     """Calculates the typescript output path for a file_name.
diff --git a/polygerrit-ui/app/samples/bind-parameters.js b/polygerrit-ui/app/samples/bind-parameters.js
index 2c89064..30c7c3d 100644
--- a/polygerrit-ui/app/samples/bind-parameters.js
+++ b/polygerrit-ui/app/samples/bind-parameters.js
@@ -51,7 +51,7 @@
   }
 
   _onRevisionChanged(value) {
-    console.log(`(attributeHelper.bind) revision number: ${value._number}`);
+    console.info(`(attributeHelper.bind) revision number: ${value._number}`);
   }
 }
 
diff --git a/polygerrit-ui/app/samples/repo-command.js b/polygerrit-ui/app/samples/repo-command.js
index 5aaea30..4f64059 100644
--- a/polygerrit-ui/app/samples/repo-command.js
+++ b/polygerrit-ui/app/samples/repo-command.js
@@ -50,8 +50,8 @@
 
   connectedCallback() {
     super.connectedCallback();
-    console.log(this.repoName);
-    console.log(this.config);
+    console.info(this.repoName);
+    console.info(this.config);
     this.hidden = this.repoName !== 'All-Projects';
   }
 
diff --git a/polygerrit-ui/app/scripts/gr-group-suggestions-provider/gr-group-suggestions-provider.js b/polygerrit-ui/app/scripts/gr-group-suggestions-provider/gr-group-suggestions-provider.js
index 16b6aae..ae63c56 100644
--- a/polygerrit-ui/app/scripts/gr-group-suggestions-provider/gr-group-suggestions-provider.js
+++ b/polygerrit-ui/app/scripts/gr-group-suggestions-provider/gr-group-suggestions-provider.js
@@ -25,7 +25,7 @@
         .then(groups => {
           if (!groups) { return []; }
           const keys = Object.keys(groups);
-          return keys.map(key => Object.assign({}, groups[key], {name: key}));
+          return keys.map(key => { return {...groups[key], name: key}; });
         });
   }
 
diff --git a/polygerrit-ui/app/services/app-context-init.js b/polygerrit-ui/app/services/app-context-init.js
index fa6a44bf..531c361 100644
--- a/polygerrit-ui/app/services/app-context-init.js
+++ b/polygerrit-ui/app/services/app-context-init.js
@@ -18,6 +18,7 @@
 import {FlagsService} from './flags.js';
 import {GrReporting} from './gr-reporting/gr-reporting.js';
 import {EventEmitter} from './gr-event-interface/gr-event-interface.js';
+import {Auth} from './gr-auth.js';
 
 const initializedServices = new Map();
 
@@ -48,5 +49,6 @@
   addService('reportingService',
       () => new GrReporting(appContext.flagsService));
   addService('eventEmitter', () => new EventEmitter());
+  addService('authService', () => new Auth(appContext.eventEmitter));
   Object.defineProperties(appContext, registeredServices);
 }
diff --git a/polygerrit-ui/app/services/app-context.js b/polygerrit-ui/app/services/app-context.js
index d82750e..3f86003 100644
--- a/polygerrit-ui/app/services/app-context.js
+++ b/polygerrit-ui/app/services/app-context.js
@@ -25,4 +25,5 @@
   flagsService: null,
   reportingService: null,
   eventEmitter: null,
+  authService: null,
 };
\ No newline at end of file
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-auth.js b/polygerrit-ui/app/services/gr-auth.js
similarity index 89%
rename from polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-auth.js
rename to polygerrit-ui/app/services/gr-auth.js
index 5fcd1a4..21081cc 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-auth.js
+++ b/polygerrit-ui/app/services/gr-auth.js
@@ -14,8 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {getBaseUrl} from '../../../utils/url-util.js';
-import {appContext} from '../../../services/app-context.js';
+import {getBaseUrl} from '../utils/url-util.js';
 
 const MAX_AUTH_CHECK_WAIT_TIME_MS = 1000 * 30; // 30s
 const MAX_GET_TOKEN_RETRIES = 2;
@@ -24,9 +23,7 @@
  * Auth class.
  */
 export class Auth {
-  // TODO(taoalpha): this whole thing should be moved to a service
-
-  constructor() {
+  constructor(eventEmitter) {
     this._type = null;
     this._cachedTokenPromise = null;
     this._defaultOptions = {};
@@ -34,7 +31,7 @@
     this._status = Auth.STATUS.UNDETERMINED;
     this._authCheckPromise = null;
     this._last_auth_check_time = Date.now();
-    this.eventEmitter = appContext.eventEmitter;
+    this.eventEmitter = eventEmitter;
   }
 
   get baseUrl() {
@@ -137,9 +134,11 @@
    * @return {!Promise<!Response>}
    */
   fetch(url, opt_options) {
-    const options = Object.assign({
+    const options = {
       headers: new Headers(),
-    }, this._defaultOptions, opt_options);
+      ...this._defaultOptions,
+      ...opt_options,
+    };
     if (this._type === Auth.TYPE.ACCESS_TOKEN) {
       return this._getAccessToken().then(
           accessToken =>
@@ -159,6 +158,7 @@
         result = c.substring(key.length);
         return true;
       }
+      return false;
     });
     return result;
   }
@@ -263,12 +263,3 @@
 };
 
 Auth.CREDS_EXPIRED_MSG = 'Credentials expired.';
-// TODO(dmfilippov) move to appContext
-export const authService = new Auth();
-
-// TODO(dmfilippov) Remove the following lines with assignments
-// Plugins can use global Auth because it was accessible with
-// the global Gerrit... variable. To avoid breaking changes in plugins
-// temporary assign global variables.
-window.Gerrit = window.Gerrit || {};
-window.Gerrit.Auth = authService;
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-auth_test.js b/polygerrit-ui/app/services/gr-auth_test.js
similarity index 97%
rename from polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-auth_test.js
rename to polygerrit-ui/app/services/gr-auth_test.js
index efdd4d1..541cd42 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-auth_test.js
+++ b/polygerrit-ui/app/services/gr-auth_test.js
@@ -15,22 +15,22 @@
  * limitations under the License.
  */
 
-import '../../../test/common-test-setup-karma.js';
-import {Auth, authService} from './gr-auth.js';
-import {appContext} from '../../../services/app-context.js';
-import {stubBaseUrl} from '../../../test/test-utils.js';
+import '../test/common-test-setup-karma.js';
+import {Auth} from './gr-auth.js';
+import {appContext} from './app-context.js';
+import {stubBaseUrl} from '../test/test-utils.js';
 
 suite('gr-auth', () => {
   let auth;
 
   setup(() => {
-    auth = authService;
+    auth = appContext.authService;
   });
 
   suite('Auth class methods', () => {
     let fakeFetch;
     setup(() => {
-      auth = new Auth();
+      auth = new Auth(appContext.eventEmitter);
       fakeFetch = sinon.stub(window, 'fetch');
     });
 
@@ -75,7 +75,7 @@
     let fakeFetch;
     let clock;
     setup(() => {
-      auth = new Auth();
+      auth = new Auth(appContext.eventEmitter);
       clock = sinon.useFakeTimers();
       fakeFetch = sinon.stub(window, 'fetch');
     });
diff --git a/polygerrit-ui/app/services/gr-reporting/gr-reporting.js b/polygerrit-ui/app/services/gr-reporting/gr-reporting.js
index 42d112e..ea69d5f 100644
--- a/polygerrit-ui/app/services/gr-reporting/gr-reporting.js
+++ b/polygerrit-ui/app/services/gr-reporting/gr-reporting.js
@@ -286,9 +286,9 @@
     if (opt_noLog) { return; }
     if (type !== ERROR.TYPE) {
       if (value !== undefined) {
-        console.log(`Reporting: ${name}: ${value}`);
+        console.info(`Reporting: ${name}: ${value}`);
       } else {
-        console.log(`Reporting: ${name}`);
+        console.info(`Reporting: ${name}`);
       }
     }
   }
@@ -651,4 +651,4 @@
   }
 }
 
-export const DEFAULT_STARTUP_TIMERS = Object.assign({}, STARTUP_TIMERS);
+export const DEFAULT_STARTUP_TIMERS = {...STARTUP_TIMERS};
diff --git a/polygerrit-ui/app/services/gr-reporting/gr-reporting_test.js b/polygerrit-ui/app/services/gr-reporting/gr-reporting_test.js
index 01ba3cb..1e50766 100644
--- a/polygerrit-ui/app/services/gr-reporting/gr-reporting_test.js
+++ b/polygerrit-ui/app/services/gr-reporting/gr-reporting_test.js
@@ -29,7 +29,7 @@
   setup(() => {
     clock = sinon.useFakeTimers(NOW_TIME);
     service = new GrReporting(appContext.flagsService);
-    service._baselines = Object.assign({}, DEFAULT_STARTUP_TIMERS);
+    service._baselines = {...DEFAULT_STARTUP_TIMERS};
     sinon.stub(service, 'reporter');
   });
 
diff --git a/polygerrit-ui/app/template_test.sh b/polygerrit-ui/app/template_test.sh
deleted file mode 100755
index d42f23f..0000000
--- a/polygerrit-ui/app/template_test.sh
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/bin/bash
-
-# TODO(dmfilippov): Update template_test to support Polymer 2/Polymer 3 or delete it completely
-# The following line temporary disable template tests. Existing implementation doesn't compatible
-# with Polymer 2 & 3 class-based components. Polymer linter makes some checks regarding
-# templates and binding, but not all.
-exit 0
-
-set -ex
-
-node_bin=$(which node) && true
-if [ -z "$node_bin" ]; then
-    echo "node must be on the path."
-    exit 1
-fi
-
-npm_bin=$(which npm) && true
-if [[ -z "$npm_bin" ]]; then
-    echo "NPM must be on the path. (https://www.npmjs.com/)"
-    exit 1
-fi
-
-# Have to find where node_modules are installed and set the NODE_PATH
-
-get_node_path() {
-    cd $(dirname $node_bin)
-    cd ../lib/node_modules
-    pwd
-}
-
-export NODE_PATH=$(get_node_path)
-
-unzip -o polygerrit-ui/polygerrit_components.bower_components.zip -d polygerrit-ui/app
-python $TEST_SRCDIR/gerrit/polygerrit-ui/app/template_test_srcs/convert_for_template_tests.py
-# Pass a file name argument from the --test_args (example: --test_arg=gr-list-view)
-${node_bin} $TEST_SRCDIR/gerrit/polygerrit-ui/app/template_test_srcs/template_test.js $1 $2
diff --git a/polygerrit-ui/app/template_test_srcs/convert_for_template_tests.py b/polygerrit-ui/app/template_test_srcs/convert_for_template_tests.py
deleted file mode 100644
index 579e783..0000000
--- a/polygerrit-ui/app/template_test_srcs/convert_for_template_tests.py
+++ /dev/null
@@ -1,129 +0,0 @@
-import json
-import os
-import re
-
-polymerRegex = r"Polymer\({"
-polymerCompiledRegex = re.compile(polymerRegex)
-
-removeSelfInvokeRegex = r"\(function\(\) {\n(.+)}\)\(\);"
-fnCompiledRegex = re.compile(removeSelfInvokeRegex, re.DOTALL)
-
-regexBehavior = r"<script>(.+)<\/script>"
-behaviorCompiledRegex = re.compile(regexBehavior, re.DOTALL)
-
-
-def _open(filename, mode="r"):
-    try:
-        return open(filename, mode, encoding="utf-8")
-    except TypeError:
-        return open(filename, mode)
-
-
-def replaceBehaviorLikeHTML(fileIn, fileOut):
-    with _open(fileIn) as f:
-        file_str = f.read()
-        match = behaviorCompiledRegex.search(file_str)
-        if match:
-            with _open("polygerrit-ui/temp/behaviors/" +
-                       fileOut.replace("html", "js"), "w+") as f:
-                f.write(match.group(1))
-
-
-def replaceBehaviorLikeJS(fileIn, fileOut):
-    with _open(fileIn) as f:
-        file_str = f.read()
-        with _open("polygerrit-ui/temp/behaviors/" + fileOut, "w+") as f:
-            f.write(file_str)
-
-
-def generateStubBehavior(behaviorName):
-    with _open("polygerrit-ui/temp/behaviors/" +
-               behaviorName + ".js", "w+") as f:
-        f.write("/** @polymerBehavior **/\n" + behaviorName + "= {};")
-
-
-def replacePolymerElement(fileIn, fileOut, root):
-    with _open(fileIn) as f:
-        key = fileOut.split('.')[0]
-        # Removed self invoked function
-        file_str = f.read()
-        file_str_no_fn = fnCompiledRegex.search(file_str)
-
-        if file_str_no_fn:
-            package = root.replace("/", ".") + "." + fileOut
-
-            with _open("polygerrit-ui/temp/" + fileOut, "w+") as f:
-                mainFileContents = re.sub(
-                    polymerCompiledRegex,
-                    "exports = Polymer({",
-                    file_str_no_fn.group(1)).replace("'use strict';", "")
-                f.write("/** \n"
-                        "* @fileoverview \n"
-                        "* @suppress {missingProperties} \n"
-                        "*/ \n\n"
-                        "goog.module('polygerrit." + package + "')\n\n" +
-                        mainFileContents)
-
-            # Add package and javascript to files object.
-            elements[key]["js"] = "polygerrit-ui/temp/" + fileOut
-            elements[key]["package"] = package
-
-
-def writeTempFile(file, root):
-    # This is included in an extern because it is directly on the window object
-    # (for now at least).
-    if "gr-reporting" in file:
-        return
-    key = file.split('.')[0]
-    if key not in elements:
-        # gr-app doesn't have an additional level
-        elements[key] = {
-            "directory":
-                'gr-app' if len(root.split("/")) < 4 else root.split("/")[3]
-        }
-    if file.endswith(".html") and not file.endswith("_test.html"):
-        # gr-navigation is treated like a behavior rather than a standard
-        # element because of the way it added to the Gerrit object.
-        if file.endswith("gr-navigation.html"):
-            replaceBehaviorLikeHTML(os.path.join(root, file), file)
-        else:
-            elements[key]["html"] = os.path.join(root, file)
-    if file.endswith(".js"):
-        replacePolymerElement(os.path.join(root, file), file, root)
-
-
-if __name__ == "__main__":
-    # Create temp directory.
-    if not os.path.exists("polygerrit-ui/temp"):
-        os.makedirs("polygerrit-ui/temp")
-
-    # Within temp directory create behavior directory.
-    if not os.path.exists("polygerrit-ui/temp/behaviors"):
-        os.makedirs("polygerrit-ui/temp/behaviors")
-
-    elements = {}
-
-    # Go through every file in app/elements, and re-write accordingly to temp
-    # directory, and also added to elements object, which is used to generate a
-    # map of html files, package names, and javascript files.
-    for root, dirs, files in os.walk("polygerrit-ui/app/elements"):
-        for file in files:
-            writeTempFile(file, root)
-
-    # Special case for polymer behaviors we are using.
-    replaceBehaviorLikeHTML("polygerrit-ui/app/bower_components/iron-a11y-keys-behavior/iron-a11y-keys-behavior.html", "iron-a11y-keys-behavior.html")
-    generateStubBehavior("Polymer.IronOverlayBehavior")
-    generateStubBehavior("Polymer.IronFitBehavior")
-
-    # TODO figure out something to do with iron-overlay-behavior.
-    # it is hard-coded reformatted.
-
-    with _open("polygerrit-ui/temp/map.json", "w+") as f:
-        f.write(json.dumps(elements))
-
-    for root, dirs, files in os.walk("polygerrit-ui/app/behaviors"):
-        for file in files:
-            if file.endswith("behavior.html"):
-                replaceBehaviorLikeHTML(os.path.join(root, file), file)
-            elif file.endswith("behavior.js"):
-                replaceBehaviorLikeJS(os.path.join(root, file), file)
diff --git a/polygerrit-ui/app/template_test_srcs/template_test.js b/polygerrit-ui/app/template_test_srcs/template_test.js
deleted file mode 100644
index 5592825..0000000
--- a/polygerrit-ui/app/template_test_srcs/template_test.js
+++ /dev/null
@@ -1,87 +0,0 @@
-const fs = require('fs');
-const twinkie = require('fried-twinkie');
-
-fs.readdir('./polygerrit-ui/temp/behaviors/', (err, data) => {
-  if (err) {
-    console.log('error /polygerrit-ui/temp/behaviors/ directory');
-  }
-  const behaviors = data;
-  const additionalSources = [];
-  const externMap = {};
-
-  for (const behavior of behaviors) {
-    if (!externMap[behavior]) {
-      additionalSources.push({
-        path: `./polygerrit-ui/temp/behaviors/${behavior}`,
-        src: fs.readFileSync(
-            `./polygerrit-ui/temp/behaviors/${behavior}`, 'utf-8'),
-      });
-      externMap[behavior] = true;
-    }
-  }
-
-  let mappings = JSON.parse(fs.readFileSync(
-      `./polygerrit-ui/temp/map.json`, 'utf-8'));
-
-  // The directory is passed as arg2 by the test target.
-  const directory = process.argv[2];
-  if (directory) {
-    const mappingSpecificDirectory = {};
-
-    for (key of Object.keys(mappings)) {
-      if (directory === mappings[key].directory) {
-        mappingSpecificDirectory[key] = mappings[key];
-      }
-    }
-    mappings = mappingSpecificDirectory;
-  }
-
-  // If a particular file was passed by the user, don't test everything.
-  const file = process.argv[3];
-  if (file) {
-    const mappingSpecificFile = {};
-    for (key of Object.keys(mappings)) {
-      if (key.includes(file)) {
-        mappingSpecificFile[key] = mappings[key];
-      }
-    }
-    mappings = mappingSpecificFile;
-  }
-
-  /**
-   * Types in Gerrit.
-   * All types should be under `./polygerrit-ui/app/types` folder and end with `js`.
-   */
-  fs.readdir('./polygerrit-ui/app/types/', (err, typeFiles) => {
-    for (const typeFile of typeFiles) {
-      if (!typeFile.endsWith('.js')) continue;
-      additionalSources.push({
-        path: `./polygerrit-ui/app/types/${typeFile}`,
-        src: fs.readFileSync(
-            `./polygerrit-ui/app/types/${typeFile}`, 'utf-8'),
-      });
-    }
-
-    const toCheck = [];
-    for (key of Object.keys(mappings)) {
-      if (mappings[key].html && mappings[key].js) {
-        toCheck.push({
-          htmlSrcPath: mappings[key].html,
-          jsSrcPath: mappings[key].js,
-          jsModule: 'polygerrit.' + mappings[key].package,
-        });
-      }
-    }
-
-    twinkie.checkTemplate(toCheck, additionalSources)
-        .then(() => {}, joinedErrors => {
-          if (joinedErrors) {
-            process.exit(1);
-          }
-        })
-        .catch(e => {
-          console.error(e);
-          process.exit(1);
-        });
-  });
-});
diff --git a/polygerrit-ui/app/utils/admin-nav-util.js b/polygerrit-ui/app/utils/admin-nav-util.js
index c1f3465..8356db1 100644
--- a/polygerrit-ui/app/utils/admin-nav-util.js
+++ b/polygerrit-ui/app/utils/admin-nav-util.js
@@ -115,7 +115,7 @@
   // set in addition to the subsection that should be displayed if it
   // exists.
   for (const link of links) {
-    const linkCopy = Object.assign({}, link);
+    const linkCopy = {...link};
     if (linkCopy.name === 'Repositories' && repoName) {
       linkCopy.subsection = getRepoSubsections(repoName);
       expandedSection = linkCopy.subsection;
diff --git a/polygerrit-ui/app/utils/dom-util.js b/polygerrit-ui/app/utils/dom-util.js
index e26bf74..16d9e00 100644
--- a/polygerrit-ui/app/utils/dom-util.js
+++ b/polygerrit-ui/app/utils/dom-util.js
@@ -189,4 +189,14 @@
  */
 export function strToClassName(str = '', prefix = 'generated_') {
   return `${prefix}${str.replace(/[^a-zA-Z0-9-_]/g, '_')}`;
+}
+
+// shared API element
+let _sharedApiEl;
+
+export function getSharedApiEl() {
+  if (!_sharedApiEl) {
+    _sharedApiEl = document.createElement('gr-js-api-interface');
+  }
+  return _sharedApiEl;
 }
\ No newline at end of file
diff --git a/polygerrit-ui/app/utils/patch-set-util.js b/polygerrit-ui/app/utils/patch-set-util.js
index 562b8ee..c27a8cd 100644
--- a/polygerrit-ui/app/utils/patch-set-util.js
+++ b/polygerrit-ui/app/utils/patch-set-util.js
@@ -143,7 +143,7 @@
   let patchNums = [];
   if (change.revisions && Object.keys(change.revisions).length) {
     const revisions = Object.keys(change.revisions)
-        .map(sha => Object.assign({sha}, change.revisions[sha]));
+        .map(sha => { return {sha, ...change.revisions[sha]}; });
     patchNums = sortRevisions(revisions)
         .map(e => {
           // TODO(kaspern): Mark which patchset an edit was made on, if an
diff --git a/polygerrit-ui/server.go b/polygerrit-ui/server.go
index b2eb3dc..d03dada 100644
--- a/polygerrit-ui/server.go
+++ b/polygerrit-ui/server.go
@@ -493,7 +493,11 @@
 var (
 	tsStartingCompilation     = "- Starting compilation in watch mode..."
 	tsFileChangeDetectedMsg   = "- File change detected. Starting incremental compilation..."
-	tsStartWatchingMsg        = regexp.MustCompile(`^.* - Found \d+ errors\. Watching for file changes\.$`)
+	// If there is only one error typescript outputs:
+	// Found 1 error
+	// In all other cases it outputs
+	// Found X errors
+	tsStartWatchingMsg        = regexp.MustCompile(`^.* - Found \d+ error(s)?\. Watching for file changes\.$`)
 	waitForNextChangeInterval = 1 * time.Second
 )
 
diff --git a/proto/cache.proto b/proto/cache.proto
index 4e75be2..29b5870 100644
--- a/proto/cache.proto
+++ b/proto/cache.proto
@@ -355,3 +355,111 @@
   int32 max = 4;
   GroupReferenceProto group = 5;
 }
+
+// Serialized form of com.google.gerrit.common.data.Permission.
+// Next ID: 4
+message PermissionProto {
+  string name = 1;
+  bool exclusive_group = 2;
+  repeated PermissionRuleProto rules = 3;
+}
+
+// Serialized form of com.google.gerrit.common.data.AccessSection.
+// Next ID: 3
+message AccessSectionProto {
+  string name = 1;
+  repeated PermissionProto permissions = 2;
+}
+
+// Serialized form of com.google.gerrit.server.git.BranchOrderSection.
+// Next ID: 2
+message BranchOrderSectionProto {
+  repeated string branches_in_order = 1;
+}
+
+// Serialized form of com.google.gerrit.common.data.ContributorAgreement.
+// Next ID: 8
+message ContributorAgreementProto {
+  string name = 1;
+  string description = 2;
+  repeated PermissionRuleProto accepted = 3;
+  GroupReferenceProto auto_verify = 4;
+  string url = 5;
+  repeated string exclude_regular_expressions = 6;
+  repeated string match_regular_expressions = 7;
+}
+
+// Serialized form of com.google.gerrit.entities.Address.
+// Next ID: 3
+message AddressProto {
+  string name = 1;
+  string email = 2;
+}
+
+// Serialized form of com.google.gerrit.entities.NotifyConfig.
+// Next ID: 7
+message NotifyConfigProto {
+  string name = 1;
+  repeated string type = 2; // ENUM as String
+  string filter = 3;
+  string header = 4; // ENUM as String
+  repeated GroupReferenceProto groups = 5;
+  repeated AddressProto addresses = 6;
+}
+
+// Serialized form of com.google.gerrit.entities.LabelValue.
+// Next ID: 3
+message LabelValueProto {
+  string text = 1;
+  int32 value = 2;
+}
+
+// Serialized form of com.google.gerrit.common.data.LabelType.
+// Next ID: 19
+message LabelTypeProto {
+  string name = 1;
+  string function = 2; // ENUM as String
+  bool copy_any_score = 3;
+  bool copy_min_score = 4;
+  bool copy_max_score = 5;
+  bool copy_all_scores_on_merge_first_parent_update = 6;
+  bool copy_all_scores_on_trivial_rebase = 7;
+  bool copy_all_scores_if_no_code_change = 8;
+  bool copy_all_scores_if_no_change = 9;
+  repeated int32 copy_values = 10;
+  bool allow_post_submit = 11;
+  bool ignore_self_approval = 12;
+  int32 default_value = 13;
+  repeated LabelValueProto values = 14;
+  int32 max_negative = 15;
+  int32 max_positive = 16;
+  bool can_override = 17;
+  repeated string ref_patterns = 18;
+}
+
+// Serialized form of com.google.gerrit.server.project.ConfiguredMimeTypes.
+// Next ID: 4
+message ConfiguredMimeTypeProto {
+  string type = 1;
+  string pattern = 2;
+  bool is_regular_expression = 3;
+}
+
+// Serialized form of com.google.gerrit.common.data.SubscribeSection.
+// Next ID: 4
+message SubscribeSectionProto {
+  string project_name = 1;
+  repeated string multi_match_ref_specs = 2;
+  repeated string matching_ref_specs = 3;
+}
+
+// Serialized form of com.google.gerrit.entities.StoredCommentLinkInfo.
+// Next ID: 7
+message StoredCommentLinkInfoProto {
+  string name = 1;
+  string match = 2;
+  string link = 3;
+  string html = 4;
+  bool enabled = 5;
+  bool override_only = 6;
+}
diff --git a/tools/bzl/js.bzl b/tools/bzl/js.bzl
index eeb5e6b..bbb1432 100644
--- a/tools/bzl/js.bzl
+++ b/tools/bzl/js.bzl
@@ -1,4 +1,4 @@
-load("@npm_bazel_terser//:index.bzl", "terser_minified")
+load("@npm//@bazel/terser:index.bzl", "terser_minified")
 load("//lib/js:npm.bzl", "NPM_SHA1S", "NPM_VERSIONS")
 
 NPMJS = "NPMJS"
diff --git a/tools/dev-hooks/pre-commit b/tools/dev-hooks/pre-commit
deleted file mode 100755
index af87b7e..0000000
--- a/tools/dev-hooks/pre-commit
+++ /dev/null
@@ -1,47 +0,0 @@
-#!/bin/sh
-#
-# Part of Gerrit Code Review (https://www.gerritcodereview.com/)
-#
-# Copyright (C) 2019 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-
-# To enable this hook:
-# - copy this file or content to ".git/hooks/pre-commit"
-# - (optional if you copied this file) make it executable: `chmod +x .git/hooks/pre-commit`
-
-set -ue
-
-# gitroot, default to .
-gitroot=$(git rev-parse --show-cdup)
-gitroot=${gitroot:-.};
-
-# eslint
-eslint=${gitroot}/node_modules/eslint/bin/eslint.js
-
-# Run eslint over changed frontend code
-CHANGED_UI_FILES=$(git diff --cached --name-only --diff-filter=ACM -- '*.js' '*.html' | grep 'polygerrit-ui') && true
-if [ "${CHANGED_UI_FILES}" ]; then
-  if $eslint --fix ${CHANGED_UI_FILES}; then
-    # Add again in case lint fix modified some files
-    git add ${CHANGED_UI_FILES}
-    exit 0
-  else
-    echo "Failed to fix all linter issues.";
-    exit 1
-  fi
-else
-  echo "No UI files changed"
-  exit 0
-fi
\ No newline at end of file
diff --git a/tools/node_tools/node_modules_licenses/BUILD b/tools/node_tools/node_modules_licenses/BUILD
index bd7e854..581b3a9 100644
--- a/tools/node_tools/node_modules_licenses/BUILD
+++ b/tools/node_tools/node_modules_licenses/BUILD
@@ -1,6 +1,6 @@
-load("@npm_bazel_typescript//:index.bzl", "ts_library")
 load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary")
-load("@npm_bazel_rollup//:index.bzl", "rollup_bundle")
+load("@npm//@bazel/typescript:index.bzl", "ts_library")
+load("@npm//@bazel/rollup:index.bzl", "rollup_bundle")
 
 package(default_visibility = ["//visibility:public"])
 
diff --git a/tools/node_tools/package.json b/tools/node_tools/package.json
index 6fafe63..67a85a4 100644
--- a/tools/node_tools/package.json
+++ b/tools/node_tools/package.json
@@ -3,8 +3,8 @@
   "description": "Gerrit Build Tools",
   "browser": false,
   "dependencies": {
-    "@bazel/rollup": "^1.6.1",
-    "@bazel/typescript": "^1.6.1",
+    "@bazel/rollup": "^2.0.0",
+    "@bazel/typescript": "^2.0.0",
     "@types/node": "^10.17.12",
     "@types/parse5": "^4.0.0",
     "@types/parse5-html-rewriting-stream": "^5.1.2",
diff --git a/tools/node_tools/polygerrit_app_preprocessor/BUILD b/tools/node_tools/polygerrit_app_preprocessor/BUILD
index b031293..b5ee34f 100644
--- a/tools/node_tools/polygerrit_app_preprocessor/BUILD
+++ b/tools/node_tools/polygerrit_app_preprocessor/BUILD
@@ -1,6 +1,6 @@
-load("@npm_bazel_typescript//:index.bzl", "ts_library")
 load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary")
-load("@npm_bazel_rollup//:index.bzl", "rollup_bundle")
+load("@npm//@bazel/rollup:index.bzl", "rollup_bundle")
+load("@npm//@bazel/typescript:index.bzl", "ts_library")
 
 package(default_visibility = ["//visibility:public"])
 
diff --git a/tools/node_tools/utils/BUILD b/tools/node_tools/utils/BUILD
index fca3c12..5c407ca 100644
--- a/tools/node_tools/utils/BUILD
+++ b/tools/node_tools/utils/BUILD
@@ -1,4 +1,4 @@
-load("@npm_bazel_typescript//:index.bzl", "ts_library")
+load("@npm//@bazel/typescript:index.bzl", "ts_library")
 
 package(default_visibility = ["//visibility:public"])
 
diff --git a/tools/node_tools/yarn.lock b/tools/node_tools/yarn.lock
index 78349fa..a3ac4af 100644
--- a/tools/node_tools/yarn.lock
+++ b/tools/node_tools/yarn.lock
@@ -492,15 +492,15 @@
     lodash "^4.17.13"
     to-fast-properties "^2.0.0"
 
-"@bazel/rollup@^1.6.1":
-  version "1.6.1"
-  resolved "https://registry.yarnpkg.com/@bazel/rollup/-/rollup-1.6.1.tgz#7ec9d39a3fca23256fca55410339724804802616"
-  integrity sha512-FhblJkpd8VKl9txhAAIotSsIOHRpPd2FgJG7Op3uV7LfaCVBmUs3XDBZCgfwt5wmEpd3lwCHA1Ei+O/URS2+5w==
+"@bazel/rollup@^2.0.0":
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/@bazel/rollup/-/rollup-2.0.0.tgz#1980cb3f6922227659260bfdca99c457889a5bc1"
+  integrity sha512-mifUfCZbD1RIhfowh4N8E4881ag3FChz7F4z35wxMOP52g1q3+6Bvh5wv9iysFQopxGmS5jNEj3Dq/CWtSoOnw==
 
-"@bazel/typescript@^1.6.1":
-  version "1.6.1"
-  resolved "https://registry.yarnpkg.com/@bazel/typescript/-/typescript-1.6.1.tgz#1bf83c20021d359bc9b532181981ac540584a30c"
-  integrity sha512-wQ9AASRcG1jLQOpJfNOMjZzPpwIV/9qTOxCFvp55ga6A5a2qveQr8JJ7jHHbBM0LtK+slEPixXmVmtEOwfKsIg==
+"@bazel/typescript@^2.0.0":
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/@bazel/typescript/-/typescript-2.0.0.tgz#2ff5615f09c733cc681ba2ada92b11c356b694cd"
+  integrity sha512-5FPkxULWIjAKLG5J1XvpXpY1/4IK39dAoWA/Hhg+16gXTES32fT8w42k96pb6BTaNnyBuYgIHBpELEAJ40OOAQ==
   dependencies:
     protobufjs "6.8.8"
     semver "5.6.0"
@@ -950,9 +950,9 @@
   integrity sha512-N33cKXGSqhOYaPiT4xUGsYlPPDwFtQM/6QxJxuMXA/7BcySW+lkn2yigWP7vfs4daiL/7NJNU6DMCqg5N4B+xQ==
 
 "@types/node@^10.1.0":
-  version "10.17.13"
-  resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.13.tgz#ccebcdb990bd6139cd16e84c39dc2fb1023ca90c"
-  integrity sha512-pMCcqU2zT4TjqYFrWtYHKal7Sl30Ims6ulZ4UFXxI4xbtQqK/qqKwkDoBFCfooRqqmRu9vY3xaJRwxSh673aYg==
+  version "10.17.27"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.27.tgz#391cb391c75646c8ad2a7b6ed3bbcee52d1bdf19"
+  integrity sha512-J0oqm9ZfAXaPdwNXMMgAhylw5fhmXkToJd06vuDUSAgEDZ/n/69/69UmyBZbc+zT34UnShuDSBqvim3SPnozJg==
 
 "@types/node@^10.17.12":
   version "10.17.24"
@@ -7834,7 +7834,12 @@
   resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.3.0.tgz#a595214c7298db8339eeeee083e4d10bd8cb8dd9"
   integrity sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==
 
-tslib@^1.8.1, tslib@^1.9.0:
+tslib@^1.8.1:
+  version "1.13.0"
+  resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043"
+  integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==
+
+tslib@^1.9.0:
   version "1.10.0"
   resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a"
   integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==
diff --git a/yarn.lock b/yarn.lock
index 0c4383f..a20b1c7 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -485,20 +485,20 @@
     lodash "^4.17.11"
     to-fast-properties "^2.0.0"
 
-"@bazel/rollup@^1.6.1":
-  version "1.6.1"
-  resolved "https://registry.yarnpkg.com/@bazel/rollup/-/rollup-1.6.1.tgz#7ec9d39a3fca23256fca55410339724804802616"
-  integrity sha512-FhblJkpd8VKl9txhAAIotSsIOHRpPd2FgJG7Op3uV7LfaCVBmUs3XDBZCgfwt5wmEpd3lwCHA1Ei+O/URS2+5w==
+"@bazel/rollup@^2.0.0":
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/@bazel/rollup/-/rollup-2.0.0.tgz#1980cb3f6922227659260bfdca99c457889a5bc1"
+  integrity sha512-mifUfCZbD1RIhfowh4N8E4881ag3FChz7F4z35wxMOP52g1q3+6Bvh5wv9iysFQopxGmS5jNEj3Dq/CWtSoOnw==
 
-"@bazel/terser@^1.7.0":
-  version "1.7.0"
-  resolved "https://registry.yarnpkg.com/@bazel/terser/-/terser-1.7.0.tgz#c43e711e13b9a71c7abd3ade04fb4650d547ad01"
-  integrity sha512-u/UXk0WUinvkk1g5xxfqGieBz3r12Bj2y2m25lC5GjHBgCpGk7DyeGGi9H3QQNO1Wmpw51QSE9gaPzKzjUVGug==
+"@bazel/terser@^2.0.0":
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/@bazel/terser/-/terser-2.0.0.tgz#a841db8aefd7c51c216b34a26bc02a6c93d5e56a"
+  integrity sha512-6mBYcfzP6pWxycYZ8r4Lz5kgiWZ7n08bVHZBIRExFeqs7Yy92dD92LPeA9FZIzFiX00IuR9Q1Lqy23xH5q7FeQ==
 
-"@bazel/typescript@^1.6.1":
-  version "1.6.1"
-  resolved "https://registry.yarnpkg.com/@bazel/typescript/-/typescript-1.6.1.tgz#1bf83c20021d359bc9b532181981ac540584a30c"
-  integrity sha512-wQ9AASRcG1jLQOpJfNOMjZzPpwIV/9qTOxCFvp55ga6A5a2qveQr8JJ7jHHbBM0LtK+slEPixXmVmtEOwfKsIg==
+"@bazel/typescript@^2.0.0":
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/@bazel/typescript/-/typescript-2.0.0.tgz#2ff5615f09c733cc681ba2ada92b11c356b694cd"
+  integrity sha512-5FPkxULWIjAKLG5J1XvpXpY1/4IK39dAoWA/Hhg+16gXTES32fT8w42k96pb6BTaNnyBuYgIHBpELEAJ40OOAQ==
   dependencies:
     protobufjs "6.8.8"
     semver "5.6.0"
@@ -711,13 +711,6 @@
   dependencies:
     chalk "*"
 
-"@types/cheerio@^0.22.2":
-  version "0.22.15"
-  resolved "https://registry.yarnpkg.com/@types/cheerio/-/cheerio-0.22.15.tgz#69040ffa92c309beeeeb7e92db66ac3f80700c0b"
-  integrity sha512-UGiiVtJK5niCqMKYmLEFz1Wl/3L5zF/u78lu8CwoUywWXRr9LDimeYuOzXVLXBMO758fcTdFtgjvqlztMH90MA==
-  dependencies:
-    "@types/node" "*"
-
 "@types/clean-css@*":
   version "4.2.1"
   resolved "https://registry.yarnpkg.com/@types/clean-css/-/clean-css-4.2.1.tgz#cb0134241ec5e6ede1b5344bc829668fd9871a8d"
@@ -1367,11 +1360,6 @@
     json-schema-traverse "^0.4.1"
     uri-js "^4.2.2"
 
-amdefine@>=0.0.4:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5"
-  integrity sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=
-
 ansi-align@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-1.1.0.tgz#2f0c1658829739add5ebb15e6b0c6e3423f016ba"
@@ -2089,11 +2077,6 @@
     raw-body "2.4.0"
     type-is "~1.6.17"
 
-boolbase@~1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
-  integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24=
-
 bower-config@^1.4.0, bower-config@^1.4.1:
   version "1.4.1"
   resolved "https://registry.yarnpkg.com/bower-config/-/bower-config-1.4.1.tgz#85fd9df367c2b8dbbd0caa4c5f2bad40cd84c2cc"
@@ -2441,18 +2424,6 @@
   resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667"
   integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=
 
-cheerio@^1.0.0-rc.2:
-  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@^1.7.0:
   version "1.7.0"
   resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468"
@@ -2967,16 +2938,6 @@
   resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5"
   integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==
 
-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=
-  dependencies:
-    boolbase "~1.0.0"
-    css-what "2.1"
-    domutils "1.5.1"
-    nth-check "~1.0.1"
-
 css-slam@^2.1.2:
   version "2.1.2"
   resolved "https://registry.yarnpkg.com/css-slam/-/css-slam-2.1.2.tgz#3d35b1922cb3e0002a45c89ab189492508c493e5"
@@ -2988,7 +2949,7 @@
     parse5 "^4.0.0"
     shady-css-parser "^0.1.0"
 
-css-what@2.1, css-what@^2.1.0:
+css-what@^2.1.0:
   version "2.1.3"
   resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2"
   integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==
@@ -3265,14 +3226,6 @@
     domelementtype "^2.0.1"
     entities "^2.0.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==
-  dependencies:
-    domelementtype "^1.3.0"
-    entities "^1.1.1"
-
 dom-urls@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/dom-urls/-/dom-urls-1.1.0.tgz#001ddf81628cd1e706125c7176f53ccec55d918e"
@@ -3289,7 +3242,7 @@
     clone "^2.1.0"
     parse5 "^4.0.0"
 
-domelementtype@1, domelementtype@^1.3.0, domelementtype@^1.3.1:
+domelementtype@1, domelementtype@^1.3.1:
   version "1.3.1"
   resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f"
   integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==
@@ -3306,14 +3259,6 @@
   dependencies:
     domelementtype "1"
 
-domutils@1.5.1:
-  version "1.5.1"
-  resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf"
-  integrity sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=
-  dependencies:
-    dom-serializer "0"
-    domelementtype "1"
-
 domutils@^1.5.1:
   version "1.7.0"
   resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a"
@@ -3477,7 +3422,7 @@
     engine.io-parser "~2.2.0"
     ws "^7.1.2"
 
-entities@^1.1.1, 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==
@@ -4282,19 +4227,6 @@
   resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
   integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=
 
-fried-twinkie@^0.2.2:
-  version "0.2.2"
-  resolved "https://registry.yarnpkg.com/fried-twinkie/-/fried-twinkie-0.2.2.tgz#fafa52b3b7957dc78d7867b28f74b6e01bdb2aee"
-  integrity sha512-rb/i+7VXEToBjrYZ2jSew4bI9znWF15P52dAGuoJcxpaBibWz2PI5tRx0ZSjDM+a/gZI2Pgr2XHT6wwNVZQ7/g==
-  dependencies:
-    "@types/cheerio" "^0.22.2"
-    chalk "^2.1.0"
-    google-closure-compiler-js "^20170626.0.0"
-    tmp "^0.0.31"
-    tsickle "^0.23.4"
-    twinkie "0.0.11"
-    typescript "^2.4.1"
-
 fs-constants@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
@@ -4605,15 +4537,6 @@
     pify "^3.0.0"
     slash "^1.0.0"
 
-google-closure-compiler-js@^20170626.0.0:
-  version "20170626.0.0"
-  resolved "https://registry.yarnpkg.com/google-closure-compiler-js/-/google-closure-compiler-js-20170626.0.0.tgz#5df265b277d1ec6fdea12eed131d1491cd8a8d71"
-  integrity sha512-LQvWXN3yS2l88TsXiHZ0aWtGR51tep/bNvS7cuUldnKkppgknTo35jThYwE+JOU9lviERZjMHhiqxz2CXzIRuw==
-  dependencies:
-    minimist "^1.2.0"
-    vinyl "^2.0.1"
-    webpack-core "^0.6.8"
-
 got@^5.0.0:
   version "5.7.1"
   resolved "https://registry.yarnpkg.com/got/-/got-5.7.1.tgz#5f81635a61e4a6589f180569ea4e381680a51f35"
@@ -4933,7 +4856,7 @@
     relateurl "0.2.x"
     uglify-js "3.4.x"
 
-htmlparser2@^3.10.1, htmlparser2@^3.9.1:
+htmlparser2@^3.10.1:
   version "3.10.1"
   resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f"
   integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==
@@ -6009,7 +5932,7 @@
   resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6"
   integrity sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=
 
-lodash@^4.0.0, lodash@^4.15.0, lodash@^4.16.6, lodash@^4.17.14, lodash@^4.17.15:
+lodash@^4.0.0, lodash@^4.16.6, lodash@^4.17.14, lodash@^4.17.15:
   version "4.17.15"
   resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
   integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
@@ -6675,13 +6598,6 @@
     gauge "~2.7.3"
     set-blocking "~2.0.0"
 
-nth-check@~1.0.1:
-  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"
-
 number-is-nan@^1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
@@ -7055,13 +6971,6 @@
   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==
-  dependencies:
-    "@types/node" "*"
-
 parse5@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608"
@@ -8615,11 +8524,6 @@
   dependencies:
     is-plain-obj "^1.0.0"
 
-source-list-map@~0.1.7:
-  version "0.1.8"
-  resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-0.1.8.tgz#c550b2ab5427f6b3f21f5afead88c4f5587b2106"
-  integrity sha1-xVCyq1Qn9rPyH1r+rYjE9Vh7IQY=
-
 source-map-resolve@^0.5.0:
   version "0.5.3"
   resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a"
@@ -8639,13 +8543,6 @@
     buffer-from "^1.0.0"
     source-map "^0.6.0"
 
-source-map-support@^0.4.2:
-  version "0.4.18"
-  resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.18.tgz#0286a6de8be42641338594e97ccea75f0a2c585f"
-  integrity sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==
-  dependencies:
-    source-map "^0.5.6"
-
 source-map-support@~0.5.12:
   version "0.5.19"
   resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61"
@@ -8669,13 +8566,6 @@
   resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
   integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
 
-source-map@~0.4.1:
-  version "0.4.4"
-  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b"
-  integrity sha1-66T12pwNyZneaAMti092FzZSA2s=
-  dependencies:
-    amdefine ">=0.0.4"
-
 spawn-sync@^1.0.15:
   version "1.0.15"
   resolved "https://registry.yarnpkg.com/spawn-sync/-/spawn-sync-1.0.15.tgz#b00799557eb7fb0c8376c29d44e8a1ea67e57476"
@@ -9253,13 +9143,6 @@
   dependencies:
     os-tmpdir "~1.0.1"
 
-tmp@^0.0.31:
-  version "0.0.31"
-  resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.31.tgz#8f38ab9438e17315e5dbd8b3657e8bfb277ae4a7"
-  integrity sha1-jzirlDjhcxXl29izZX6L+yd65Kc=
-  dependencies:
-    os-tmpdir "~1.0.1"
-
 tmp@^0.0.33:
   version "0.0.33"
   resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
@@ -9364,16 +9247,6 @@
   resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.3.0.tgz#a595214c7298db8339eeeee083e4d10bd8cb8dd9"
   integrity sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==
 
-tsickle@^0.23.4:
-  version "0.23.6"
-  resolved "https://registry.yarnpkg.com/tsickle/-/tsickle-0.23.6.tgz#fcee57a5cb7f92a8c3a9e578ee0a286427dcfacd"
-  integrity sha1-/O5Xpct/kqjDqeV47gooZCfc+s0=
-  dependencies:
-    minimist "^1.2.0"
-    mkdirp "^0.5.1"
-    source-map "^0.5.6"
-    source-map-support "^0.4.2"
-
 tslib@^1.8.1:
   version "1.13.0"
   resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043"
@@ -9410,13 +9283,6 @@
   resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
   integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=
 
-twinkie@0.0.11:
-  version "0.0.11"
-  resolved "https://registry.yarnpkg.com/twinkie/-/twinkie-0.0.11.tgz#013c5e4b6b23ac8dec5d4eb8f9f84858bc143a74"
-  integrity sha1-ATxeS2sjrI3sXU64+fhIWLwUOnQ=
-  dependencies:
-    cheerio "^1.0.0-rc.2"
-
 type-check@~0.3.2:
   version "0.3.2"
   resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72"
@@ -9469,11 +9335,6 @@
   resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.8.2.tgz#91d6868aaead7da74f493c553aeff76c0c0b1d5a"
   integrity sha512-EgOVgL/4xfVrCMbhYKUQTdF37SQn4Iw73H5BgCrF1Abdun7Kwy/QZsE/ssAy0y4LxBbvua3PIbFsbRczWWnDdQ==
 
-typescript@^2.4.1:
-  version "2.9.2"
-  resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.9.2.tgz#1cbf61d05d6b96269244eb6a3bce4bd914e0f00c"
-  integrity sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==
-
 typical@^2.6.1:
   version "2.6.1"
   resolved "https://registry.yarnpkg.com/typical/-/typical-2.6.1.tgz#5c080e5d661cbbe38259d2e70a3c7253e873881d"
@@ -9930,14 +9791,6 @@
   resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"
   integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==
 
-webpack-core@^0.6.8:
-  version "0.6.9"
-  resolved "https://registry.yarnpkg.com/webpack-core/-/webpack-core-0.6.9.tgz#fc571588c8558da77be9efb6debdc5a3b172bdc2"
-  integrity sha1-/FcViMhVjad76e+23r3Fo7FyvcI=
-  dependencies:
-    source-list-map "~0.1.7"
-    source-map "~0.4.1"
-
 whatwg-url@^6.4.0:
   version "6.5.0"
   resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-6.5.0.tgz#f2df02bff176fd65070df74ad5ccbb5a199965a8"