Fix web lint for current plugin tooling

The lint config still loaded ../../eslint.config.js, but Gerrit's
shared plugin lint config is plugins/eslint-plugin.config.js. The old
path is not present in Bazel runfiles, so lint_test cannot load ESLint.

Point the config at the shared plugin config. Also replace the loose
any types in change-message parsing with local types and mark the async
change processing call with void, so current lint rules pass without
suppressions.

Verified: ZRS=//plugins/zuul-results-summary/web
  bazelisk build //plugins/zuul-results-summary:zuul-results-summary
  bazelisk test :lint_test
  bazelisk test :zuul-results-summary-ts_typecheck_test
  bazelisk test :zuul-results-summary-ts_transitive_typecheck_test
Change-Id: Ic0b0dc2cfb5cfb77373a399da870f1ef5af02883
diff --git a/web/eslint.config.js b/web/eslint.config.js
index 74ce3bf..1d6bfe8 100644
--- a/web/eslint.config.js
+++ b/web/eslint.config.js
@@ -9,10 +9,10 @@
 // eslint-disable-next-line no-undef
 __plugindir = 'zuul-results-summary/web';
 
-const gerritEslint = require('../../eslint.config.js');
+const gerritEslint = require('../../eslint-plugin.config.js');
 
 module.exports = defineConfig([
   {
     extends: [gerritEslint],
   },
-]);
\ No newline at end of file
+]);
diff --git a/web/zuul-summary-status-tab.ts b/web/zuul-summary-status-tab.ts
index 93c091e..c043376 100644
--- a/web/zuul-summary-status-tab.ts
+++ b/web/zuul-summary-status-tab.ts
@@ -18,6 +18,7 @@
 import {PluginApi} from '@gerritcodereview/typescript-api/plugin';
 import {
   ChangeInfo,
+  ChangeMessageInfo,
   RevisionInfo,
 } from '@gerritcodereview/typescript-api/rest-api';
 import {LitElement, html, css, nothing} from 'lit';
@@ -43,16 +44,23 @@
 
 interface ZuulTableItem {
   succeeded: boolean;
+  author_id?: number;
   author_name: string;
-  revision: string | number;
+  revision?: string | number;
   pipeline: string;
-  rechecks?: number;
+  rechecks: number;
+  date: Date;
   gr_date: string;
   results: ZuulJobResult[];
 }
 
 const ZUUL_PRIORITY = [22348];
 
+function priority(authorId?: number) {
+  const index = authorId === undefined ? -1 : ZUUL_PRIORITY.indexOf(authorId);
+  return index === -1 ? Number.MAX_SAFE_INTEGER : index;
+}
+
 @customElement('zuul-summary-status-tab')
 export class ZuulSummaryStatusTab extends LitElement {
   @property({type: Object})
@@ -129,7 +137,7 @@
   }
 
   override updated(changed: Map<string, unknown>) {
-    if (changed.has('change')) this._processChange(this.change);
+    if (changed.has('change')) void this._processChange(this.change);
   }
 
   override render() {
@@ -199,7 +207,8 @@
     `;
   }
 
-  private async _processChange(change: any) {
+  private async _processChange(change?: ChangeInfo) {
+    if (!change) return;
     this._enabled = await this._projectEnabled(change.project);
     if (this._enabled) this._processMessages(change);
   }
@@ -220,26 +229,28 @@
     }
   }
 
-  private _match_message_via_tag(msg: any) {
+  private _match_message_via_tag(msg: ChangeMessageInfo) {
     return !!msg.tag?.startsWith('autogenerated:zuul');
   }
 
-  private _match_message_via_regex(msg: any) {
-    return /^(.* CI|Zuul)/.test(msg.author.name);
+  private _match_message_via_regex(msg: ChangeMessageInfo) {
+    return /^(.* CI|Zuul)/.test(msg.author?.name ?? '');
   }
 
-  private _get_status_and_pipeline(msg: any): [string, string] | false {
+  private _get_status_and_pipeline(
+    msg: ChangeMessageInfo,
+  ): [string, string] | undefined {
     const fullMatch = /^Build (\w+) \(([\w]+) pipeline\)\./gm.exec(msg.message);
     if (fullMatch) return [fullMatch[1], fullMatch[2]];
     const simpleMatch = /^Build (\w+)\./gm.exec(msg.message);
     if (simpleMatch) return [simpleMatch[1], 'unknown'];
-    return false;
+    return undefined;
   }
 
-  private _processMessages(change: any) {
-    const table: any[] = [];
+  private _processMessages(change: ChangeInfo) {
+    const table: ZuulTableItem[] = [];
 
-    change.messages.forEach((message: any) => {
+    (change.messages ?? []).forEach((message: ChangeMessageInfo) => {
       if (
         !(
           this._match_message_via_tag(message) ||
@@ -250,14 +261,13 @@
 
       const date = new Date(message.date);
       const revision = message._revision_number;
-      const [status, pipeline] = this._get_status_and_pipeline(message) || [];
-
-      if (!status) return;
+      const statusAndPipeline = this._get_status_and_pipeline(message);
+      if (!statusAndPipeline) return;
+      const [status, pipeline] = statusAndPipeline;
+      const authorId = message.author?._account_id;
 
       const existingIdx = table.findIndex(
-        entry =>
-          entry.author_id === message.author._account_id &&
-          entry.pipeline === pipeline,
+        entry => entry.author_id === authorId && entry.pipeline === pipeline,
       );
 
       let rechecks = 0;
@@ -265,30 +275,32 @@
         rechecks = table[existingIdx].rechecks + 1;
       }
 
-      const results: any[] = [];
+      const results: ZuulJobResult[] = [];
       const resultRe =
         /^- (?<job>[^ ]+) (?:(?<link>https?:\/\/[^ ]+)|[^ ]+) : ((ERROR (?<errormsg>.*?) in (?<errtime>.*))|(?<result>[^ ]+)( in (?<time>.*))?)/;
 
       message.message.split('\n').forEach((line: string) => {
         const match = resultRe.exec(line);
-        if (match?.groups) {
-          if (match.groups.result === 'SKIPPED') match.groups.link = '';
-          if (match.groups.errormsg) {
-            match.groups.result = 'ERROR';
-            match.groups.time = match.groups.errtime;
-          }
-          results.push(match.groups);
-        }
+        const groups = match?.groups;
+        if (!groups) return;
+        const result = groups.errormsg ? 'ERROR' : groups.result;
+        if (!groups.job || !result) return;
+        results.push({
+          job: groups.job,
+          link: result === 'SKIPPED' ? '' : groups.link,
+          result,
+          errormsg: groups.errormsg,
+          time: groups.errormsg ? groups.errtime : groups.time,
+        });
       });
 
-      const row = {
-        author_name: message.author.name,
-        author_id: message.author._account_id,
+      const row: ZuulTableItem = {
+        author_name: message.author?.name ?? '',
+        author_id: authorId,
         revision,
         rechecks,
         date,
         gr_date: message.date,
-        status,
         succeeded: status === 'succeeded',
         pipeline,
         results,
@@ -302,9 +314,9 @@
     });
 
     this.__table = table.sort((a, b) => {
-      const pa = ZUUL_PRIORITY.indexOf(a.author_id) >>> 0;
-      const pb = ZUUL_PRIORITY.indexOf(b.author_id) >>> 0;
-      return pa - pb || b.date - a.date;
+      const pa = priority(a.author_id);
+      const pb = priority(b.author_id);
+      return pa - pb || b.date.getTime() - a.date.getTime();
     });
   }
 }