mediawiki/extensions/GlobalWatchlist: main (log #2030389)

sourcepatches

This run took 120 seconds.

From e6c9bf3bccf4b1d7017886e51e52ace42043c4b9 Mon Sep 17 00:00:00 2001
From: libraryupgrader <tools.libraryupgrader@tools.wmflabs.org>
Date: Mon, 28 Jul 2025 04:21:07 +0000
Subject: [PATCH] build: Updating eslint-config-wikimedia to 0.31.0

Change-Id: Iabd057ca15d4a91744c529e99db3c4d88e744f82
---
 package-lock.json | 1204 ++++++++++++++++++++++++++++++++++++++++++---
 package.json      |    2 +-
 2 files changed, 1145 insertions(+), 61 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index f166d2a..96f32ef 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,7 +11,7 @@
 				"@wdio/local-runner": "9.15.0",
 				"@wdio/mocha-framework": "9.15.0",
 				"@wdio/spec-reporter": "9.15.0",
-				"eslint-config-wikimedia": "0.30.0",
+				"eslint-config-wikimedia": "0.31.0",
 				"grunt-banana-checker": "0.13.0",
 				"jsdoc": "4.0.4",
 				"jsdoc-wmf-theme": "1.1.0",
@@ -1227,6 +1227,249 @@
 				"url": "https://github.com/sponsors/sindresorhus"
 			}
 		},
+		"node_modules/@stylistic/eslint-plugin": {
+			"version": "3.1.0",
+			"resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-3.1.0.tgz",
+			"integrity": "sha512-pA6VOrOqk0+S8toJYhQGv2MWpQQR0QpeUo9AhNkC49Y26nxBQ/nH1rta9bUU1rPw2fJ1zZEMV5oCX5AazT7J2g==",
+			"dev": true,
+			"dependencies": {
+				"@typescript-eslint/utils": "^8.13.0",
+				"eslint-visitor-keys": "^4.2.0",
+				"espree": "^10.3.0",
+				"estraverse": "^5.3.0",
+				"picomatch": "^4.0.2"
+			},
+			"engines": {
+				"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+			},
+			"peerDependencies": {
+				"eslint": ">=8.40.0"
+			}
+		},
+		"node_modules/@stylistic/eslint-plugin/node_modules/@typescript-eslint/project-service": {
+			"version": "8.38.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.38.0.tgz",
+			"integrity": "sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==",
+			"dev": true,
+			"dependencies": {
+				"@typescript-eslint/tsconfig-utils": "^8.38.0",
+				"@typescript-eslint/types": "^8.38.0",
+				"debug": "^4.3.4"
+			},
+			"engines": {
+				"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			},
+			"peerDependencies": {
+				"typescript": ">=4.8.4 <5.9.0"
+			}
+		},
+		"node_modules/@stylistic/eslint-plugin/node_modules/@typescript-eslint/scope-manager": {
+			"version": "8.38.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.38.0.tgz",
+			"integrity": "sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==",
+			"dev": true,
+			"dependencies": {
+				"@typescript-eslint/types": "8.38.0",
+				"@typescript-eslint/visitor-keys": "8.38.0"
+			},
+			"engines": {
+				"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			}
+		},
+		"node_modules/@stylistic/eslint-plugin/node_modules/@typescript-eslint/tsconfig-utils": {
+			"version": "8.38.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.38.0.tgz",
+			"integrity": "sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==",
+			"dev": true,
+			"engines": {
+				"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			},
+			"peerDependencies": {
+				"typescript": ">=4.8.4 <5.9.0"
+			}
+		},
+		"node_modules/@stylistic/eslint-plugin/node_modules/@typescript-eslint/types": {
+			"version": "8.38.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.38.0.tgz",
+			"integrity": "sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==",
+			"dev": true,
+			"engines": {
+				"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			}
+		},
+		"node_modules/@stylistic/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": {
+			"version": "8.38.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.38.0.tgz",
+			"integrity": "sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==",
+			"dev": true,
+			"dependencies": {
+				"@typescript-eslint/project-service": "8.38.0",
+				"@typescript-eslint/tsconfig-utils": "8.38.0",
+				"@typescript-eslint/types": "8.38.0",
+				"@typescript-eslint/visitor-keys": "8.38.0",
+				"debug": "^4.3.4",
+				"fast-glob": "^3.3.2",
+				"is-glob": "^4.0.3",
+				"minimatch": "^9.0.4",
+				"semver": "^7.6.0",
+				"ts-api-utils": "^2.1.0"
+			},
+			"engines": {
+				"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			},
+			"peerDependencies": {
+				"typescript": ">=4.8.4 <5.9.0"
+			}
+		},
+		"node_modules/@stylistic/eslint-plugin/node_modules/@typescript-eslint/utils": {
+			"version": "8.38.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.38.0.tgz",
+			"integrity": "sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==",
+			"dev": true,
+			"dependencies": {
+				"@eslint-community/eslint-utils": "^4.7.0",
+				"@typescript-eslint/scope-manager": "8.38.0",
+				"@typescript-eslint/types": "8.38.0",
+				"@typescript-eslint/typescript-estree": "8.38.0"
+			},
+			"engines": {
+				"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			},
+			"peerDependencies": {
+				"eslint": "^8.57.0 || ^9.0.0",
+				"typescript": ">=4.8.4 <5.9.0"
+			}
+		},
+		"node_modules/@stylistic/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": {
+			"version": "8.38.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.38.0.tgz",
+			"integrity": "sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==",
+			"dev": true,
+			"dependencies": {
+				"@typescript-eslint/types": "8.38.0",
+				"eslint-visitor-keys": "^4.2.1"
+			},
+			"engines": {
+				"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			}
+		},
+		"node_modules/@stylistic/eslint-plugin/node_modules/brace-expansion": {
+			"version": "2.0.2",
+			"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+			"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+			"dev": true,
+			"dependencies": {
+				"balanced-match": "^1.0.0"
+			}
+		},
+		"node_modules/@stylistic/eslint-plugin/node_modules/eslint-visitor-keys": {
+			"version": "4.2.1",
+			"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+			"integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+			"dev": true,
+			"engines": {
+				"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+			},
+			"funding": {
+				"url": "https://opencollective.com/eslint"
+			}
+		},
+		"node_modules/@stylistic/eslint-plugin/node_modules/espree": {
+			"version": "10.4.0",
+			"resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
+			"integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
+			"dev": true,
+			"dependencies": {
+				"acorn": "^8.15.0",
+				"acorn-jsx": "^5.3.2",
+				"eslint-visitor-keys": "^4.2.1"
+			},
+			"engines": {
+				"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+			},
+			"funding": {
+				"url": "https://opencollective.com/eslint"
+			}
+		},
+		"node_modules/@stylistic/eslint-plugin/node_modules/minimatch": {
+			"version": "9.0.5",
+			"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+			"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+			"dev": true,
+			"dependencies": {
+				"brace-expansion": "^2.0.1"
+			},
+			"engines": {
+				"node": ">=16 || 14 >=14.17"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/isaacs"
+			}
+		},
+		"node_modules/@stylistic/eslint-plugin/node_modules/picomatch": {
+			"version": "4.0.3",
+			"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
+			"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+			"dev": true,
+			"engines": {
+				"node": ">=12"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/jonschlinkert"
+			}
+		},
+		"node_modules/@stylistic/eslint-plugin/node_modules/semver": {
+			"version": "7.7.2",
+			"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
+			"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
+			"dev": true,
+			"bin": {
+				"semver": "bin/semver.js"
+			},
+			"engines": {
+				"node": ">=10"
+			}
+		},
+		"node_modules/@stylistic/eslint-plugin/node_modules/ts-api-utils": {
+			"version": "2.1.0",
+			"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
+			"integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==",
+			"dev": true,
+			"engines": {
+				"node": ">=18.12"
+			},
+			"peerDependencies": {
+				"typescript": ">=4.8.4"
+			}
+		},
 		"node_modules/@stylistic/stylelint-config": {
 			"version": "2.0.0",
 			"resolved": "https://registry.npmjs.org/@stylistic/stylelint-config/-/stylelint-config-2.0.0.tgz",
@@ -1445,6 +1688,86 @@
 				"@types/node": "*"
 			}
 		},
+		"node_modules/@typescript-eslint/eslint-plugin": {
+			"version": "7.18.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz",
+			"integrity": "sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==",
+			"dev": true,
+			"optional": true,
+			"peer": true,
+			"dependencies": {
+				"@eslint-community/regexpp": "^4.10.0",
+				"@typescript-eslint/scope-manager": "7.18.0",
+				"@typescript-eslint/type-utils": "7.18.0",
+				"@typescript-eslint/utils": "7.18.0",
+				"@typescript-eslint/visitor-keys": "7.18.0",
+				"graphemer": "^1.4.0",
+				"ignore": "^5.3.1",
+				"natural-compare": "^1.4.0",
+				"ts-api-utils": "^1.3.0"
+			},
+			"engines": {
+				"node": "^18.18.0 || >=20.0.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			},
+			"peerDependencies": {
+				"@typescript-eslint/parser": "^7.0.0",
+				"eslint": "^8.56.0"
+			},
+			"peerDependenciesMeta": {
+				"typescript": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/@typescript-eslint/parser": {
+			"version": "7.18.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.18.0.tgz",
+			"integrity": "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==",
+			"dev": true,
+			"optional": true,
+			"peer": true,
+			"dependencies": {
+				"@typescript-eslint/scope-manager": "7.18.0",
+				"@typescript-eslint/types": "7.18.0",
+				"@typescript-eslint/typescript-estree": "7.18.0",
+				"@typescript-eslint/visitor-keys": "7.18.0",
+				"debug": "^4.3.4"
+			},
+			"engines": {
+				"node": "^18.18.0 || >=20.0.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			},
+			"peerDependencies": {
+				"eslint": "^8.56.0"
+			},
+			"peerDependenciesMeta": {
+				"typescript": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": {
+			"version": "7.18.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz",
+			"integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==",
+			"dev": true,
+			"optional": true,
+			"peer": true,
+			"engines": {
+				"node": "^18.18.0 || >=20.0.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			}
+		},
 		"node_modules/@typescript-eslint/project-service": {
 			"version": "8.34.0",
 			"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.34.0.tgz",
@@ -1480,13 +1803,13 @@
 			}
 		},
 		"node_modules/@typescript-eslint/scope-manager": {
-			"version": "7.12.0",
-			"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.12.0.tgz",
-			"integrity": "sha512-itF1pTnN6F3unPak+kutH9raIkL3lhH1YRPGgt7QQOh43DQKVJXmWkpb+vpc/TiDHs6RSd9CTbDsc/Y+Ygq7kg==",
+			"version": "7.18.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz",
+			"integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==",
 			"dev": true,
 			"dependencies": {
-				"@typescript-eslint/types": "7.12.0",
-				"@typescript-eslint/visitor-keys": "7.12.0"
+				"@typescript-eslint/types": "7.18.0",
+				"@typescript-eslint/visitor-keys": "7.18.0"
 			},
 			"engines": {
 				"node": "^18.18.0 || >=20.0.0"
@@ -1496,6 +1819,19 @@
 				"url": "https://opencollective.com/typescript-eslint"
 			}
 		},
+		"node_modules/@typescript-eslint/scope-manager/node_modules/@typescript-eslint/types": {
+			"version": "7.18.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz",
+			"integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==",
+			"dev": true,
+			"engines": {
+				"node": "^18.18.0 || >=20.0.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			}
+		},
 		"node_modules/@typescript-eslint/tsconfig-utils": {
 			"version": "8.34.0",
 			"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.34.0.tgz",
@@ -1512,6 +1848,35 @@
 				"typescript": ">=4.8.4 <5.9.0"
 			}
 		},
+		"node_modules/@typescript-eslint/type-utils": {
+			"version": "7.18.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.18.0.tgz",
+			"integrity": "sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==",
+			"dev": true,
+			"optional": true,
+			"peer": true,
+			"dependencies": {
+				"@typescript-eslint/typescript-estree": "7.18.0",
+				"@typescript-eslint/utils": "7.18.0",
+				"debug": "^4.3.4",
+				"ts-api-utils": "^1.3.0"
+			},
+			"engines": {
+				"node": "^18.18.0 || >=20.0.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			},
+			"peerDependencies": {
+				"eslint": "^8.56.0"
+			},
+			"peerDependenciesMeta": {
+				"typescript": {
+					"optional": true
+				}
+			}
+		},
 		"node_modules/@typescript-eslint/types": {
 			"version": "7.12.0",
 			"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.12.0.tgz",
@@ -1526,13 +1891,13 @@
 			}
 		},
 		"node_modules/@typescript-eslint/typescript-estree": {
-			"version": "7.12.0",
-			"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.12.0.tgz",
-			"integrity": "sha512-5bwqLsWBULv1h6pn7cMW5dXX/Y2amRqLaKqsASVwbBHMZSnHqE/HN4vT4fE0aFsiwxYvr98kqOWh1a8ZKXalCQ==",
+			"version": "7.18.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz",
+			"integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==",
 			"dev": true,
 			"dependencies": {
-				"@typescript-eslint/types": "7.12.0",
-				"@typescript-eslint/visitor-keys": "7.12.0",
+				"@typescript-eslint/types": "7.18.0",
+				"@typescript-eslint/visitor-keys": "7.18.0",
 				"debug": "^4.3.4",
 				"globby": "^11.1.0",
 				"is-glob": "^4.0.3",
@@ -1553,6 +1918,19 @@
 				}
 			}
 		},
+		"node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/types": {
+			"version": "7.18.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz",
+			"integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==",
+			"dev": true,
+			"engines": {
+				"node": "^18.18.0 || >=20.0.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			}
+		},
 		"node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
 			"version": "2.0.2",
 			"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
@@ -1578,9 +1956,9 @@
 			}
 		},
 		"node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
-			"version": "7.6.2",
-			"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz",
-			"integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==",
+			"version": "7.7.2",
+			"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
+			"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
 			"dev": true,
 			"bin": {
 				"semver": "bin/semver.js"
@@ -1590,15 +1968,15 @@
 			}
 		},
 		"node_modules/@typescript-eslint/utils": {
-			"version": "7.12.0",
-			"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.12.0.tgz",
-			"integrity": "sha512-Y6hhwxwDx41HNpjuYswYp6gDbkiZ8Hin9Bf5aJQn1bpTs3afYY4GX+MPYxma8jtoIV2GRwTM/UJm/2uGCVv+DQ==",
+			"version": "7.18.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz",
+			"integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==",
 			"dev": true,
 			"dependencies": {
 				"@eslint-community/eslint-utils": "^4.4.0",
-				"@typescript-eslint/scope-manager": "7.12.0",
-				"@typescript-eslint/types": "7.12.0",
-				"@typescript-eslint/typescript-estree": "7.12.0"
+				"@typescript-eslint/scope-manager": "7.18.0",
+				"@typescript-eslint/types": "7.18.0",
+				"@typescript-eslint/typescript-estree": "7.18.0"
 			},
 			"engines": {
 				"node": "^18.18.0 || >=20.0.0"
@@ -1611,13 +1989,26 @@
 				"eslint": "^8.56.0"
 			}
 		},
+		"node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": {
+			"version": "7.18.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz",
+			"integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==",
+			"dev": true,
+			"engines": {
+				"node": "^18.18.0 || >=20.0.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			}
+		},
 		"node_modules/@typescript-eslint/visitor-keys": {
-			"version": "7.12.0",
-			"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.12.0.tgz",
-			"integrity": "sha512-uZk7DevrQLL3vSnfFl5bj4sL75qC9D6EdjemIdbtkuUmIheWpuiiylSY01JxJE7+zGrOWDZrp1WxOuDntvKrHQ==",
+			"version": "7.18.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz",
+			"integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==",
 			"dev": true,
 			"dependencies": {
-				"@typescript-eslint/types": "7.12.0",
+				"@typescript-eslint/types": "7.18.0",
 				"eslint-visitor-keys": "^3.4.3"
 			},
 			"engines": {
@@ -1628,6 +2019,19 @@
 				"url": "https://opencollective.com/typescript-eslint"
 			}
 		},
+		"node_modules/@typescript-eslint/visitor-keys/node_modules/@typescript-eslint/types": {
+			"version": "7.18.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz",
+			"integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==",
+			"dev": true,
+			"engines": {
+				"node": "^18.18.0 || >=20.0.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			}
+		},
 		"node_modules/@ungap/structured-clone": {
 			"version": "1.2.0",
 			"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
@@ -2369,9 +2773,9 @@
 			}
 		},
 		"node_modules/acorn": {
-			"version": "8.11.3",
-			"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
-			"integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
+			"version": "8.15.0",
+			"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
+			"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
 			"dev": true,
 			"bin": {
 				"acorn": "bin/acorn"
@@ -4244,11 +4648,14 @@
 			}
 		},
 		"node_modules/eslint-config-wikimedia": {
-			"version": "0.30.0",
-			"resolved": "https://registry.npmjs.org/eslint-config-wikimedia/-/eslint-config-wikimedia-0.30.0.tgz",
-			"integrity": "sha512-i8ESzSoo0x3Jur/0JhAgCVPxbV51zfdI3MN3MVQPnjiFdmo21CNKmiBBmw8JnJ3fx/d5zHDrBa+yDjxSLpnDlA==",
+			"version": "0.31.0",
+			"resolved": "https://registry.npmjs.org/eslint-config-wikimedia/-/eslint-config-wikimedia-0.31.0.tgz",
+			"integrity": "sha512-Z/t/zGPdxs/ehxb0EM6THNWAzueT7GtuqzjUvmBpkxcTKzZPJEXWnnpswdj/hgv8Ce8PIeDp0zwQxR4e3c9CIw==",
 			"dev": true,
 			"dependencies": {
+				"@stylistic/eslint-plugin": "^3.1.0",
+				"@typescript-eslint/eslint-plugin": "8.35.1",
+				"@typescript-eslint/parser": "8.35.1",
 				"browserslist-config-wikimedia": "^0.7.0",
 				"eslint": "^8.57.0",
 				"eslint-plugin-compat": "^4.2.0",
@@ -4268,7 +4675,287 @@
 				"eslint-plugin-yml": "^1.14.0"
 			},
 			"engines": {
-				"node": ">=18 <23"
+				"node": ">=18 <25"
+			}
+		},
+		"node_modules/eslint-config-wikimedia/node_modules/@typescript-eslint/eslint-plugin": {
+			"version": "8.35.1",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.35.1.tgz",
+			"integrity": "sha512-9XNTlo7P7RJxbVeICaIIIEipqxLKguyh+3UbXuT2XQuFp6d8VOeDEGuz5IiX0dgZo8CiI6aOFLg4e8cF71SFVg==",
+			"dev": true,
+			"dependencies": {
+				"@eslint-community/regexpp": "^4.10.0",
+				"@typescript-eslint/scope-manager": "8.35.1",
+				"@typescript-eslint/type-utils": "8.35.1",
+				"@typescript-eslint/utils": "8.35.1",
+				"@typescript-eslint/visitor-keys": "8.35.1",
+				"graphemer": "^1.4.0",
+				"ignore": "^7.0.0",
+				"natural-compare": "^1.4.0",
+				"ts-api-utils": "^2.1.0"
+			},
+			"engines": {
+				"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			},
+			"peerDependencies": {
+				"@typescript-eslint/parser": "^8.35.1",
+				"eslint": "^8.57.0 || ^9.0.0",
+				"typescript": ">=4.8.4 <5.9.0"
+			}
+		},
+		"node_modules/eslint-config-wikimedia/node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/type-utils": {
+			"version": "8.35.1",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.35.1.tgz",
+			"integrity": "sha512-HOrUBlfVRz5W2LIKpXzZoy6VTZzMu2n8q9C2V/cFngIC5U1nStJgv0tMV4sZPzdf4wQm9/ToWUFPMN9Vq9VJQQ==",
+			"dev": true,
+			"dependencies": {
+				"@typescript-eslint/typescript-estree": "8.35.1",
+				"@typescript-eslint/utils": "8.35.1",
+				"debug": "^4.3.4",
+				"ts-api-utils": "^2.1.0"
+			},
+			"engines": {
+				"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			},
+			"peerDependencies": {
+				"eslint": "^8.57.0 || ^9.0.0",
+				"typescript": ">=4.8.4 <5.9.0"
+			}
+		},
+		"node_modules/eslint-config-wikimedia/node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": {
+			"version": "8.35.1",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.35.1.tgz",
+			"integrity": "sha512-lhnwatFmOFcazAsUm3ZnZFpXSxiwoa1Lj50HphnDe1Et01NF4+hrdXONSUHIcbVu2eFb1bAf+5yjXkGVkXBKAQ==",
+			"dev": true,
+			"dependencies": {
+				"@eslint-community/eslint-utils": "^4.7.0",
+				"@typescript-eslint/scope-manager": "8.35.1",
+				"@typescript-eslint/types": "8.35.1",
+				"@typescript-eslint/typescript-estree": "8.35.1"
+			},
+			"engines": {
+				"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			},
+			"peerDependencies": {
+				"eslint": "^8.57.0 || ^9.0.0",
+				"typescript": ">=4.8.4 <5.9.0"
+			}
+		},
+		"node_modules/eslint-config-wikimedia/node_modules/@typescript-eslint/parser": {
+			"version": "8.35.1",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.35.1.tgz",
+			"integrity": "sha512-3MyiDfrfLeK06bi/g9DqJxP5pV74LNv4rFTyvGDmT3x2p1yp1lOd+qYZfiRPIOf/oON+WRZR5wxxuF85qOar+w==",
+			"dev": true,
+			"dependencies": {
+				"@typescript-eslint/scope-manager": "8.35.1",
+				"@typescript-eslint/types": "8.35.1",
+				"@typescript-eslint/typescript-estree": "8.35.1",
+				"@typescript-eslint/visitor-keys": "8.35.1",
+				"debug": "^4.3.4"
+			},
+			"engines": {
+				"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			},
+			"peerDependencies": {
+				"eslint": "^8.57.0 || ^9.0.0",
+				"typescript": ">=4.8.4 <5.9.0"
+			}
+		},
+		"node_modules/eslint-config-wikimedia/node_modules/@typescript-eslint/project-service": {
+			"version": "8.35.1",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.35.1.tgz",
+			"integrity": "sha512-VYxn/5LOpVxADAuP3NrnxxHYfzVtQzLKeldIhDhzC8UHaiQvYlXvKuVho1qLduFbJjjy5U5bkGwa3rUGUb1Q6Q==",
+			"dev": true,
+			"dependencies": {
+				"@typescript-eslint/tsconfig-utils": "^8.35.1",
+				"@typescript-eslint/types": "^8.35.1",
+				"debug": "^4.3.4"
+			},
+			"engines": {
+				"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			},
+			"peerDependencies": {
+				"typescript": ">=4.8.4 <5.9.0"
+			}
+		},
+		"node_modules/eslint-config-wikimedia/node_modules/@typescript-eslint/scope-manager": {
+			"version": "8.35.1",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.35.1.tgz",
+			"integrity": "sha512-s/Bpd4i7ht2934nG+UoSPlYXd08KYz3bmjLEb7Ye1UVob0d1ENiT3lY8bsCmik4RqfSbPw9xJJHbugpPpP5JUg==",
+			"dev": true,
+			"dependencies": {
+				"@typescript-eslint/types": "8.35.1",
+				"@typescript-eslint/visitor-keys": "8.35.1"
+			},
+			"engines": {
+				"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			}
+		},
+		"node_modules/eslint-config-wikimedia/node_modules/@typescript-eslint/tsconfig-utils": {
+			"version": "8.35.1",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.35.1.tgz",
+			"integrity": "sha512-K5/U9VmT9dTHoNowWZpz+/TObS3xqC5h0xAIjXPw+MNcKV9qg6eSatEnmeAwkjHijhACH0/N7bkhKvbt1+DXWQ==",
+			"dev": true,
+			"engines": {
+				"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			},
+			"peerDependencies": {
+				"typescript": ">=4.8.4 <5.9.0"
+			}
+		},
+		"node_modules/eslint-config-wikimedia/node_modules/@typescript-eslint/types": {
+			"version": "8.35.1",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.35.1.tgz",
+			"integrity": "sha512-q/O04vVnKHfrrhNAscndAn1tuQhIkwqnaW+eu5waD5IPts2eX1dgJxgqcPx5BX109/qAz7IG6VrEPTOYKCNfRQ==",
+			"dev": true,
+			"engines": {
+				"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			}
+		},
+		"node_modules/eslint-config-wikimedia/node_modules/@typescript-eslint/typescript-estree": {
+			"version": "8.35.1",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.35.1.tgz",
+			"integrity": "sha512-Vvpuvj4tBxIka7cPs6Y1uvM7gJgdF5Uu9F+mBJBPY4MhvjrjWGK4H0lVgLJd/8PWZ23FTqsaJaLEkBCFUk8Y9g==",
+			"dev": true,
+			"dependencies": {
+				"@typescript-eslint/project-service": "8.35.1",
+				"@typescript-eslint/tsconfig-utils": "8.35.1",
+				"@typescript-eslint/types": "8.35.1",
+				"@typescript-eslint/visitor-keys": "8.35.1",
+				"debug": "^4.3.4",
+				"fast-glob": "^3.3.2",
+				"is-glob": "^4.0.3",
+				"minimatch": "^9.0.4",
+				"semver": "^7.6.0",
+				"ts-api-utils": "^2.1.0"
+			},
+			"engines": {
+				"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			},
+			"peerDependencies": {
+				"typescript": ">=4.8.4 <5.9.0"
+			}
+		},
+		"node_modules/eslint-config-wikimedia/node_modules/@typescript-eslint/visitor-keys": {
+			"version": "8.35.1",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.35.1.tgz",
+			"integrity": "sha512-VRwixir4zBWCSTP/ljEo091lbpypz57PoeAQ9imjG+vbeof9LplljsL1mos4ccG6H9IjfrVGM359RozUnuFhpw==",
+			"dev": true,
+			"dependencies": {
+				"@typescript-eslint/types": "8.35.1",
+				"eslint-visitor-keys": "^4.2.1"
+			},
+			"engines": {
+				"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			}
+		},
+		"node_modules/eslint-config-wikimedia/node_modules/brace-expansion": {
+			"version": "2.0.2",
+			"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+			"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+			"dev": true,
+			"dependencies": {
+				"balanced-match": "^1.0.0"
+			}
+		},
+		"node_modules/eslint-config-wikimedia/node_modules/eslint-visitor-keys": {
+			"version": "4.2.1",
+			"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+			"integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+			"dev": true,
+			"engines": {
+				"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+			},
+			"funding": {
+				"url": "https://opencollective.com/eslint"
+			}
+		},
+		"node_modules/eslint-config-wikimedia/node_modules/ignore": {
+			"version": "7.0.5",
+			"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
+			"integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
+			"dev": true,
+			"engines": {
+				"node": ">= 4"
+			}
+		},
+		"node_modules/eslint-config-wikimedia/node_modules/minimatch": {
+			"version": "9.0.5",
+			"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+			"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+			"dev": true,
+			"dependencies": {
+				"brace-expansion": "^2.0.1"
+			},
+			"engines": {
+				"node": ">=16 || 14 >=14.17"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/isaacs"
+			}
+		},
+		"node_modules/eslint-config-wikimedia/node_modules/semver": {
+			"version": "7.7.2",
+			"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
+			"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
+			"dev": true,
+			"bin": {
+				"semver": "bin/semver.js"
+			},
+			"engines": {
+				"node": ">=10"
+			}
+		},
+		"node_modules/eslint-config-wikimedia/node_modules/ts-api-utils": {
+			"version": "2.1.0",
+			"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
+			"integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==",
+			"dev": true,
+			"engines": {
+				"node": ">=18.12"
+			},
+			"peerDependencies": {
+				"typescript": ">=4.8.4"
 			}
 		},
 		"node_modules/eslint-plugin-compat": {
@@ -11224,6 +11911,149 @@
 			"integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==",
 			"dev": true
 		},
+		"@stylistic/eslint-plugin": {
+			"version": "3.1.0",
+			"resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-3.1.0.tgz",
+			"integrity": "sha512-pA6VOrOqk0+S8toJYhQGv2MWpQQR0QpeUo9AhNkC49Y26nxBQ/nH1rta9bUU1rPw2fJ1zZEMV5oCX5AazT7J2g==",
+			"dev": true,
+			"requires": {
+				"@typescript-eslint/utils": "^8.13.0",
+				"eslint-visitor-keys": "^4.2.0",
+				"espree": "^10.3.0",
+				"estraverse": "^5.3.0",
+				"picomatch": "^4.0.2"
+			},
+			"dependencies": {
+				"@typescript-eslint/project-service": {
+					"version": "8.38.0",
+					"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.38.0.tgz",
+					"integrity": "sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==",
+					"dev": true,
+					"requires": {
+						"@typescript-eslint/tsconfig-utils": "^8.38.0",
+						"@typescript-eslint/types": "^8.38.0",
+						"debug": "^4.3.4"
+					}
+				},
+				"@typescript-eslint/scope-manager": {
+					"version": "8.38.0",
+					"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.38.0.tgz",
+					"integrity": "sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==",
+					"dev": true,
+					"requires": {
+						"@typescript-eslint/types": "8.38.0",
+						"@typescript-eslint/visitor-keys": "8.38.0"
+					}
+				},
+				"@typescript-eslint/tsconfig-utils": {
+					"version": "8.38.0",
+					"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.38.0.tgz",
+					"integrity": "sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==",
+					"dev": true,
+					"requires": {}
+				},
+				"@typescript-eslint/types": {
+					"version": "8.38.0",
+					"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.38.0.tgz",
+					"integrity": "sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==",
+					"dev": true
+				},
+				"@typescript-eslint/typescript-estree": {
+					"version": "8.38.0",
+					"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.38.0.tgz",
+					"integrity": "sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==",
+					"dev": true,
+					"requires": {
+						"@typescript-eslint/project-service": "8.38.0",
+						"@typescript-eslint/tsconfig-utils": "8.38.0",
+						"@typescript-eslint/types": "8.38.0",
+						"@typescript-eslint/visitor-keys": "8.38.0",
+						"debug": "^4.3.4",
+						"fast-glob": "^3.3.2",
+						"is-glob": "^4.0.3",
+						"minimatch": "^9.0.4",
+						"semver": "^7.6.0",
+						"ts-api-utils": "^2.1.0"
+					}
+				},
+				"@typescript-eslint/utils": {
+					"version": "8.38.0",
+					"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.38.0.tgz",
+					"integrity": "sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==",
+					"dev": true,
+					"requires": {
+						"@eslint-community/eslint-utils": "^4.7.0",
+						"@typescript-eslint/scope-manager": "8.38.0",
+						"@typescript-eslint/types": "8.38.0",
+						"@typescript-eslint/typescript-estree": "8.38.0"
+					}
+				},
+				"@typescript-eslint/visitor-keys": {
+					"version": "8.38.0",
+					"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.38.0.tgz",
+					"integrity": "sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==",
+					"dev": true,
+					"requires": {
+						"@typescript-eslint/types": "8.38.0",
+						"eslint-visitor-keys": "^4.2.1"
+					}
+				},
+				"brace-expansion": {
+					"version": "2.0.2",
+					"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+					"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+					"dev": true,
+					"requires": {
+						"balanced-match": "^1.0.0"
+					}
+				},
+				"eslint-visitor-keys": {
+					"version": "4.2.1",
+					"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+					"integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+					"dev": true
+				},
+				"espree": {
+					"version": "10.4.0",
+					"resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
+					"integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
+					"dev": true,
+					"requires": {
+						"acorn": "^8.15.0",
+						"acorn-jsx": "^5.3.2",
+						"eslint-visitor-keys": "^4.2.1"
+					}
+				},
+				"minimatch": {
+					"version": "9.0.5",
+					"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+					"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+					"dev": true,
+					"requires": {
+						"brace-expansion": "^2.0.1"
+					}
+				},
+				"picomatch": {
+					"version": "4.0.3",
+					"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
+					"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+					"dev": true
+				},
+				"semver": {
+					"version": "7.7.2",
+					"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
+					"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
+					"dev": true
+				},
+				"ts-api-utils": {
+					"version": "2.1.0",
+					"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
+					"integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==",
+					"dev": true,
+					"requires": {}
+				}
+			}
+		},
 		"@stylistic/stylelint-config": {
 			"version": "2.0.0",
 			"resolved": "https://registry.npmjs.org/@stylistic/stylelint-config/-/stylelint-config-2.0.0.tgz",
@@ -11429,6 +12259,50 @@
 				"@types/node": "*"
 			}
 		},
+		"@typescript-eslint/eslint-plugin": {
+			"version": "7.18.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz",
+			"integrity": "sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==",
+			"dev": true,
+			"optional": true,
+			"peer": true,
+			"requires": {
+				"@eslint-community/regexpp": "^4.10.0",
+				"@typescript-eslint/scope-manager": "7.18.0",
+				"@typescript-eslint/type-utils": "7.18.0",
+				"@typescript-eslint/utils": "7.18.0",
+				"@typescript-eslint/visitor-keys": "7.18.0",
+				"graphemer": "^1.4.0",
+				"ignore": "^5.3.1",
+				"natural-compare": "^1.4.0",
+				"ts-api-utils": "^1.3.0"
+			}
+		},
+		"@typescript-eslint/parser": {
+			"version": "7.18.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.18.0.tgz",
+			"integrity": "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==",
+			"dev": true,
+			"optional": true,
+			"peer": true,
+			"requires": {
+				"@typescript-eslint/scope-manager": "7.18.0",
+				"@typescript-eslint/types": "7.18.0",
+				"@typescript-eslint/typescript-estree": "7.18.0",
+				"@typescript-eslint/visitor-keys": "7.18.0",
+				"debug": "^4.3.4"
+			},
+			"dependencies": {
+				"@typescript-eslint/types": {
+					"version": "7.18.0",
+					"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz",
+					"integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==",
+					"dev": true,
+					"optional": true,
+					"peer": true
+				}
+			}
+		},
 		"@typescript-eslint/project-service": {
 			"version": "8.34.0",
 			"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.34.0.tgz",
@@ -11449,13 +12323,21 @@
 			}
 		},
 		"@typescript-eslint/scope-manager": {
-			"version": "7.12.0",
-			"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.12.0.tgz",
-			"integrity": "sha512-itF1pTnN6F3unPak+kutH9raIkL3lhH1YRPGgt7QQOh43DQKVJXmWkpb+vpc/TiDHs6RSd9CTbDsc/Y+Ygq7kg==",
+			"version": "7.18.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz",
+			"integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==",
 			"dev": true,
 			"requires": {
-				"@typescript-eslint/types": "7.12.0",
-				"@typescript-eslint/visitor-keys": "7.12.0"
+				"@typescript-eslint/types": "7.18.0",
+				"@typescript-eslint/visitor-keys": "7.18.0"
+			},
+			"dependencies": {
+				"@typescript-eslint/types": {
+					"version": "7.18.0",
+					"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz",
+					"integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==",
+					"dev": true
+				}
 			}
 		},
 		"@typescript-eslint/tsconfig-utils": {
@@ -11465,6 +12347,20 @@
 			"dev": true,
 			"requires": {}
 		},
+		"@typescript-eslint/type-utils": {
+			"version": "7.18.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.18.0.tgz",
+			"integrity": "sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==",
+			"dev": true,
+			"optional": true,
+			"peer": true,
+			"requires": {
+				"@typescript-eslint/typescript-estree": "7.18.0",
+				"@typescript-eslint/utils": "7.18.0",
+				"debug": "^4.3.4",
+				"ts-api-utils": "^1.3.0"
+			}
+		},
 		"@typescript-eslint/types": {
 			"version": "7.12.0",
 			"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.12.0.tgz",
@@ -11472,13 +12368,13 @@
 			"dev": true
 		},
 		"@typescript-eslint/typescript-estree": {
-			"version": "7.12.0",
-			"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.12.0.tgz",
-			"integrity": "sha512-5bwqLsWBULv1h6pn7cMW5dXX/Y2amRqLaKqsASVwbBHMZSnHqE/HN4vT4fE0aFsiwxYvr98kqOWh1a8ZKXalCQ==",
+			"version": "7.18.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz",
+			"integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==",
 			"dev": true,
 			"requires": {
-				"@typescript-eslint/types": "7.12.0",
-				"@typescript-eslint/visitor-keys": "7.12.0",
+				"@typescript-eslint/types": "7.18.0",
+				"@typescript-eslint/visitor-keys": "7.18.0",
 				"debug": "^4.3.4",
 				"globby": "^11.1.0",
 				"is-glob": "^4.0.3",
@@ -11487,6 +12383,12 @@
 				"ts-api-utils": "^1.3.0"
 			},
 			"dependencies": {
+				"@typescript-eslint/types": {
+					"version": "7.18.0",
+					"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz",
+					"integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==",
+					"dev": true
+				},
 				"brace-expansion": {
 					"version": "2.0.2",
 					"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
@@ -11506,33 +12408,49 @@
 					}
 				},
 				"semver": {
-					"version": "7.6.2",
-					"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz",
-					"integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==",
+					"version": "7.7.2",
+					"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
+					"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
 					"dev": true
 				}
 			}
 		},
 		"@typescript-eslint/utils": {
-			"version": "7.12.0",
-			"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.12.0.tgz",
-			"integrity": "sha512-Y6hhwxwDx41HNpjuYswYp6gDbkiZ8Hin9Bf5aJQn1bpTs3afYY4GX+MPYxma8jtoIV2GRwTM/UJm/2uGCVv+DQ==",
+			"version": "7.18.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz",
+			"integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==",
 			"dev": true,
 			"requires": {
 				"@eslint-community/eslint-utils": "^4.4.0",
-				"@typescript-eslint/scope-manager": "7.12.0",
-				"@typescript-eslint/types": "7.12.0",
-				"@typescript-eslint/typescript-estree": "7.12.0"
+				"@typescript-eslint/scope-manager": "7.18.0",
+				"@typescript-eslint/types": "7.18.0",
+				"@typescript-eslint/typescript-estree": "7.18.0"
+			},
+			"dependencies": {
+				"@typescript-eslint/types": {
+					"version": "7.18.0",
+					"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz",
+					"integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==",
+					"dev": true
+				}
 			}
 		},
 		"@typescript-eslint/visitor-keys": {
-			"version": "7.12.0",
-			"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.12.0.tgz",
-			"integrity": "sha512-uZk7DevrQLL3vSnfFl5bj4sL75qC9D6EdjemIdbtkuUmIheWpuiiylSY01JxJE7+zGrOWDZrp1WxOuDntvKrHQ==",
+			"version": "7.18.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz",
+			"integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==",
 			"dev": true,
 			"requires": {
-				"@typescript-eslint/types": "7.12.0",
+				"@typescript-eslint/types": "7.18.0",
 				"eslint-visitor-keys": "^3.4.3"
+			},
+			"dependencies": {
+				"@typescript-eslint/types": {
+					"version": "7.18.0",
+					"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz",
+					"integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==",
+					"dev": true
+				}
 			}
 		},
 		"@ungap/structured-clone": {
@@ -12073,9 +12991,9 @@
 			}
 		},
 		"acorn": {
-			"version": "8.11.3",
-			"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
-			"integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
+			"version": "8.15.0",
+			"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
+			"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
 			"dev": true
 		},
 		"acorn-jsx": {
@@ -13505,11 +14423,14 @@
 			}
 		},
 		"eslint-config-wikimedia": {
-			"version": "0.30.0",
-			"resolved": "https://registry.npmjs.org/eslint-config-wikimedia/-/eslint-config-wikimedia-0.30.0.tgz",
-			"integrity": "sha512-i8ESzSoo0x3Jur/0JhAgCVPxbV51zfdI3MN3MVQPnjiFdmo21CNKmiBBmw8JnJ3fx/d5zHDrBa+yDjxSLpnDlA==",
+			"version": "0.31.0",
+			"resolved": "https://registry.npmjs.org/eslint-config-wikimedia/-/eslint-config-wikimedia-0.31.0.tgz",
+			"integrity": "sha512-Z/t/zGPdxs/ehxb0EM6THNWAzueT7GtuqzjUvmBpkxcTKzZPJEXWnnpswdj/hgv8Ce8PIeDp0zwQxR4e3c9CIw==",
 			"dev": true,
 			"requires": {
+				"@stylistic/eslint-plugin": "^3.1.0",
+				"@typescript-eslint/eslint-plugin": "8.35.1",
+				"@typescript-eslint/parser": "8.35.1",
 				"browserslist-config-wikimedia": "^0.7.0",
 				"eslint": "^8.57.0",
 				"eslint-plugin-compat": "^4.2.0",
@@ -13527,6 +14448,169 @@
 				"eslint-plugin-vue": "^9.26.0",
 				"eslint-plugin-wdio": "^8.24.12",
 				"eslint-plugin-yml": "^1.14.0"
+			},
+			"dependencies": {
+				"@typescript-eslint/eslint-plugin": {
+					"version": "8.35.1",
+					"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.35.1.tgz",
+					"integrity": "sha512-9XNTlo7P7RJxbVeICaIIIEipqxLKguyh+3UbXuT2XQuFp6d8VOeDEGuz5IiX0dgZo8CiI6aOFLg4e8cF71SFVg==",
+					"dev": true,
+					"requires": {
+						"@eslint-community/regexpp": "^4.10.0",
+						"@typescript-eslint/scope-manager": "8.35.1",
+						"@typescript-eslint/type-utils": "8.35.1",
+						"@typescript-eslint/utils": "8.35.1",
+						"@typescript-eslint/visitor-keys": "8.35.1",
+						"graphemer": "^1.4.0",
+						"ignore": "^7.0.0",
+						"natural-compare": "^1.4.0",
+						"ts-api-utils": "^2.1.0"
+					},
+					"dependencies": {
+						"@typescript-eslint/type-utils": {
+							"version": "8.35.1",
+							"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.35.1.tgz",
+							"integrity": "sha512-HOrUBlfVRz5W2LIKpXzZoy6VTZzMu2n8q9C2V/cFngIC5U1nStJgv0tMV4sZPzdf4wQm9/ToWUFPMN9Vq9VJQQ==",
+							"dev": true,
+							"requires": {
+								"@typescript-eslint/typescript-estree": "8.35.1",
+								"@typescript-eslint/utils": "8.35.1",
+								"debug": "^4.3.4",
+								"ts-api-utils": "^2.1.0"
+							}
+						},
+						"@typescript-eslint/utils": {
+							"version": "8.35.1",
+							"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.35.1.tgz",
+							"integrity": "sha512-lhnwatFmOFcazAsUm3ZnZFpXSxiwoa1Lj50HphnDe1Et01NF4+hrdXONSUHIcbVu2eFb1bAf+5yjXkGVkXBKAQ==",
+							"dev": true,
+							"requires": {
+								"@eslint-community/eslint-utils": "^4.7.0",
+								"@typescript-eslint/scope-manager": "8.35.1",
+								"@typescript-eslint/types": "8.35.1",
+								"@typescript-eslint/typescript-estree": "8.35.1"
+							}
+						}
+					}
+				},
+				"@typescript-eslint/parser": {
+					"version": "8.35.1",
+					"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.35.1.tgz",
+					"integrity": "sha512-3MyiDfrfLeK06bi/g9DqJxP5pV74LNv4rFTyvGDmT3x2p1yp1lOd+qYZfiRPIOf/oON+WRZR5wxxuF85qOar+w==",
+					"dev": true,
+					"requires": {
+						"@typescript-eslint/scope-manager": "8.35.1",
+						"@typescript-eslint/types": "8.35.1",
+						"@typescript-eslint/typescript-estree": "8.35.1",
+						"@typescript-eslint/visitor-keys": "8.35.1",
+						"debug": "^4.3.4"
+					}
+				},
+				"@typescript-eslint/project-service": {
+					"version": "8.35.1",
+					"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.35.1.tgz",
+					"integrity": "sha512-VYxn/5LOpVxADAuP3NrnxxHYfzVtQzLKeldIhDhzC8UHaiQvYlXvKuVho1qLduFbJjjy5U5bkGwa3rUGUb1Q6Q==",
+					"dev": true,
+					"requires": {
+						"@typescript-eslint/tsconfig-utils": "^8.35.1",
+						"@typescript-eslint/types": "^8.35.1",
+						"debug": "^4.3.4"
+					}
+				},
+				"@typescript-eslint/scope-manager": {
+					"version": "8.35.1",
+					"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.35.1.tgz",
+					"integrity": "sha512-s/Bpd4i7ht2934nG+UoSPlYXd08KYz3bmjLEb7Ye1UVob0d1ENiT3lY8bsCmik4RqfSbPw9xJJHbugpPpP5JUg==",
+					"dev": true,
+					"requires": {
+						"@typescript-eslint/types": "8.35.1",
+						"@typescript-eslint/visitor-keys": "8.35.1"
+					}
+				},
+				"@typescript-eslint/tsconfig-utils": {
+					"version": "8.35.1",
+					"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.35.1.tgz",
+					"integrity": "sha512-K5/U9VmT9dTHoNowWZpz+/TObS3xqC5h0xAIjXPw+MNcKV9qg6eSatEnmeAwkjHijhACH0/N7bkhKvbt1+DXWQ==",
+					"dev": true,
+					"requires": {}
+				},
+				"@typescript-eslint/types": {
+					"version": "8.35.1",
+					"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.35.1.tgz",
+					"integrity": "sha512-q/O04vVnKHfrrhNAscndAn1tuQhIkwqnaW+eu5waD5IPts2eX1dgJxgqcPx5BX109/qAz7IG6VrEPTOYKCNfRQ==",
+					"dev": true
+				},
+				"@typescript-eslint/typescript-estree": {
+					"version": "8.35.1",
+					"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.35.1.tgz",
+					"integrity": "sha512-Vvpuvj4tBxIka7cPs6Y1uvM7gJgdF5Uu9F+mBJBPY4MhvjrjWGK4H0lVgLJd/8PWZ23FTqsaJaLEkBCFUk8Y9g==",
+					"dev": true,
+					"requires": {
+						"@typescript-eslint/project-service": "8.35.1",
+						"@typescript-eslint/tsconfig-utils": "8.35.1",
+						"@typescript-eslint/types": "8.35.1",
+						"@typescript-eslint/visitor-keys": "8.35.1",
+						"debug": "^4.3.4",
+						"fast-glob": "^3.3.2",
+						"is-glob": "^4.0.3",
+						"minimatch": "^9.0.4",
+						"semver": "^7.6.0",
+						"ts-api-utils": "^2.1.0"
+					}
+				},
+				"@typescript-eslint/visitor-keys": {
+					"version": "8.35.1",
+					"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.35.1.tgz",
+					"integrity": "sha512-VRwixir4zBWCSTP/ljEo091lbpypz57PoeAQ9imjG+vbeof9LplljsL1mos4ccG6H9IjfrVGM359RozUnuFhpw==",
+					"dev": true,
+					"requires": {
+						"@typescript-eslint/types": "8.35.1",
+						"eslint-visitor-keys": "^4.2.1"
+					}
+				},
+				"brace-expansion": {
+					"version": "2.0.2",
+					"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+					"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+					"dev": true,
+					"requires": {
+						"balanced-match": "^1.0.0"
+					}
+				},
+				"eslint-visitor-keys": {
+					"version": "4.2.1",
+					"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+					"integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+					"dev": true
+				},
+				"ignore": {
+					"version": "7.0.5",
+					"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
+					"integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
+					"dev": true
+				},
+				"minimatch": {
+					"version": "9.0.5",
+					"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+					"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+					"dev": true,
+					"requires": {
+						"brace-expansion": "^2.0.1"
+					}
+				},
+				"semver": {
+					"version": "7.7.2",
+					"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
+					"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
+					"dev": true
+				},
+				"ts-api-utils": {
+					"version": "2.1.0",
+					"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
+					"integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==",
+					"dev": true,
+					"requires": {}
+				}
 			}
 		},
 		"eslint-plugin-compat": {
diff --git a/package.json b/package.json
index 830be39..5024cae 100644
--- a/package.json
+++ b/package.json
@@ -18,7 +18,7 @@
 		"@wdio/local-runner": "9.15.0",
 		"@wdio/mocha-framework": "9.15.0",
 		"@wdio/spec-reporter": "9.15.0",
-		"eslint-config-wikimedia": "0.30.0",
+		"eslint-config-wikimedia": "0.31.0",
 		"grunt-banana-checker": "0.13.0",
 		"jsdoc": "4.0.4",
 		"jsdoc-wmf-theme": "1.1.0",
-- 
2.39.5

$ date
--- stdout ---
Mon Jul 28 04:19:23 UTC 2025

--- end ---
$ git clone file:///srv/git/mediawiki-extensions-GlobalWatchlist.git repo --depth=1 -b master
--- stderr ---
Cloning into 'repo'...
--- stdout ---

--- end ---
$ git config user.name libraryupgrader
--- stdout ---

--- end ---
$ git config user.email tools.libraryupgrader@tools.wmflabs.org
--- stdout ---

--- end ---
$ git submodule update --init
--- stdout ---

--- end ---
$ grr init
--- stdout ---
Installed commit-msg hook.

--- end ---
$ git show-ref refs/heads/master
--- stdout ---
3dde2b5ff74b8a592d28457a3cd4f402226d0af8 refs/heads/master

--- end ---
$ /usr/bin/npm audit --json
--- stdout ---
{
  "auditReportVersion": 2,
  "vulnerabilities": {
    "form-data": {
      "name": "form-data",
      "severity": "critical",
      "isDirect": false,
      "via": [
        {
          "source": 1106509,
          "name": "form-data",
          "dependency": "form-data",
          "title": "form-data uses unsafe random function in form-data for choosing boundary",
          "url": "https://github.com/advisories/GHSA-fjxv-7rqg-78g4",
          "severity": "critical",
          "cwe": [
            "CWE-330"
          ],
          "cvss": {
            "score": 0,
            "vectorString": null
          },
          "range": "<2.5.4"
        }
      ],
      "effects": [
        "request"
      ],
      "range": "<2.5.4",
      "nodes": [
        "node_modules/form-data"
      ],
      "fixAvailable": false
    },
    "mwbot": {
      "name": "mwbot",
      "severity": "moderate",
      "isDirect": false,
      "via": [
        "request"
      ],
      "effects": [
        "wdio-mediawiki"
      ],
      "range": ">=0.1.6",
      "nodes": [
        "node_modules/mwbot"
      ],
      "fixAvailable": false
    },
    "request": {
      "name": "request",
      "severity": "critical",
      "isDirect": false,
      "via": [
        {
          "source": 1096727,
          "name": "request",
          "dependency": "request",
          "title": "Server-Side Request Forgery in Request",
          "url": "https://github.com/advisories/GHSA-p8p7-x288-28g6",
          "severity": "moderate",
          "cwe": [
            "CWE-918"
          ],
          "cvss": {
            "score": 6.1,
            "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N"
          },
          "range": "<=2.88.2"
        },
        "form-data",
        "tough-cookie"
      ],
      "effects": [
        "mwbot"
      ],
      "range": "*",
      "nodes": [
        "node_modules/request"
      ],
      "fixAvailable": false
    },
    "tough-cookie": {
      "name": "tough-cookie",
      "severity": "moderate",
      "isDirect": false,
      "via": [
        {
          "source": 1097682,
          "name": "tough-cookie",
          "dependency": "tough-cookie",
          "title": "tough-cookie Prototype Pollution vulnerability",
          "url": "https://github.com/advisories/GHSA-72xf-g2v4-qvf3",
          "severity": "moderate",
          "cwe": [
            "CWE-1321"
          ],
          "cvss": {
            "score": 6.5,
            "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N"
          },
          "range": "<4.1.3"
        }
      ],
      "effects": [
        "request"
      ],
      "range": "<4.1.3",
      "nodes": [
        "node_modules/tough-cookie"
      ],
      "fixAvailable": false
    },
    "wdio-mediawiki": {
      "name": "wdio-mediawiki",
      "severity": "moderate",
      "isDirect": true,
      "via": [
        "mwbot"
      ],
      "effects": [],
      "range": "*",
      "nodes": [
        "node_modules/wdio-mediawiki"
      ],
      "fixAvailable": false
    }
  },
  "metadata": {
    "vulnerabilities": {
      "info": 0,
      "low": 0,
      "moderate": 3,
      "high": 0,
      "critical": 2,
      "total": 5
    },
    "dependencies": {
      "prod": 1,
      "dev": 841,
      "optional": 37,
      "peer": 1,
      "peerOptional": 0,
      "total": 841
    }
  }
}

--- end ---
$ /usr/bin/composer install
--- stderr ---
No composer.lock file present. Updating dependencies to latest instead of installing from lock file. See https://getcomposer.org/install for more information.
Loading composer repositories with package information
Updating dependencies
Lock file operations: 38 installs, 0 updates, 0 removals
  - Locking composer/pcre (3.3.2)
  - Locking composer/semver (3.4.3)
  - Locking composer/spdx-licenses (1.5.9)
  - Locking composer/xdebug-handler (3.0.5)
  - Locking dealerdirect/phpcodesniffer-composer-installer (v1.1.2)
  - Locking doctrine/deprecations (1.1.5)
  - Locking felixfbecker/advanced-json-rpc (v3.2.1)
  - Locking mediawiki/mediawiki-codesniffer (v47.0.0)
  - Locking mediawiki/mediawiki-phan-config (0.16.0)
  - Locking mediawiki/minus-x (1.1.3)
  - Locking mediawiki/phan-taint-check-plugin (6.2.1)
  - Locking microsoft/tolerant-php-parser (v0.1.2)
  - Locking netresearch/jsonmapper (v4.5.0)
  - Locking phan/phan (5.5.0)
  - Locking php-parallel-lint/php-console-color (v1.0.1)
  - Locking php-parallel-lint/php-console-highlighter (v1.0.0)
  - Locking php-parallel-lint/php-parallel-lint (v1.4.0)
  - Locking phpcsstandards/phpcsextra (1.2.1)
  - Locking phpcsstandards/phpcsutils (1.0.12)
  - Locking phpdocumentor/reflection-common (2.2.0)
  - Locking phpdocumentor/reflection-docblock (5.6.2)
  - Locking phpdocumentor/type-resolver (1.10.0)
  - Locking phpstan/phpdoc-parser (2.2.0)
  - Locking psr/container (2.0.2)
  - Locking psr/log (3.0.2)
  - Locking sabre/event (5.1.7)
  - Locking squizlabs/php_codesniffer (3.12.2)
  - Locking symfony/console (v7.3.1)
  - Locking symfony/deprecation-contracts (v3.6.0)
  - Locking symfony/polyfill-ctype (v1.32.0)
  - Locking symfony/polyfill-intl-grapheme (v1.32.0)
  - Locking symfony/polyfill-intl-normalizer (v1.32.0)
  - Locking symfony/polyfill-mbstring (v1.32.0)
  - Locking symfony/polyfill-php80 (v1.32.0)
  - Locking symfony/service-contracts (v3.6.0)
  - Locking symfony/string (v7.3.0)
  - Locking tysonandre/var_representation_polyfill (0.1.3)
  - Locking webmozart/assert (1.11.0)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 38 installs, 0 updates, 0 removals
    0 [>---------------------------]    0 [->--------------------------]
  - Installing squizlabs/php_codesniffer (3.12.2): Extracting archive
  - Installing dealerdirect/phpcodesniffer-composer-installer (v1.1.2): Extracting archive
  - Installing composer/pcre (3.3.2): Extracting archive
  - Installing symfony/polyfill-php80 (v1.32.0): Extracting archive
  - Installing phpcsstandards/phpcsutils (1.0.12): Extracting archive
  - Installing phpcsstandards/phpcsextra (1.2.1): Extracting archive
  - Installing symfony/polyfill-mbstring (v1.32.0): Extracting archive
  - Installing composer/spdx-licenses (1.5.9): Extracting archive
  - Installing composer/semver (3.4.3): Extracting archive
  - Installing mediawiki/mediawiki-codesniffer (v47.0.0): Extracting archive
  - Installing tysonandre/var_representation_polyfill (0.1.3): Extracting archive
  - Installing symfony/polyfill-intl-normalizer (v1.32.0): Extracting archive
  - Installing symfony/polyfill-intl-grapheme (v1.32.0): Extracting archive
  - Installing symfony/polyfill-ctype (v1.32.0): Extracting archive
  - Installing symfony/string (v7.3.0): Extracting archive
  - Installing symfony/deprecation-contracts (v3.6.0): Extracting archive
  - Installing psr/container (2.0.2): Extracting archive
  - Installing symfony/service-contracts (v3.6.0): Extracting archive
  - Installing symfony/console (v7.3.1): Extracting archive
  - Installing sabre/event (5.1.7): Extracting archive
  - Installing netresearch/jsonmapper (v4.5.0): Extracting archive
  - Installing microsoft/tolerant-php-parser (v0.1.2): Extracting archive
  - Installing webmozart/assert (1.11.0): Extracting archive
  - Installing phpstan/phpdoc-parser (2.2.0): Extracting archive
  - Installing phpdocumentor/reflection-common (2.2.0): Extracting archive
  - Installing doctrine/deprecations (1.1.5): Extracting archive
  - Installing phpdocumentor/type-resolver (1.10.0): Extracting archive
  - Installing phpdocumentor/reflection-docblock (5.6.2): Extracting archive
  - Installing felixfbecker/advanced-json-rpc (v3.2.1): Extracting archive
  - Installing psr/log (3.0.2): Extracting archive
  - Installing composer/xdebug-handler (3.0.5): Extracting archive
  - Installing phan/phan (5.5.0): Extracting archive
  - Installing mediawiki/phan-taint-check-plugin (6.2.1): Extracting archive
  - Installing mediawiki/mediawiki-phan-config (0.16.0): Extracting archive
  - Installing mediawiki/minus-x (1.1.3): Extracting archive
  - Installing php-parallel-lint/php-console-color (v1.0.1): Extracting archive
  - Installing php-parallel-lint/php-console-highlighter (v1.0.0): Extracting archive
  - Installing php-parallel-lint/php-parallel-lint (v1.4.0): Extracting archive
  0/36 [>---------------------------]   0%
 20/36 [===============>------------]  55%
 33/36 [=========================>--]  91%
 36/36 [============================] 100%
1 package suggestions were added by new dependencies, use `composer suggest` to see details.
Generating autoload files
17 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
--- stdout ---
PHP CodeSniffer Config installed_paths set to ../../mediawiki/mediawiki-codesniffer,../../phpcsstandards/phpcsextra,../../phpcsstandards/phpcsutils

--- end ---
Upgrading n:eslint-config-wikimedia from 0.30.0 -> 0.31.0
$ /usr/bin/npm install
--- stderr ---
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/cli@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/config@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/dot-reporter@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/globals@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/junit-reporter@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/local-runner@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/logger@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/mocha-framework@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/repl@9.4.4',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/reporter@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/runner@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/spec-reporter@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/types@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/utils@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: 'undici@7.10.0',
npm WARN EBADENGINE   required: { node: '>=20.18.1' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: 'webdriver@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: 'webdriverio@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN deprecated lodash.isequal@4.5.0: This package is deprecated. Use require('node:util').isDeepStrictEqual instead.
npm WARN deprecated har-validator@5.1.5: this library is no longer supported
npm WARN deprecated glob@8.1.0: Glob versions prior to v9 are no longer supported
npm WARN deprecated uuid@3.4.0: Please upgrade  to version 7 or higher.  Older versions may use Math.random() in certain circumstances, which is known to be problematic.  See https://v8.dev/blog/math-random for details.
npm WARN deprecated request@2.88.2: request has been deprecated, see https://github.com/request/request/issues/3142
npm WARN deprecated node-domexception@1.0.0: Use your platform's native DOMException instead
--- stdout ---

added 855 packages, and audited 856 packages in 15s

211 packages are looking for funding
  run `npm fund` for details

5 vulnerabilities (3 moderate, 2 critical)

Some issues need review, and may require choosing
a different dependency.

Run `npm audit` for details.

--- end ---
$ package-lock-lint package-lock.json
--- stdout ---
Checking package-lock.json

--- end ---
$ package-lock-lint package-lock.json
--- stdout ---
Checking package-lock.json

--- end ---
$ ./node_modules/.bin/eslint . --fix
--- stdout ---

/src/repo/modules/specialglobalwatchlist/EntryBase.js
  50:1  warning  The type 'GlobalWatchlistWikibaseHandler' is undefined  jsdoc/no-undefined-types
  56:1  warning  The type 'GlobalWatchlistWikibaseHandler' is undefined  jsdoc/no-undefined-types

/src/repo/modules/specialglobalwatchlist/MultiSiteWrapper.js
   9:1  warning  The type 'GlobalWatchlistSiteDisplay' is undefined  jsdoc/no-undefined-types
  10:1  warning  The type 'GlobalWatchlistDebugger' is undefined     jsdoc/no-undefined-types

/src/repo/modules/specialglobalwatchlist/SiteBase.js
    8:1  warning  The type 'GlobalWatchlistDebugger' is undefined        jsdoc/no-undefined-types
    9:1  warning  The type 'GlobalWatchlistLinker' is undefined          jsdoc/no-undefined-types
   12:1  warning  The type 'GlobalWatchlistWatchlistUtils' is undefined  jsdoc/no-undefined-types
  244:1  warning  The type 'GlobalWatchlistSiteDisplay' is undefined     jsdoc/no-undefined-types
  368:1  warning  The type 'GlobalWatchlistSiteDisplay' is undefined     jsdoc/no-undefined-types
  370:1  warning  The type 'GlobalWatchlistEntryBase' is undefined       jsdoc/no-undefined-types
  379:1  warning  The type 'GlobalWatchlistEntryBase' is undefined       jsdoc/no-undefined-types
  424:1  warning  The type 'GlobalWatchlistSiteDisplay' is undefined     jsdoc/no-undefined-types
  433:1  warning  The type 'GlobalWatchlistSiteDisplay' is undefined     jsdoc/no-undefined-types
  443:1  warning  The type 'GlobalWatchlistSiteDisplay' is undefined     jsdoc/no-undefined-types

/src/repo/modules/specialglobalwatchlist/SiteDisplay.js
   14:1  warning  The type 'GlobalWatchlistDebugger' is undefined        jsdoc/no-undefined-types
   15:1  warning  The type 'GlobalWatchlistLinker' is undefined          jsdoc/no-undefined-types
   18:1  warning  The type 'GlobalWatchlistWatchlistUtils' is undefined  jsdoc/no-undefined-types
   48:1  warning  The type 'GlobalWatchlistEntryBase' is undefined       jsdoc/no-undefined-types
  233:1  warning  The type 'GlobalWatchlistEntryBase' is undefined       jsdoc/no-undefined-types

/src/repo/modules/specialglobalwatchlist/WatchlistUtils.js
    7:1  warning  The type 'GlobalWatchlistLinker' is undefined          jsdoc/no-undefined-types
   70:1  warning  This line has a length of 105. Maximum allowed is 100  max-len
  300:1  warning  The type 'GlobalWatchlistSiteBase' is undefined        jsdoc/no-undefined-types
  312:1  warning  The type 'GlobalWatchlistEntryBase' is undefined       jsdoc/no-undefined-types
  329:1  warning  The type 'GlobalWatchlistEntryEdits' is undefined      jsdoc/no-undefined-types
  329:1  warning  The type 'GlobalWatchlistEntryLog' is undefined        jsdoc/no-undefined-types
  332:1  warning  The type 'GlobalWatchlistEntryEdits' is undefined      jsdoc/no-undefined-types
  332:1  warning  The type 'GlobalWatchlistEntryLog' is undefined        jsdoc/no-undefined-types
  412:1  warning  The type 'GlobalWatchlistEntryBase' is undefined       jsdoc/no-undefined-types
  418:1  warning  The type 'GlobalWatchlistEntryEdits' is undefined      jsdoc/no-undefined-types
  418:1  warning  The type 'GlobalWatchlistEntryLog' is undefined        jsdoc/no-undefined-types

/src/repo/modules/specialglobalwatchlist/WikibaseHandler.js
    9:1  warning  The type 'GlobalWatchlistDebugger' is undefined   jsdoc/no-undefined-types
  180:1  warning  The type 'GlobalWatchlistEntryBase' is undefined  jsdoc/no-undefined-types
  214:1  warning  The type 'GlobalWatchlistEntryBase' is undefined  jsdoc/no-undefined-types

/src/repo/modules/specialglobalwatchlist/getSettings.js
  24:1  warning  The type 'GlobalWatchlistDebugger' is undefined  jsdoc/no-undefined-types

/src/repo/tests/qunit/WatchlistUtils.test.js
  8:1  warning  The type 'GlobalWatchlistEntryBase' is undefined  jsdoc/no-undefined-types

✖ 35 problems (0 errors, 35 warnings)


--- end ---
$ ./node_modules/.bin/eslint . -f json
--- stdout ---
[{"filePath":"/src/repo/.eslintrc.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"arrow-parens","replacedBy":[]},{"ruleId":"arrow-spacing","replacedBy":[]},{"ruleId":"lines-between-class-members","replacedBy":[]},{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"template-curly-spacing","replacedBy":[]},{"ruleId":"implicit-arrow-linebreak","replacedBy":[]},{"ruleId":"indent","replacedBy":[]},{"ruleId":"comma-dangle","replacedBy":[]},{"ruleId":"no-extra-parens","replacedBy":[]},{"ruleId":"quotes","replacedBy":[]},{"ruleId":"quote-props","replacedBy":[]},{"ruleId":"array-bracket-spacing","replacedBy":[]},{"ruleId":"block-spacing","replacedBy":[]},{"ruleId":"brace-style","replacedBy":[]},{"ruleId":"comma-spacing","replacedBy":[]},{"ruleId":"comma-style","replacedBy":[]},{"ruleId":"computed-property-spacing","replacedBy":[]},{"ruleId":"dot-location","replacedBy":[]},{"ruleId":"eol-last","replacedBy":[]},{"ruleId":"func-call-spacing","replacedBy":[]},{"ruleId":"key-spacing","replacedBy":[]},{"ruleId":"keyword-spacing","replacedBy":[]},{"ruleId":"linebreak-style","replacedBy":[]},{"ruleId":"max-statements-per-line","replacedBy":[]},{"ruleId":"new-parens","replacedBy":[]},{"ruleId":"no-floating-decimal","replacedBy":[]},{"ruleId":"no-multi-spaces","replacedBy":[]},{"ruleId":"no-multiple-empty-lines","replacedBy":[]},{"ruleId":"no-new-object","replacedBy":["no-object-constructor"]},{"ruleId":"no-tabs","replacedBy":[]},{"ruleId":"no-trailing-spaces","replacedBy":[]},{"ruleId":"no-whitespace-before-property","replacedBy":[]},{"ruleId":"object-curly-spacing","replacedBy":[]},{"ruleId":"operator-linebreak","replacedBy":[]},{"ruleId":"semi","replacedBy":[]},{"ruleId":"semi-spacing","replacedBy":[]},{"ruleId":"semi-style","replacedBy":[]},{"ruleId":"space-before-blocks","replacedBy":[]},{"ruleId":"space-before-function-paren","replacedBy":[]},{"ruleId":"space-in-parens","replacedBy":[]},{"ruleId":"space-infix-ops","replacedBy":[]},{"ruleId":"space-unary-ops","replacedBy":[]},{"ruleId":"spaced-comment","replacedBy":[]},{"ruleId":"switch-colon-spacing","replacedBy":[]},{"ruleId":"wrap-iife","replacedBy":[]},{"ruleId":"no-extra-semi","replacedBy":[]},{"ruleId":"no-mixed-spaces-and-tabs","replacedBy":[]}]},{"filePath":"/src/repo/.stylelintrc.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"arrow-parens","replacedBy":[]},{"ruleId":"arrow-spacing","replacedBy":[]},{"ruleId":"lines-between-class-members","replacedBy":[]},{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"template-curly-spacing","replacedBy":[]},{"ruleId":"implicit-arrow-linebreak","replacedBy":[]},{"ruleId":"indent","replacedBy":[]},{"ruleId":"comma-dangle","replacedBy":[]},{"ruleId":"no-extra-parens","replacedBy":[]},{"ruleId":"quotes","replacedBy":[]},{"ruleId":"quote-props","replacedBy":[]},{"ruleId":"array-bracket-spacing","replacedBy":[]},{"ruleId":"block-spacing","replacedBy":[]},{"ruleId":"brace-style","replacedBy":[]},{"ruleId":"comma-spacing","replacedBy":[]},{"ruleId":"comma-style","replacedBy":[]},{"ruleId":"computed-property-spacing","replacedBy":[]},{"ruleId":"dot-location","replacedBy":[]},{"ruleId":"eol-last","replacedBy":[]},{"ruleId":"func-call-spacing","replacedBy":[]},{"ruleId":"key-spacing","replacedBy":[]},{"ruleId":"keyword-spacing","replacedBy":[]},{"ruleId":"linebreak-style","replacedBy":[]},{"ruleId":"max-statements-per-line","replacedBy":[]},{"ruleId":"new-parens","replacedBy":[]},{"ruleId":"no-floating-decimal","replacedBy":[]},{"ruleId":"no-multi-spaces","replacedBy":[]},{"ruleId":"no-multiple-empty-lines","replacedBy":[]},{"ruleId":"no-new-object","replacedBy":["no-object-constructor"]},{"ruleId":"no-tabs","replacedBy":[]},{"ruleId":"no-trailing-spaces","replacedBy":[]},{"ruleId":"no-whitespace-before-property","replacedBy":[]},{"ruleId":"object-curly-spacing","replacedBy":[]},{"ruleId":"operator-linebreak","replacedBy":[]},{"ruleId":"semi","replacedBy":[]},{"ruleId":"semi-spacing","replacedBy":[]},{"ruleId":"semi-style","replacedBy":[]},{"ruleId":"space-before-blocks","replacedBy":[]},{"ruleId":"space-before-function-paren","replacedBy":[]},{"ruleId":"space-in-parens","replacedBy":[]},{"ruleId":"space-infix-ops","replacedBy":[]},{"ruleId":"space-unary-ops","replacedBy":[]},{"ruleId":"spaced-comment","replacedBy":[]},{"ruleId":"switch-colon-spacing","replacedBy":[]},{"ruleId":"wrap-iife","replacedBy":[]},{"ruleId":"no-extra-semi","replacedBy":[]},{"ruleId":"no-mixed-spaces-and-tabs","replacedBy":[]}]},{"filePath":"/src/repo/composer.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"arrow-parens","replacedBy":[]},{"ruleId":"arrow-spacing","replacedBy":[]},{"ruleId":"lines-between-class-members","replacedBy":[]},{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"template-curly-spacing","replacedBy":[]},{"ruleId":"implicit-arrow-linebreak","replacedBy":[]},{"ruleId":"indent","replacedBy":[]},{"ruleId":"comma-dangle","replacedBy":[]},{"ruleId":"no-extra-parens","replacedBy":[]},{"ruleId":"quotes","replacedBy":[]},{"ruleId":"quote-props","replacedBy":[]},{"ruleId":"array-bracket-spacing","replacedBy":[]},{"ruleId":"block-spacing","replacedBy":[]},{"ruleId":"brace-style","replacedBy":[]},{"ruleId":"comma-spacing","replacedBy":[]},{"ruleId":"comma-style","replacedBy":[]},{"ruleId":"computed-property-spacing","replacedBy":[]},{"ruleId":"dot-location","replacedBy":[]},{"ruleId":"eol-last","replacedBy":[]},{"ruleId":"func-call-spacing","replacedBy":[]},{"ruleId":"key-spacing","replacedBy":[]},{"ruleId":"keyword-spacing","replacedBy":[]},{"ruleId":"linebreak-style","replacedBy":[]},{"ruleId":"max-statements-per-line","replacedBy":[]},{"ruleId":"new-parens","replacedBy":[]},{"ruleId":"no-floating-decimal","replacedBy":[]},{"ruleId":"no-multi-spaces","replacedBy":[]},{"ruleId":"no-multiple-empty-lines","replacedBy":[]},{"ruleId":"no-new-object","replacedBy":["no-object-constructor"]},{"ruleId":"no-tabs","replacedBy":[]},{"ruleId":"no-trailing-spaces","replacedBy":[]},{"ruleId":"no-whitespace-before-property","replacedBy":[]},{"ruleId":"object-curly-spacing","replacedBy":[]},{"ruleId":"operator-linebreak","replacedBy":[]},{"ruleId":"semi","replacedBy":[]},{"ruleId":"semi-spacing","replacedBy":[]},{"ruleId":"semi-style","replacedBy":[]},{"ruleId":"space-before-blocks","replacedBy":[]},{"ruleId":"space-before-function-paren","replacedBy":[]},{"ruleId":"space-in-parens","replacedBy":[]},{"ruleId":"space-infix-ops","replacedBy":[]},{"ruleId":"space-unary-ops","replacedBy":[]},{"ruleId":"spaced-comment","replacedBy":[]},{"ruleId":"switch-colon-spacing","replacedBy":[]},{"ruleId":"wrap-iife","replacedBy":[]},{"ruleId":"no-extra-semi","replacedBy":[]},{"ruleId":"no-mixed-spaces-and-tabs","replacedBy":[]}]},{"filePath":"/src/repo/extension.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"arrow-parens","replacedBy":[]},{"ruleId":"arrow-spacing","replacedBy":[]},{"ruleId":"lines-between-class-members","replacedBy":[]},{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"template-curly-spacing","replacedBy":[]},{"ruleId":"implicit-arrow-linebreak","replacedBy":[]},{"ruleId":"indent","replacedBy":[]},{"ruleId":"comma-dangle","replacedBy":[]},{"ruleId":"no-extra-parens","replacedBy":[]},{"ruleId":"quotes","replacedBy":[]},{"ruleId":"quote-props","replacedBy":[]},{"ruleId":"array-bracket-spacing","replacedBy":[]},{"ruleId":"block-spacing","replacedBy":[]},{"ruleId":"brace-style","replacedBy":[]},{"ruleId":"comma-spacing","replacedBy":[]},{"ruleId":"comma-style","replacedBy":[]},{"ruleId":"computed-property-spacing","replacedBy":[]},{"ruleId":"dot-location","replacedBy":[]},{"ruleId":"eol-last","replacedBy":[]},{"ruleId":"func-call-spacing","replacedBy":[]},{"ruleId":"key-spacing","replacedBy":[]},{"ruleId":"keyword-spacing","replacedBy":[]},{"ruleId":"linebreak-style","replacedBy":[]},{"ruleId":"max-statements-per-line","replacedBy":[]},{"ruleId":"new-parens","replacedBy":[]},{"ruleId":"no-floating-decimal","replacedBy":[]},{"ruleId":"no-multi-spaces","replacedBy":[]},{"ruleId":"no-multiple-empty-lines","replacedBy":[]},{"ruleId":"no-new-object","replacedBy":["no-object-constructor"]},{"ruleId":"no-tabs","replacedBy":[]},{"ruleId":"no-trailing-spaces","replacedBy":[]},{"ruleId":"no-whitespace-before-property","replacedBy":[]},{"ruleId":"object-curly-spacing","replacedBy":[]},{"ruleId":"operator-linebreak","replacedBy":[]},{"ruleId":"semi","replacedBy":[]},{"ruleId":"semi-spacing","replacedBy":[]},{"ruleId":"semi-style","replacedBy":[]},{"ruleId":"space-before-blocks","replacedBy":[]},{"ruleId":"space-before-function-paren","replacedBy":[]},{"ruleId":"space-in-parens","replacedBy":[]},{"ruleId":"space-infix-ops","replacedBy":[]},{"ruleId":"space-unary-ops","replacedBy":[]},{"ruleId":"spaced-comment","replacedBy":[]},{"ruleId":"switch-colon-spacing","replacedBy":[]},{"ruleId":"wrap-iife","replacedBy":[]},{"ruleId":"no-extra-semi","replacedBy":[]},{"ruleId":"no-mixed-spaces-and-tabs","replacedBy":[]}]},{"filePath":"/src/repo/i18n/en.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"arrow-parens","replacedBy":[]},{"ruleId":"arrow-spacing","replacedBy":[]},{"ruleId":"lines-between-class-members","replacedBy":[]},{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"template-curly-spacing","replacedBy":[]},{"ruleId":"implicit-arrow-linebreak","replacedBy":[]},{"ruleId":"indent","replacedBy":[]},{"ruleId":"comma-dangle","replacedBy":[]},{"ruleId":"no-extra-parens","replacedBy":[]},{"ruleId":"quotes","replacedBy":[]},{"ruleId":"quote-props","replacedBy":[]},{"ruleId":"array-bracket-spacing","replacedBy":[]},{"ruleId":"block-spacing","replacedBy":[]},{"ruleId":"brace-style","replacedBy":[]},{"ruleId":"comma-spacing","replacedBy":[]},{"ruleId":"comma-style","replacedBy":[]},{"ruleId":"computed-property-spacing","replacedBy":[]},{"ruleId":"dot-location","replacedBy":[]},{"ruleId":"eol-last","replacedBy":[]},{"ruleId":"func-call-spacing","replacedBy":[]},{"ruleId":"key-spacing","replacedBy":[]},{"ruleId":"keyword-spacing","replacedBy":[]},{"ruleId":"linebreak-style","replacedBy":[]},{"ruleId":"max-statements-per-line","replacedBy":[]},{"ruleId":"new-parens","replacedBy":[]},{"ruleId":"no-floating-decimal","replacedBy":[]},{"ruleId":"no-multi-spaces","replacedBy":[]},{"ruleId":"no-multiple-empty-lines","replacedBy":[]},{"ruleId":"no-new-object","replacedBy":["no-object-constructor"]},{"ruleId":"no-tabs","replacedBy":[]},{"ruleId":"no-trailing-spaces","replacedBy":[]},{"ruleId":"no-whitespace-before-property","replacedBy":[]},{"ruleId":"object-curly-spacing","replacedBy":[]},{"ruleId":"operator-linebreak","replacedBy":[]},{"ruleId":"semi","replacedBy":[]},{"ruleId":"semi-spacing","replacedBy":[]},{"ruleId":"semi-style","replacedBy":[]},{"ruleId":"space-before-blocks","replacedBy":[]},{"ruleId":"space-before-function-paren","replacedBy":[]},{"ruleId":"space-in-parens","replacedBy":[]},{"ruleId":"space-infix-ops","replacedBy":[]},{"ruleId":"space-unary-ops","replacedBy":[]},{"ruleId":"spaced-comment","replacedBy":[]},{"ruleId":"switch-colon-spacing","replacedBy":[]},{"ruleId":"wrap-iife","replacedBy":[]},{"ruleId":"no-extra-semi","replacedBy":[]},{"ruleId":"no-mixed-spaces-and-tabs","replacedBy":[]}]},{"filePath":"/src/repo/i18n/qqq.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"arrow-parens","replacedBy":[]},{"ruleId":"arrow-spacing","replacedBy":[]},{"ruleId":"lines-between-class-members","replacedBy":[]},{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"template-curly-spacing","replacedBy":[]},{"ruleId":"implicit-arrow-linebreak","replacedBy":[]},{"ruleId":"indent","replacedBy":[]},{"ruleId":"comma-dangle","replacedBy":[]},{"ruleId":"no-extra-parens","replacedBy":[]},{"ruleId":"quotes","replacedBy":[]},{"ruleId":"quote-props","replacedBy":[]},{"ruleId":"array-bracket-spacing","replacedBy":[]},{"ruleId":"block-spacing","replacedBy":[]},{"ruleId":"brace-style","replacedBy":[]},{"ruleId":"comma-spacing","replacedBy":[]},{"ruleId":"comma-style","replacedBy":[]},{"ruleId":"computed-property-spacing","replacedBy":[]},{"ruleId":"dot-location","replacedBy":[]},{"ruleId":"eol-last","replacedBy":[]},{"ruleId":"func-call-spacing","replacedBy":[]},{"ruleId":"key-spacing","replacedBy":[]},{"ruleId":"keyword-spacing","replacedBy":[]},{"ruleId":"linebreak-style","replacedBy":[]},{"ruleId":"max-statements-per-line","replacedBy":[]},{"ruleId":"new-parens","replacedBy":[]},{"ruleId":"no-floating-decimal","replacedBy":[]},{"ruleId":"no-multi-spaces","replacedBy":[]},{"ruleId":"no-multiple-empty-lines","replacedBy":[]},{"ruleId":"no-new-object","replacedBy":["no-object-constructor"]},{"ruleId":"no-tabs","replacedBy":[]},{"ruleId":"no-trailing-spaces","replacedBy":[]},{"ruleId":"no-whitespace-before-property","replacedBy":[]},{"ruleId":"object-curly-spacing","replacedBy":[]},{"ruleId":"operator-linebreak","replacedBy":[]},{"ruleId":"semi","replacedBy":[]},{"ruleId":"semi-spacing","replacedBy":[]},{"ruleId":"semi-style","replacedBy":[]},{"ruleId":"space-before-blocks","replacedBy":[]},{"ruleId":"space-before-function-paren","replacedBy":[]},{"ruleId":"space-in-parens","replacedBy":[]},{"ruleId":"space-infix-ops","replacedBy":[]},{"ruleId":"space-unary-ops","replacedBy":[]},{"ruleId":"spaced-comment","replacedBy":[]},{"ruleId":"switch-colon-spacing","replacedBy":[]},{"ruleId":"wrap-iife","replacedBy":[]},{"ruleId":"no-extra-semi","replacedBy":[]},{"ruleId":"no-mixed-spaces-and-tabs","replacedBy":[]}]},{"filePath":"/src/repo/jsdoc.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"arrow-parens","replacedBy":[]},{"ruleId":"arrow-spacing","replacedBy":[]},{"ruleId":"lines-between-class-members","replacedBy":[]},{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"template-curly-spacing","replacedBy":[]},{"ruleId":"implicit-arrow-linebreak","replacedBy":[]},{"ruleId":"indent","replacedBy":[]},{"ruleId":"comma-dangle","replacedBy":[]},{"ruleId":"no-extra-parens","replacedBy":[]},{"ruleId":"quotes","replacedBy":[]},{"ruleId":"quote-props","replacedBy":[]},{"ruleId":"array-bracket-spacing","replacedBy":[]},{"ruleId":"block-spacing","replacedBy":[]},{"ruleId":"brace-style","replacedBy":[]},{"ruleId":"comma-spacing","replacedBy":[]},{"ruleId":"comma-style","replacedBy":[]},{"ruleId":"computed-property-spacing","replacedBy":[]},{"ruleId":"dot-location","replacedBy":[]},{"ruleId":"eol-last","replacedBy":[]},{"ruleId":"func-call-spacing","replacedBy":[]},{"ruleId":"key-spacing","replacedBy":[]},{"ruleId":"keyword-spacing","replacedBy":[]},{"ruleId":"linebreak-style","replacedBy":[]},{"ruleId":"max-statements-per-line","replacedBy":[]},{"ruleId":"new-parens","replacedBy":[]},{"ruleId":"no-floating-decimal","replacedBy":[]},{"ruleId":"no-multi-spaces","replacedBy":[]},{"ruleId":"no-multiple-empty-lines","replacedBy":[]},{"ruleId":"no-new-object","replacedBy":["no-object-constructor"]},{"ruleId":"no-tabs","replacedBy":[]},{"ruleId":"no-trailing-spaces","replacedBy":[]},{"ruleId":"no-whitespace-before-property","replacedBy":[]},{"ruleId":"object-curly-spacing","replacedBy":[]},{"ruleId":"operator-linebreak","replacedBy":[]},{"ruleId":"semi","replacedBy":[]},{"ruleId":"semi-spacing","replacedBy":[]},{"ruleId":"semi-style","replacedBy":[]},{"ruleId":"space-before-blocks","replacedBy":[]},{"ruleId":"space-before-function-paren","replacedBy":[]},{"ruleId":"space-in-parens","replacedBy":[]},{"ruleId":"space-infix-ops","replacedBy":[]},{"ruleId":"space-unary-ops","replacedBy":[]},{"ruleId":"spaced-comment","replacedBy":[]},{"ruleId":"switch-colon-spacing","replacedBy":[]},{"ruleId":"wrap-iife","replacedBy":[]},{"ruleId":"no-extra-semi","replacedBy":[]},{"ruleId":"no-mixed-spaces-and-tabs","replacedBy":[]}]},{"filePath":"/src/repo/modules/.eslintrc.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]},{"ruleId":"indent","replacedBy":[]},{"ruleId":"comma-dangle","replacedBy":[]},{"ruleId":"no-extra-parens","replacedBy":[]},{"ruleId":"quotes","replacedBy":[]},{"ruleId":"quote-props","replacedBy":[]},{"ruleId":"arrow-parens","replacedBy":[]},{"ruleId":"arrow-spacing","replacedBy":[]},{"ruleId":"lines-between-class-members","replacedBy":[]},{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"template-curly-spacing","replacedBy":[]},{"ruleId":"implicit-arrow-linebreak","replacedBy":[]},{"ruleId":"array-bracket-spacing","replacedBy":[]},{"ruleId":"block-spacing","replacedBy":[]},{"ruleId":"brace-style","replacedBy":[]},{"ruleId":"comma-spacing","replacedBy":[]},{"ruleId":"comma-style","replacedBy":[]},{"ruleId":"computed-property-spacing","replacedBy":[]},{"ruleId":"dot-location","replacedBy":[]},{"ruleId":"eol-last","replacedBy":[]},{"ruleId":"func-call-spacing","replacedBy":[]},{"ruleId":"key-spacing","replacedBy":[]},{"ruleId":"keyword-spacing","replacedBy":[]},{"ruleId":"linebreak-style","replacedBy":[]},{"ruleId":"max-statements-per-line","replacedBy":[]},{"ruleId":"new-parens","replacedBy":[]},{"ruleId":"no-floating-decimal","replacedBy":[]},{"ruleId":"no-multi-spaces","replacedBy":[]},{"ruleId":"no-multiple-empty-lines","replacedBy":[]},{"ruleId":"no-new-object","replacedBy":["no-object-constructor"]},{"ruleId":"no-tabs","replacedBy":[]},{"ruleId":"no-trailing-spaces","replacedBy":[]},{"ruleId":"no-whitespace-before-property","replacedBy":[]},{"ruleId":"object-curly-spacing","replacedBy":[]},{"ruleId":"operator-linebreak","replacedBy":[]},{"ruleId":"semi","replacedBy":[]},{"ruleId":"semi-spacing","replacedBy":[]},{"ruleId":"semi-style","replacedBy":[]},{"ruleId":"space-before-blocks","replacedBy":[]},{"ruleId":"space-before-function-paren","replacedBy":[]},{"ruleId":"space-in-parens","replacedBy":[]},{"ruleId":"space-infix-ops","replacedBy":[]},{"ruleId":"space-unary-ops","replacedBy":[]},{"ruleId":"spaced-comment","replacedBy":[]},{"ruleId":"switch-colon-spacing","replacedBy":[]},{"ruleId":"wrap-iife","replacedBy":[]},{"ruleId":"no-extra-semi","replacedBy":[]},{"ruleId":"no-mixed-spaces-and-tabs","replacedBy":[]}]},{"filePath":"/src/repo/modules/getsettingserror/getSettings.error.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]},{"ruleId":"max-len","replacedBy":[]},{"ruleId":"arrow-parens","replacedBy":[]},{"ruleId":"arrow-spacing","replacedBy":[]},{"ruleId":"lines-between-class-members","replacedBy":[]},{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"template-curly-spacing","replacedBy":[]},{"ruleId":"implicit-arrow-linebreak","replacedBy":[]},{"ruleId":"array-bracket-spacing","replacedBy":[]},{"ruleId":"block-spacing","replacedBy":[]},{"ruleId":"brace-style","replacedBy":[]},{"ruleId":"comma-dangle","replacedBy":[]},{"ruleId":"comma-spacing","replacedBy":[]},{"ruleId":"comma-style","replacedBy":[]},{"ruleId":"computed-property-spacing","replacedBy":[]},{"ruleId":"dot-location","replacedBy":[]},{"ruleId":"eol-last","replacedBy":[]},{"ruleId":"func-call-spacing","replacedBy":[]},{"ruleId":"indent","replacedBy":[]},{"ruleId":"key-spacing","replacedBy":[]},{"ruleId":"keyword-spacing","replacedBy":[]},{"ruleId":"linebreak-style","replacedBy":[]},{"ruleId":"max-statements-per-line","replacedBy":[]},{"ruleId":"new-parens","replacedBy":[]},{"ruleId":"no-floating-decimal","replacedBy":[]},{"ruleId":"no-multi-spaces","replacedBy":[]},{"ruleId":"no-multiple-empty-lines","replacedBy":[]},{"ruleId":"no-new-object","replacedBy":["no-object-constructor"]},{"ruleId":"no-tabs","replacedBy":[]},{"ruleId":"no-trailing-spaces","replacedBy":[]},{"ruleId":"no-whitespace-before-property","replacedBy":[]},{"ruleId":"object-curly-spacing","replacedBy":[]},{"ruleId":"operator-linebreak","replacedBy":[]},{"ruleId":"quote-props","replacedBy":[]},{"ruleId":"quotes","replacedBy":[]},{"ruleId":"semi","replacedBy":[]},{"ruleId":"semi-spacing","replacedBy":[]},{"ruleId":"semi-style","replacedBy":[]},{"ruleId":"space-before-blocks","replacedBy":[]},{"ruleId":"space-before-function-paren","replacedBy":[]},{"ruleId":"space-in-parens","replacedBy":[]},{"ruleId":"space-infix-ops","replacedBy":[]},{"ruleId":"space-unary-ops","replacedBy":[]},{"ruleId":"spaced-comment","replacedBy":[]},{"ruleId":"switch-colon-spacing","replacedBy":[]},{"ruleId":"wrap-iife","replacedBy":[]},{"ruleId":"no-extra-semi","replacedBy":[]},{"ruleId":"no-mixed-spaces-and-tabs","replacedBy":[]}]},{"filePath":"/src/repo/modules/specialglobalwatchlist/Debug.js","messages":[],"suppressedMessages":[{"ruleId":"no-console","severity":2,"message":"Unexpected console statement.","line":25,"column":3,"nodeType":"MemberExpression","messageId":"unexpected","endLine":25,"endColumn":14,"suggestions":[{"messageId":"removeConsole","data":{"propertyName":"log"},"fix":{"range":[717,757],"text":""},"desc":"Remove the console.log()."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-console","severity":2,"message":"Unexpected console statement.","line":29,"column":4,"nodeType":"MemberExpression","messageId":"unexpected","endLine":29,"endColumn":15,"suggestions":[{"messageId":"removeConsole","data":{"propertyName":"log"},"fix":{"range":[828,853],"text":""},"desc":"Remove the console.log()."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-console","severity":2,"message":"Unexpected console statement.","line":62,"column":2,"nodeType":"MemberExpression","messageId":"unexpected","endLine":62,"endColumn":15,"suggestions":[{"messageId":"removeConsole","data":{"propertyName":"error"},"fix":{"range":[1718,1748],"text":""},"desc":"Remove the console.error()."}],"suppressions":[{"kind":"directive","justification":""}]}],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]},{"ruleId":"max-len","replacedBy":[]},{"ruleId":"arrow-parens","replacedBy":[]},{"ruleId":"arrow-spacing","replacedBy":[]},{"ruleId":"lines-between-class-members","replacedBy":[]},{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"template-curly-spacing","replacedBy":[]},{"ruleId":"implicit-arrow-linebreak","replacedBy":[]},{"ruleId":"array-bracket-spacing","replacedBy":[]},{"ruleId":"block-spacing","replacedBy":[]},{"ruleId":"brace-style","replacedBy":[]},{"ruleId":"comma-dangle","replacedBy":[]},{"ruleId":"comma-spacing","replacedBy":[]},{"ruleId":"comma-style","replacedBy":[]},{"ruleId":"computed-property-spacing","replacedBy":[]},{"ruleId":"dot-location","replacedBy":[]},{"ruleId":"eol-last","replacedBy":[]},{"ruleId":"func-call-spacing","replacedBy":[]},{"ruleId":"indent","replacedBy":[]},{"ruleId":"key-spacing","replacedBy":[]},{"ruleId":"keyword-spacing","replacedBy":[]},{"ruleId":"linebreak-style","replacedBy":[]},{"ruleId":"max-statements-per-line","replacedBy":[]},{"ruleId":"new-parens","replacedBy":[]},{"ruleId":"no-floating-decimal","replacedBy":[]},{"ruleId":"no-multi-spaces","replacedBy":[]},{"ruleId":"no-multiple-empty-lines","replacedBy":[]},{"ruleId":"no-new-object","replacedBy":["no-object-constructor"]},{"ruleId":"no-tabs","replacedBy":[]},{"ruleId":"no-trailing-spaces","replacedBy":[]},{"ruleId":"no-whitespace-before-property","replacedBy":[]},{"ruleId":"object-curly-spacing","replacedBy":[]},{"ruleId":"operator-linebreak","replacedBy":[]},{"ruleId":"quote-props","replacedBy":[]},{"ruleId":"quotes","replacedBy":[]},{"ruleId":"semi","replacedBy":[]},{"ruleId":"semi-spacing","replacedBy":[]},{"ruleId":"semi-style","replacedBy":[]},{"ruleId":"space-before-blocks","replacedBy":[]},{"ruleId":"space-before-function-paren","replacedBy":[]},{"ruleId":"space-in-parens","replacedBy":[]},{"ruleId":"space-infix-ops","replacedBy":[]},{"ruleId":"space-unary-ops","replacedBy":[]},{"ruleId":"spaced-comment","replacedBy":[]},{"ruleId":"switch-colon-spacing","replacedBy":[]},{"ruleId":"wrap-iife","replacedBy":[]},{"ruleId":"no-extra-semi","replacedBy":[]},{"ruleId":"no-mixed-spaces-and-tabs","replacedBy":[]}]},{"filePath":"/src/repo/modules/specialglobalwatchlist/EntryBase.js","messages":[{"ruleId":"jsdoc/no-undefined-types","severity":1,"message":"The type 'GlobalWatchlistWikibaseHandler' is undefined.","line":50,"column":1,"nodeType":"Block","endLine":50,"endColumn":1},{"ruleId":"jsdoc/no-undefined-types","severity":1,"message":"The type 'GlobalWatchlistWikibaseHandler' is undefined.","line":56,"column":1,"nodeType":"Block","endLine":56,"endColumn":1}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":0,"source":"/**\n * Represents a base entry of any type that is shown\n *\n * @class GlobalWatchlistEntryBase\n * @abstract\n *\n * @constructor\n * @param {Object} info Should have all of the properties that are documented below\n */\nfunction GlobalWatchlistEntryBase( info ) {\n\t/**\n\t * @property {string} entryType Either 'edit' or 'log'\n\t */\n\tthis.entryType = info.entryType;\n\n\t/**\n\t * @property {string|boolean} timestamp Either `false` or a string to display\n\t */\n\tthis.timestamp = info.timestamp;\n\n\t/**\n\t * @property {string|null} timestampTitle Either `null` for single changes, or\n\t *   a string to display as a tooltip for grouped edits\n\t */\n\tthis.timestampTitle = info.timestampTitle;\n\n\t/**\n\t * @property {string|boolean} expiry Either `false` or a string explaining when the\n\t *   watchlist entry expires\n\t */\n\tthis.expiry = info.expiry;\n\n\t/**\n\t * @property {string|boolean} flags Either `false` or a string of flags to show\n\t */\n\tthis.flags = info.flags;\n\n\t/**\n\t * @property {string} userDisplay Raw HTML to show for the user(s) that made this entry\n\t */\n\tthis.userDisplay = info.userDisplay;\n\n\t/**\n\t * @property {string} title Title of the entry\n\t */\n\tthis.title = info.title;\n\n\t/**\n\t * @property {string} titleMsg Display text for the title of this entry, might be\n\t *   changed by {@link GlobalWatchlistWikibaseHandler}\n\t */\n\tthis.titleMsg = info.title;\n\n\t/**\n\t * @property {number} ns Namespace for this entry, can be used by\n\t *   {@link GlobalWatchlistWikibaseHandler} to decide if a label should be added.\n\t */\n\tthis.ns = info.ns;\n\n\t/**\n\t * @property {string|boolean} commentDisplay Either `false` or a raw HTML string for\n\t *   the parsed comment that should be shown\n\t */\n\tthis.commentDisplay = info.commentDisplay;\n\n\t/**\n\t * @property {string|boolean} tagsDisplay Either `false` or a raw HTML string for the\n\t *   parsed tags information that should be shown\n\t */\n\tthis.tagsDisplay = info.tagsDisplay;\n}\n\nmodule.exports = GlobalWatchlistEntryBase;\n","usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]},{"ruleId":"max-len","replacedBy":[]},{"ruleId":"arrow-parens","replacedBy":[]},{"ruleId":"arrow-spacing","replacedBy":[]},{"ruleId":"lines-between-class-members","replacedBy":[]},{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"template-curly-spacing","replacedBy":[]},{"ruleId":"implicit-arrow-linebreak","replacedBy":[]},{"ruleId":"array-bracket-spacing","replacedBy":[]},{"ruleId":"block-spacing","replacedBy":[]},{"ruleId":"brace-style","replacedBy":[]},{"ruleId":"comma-dangle","replacedBy":[]},{"ruleId":"comma-spacing","replacedBy":[]},{"ruleId":"comma-style","replacedBy":[]},{"ruleId":"computed-property-spacing","replacedBy":[]},{"ruleId":"dot-location","replacedBy":[]},{"ruleId":"eol-last","replacedBy":[]},{"ruleId":"func-call-spacing","replacedBy":[]},{"ruleId":"indent","replacedBy":[]},{"ruleId":"key-spacing","replacedBy":[]},{"ruleId":"keyword-spacing","replacedBy":[]},{"ruleId":"linebreak-style","replacedBy":[]},{"ruleId":"max-statements-per-line","replacedBy":[]},{"ruleId":"new-parens","replacedBy":[]},{"ruleId":"no-floating-decimal","replacedBy":[]},{"ruleId":"no-multi-spaces","replacedBy":[]},{"ruleId":"no-multiple-empty-lines","replacedBy":[]},{"ruleId":"no-new-object","replacedBy":["no-object-constructor"]},{"ruleId":"no-tabs","replacedBy":[]},{"ruleId":"no-trailing-spaces","replacedBy":[]},{"ruleId":"no-whitespace-before-property","replacedBy":[]},{"ruleId":"object-curly-spacing","replacedBy":[]},{"ruleId":"operator-linebreak","replacedBy":[]},{"ruleId":"quote-props","replacedBy":[]},{"ruleId":"quotes","replacedBy":[]},{"ruleId":"semi","replacedBy":[]},{"ruleId":"semi-spacing","replacedBy":[]},{"ruleId":"semi-style","replacedBy":[]},{"ruleId":"space-before-blocks","replacedBy":[]},{"ruleId":"space-before-function-paren","replacedBy":[]},{"ruleId":"space-in-parens","replacedBy":[]},{"ruleId":"space-infix-ops","replacedBy":[]},{"ruleId":"space-unary-ops","replacedBy":[]},{"ruleId":"spaced-comment","replacedBy":[]},{"ruleId":"switch-colon-spacing","replacedBy":[]},{"ruleId":"wrap-iife","replacedBy":[]},{"ruleId":"no-extra-semi","replacedBy":[]},{"ruleId":"no-mixed-spaces-and-tabs","replacedBy":[]}]},{"filePath":"/src/repo/modules/specialglobalwatchlist/EntryEdits.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]},{"ruleId":"max-len","replacedBy":[]},{"ruleId":"arrow-parens","replacedBy":[]},{"ruleId":"arrow-spacing","replacedBy":[]},{"ruleId":"lines-between-class-members","replacedBy":[]},{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"template-curly-spacing","replacedBy":[]},{"ruleId":"implicit-arrow-linebreak","replacedBy":[]},{"ruleId":"array-bracket-spacing","replacedBy":[]},{"ruleId":"block-spacing","replacedBy":[]},{"ruleId":"brace-style","replacedBy":[]},{"ruleId":"comma-dangle","replacedBy":[]},{"ruleId":"comma-spacing","replacedBy":[]},{"ruleId":"comma-style","replacedBy":[]},{"ruleId":"computed-property-spacing","replacedBy":[]},{"ruleId":"dot-location","replacedBy":[]},{"ruleId":"eol-last","replacedBy":[]},{"ruleId":"func-call-spacing","replacedBy":[]},{"ruleId":"indent","replacedBy":[]},{"ruleId":"key-spacing","replacedBy":[]},{"ruleId":"keyword-spacing","replacedBy":[]},{"ruleId":"linebreak-style","replacedBy":[]},{"ruleId":"max-statements-per-line","replacedBy":[]},{"ruleId":"new-parens","replacedBy":[]},{"ruleId":"no-floating-decimal","replacedBy":[]},{"ruleId":"no-multi-spaces","replacedBy":[]},{"ruleId":"no-multiple-empty-lines","replacedBy":[]},{"ruleId":"no-new-object","replacedBy":["no-object-constructor"]},{"ruleId":"no-tabs","replacedBy":[]},{"ruleId":"no-trailing-spaces","replacedBy":[]},{"ruleId":"no-whitespace-before-property","replacedBy":[]},{"ruleId":"object-curly-spacing","replacedBy":[]},{"ruleId":"operator-linebreak","replacedBy":[]},{"ruleId":"quote-props","replacedBy":[]},{"ruleId":"quotes","replacedBy":[]},{"ruleId":"semi","replacedBy":[]},{"ruleId":"semi-spacing","replacedBy":[]},{"ruleId":"semi-style","replacedBy":[]},{"ruleId":"space-before-blocks","replacedBy":[]},{"ruleId":"space-before-function-paren","replacedBy":[]},{"ruleId":"space-in-parens","replacedBy":[]},{"ruleId":"space-infix-ops","replacedBy":[]},{"ruleId":"space-unary-ops","replacedBy":[]},{"ruleId":"spaced-comment","replacedBy":[]},{"ruleId":"switch-colon-spacing","replacedBy":[]},{"ruleId":"wrap-iife","replacedBy":[]},{"ruleId":"no-extra-semi","replacedBy":[]},{"ruleId":"no-mixed-spaces-and-tabs","replacedBy":[]}]},{"filePath":"/src/repo/modules/specialglobalwatchlist/EntryLog.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]},{"ruleId":"max-len","replacedBy":[]},{"ruleId":"arrow-parens","replacedBy":[]},{"ruleId":"arrow-spacing","replacedBy":[]},{"ruleId":"lines-between-class-members","replacedBy":[]},{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"template-curly-spacing","replacedBy":[]},{"ruleId":"implicit-arrow-linebreak","replacedBy":[]},{"ruleId":"array-bracket-spacing","replacedBy":[]},{"ruleId":"block-spacing","replacedBy":[]},{"ruleId":"brace-style","replacedBy":[]},{"ruleId":"comma-dangle","replacedBy":[]},{"ruleId":"comma-spacing","replacedBy":[]},{"ruleId":"comma-style","replacedBy":[]},{"ruleId":"computed-property-spacing","replacedBy":[]},{"ruleId":"dot-location","replacedBy":[]},{"ruleId":"eol-last","replacedBy":[]},{"ruleId":"func-call-spacing","replacedBy":[]},{"ruleId":"indent","replacedBy":[]},{"ruleId":"key-spacing","replacedBy":[]},{"ruleId":"keyword-spacing","replacedBy":[]},{"ruleId":"linebreak-style","replacedBy":[]},{"ruleId":"max-statements-per-line","replacedBy":[]},{"ruleId":"new-parens","replacedBy":[]},{"ruleId":"no-floating-decimal","replacedBy":[]},{"ruleId":"no-multi-spaces","replacedBy":[]},{"ruleId":"no-multiple-empty-lines","replacedBy":[]},{"ruleId":"no-new-object","replacedBy":["no-object-constructor"]},{"ruleId":"no-tabs","replacedBy":[]},{"ruleId":"no-trailing-spaces","replacedBy":[]},{"ruleId":"no-whitespace-before-property","replacedBy":[]},{"ruleId":"object-curly-spacing","replacedBy":[]},{"ruleId":"operator-linebreak","replacedBy":[]},{"ruleId":"quote-props","replacedBy":[]},{"ruleId":"quotes","replacedBy":[]},{"ruleId":"semi","replacedBy":[]},{"ruleId":"semi-spacing","replacedBy":[]},{"ruleId":"semi-style","replacedBy":[]},{"ruleId":"space-before-blocks","replacedBy":[]},{"ruleId":"space-before-function-paren","replacedBy":[]},{"ruleId":"space-in-parens","replacedBy":[]},{"ruleId":"space-infix-ops","replacedBy":[]},{"ruleId":"space-unary-ops","replacedBy":[]},{"ruleId":"spaced-comment","replacedBy":[]},{"ruleId":"switch-colon-spacing","replacedBy":[]},{"ruleId":"wrap-iife","replacedBy":[]},{"ruleId":"no-extra-semi","replacedBy":[]},{"ruleId":"no-mixed-spaces-and-tabs","replacedBy":[]}]},{"filePath":"/src/repo/modules/specialglobalwatchlist/Linker.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]},{"ruleId":"max-len","replacedBy":[]},{"ruleId":"arrow-parens","replacedBy":[]},{"ruleId":"arrow-spacing","replacedBy":[]},{"ruleId":"lines-between-class-members","replacedBy":[]},{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"template-curly-spacing","replacedBy":[]},{"ruleId":"implicit-arrow-linebreak","replacedBy":[]},{"ruleId":"array-bracket-spacing","replacedBy":[]},{"ruleId":"block-spacing","replacedBy":[]},{"ruleId":"brace-style","replacedBy":[]},{"ruleId":"comma-dangle","replacedBy":[]},{"ruleId":"comma-spacing","replacedBy":[]},{"ruleId":"comma-style","replacedBy":[]},{"ruleId":"computed-property-spacing","replacedBy":[]},{"ruleId":"dot-location","replacedBy":[]},{"ruleId":"eol-last","replacedBy":[]},{"ruleId":"func-call-spacing","replacedBy":[]},{"ruleId":"indent","replacedBy":[]},{"ruleId":"key-spacing","replacedBy":[]},{"ruleId":"keyword-spacing","replacedBy":[]},{"ruleId":"linebreak-style","replacedBy":[]},{"ruleId":"max-statements-per-line","replacedBy":[]},{"ruleId":"new-parens","replacedBy":[]},{"ruleId":"no-floating-decimal","replacedBy":[]},{"ruleId":"no-multi-spaces","replacedBy":[]},{"ruleId":"no-multiple-empty-lines","replacedBy":[]},{"ruleId":"no-new-object","replacedBy":["no-object-constructor"]},{"ruleId":"no-tabs","replacedBy":[]},{"ruleId":"no-trailing-spaces","replacedBy":[]},{"ruleId":"no-whitespace-before-property","replacedBy":[]},{"ruleId":"object-curly-spacing","replacedBy":[]},{"ruleId":"operator-linebreak","replacedBy":[]},{"ruleId":"quote-props","replacedBy":[]},{"ruleId":"quotes","replacedBy":[]},{"ruleId":"semi","replacedBy":[]},{"ruleId":"semi-spacing","replacedBy":[]},{"ruleId":"semi-style","replacedBy":[]},{"ruleId":"space-before-blocks","replacedBy":[]},{"ruleId":"space-before-function-paren","replacedBy":[]},{"ruleId":"space-in-parens","replacedBy":[]},{"ruleId":"space-infix-ops","replacedBy":[]},{"ruleId":"space-unary-ops","replacedBy":[]},{"ruleId":"spaced-comment","replacedBy":[]},{"ruleId":"switch-colon-spacing","replacedBy":[]},{"ruleId":"wrap-iife","replacedBy":[]},{"ruleId":"no-extra-semi","replacedBy":[]},{"ruleId":"no-mixed-spaces-and-tabs","replacedBy":[]}]},{"filePath":"/src/repo/modules/specialglobalwatchlist/MultiSiteWrapper.js","messages":[{"ruleId":"jsdoc/no-undefined-types","severity":1,"message":"The type 'GlobalWatchlistSiteDisplay' is undefined.","line":9,"column":1,"nodeType":"Block","endLine":9,"endColumn":1},{"ruleId":"jsdoc/no-undefined-types","severity":1,"message":"The type 'GlobalWatchlistDebugger' is undefined.","line":10,"column":1,"nodeType":"Block","endLine":10,"endColumn":1}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":0,"source":"/**\n * Shared helper for Special:GlobalWatchlist\n *\n * @class GlobalWatchlistMultiSiteWrapper\n * @constructor\n *\n * @param {Function} SiteClass {@link GlobalWatchlistSiteDisplay}, used to create\n *    the individual site objects\n * @param {Object} config User configuration to use\n * @param {GlobalWatchlistDebugger} globalWatchlistDebug Shared debugger instance\n */\nfunction GlobalWatchlistMultiSiteWrapper( SiteClass, config, globalWatchlistDebug ) {\n\tconst GlobalWatchlistLinker = require( './Linker.js' );\n\tconst GlobalWatchlistWatchlistUtils = require( './WatchlistUtils.js' );\n\n\t// Set the Access-Control-Max-Age header - T268267\n\t// Set the Api-User-Agent header - T262177\n\tconst apiConfig = {\n\t\tajax: {\n\t\t\theaders: {\n\t\t\t\t'Access-Control-Max-Age': 300,\n\t\t\t\t'Api-User-Agent': 'GlobalWatchlist-MediaWiki/' + mw.config.get( 'wgVersion' )\n\t\t\t}\n\t\t}\n\t};\n\n\tlet linker;\n\t/**\n\t * @property {Array} siteList The individual sites\n\t */\n\tthis.siteList = config.siteList.map( ( site ) => {\n\t\tlinker = new GlobalWatchlistLinker( site );\n\t\treturn new SiteClass(\n\t\t\tglobalWatchlistDebug,\n\t\t\tlinker,\n\t\t\tconfig,\n\t\t\tnew mw.ForeignApi( '//' + site + mw.util.wikiScript( 'api' ), apiConfig ),\n\t\t\tnew GlobalWatchlistWatchlistUtils( linker ),\n\t\t\tsite\n\t\t);\n\t} );\n\n\t/**\n\t * @property {boolean} whether to ask the user to confirm their decision when\n\t * marking all sites as seen\n\t */\n\tthis.confirmMarkAllSitesSeen = config.confirmAllSites;\n}\n\n/**\n * Promise that all of the sites have retrieved their watchlists\n *\n * @param {Object} config User configuration to use. Needs to be passed rather\n *   than using the configuration we got in the constructor because some parts\n *   of it can change (specifically whether to group results by page, and the\n *   timestamp of the start of the call that is used when marking sites as seen).\n * @return {Promise} Promise that all watchlists were retrieved\n */\nGlobalWatchlistMultiSiteWrapper.prototype.getAllWatchlists = function ( config ) {\n\treturn Promise.all(\n\t\tthis.siteList.map( ( site ) => {\n\t\t\t// Reset in case it failed earlier\n\t\t\tsite.apiError = false;\n\n\t\t\treturn site.getWatchlist( config );\n\t\t} )\n\t);\n};\n\n/**\n * Promise that all of the sites have called markAllAsSeen\n *\n * @return {Promise} Promise that all sites were marked as seen\n */\nGlobalWatchlistMultiSiteWrapper.prototype.markAllSitesSeen = function () {\n\tconst that = this;\n\n\treturn new Promise( ( resolve, reject ) => {\n\t\tlet getConfirmation;\n\n\t\tif ( that.confirmMarkAllSitesSeen ) {\n\t\t\tgetConfirmation = OO.ui.confirm(\n\t\t\t\tmw.msg( 'globalwatchlist-markseen-allconfirm' )\n\t\t\t);\n\t\t} else {\n\t\t\tgetConfirmation = Promise.resolve( true );\n\t\t}\n\n\t\t/**\n\t\t * If the user opted to require confirmation, getConfirmation is the Promise\n\t\t * returned by OO.ui.confirm that resolves to the user's decision (boolean value,\n\t\t * true means that the user confirmed the intention to mark all sites as seen,\n\t\t * false means user cancelled). If the user didn't opt to require confirmation,\n\t\t * we didn't check, but to avoid code duplication we just pretend we checked and\n\t\t * that the answer was confirming the decision to mark all sites as seen, and so\n\t\t * getConfirmation is a Promise that just always resolves to true.\n\t\t */\n\t\tgetConfirmation.then(\n\t\t\t( confirmed ) => {\n\t\t\t\tif ( confirmed ) {\n\t\t\t\t\tPromise.all(\n\t\t\t\t\t\tthat.siteList.map( ( site ) => site.markAllAsSeen() )\n\t\t\t\t\t).then( () => {\n\t\t\t\t\t\tresolve();\n\t\t\t\t\t} );\n\t\t\t\t} else {\n\t\t\t\t\treject();\n\t\t\t\t}\n\t\t\t}\n\t\t);\n\t} );\n};\n\nmodule.exports = GlobalWatchlistMultiSiteWrapper;\n","usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]},{"ruleId":"max-len","replacedBy":[]},{"ruleId":"arrow-parens","replacedBy":[]},{"ruleId":"arrow-spacing","replacedBy":[]},{"ruleId":"lines-between-class-members","replacedBy":[]},{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"template-curly-spacing","replacedBy":[]},{"ruleId":"implicit-arrow-linebreak","replacedBy":[]},{"ruleId":"array-bracket-spacing","replacedBy":[]},{"ruleId":"block-spacing","replacedBy":[]},{"ruleId":"brace-style","replacedBy":[]},{"ruleId":"comma-dangle","replacedBy":[]},{"ruleId":"comma-spacing","replacedBy":[]},{"ruleId":"comma-style","replacedBy":[]},{"ruleId":"computed-property-spacing","replacedBy":[]},{"ruleId":"dot-location","replacedBy":[]},{"ruleId":"eol-last","replacedBy":[]},{"ruleId":"func-call-spacing","replacedBy":[]},{"ruleId":"indent","replacedBy":[]},{"ruleId":"key-spacing","replacedBy":[]},{"ruleId":"keyword-spacing","replacedBy":[]},{"ruleId":"linebreak-style","replacedBy":[]},{"ruleId":"max-statements-per-line","replacedBy":[]},{"ruleId":"new-parens","replacedBy":[]},{"ruleId":"no-floating-decimal","replacedBy":[]},{"ruleId":"no-multi-spaces","replacedBy":[]},{"ruleId":"no-multiple-empty-lines","replacedBy":[]},{"ruleId":"no-new-object","replacedBy":["no-object-constructor"]},{"ruleId":"no-tabs","replacedBy":[]},{"ruleId":"no-trailing-spaces","replacedBy":[]},{"ruleId":"no-whitespace-before-property","replacedBy":[]},{"ruleId":"object-curly-spacing","replacedBy":[]},{"ruleId":"operator-linebreak","replacedBy":[]},{"ruleId":"quote-props","replacedBy":[]},{"ruleId":"quotes","replacedBy":[]},{"ruleId":"semi","replacedBy":[]},{"ruleId":"semi-spacing","replacedBy":[]},{"ruleId":"semi-style","replacedBy":[]},{"ruleId":"space-before-blocks","replacedBy":[]},{"ruleId":"space-before-function-paren","replacedBy":[]},{"ruleId":"space-in-parens","replacedBy":[]},{"ruleId":"space-infix-ops","replacedBy":[]},{"ruleId":"space-unary-ops","replacedBy":[]},{"ruleId":"spaced-comment","replacedBy":[]},{"ruleId":"switch-colon-spacing","replacedBy":[]},{"ruleId":"wrap-iife","replacedBy":[]},{"ruleId":"no-extra-semi","replacedBy":[]},{"ruleId":"no-mixed-spaces-and-tabs","replacedBy":[]}]},{"filePath":"/src/repo/modules/specialglobalwatchlist/SettingsTour.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]},{"ruleId":"max-len","replacedBy":[]},{"ruleId":"arrow-parens","replacedBy":[]},{"ruleId":"arrow-spacing","replacedBy":[]},{"ruleId":"lines-between-class-members","replacedBy":[]},{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"template-curly-spacing","replacedBy":[]},{"ruleId":"implicit-arrow-linebreak","replacedBy":[]},{"ruleId":"array-bracket-spacing","replacedBy":[]},{"ruleId":"block-spacing","replacedBy":[]},{"ruleId":"brace-style","replacedBy":[]},{"ruleId":"comma-dangle","replacedBy":[]},{"ruleId":"comma-spacing","replacedBy":[]},{"ruleId":"comma-style","replacedBy":[]},{"ruleId":"computed-property-spacing","replacedBy":[]},{"ruleId":"dot-location","replacedBy":[]},{"ruleId":"eol-last","replacedBy":[]},{"ruleId":"func-call-spacing","replacedBy":[]},{"ruleId":"indent","replacedBy":[]},{"ruleId":"key-spacing","replacedBy":[]},{"ruleId":"keyword-spacing","replacedBy":[]},{"ruleId":"linebreak-style","replacedBy":[]},{"ruleId":"max-statements-per-line","replacedBy":[]},{"ruleId":"new-parens","replacedBy":[]},{"ruleId":"no-floating-decimal","replacedBy":[]},{"ruleId":"no-multi-spaces","replacedBy":[]},{"ruleId":"no-multiple-empty-lines","replacedBy":[]},{"ruleId":"no-new-object","replacedBy":["no-object-constructor"]},{"ruleId":"no-tabs","replacedBy":[]},{"ruleId":"no-trailing-spaces","replacedBy":[]},{"ruleId":"no-whitespace-before-property","replacedBy":[]},{"ruleId":"object-curly-spacing","replacedBy":[]},{"ruleId":"operator-linebreak","replacedBy":[]},{"ruleId":"quote-props","replacedBy":[]},{"ruleId":"quotes","replacedBy":[]},{"ruleId":"semi","replacedBy":[]},{"ruleId":"semi-spacing","replacedBy":[]},{"ruleId":"semi-style","replacedBy":[]},{"ruleId":"space-before-blocks","replacedBy":[]},{"ruleId":"space-before-function-paren","replacedBy":[]},{"ruleId":"space-in-parens","replacedBy":[]},{"ruleId":"space-infix-ops","replacedBy":[]},{"ruleId":"space-unary-ops","replacedBy":[]},{"ruleId":"spaced-comment","replacedBy":[]},{"ruleId":"switch-colon-spacing","replacedBy":[]},{"ruleId":"wrap-iife","replacedBy":[]},{"ruleId":"no-extra-semi","replacedBy":[]},{"ruleId":"no-mixed-spaces-and-tabs","replacedBy":[]}]},{"filePath":"/src/repo/modules/specialglobalwatchlist/SiteBase.js","messages":[{"ruleId":"jsdoc/no-undefined-types","severity":1,"message":"The type 'GlobalWatchlistDebugger' is undefined.","line":8,"column":1,"nodeType":"Block","endLine":8,"endColumn":1},{"ruleId":"jsdoc/no-undefined-types","severity":1,"message":"The type 'GlobalWatchlistLinker' is undefined.","line":9,"column":1,"nodeType":"Block","endLine":9,"endColumn":1},{"ruleId":"jsdoc/no-undefined-types","severity":1,"message":"The type 'GlobalWatchlistWatchlistUtils' is undefined.","line":12,"column":1,"nodeType":"Block","endLine":12,"endColumn":1},{"ruleId":"jsdoc/no-undefined-types","severity":1,"message":"The type 'GlobalWatchlistSiteDisplay' is undefined.","line":244,"column":1,"nodeType":"Block","endLine":244,"endColumn":1},{"ruleId":"jsdoc/no-undefined-types","severity":1,"message":"The type 'GlobalWatchlistSiteDisplay' is undefined.","line":368,"column":1,"nodeType":"Block","endLine":368,"endColumn":1},{"ruleId":"jsdoc/no-undefined-types","severity":1,"message":"The type 'GlobalWatchlistEntryBase' is undefined.","line":370,"column":1,"nodeType":"Block","endLine":370,"endColumn":1},{"ruleId":"jsdoc/no-undefined-types","severity":1,"message":"The type 'GlobalWatchlistEntryBase' is undefined.","line":379,"column":1,"nodeType":"Block","endLine":379,"endColumn":1},{"ruleId":"jsdoc/no-undefined-types","severity":1,"message":"The type 'GlobalWatchlistSiteDisplay' is undefined.","line":424,"column":1,"nodeType":"Block","endLine":424,"endColumn":1},{"ruleId":"jsdoc/no-undefined-types","severity":1,"message":"The type 'GlobalWatchlistSiteDisplay' is undefined.","line":433,"column":1,"nodeType":"Block","endLine":433,"endColumn":1},{"ruleId":"jsdoc/no-undefined-types","severity":1,"message":"The type 'GlobalWatchlistSiteDisplay' is undefined.","line":443,"column":1,"nodeType":"Block","endLine":443,"endColumn":1}],"suppressedMessages":[{"ruleId":"no-unused-vars","severity":2,"message":"'reject' is defined but never used.","line":98,"column":33,"nodeType":"Identifier","messageId":"unusedVar","endLine":98,"endColumn":39,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-unused-vars","severity":2,"message":"'pageTitle' is defined but never used.","line":248,"column":68,"nodeType":"Identifier","messageId":"unusedVar","endLine":248,"endColumn":77,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-unused-vars","severity":2,"message":"'summary' is defined but never used.","line":372,"column":64,"nodeType":"Identifier","messageId":"unusedVar","endLine":372,"endColumn":71,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-unused-vars","severity":2,"message":"'pageTitle' is defined but never used.","line":438,"column":69,"nodeType":"Identifier","messageId":"unusedVar","endLine":438,"endColumn":78,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-unused-vars","severity":2,"message":"'unwatched' is defined but never used.","line":438,"column":80,"nodeType":"Identifier","messageId":"unusedVar","endLine":438,"endColumn":89,"suppressions":[{"kind":"directive","justification":""}]}],"errorCount":0,"fatalErrorCount":0,"warningCount":10,"fixableErrorCount":0,"fixableWarningCount":0,"source":"/* eslint-disable no-unused-vars */\n/**\n * Represents a specific site\n *\n * @class GlobalWatchlistSiteBase\n * @abstract\n *\n * @param {GlobalWatchlistDebugger} globalWatchlistDebug Debugger instance to log to\n * @param {GlobalWatchlistLinker} linker Linker instance to use\n * @param {Object} config User configuration\n * @param {mw.ForeignApi} api Instance of mw.ForeignApi for this site\n * @param {GlobalWatchlistWatchlistUtils} watchlistUtils WatchlistUtils instance for this site\n * @param {string} urlFragment string for which site this represents\n */\nfunction GlobalWatchlistSiteBase(\n\tglobalWatchlistDebug,\n\tlinker,\n\tconfig,\n\tapi,\n\twatchlistUtils,\n\turlFragment\n) {\n\t// Logger to send debug info to\n\tthis.debugLogger = globalWatchlistDebug;\n\n\t// User config and other settings, retrieved from getSettings\n\tthis.config = config;\n\n\t// The api object to interact with\n\tthis.apiObject = api;\n\n\t// Utility methods (GlobalWatchlistWatchlistUtils)\n\tthis.watchlistUtils = watchlistUtils;\n\n\t// Site identifier in url format\n\tthis.site = urlFragment;\n\n\t// Linker utility (GlobalWatchlistLinker)\n\tthis.linker = linker;\n\n\t// Site identifier in format that can be used for element attributes\n\tthis.siteID = encodeURIComponent( urlFragment.replace( /\\./g, '_' ) );\n\n\t// Whether this site had any changes to show\n\tthis.isEmpty = false;\n\n\t// Cached information about the tags of a site\n\tthis.tags = {};\n\n\t// Whether there was an error when trying to use the API. To be able to use Promise.all,\n\t// API failures still resolve the Promise rather than rejecting it. If Promise.allSettled\n\t// becomes available for use, this should no longer be needed\n\tthis.apiError = false;\n\n\t// Instance of GlobalWatchlistWikibaseHandler, only used for wikibase\n\t// Don't create it if it will never be needed\n\tif ( this.site === config.wikibaseSite ) {\n\t\tconst GlobalWatchlistWikibaseHandler = require( './WikibaseHandler.js' );\n\t\tthis.wikibaseHandler = new GlobalWatchlistWikibaseHandler(\n\t\t\tglobalWatchlistDebug,\n\t\t\tapi,\n\t\t\tconfig.lang\n\t\t);\n\t}\n}\n\n/**\n * Shortcut for sending information to the debug logger\n *\n * @param {string} msg Message for debug entry\n * @param {string} [extraInfo] Extra information for debug entry\n */\nGlobalWatchlistSiteBase.prototype.debug = function ( msg, extraInfo ) {\n\tthis.debugLogger.info( this.site + ':' + msg, extraInfo );\n};\n\n/**\n * Shortcut for sending errors to the debug logger\n *\n * @param {string} msg Message for error entry\n * @param {Object} data Extra information for error entry\n */\nGlobalWatchlistSiteBase.prototype.error = function ( msg, data ) {\n\tthis.debugLogger.error( this.site + ':' + msg, data );\n};\n\n/**\n * API handler for debugging and avoiding actual important actions when testing client-side\n *\n * @param {string} func Function name\n * @param {Object} content Content to send to the api\n * @param {string} name Name, for logging purposes\n * @return {Promise} Result of the api call\n */\nGlobalWatchlistSiteBase.prototype.api = function ( func, content, name ) {\n\tconst that = this;\n\n\treturn new Promise( ( resolve, reject ) => {\n\t\tthat.debug( 'API.' + name + ' (called), with func & content:', [ func, content ] );\n\t\tthat.apiObject[ func ]( content ).then( ( response ) => {\n\t\t\tthat.debug(\n\t\t\t\t'API.' + name + ' (result); func, content, & response',\n\t\t\t\t[ func, content, response ]\n\t\t\t);\n\t\t\tresolve( response );\n\t\t} ).catch( ( code, data ) => {\n\t\t\tthat.error( 'API.' + name + ' ' + code, data );\n\t\t\tthat.apiError = true;\n\n\t\t\tconst $userNotification = $( '<div>' )\n\t\t\t\t.append(\n\t\t\t\t\tmw.msg( 'globalwatchlist-api-error', that.site ),\n\t\t\t\t\tthat.apiObject.getErrorMessage( data )\n\t\t\t\t);\n\n\t\t\tmw.notify(\n\t\t\t\t$userNotification,\n\t\t\t\t{\n\t\t\t\t\ttype: 'error',\n\t\t\t\t\tautoHide: false\n\t\t\t\t}\n\t\t\t);\n\n\t\t\t// See above on apiError for why this resolves instead of rejecting\n\t\t\t// since we don't know what exactly the caller was expected, just\n\t\t\t// resolve \"error\" and leave the handling for the caller\n\t\t\tresolve( 'ERROR' );\n\t\t} );\n\t} );\n};\n\n/**\n * Get the changes on a user's watchlist\n *\n * This method calls itself recursively until there are no remaining changes to retrieve,\n * using the `continue` functionality.\n *\n * @param {number} iteration iteration count\n * @param {string} continueFrom value of wlcontinue in the previous call\n * @return {Promise} Promise of api result\n */\nGlobalWatchlistSiteBase.prototype.actuallyGetWatchlist = function ( iteration, continueFrom ) {\n\tconst that = this;\n\n\treturn new Promise( ( resolve ) => {\n\t\tconst query = {\n\t\t\taction: 'query',\n\t\t\tformatversion: 2,\n\t\t\tlist: 'watchlist',\n\t\t\twllimit: 'max',\n\t\t\twlprop: that.config.watchlistQueryProps,\n\t\t\twlshow: that.config.watchlistQueryShow,\n\t\t\twltype: that.config.watchlistQueryTypes\n\t\t};\n\t\tif ( iteration > 1 ) {\n\t\t\tquery.wlcontinue = continueFrom;\n\t\t}\n\t\tif ( !that.config.fastMode ) {\n\t\t\tquery.wlallrev = true;\n\t\t}\n\n\t\tthat.api( 'get', query, 'actuallyGetWatchlist #' + iteration ).then( ( response ) => {\n\t\t\tif ( response === 'ERROR' ) {\n\t\t\t\tresolve( [] );\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst wlraw = response.query.watchlist;\n\t\t\tif ( response.continue && response.continue.wlcontinue ) {\n\t\t\t\tthat.actuallyGetWatchlist(\n\t\t\t\t\titeration + 1,\n\t\t\t\t\tresponse.continue.wlcontinue\n\t\t\t\t).then( ( innerResponse ) => {\n\t\t\t\t\t// If there was an error in the recursive call, this just\n\t\t\t\t\t// adds an empty array. getWatchlist checks this.apiError\n\t\t\t\t\t// before assuming that an empty response means nothing to show\n\t\t\t\t\tresolve( wlraw.concat( innerResponse ) );\n\t\t\t\t} );\n\t\t\t} else {\n\t\t\t\tresolve( wlraw );\n\t\t\t}\n\t\t} );\n\t} );\n};\n\n/**\n * Update the strikethrough and text for entries being watched/unwatched\n *\n * Calls the API to actually unwatch/rewatch a page\n *\n * Calls `processUpdateWatched` to update the display (either add or remove the strikethrough,\n *   and update the text shown)\n *\n * If fast mode is not enabled, calls `getAssociatedTalkPage` to determine the talk/subject page\n *   associated with the one that was unwatched/rewatched, and then uses `processUpdateWatched`\n *   to update the display of any entries for the associated page\n *\n * @param {string} pageTitle Title of the page to watch or unwatch\n * @param {string} func Either 'watch' or 'unwatch'\n */\nGlobalWatchlistSiteBase.prototype.changeWatched = function ( pageTitle, func ) {\n\tthis.debug( 'changeWatched - Going to ' + func + ': ' + pageTitle );\n\tconst that = this;\n\tthis.api( func, pageTitle, 'updateWatched' );\n\tthis.processUpdateWatched( pageTitle, func === 'unwatch' );\n\tif ( !this.config.fastMode ) {\n\t\tthis.getAssociatedPageTitle( pageTitle ).then( ( associatedTitle ) => {\n\t\t\tthat.processUpdateWatched( associatedTitle, func === 'unwatch' );\n\t\t\t// TODO re-add functionality for old checkChangesShown\n\t\t} );\n\t}\n};\n\n/**\n * Mark page as read\n *\n * Calls the API to reset notification timestamp for a page\n *\n * @param {string} pageTitle Title of the page to mark as read\n * @return {Promise} that resolves after the api call is made and after `afterMarkPageAsSeen`\n * is called, not necessarily after the api call is finished.\n */\nGlobalWatchlistSiteBase.prototype.markPageAsSeen = function ( pageTitle ) {\n\tconst that = this;\n\n\treturn new Promise( ( resolve ) => {\n\t\tconst setter = {\n\t\t\taction: 'setnotificationtimestamp',\n\t\t\ttitles: pageTitle,\n\t\t\ttimestamp: that.config.time.toISOString()\n\t\t};\n\t\tthat.api( 'postWithEditToken', setter );\n\n\t\tthat.afterMarkPageAsSeen( pageTitle );\n\n\t\t// Done within a promise so that display can ensure re-rendering occurs after\n\t\t// entries are updated\n\t\tresolve();\n\t} );\n};\n\n/**\n * Update display after marking a page as read\n *\n * Overriden in {@link GlobalWatchlistSiteDisplay}\n *\n * @param {string} pageTitle Page that was marked as read\n */\nGlobalWatchlistSiteBase.prototype.afterMarkPageAsSeen = function ( pageTitle ) {\n\t// STUB\n};\n\n/**\n * Returns the talk/subject page associated with a given page, since entries for the associated page\n *   also need to have their text and strikethrough updated on unwatching/rewatching\n *\n * @param {string} pageTitle Title of the page for which to retrieve the associated page\n * @return {Promise} Promise of api result\n */\nGlobalWatchlistSiteBase.prototype.getAssociatedPageTitle = function ( pageTitle ) {\n\tconst that = this;\n\treturn new Promise( ( resolve ) => {\n\t\tconst query = {\n\t\t\taction: 'query',\n\t\t\tprop: 'info',\n\t\t\ttitles: pageTitle,\n\t\t\tinprop: 'associatedpage',\n\t\t\tformatversion: 2\n\t\t};\n\t\tthat.api( 'get', query, 'getAssociatedPageTitle' ).then( ( response ) => {\n\t\t\tresolve( response.query.pages[ 0 ].associatedpage );\n\t\t} );\n\t} );\n};\n\n/**\n * Get the tags for a wiki, loading them if not already available (in fast mode we don't retrieve\n * tags information for the watchlist, so this returns an empty object)\n *\n * Once this is called once, the tag info is stored in this.tags and future calls with return early\n *\n * @return {Promise} Resolves with the tags that where retrieved, or an empty object if we are\n *   in fast mode\n */\nGlobalWatchlistSiteBase.prototype.getTagList = function () {\n\tconst that = this;\n\treturn new Promise( ( resolve ) => {\n\t\tif ( that.config.fastMode || Object.keys( that.tags ).length > 0 ) {\n\t\t\t// Either we are in fast mode, and we should return an empty object, which\n\t\t\t// is the default value of that.tags, or we already fetched the tags info\n\t\t\t// and its already available in that.tags\n\t\t\tresolve( that.tags );\n\t\t} else {\n\t\t\tconst query = {\n\t\t\t\taction: 'query',\n\t\t\t\tlist: 'tags',\n\t\t\t\ttglimit: 'max',\n\t\t\t\ttgprop: 'displayname'\n\t\t\t};\n\t\t\tthat.api( 'get', query, 'getTags' ).then( ( response ) => {\n\t\t\t\tconst asObject = {};\n\t\t\t\tresponse.query.tags.forEach( ( tag ) => {\n\t\t\t\t\tasObject[ tag.name ] = ( tag.displayname || false ) ?\n\t\t\t\t\t\tthat.linker.fixLocalLinks( tag.displayname ) :\n\t\t\t\t\t\ttag.name;\n\t\t\t\t} );\n\t\t\t\tthat.debug( 'getTagList', asObject );\n\t\t\t\t// Save for future calls (eg on refresh)\n\t\t\t\tthat.tags = asObject;\n\t\t\t\tresolve( asObject );\n\t\t\t} );\n\t\t}\n\t} );\n};\n\n/**\n * Get the rendered changes for a user's watchlist\n *\n * @param {Object} latestConfig config, can change\n * @return {Promise} Promise that the watchlist was retrieved\n */\nGlobalWatchlistSiteBase.prototype.getWatchlist = function ( latestConfig ) {\n\tthis.config = latestConfig;\n\tconst that = this;\n\treturn new Promise( ( resolve ) => {\n\t\tthat.actuallyGetWatchlist( 1, 0 ).then( ( wlraw ) => {\n\t\t\tif ( !( wlraw && wlraw[ 0 ] ) ) {\n\t\t\t\tif ( that.apiError ) {\n\t\t\t\t\tthat.debug( 'getWatchlist - error' );\n\n\t\t\t\t\t// Include in the normal display section\n\t\t\t\t\tthat.isEmpty = false;\n\n\t\t\t\t\tthat.renderApiFailure();\n\t\t\t\t} else {\n\t\t\t\t\tthat.debug( 'getWatchlist - empty' );\n\t\t\t\t\tthat.isEmpty = true;\n\t\t\t\t}\n\n\t\t\t\tresolve();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// In case it was previously set to true\n\t\t\tthat.isEmpty = false;\n\n\t\t\tthat.debug( 'getWatchlist wlraw', wlraw );\n\n\t\t\tthat.getTagList().then( ( tagsInfo ) => {\n\t\t\t\tconst prelimSummary = that.watchlistUtils.rawToSummary(\n\t\t\t\t\twlraw,\n\t\t\t\t\tthat.config.groupPage,\n\t\t\t\t\ttagsInfo\n\t\t\t\t);\n\t\t\t\tthat.debug( 'getWatchlist prelimSummary', prelimSummary );\n\n\t\t\t\tthat.makeWikidataList( prelimSummary ).then( ( summary ) => {\n\t\t\t\t\tthat.debug( 'getWatchlist summary', summary );\n\t\t\t\t\tthat.renderWatchlist( summary );\n\t\t\t\t\tresolve();\n\t\t\t\t} );\n\t\t\t} );\n\t\t} );\n\t} );\n};\n\n/**\n * Display the watchlist\n *\n * Overriden in {@link GlobalWatchlistSiteDisplay}\n *\n * @param {GlobalWatchlistEntryBase[]} summary What should be rendered\n */\nGlobalWatchlistSiteBase.prototype.renderWatchlist = function ( summary ) {\n\t// STUB\n};\n\n/**\n * Fetch and process wikibase labels when the watchlist is for wikidata\n *\n * @param {GlobalWatchlistEntryBase[]} summary Original summary, with page titles (Q1, P2, L3, etc.)\n * @return {Promise} Updated summary, with labels\n */\nGlobalWatchlistSiteBase.prototype.makeWikidataList = function ( summary ) {\n\tconst that = this;\n\treturn new Promise( ( resolve ) => {\n\t\tif ( that.site !== that.config.wikibaseSite || that.config.fastMode ) {\n\t\t\tresolve( summary );\n\t\t} else {\n\t\t\tthat.wikibaseHandler.addWikibaseLabels( summary ).then( ( updatedSummary ) => {\n\t\t\t\tresolve( updatedSummary );\n\t\t\t} );\n\t\t}\n\t} );\n};\n\n/**\n * Mark a site as seen\n *\n * @return {Promise} that resolves after the api call is made and after `afterMarkAllAsSeen`\n *   is called, not necessarily after the api call is finished.\n */\nGlobalWatchlistSiteBase.prototype.markAllAsSeen = function () {\n\tthis.debug( 'markSiteAsSeen - marking' );\n\tconst that = this;\n\n\treturn new Promise( ( resolve ) => {\n\t\tconst setter = {\n\t\t\taction: 'setnotificationtimestamp',\n\t\t\tentirewatchlist: true,\n\t\t\ttimestamp: that.config.time.toISOString()\n\t\t};\n\t\tthat.api( 'postWithEditToken', setter, 'actuallyMarkSiteAsSeen' );\n\n\t\tthat.afterMarkAllAsSeen();\n\n\t\t// Done within a promise so that display can ensure re-rendering occurs after\n\t\t// entries are updated\n\t\tresolve();\n\t} );\n};\n\n/**\n * Update display after making a site as seen\n *\n * Overriden in {@link GlobalWatchlistSiteDisplay}\n */\nGlobalWatchlistSiteBase.prototype.afterMarkAllAsSeen = function () {\n\t// STUB\n};\n\n/**\n * Update entry click handlers, text, and strikethrough for a specific title\n *\n * Overriden in {@link GlobalWatchlistSiteDisplay}\n *\n * @param {string} pageTitle Title of the page that was unwatched/rewatched.\n * @param {boolean} unwatched Whether the page was unwatched\n */\nGlobalWatchlistSiteBase.prototype.processUpdateWatched = function ( pageTitle, unwatched ) {\n\t// STUB\n};\n\n/**\n * Used by {@link GlobalWatchlistSiteDisplay} to still include an output for api failures\n */\nGlobalWatchlistSiteBase.prototype.renderApiFailure = function () {\n\t// STUB\n};\n\nmodule.exports = GlobalWatchlistSiteBase;\n","usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]},{"ruleId":"max-len","replacedBy":[]},{"ruleId":"arrow-parens","replacedBy":[]},{"ruleId":"arrow-spacing","replacedBy":[]},{"ruleId":"lines-between-class-members","replacedBy":[]},{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"template-curly-spacing","replacedBy":[]},{"ruleId":"implicit-arrow-linebreak","replacedBy":[]},{"ruleId":"array-bracket-spacing","replacedBy":[]},{"ruleId":"block-spacing","replacedBy":[]},{"ruleId":"brace-style","replacedBy":[]},{"ruleId":"comma-dangle","replacedBy":[]},{"ruleId":"comma-spacing","replacedBy":[]},{"ruleId":"comma-style","replacedBy":[]},{"ruleId":"computed-property-spacing","replacedBy":[]},{"ruleId":"dot-location","replacedBy":[]},{"ruleId":"eol-last","replacedBy":[]},{"ruleId":"func-call-spacing","replacedBy":[]},{"ruleId":"indent","replacedBy":[]},{"ruleId":"key-spacing","replacedBy":[]},{"ruleId":"keyword-spacing","replacedBy":[]},{"ruleId":"linebreak-style","replacedBy":[]},{"ruleId":"max-statements-per-line","replacedBy":[]},{"ruleId":"new-parens","replacedBy":[]},{"ruleId":"no-floating-decimal","replacedBy":[]},{"ruleId":"no-multi-spaces","replacedBy":[]},{"ruleId":"no-multiple-empty-lines","replacedBy":[]},{"ruleId":"no-new-object","replacedBy":["no-object-constructor"]},{"ruleId":"no-tabs","replacedBy":[]},{"ruleId":"no-trailing-spaces","replacedBy":[]},{"ruleId":"no-whitespace-before-property","replacedBy":[]},{"ruleId":"object-curly-spacing","replacedBy":[]},{"ruleId":"operator-linebreak","replacedBy":[]},{"ruleId":"quote-props","replacedBy":[]},{"ruleId":"quotes","replacedBy":[]},{"ruleId":"semi","replacedBy":[]},{"ruleId":"semi-spacing","replacedBy":[]},{"ruleId":"semi-style","replacedBy":[]},{"ruleId":"space-before-blocks","replacedBy":[]},{"ruleId":"space-before-function-paren","replacedBy":[]},{"ruleId":"space-in-parens","replacedBy":[]},{"ruleId":"space-infix-ops","replacedBy":[]},{"ruleId":"space-unary-ops","replacedBy":[]},{"ruleId":"spaced-comment","replacedBy":[]},{"ruleId":"switch-colon-spacing","replacedBy":[]},{"ruleId":"wrap-iife","replacedBy":[]},{"ruleId":"no-extra-semi","replacedBy":[]},{"ruleId":"no-mixed-spaces-and-tabs","replacedBy":[]}]},{"filePath":"/src/repo/modules/specialglobalwatchlist/SiteDisplay.js","messages":[{"ruleId":"jsdoc/no-undefined-types","severity":1,"message":"The type 'GlobalWatchlistDebugger' is undefined.","line":14,"column":1,"nodeType":"Block","endLine":14,"endColumn":1},{"ruleId":"jsdoc/no-undefined-types","severity":1,"message":"The type 'GlobalWatchlistLinker' is undefined.","line":15,"column":1,"nodeType":"Block","endLine":15,"endColumn":1},{"ruleId":"jsdoc/no-undefined-types","severity":1,"message":"The type 'GlobalWatchlistWatchlistUtils' is undefined.","line":18,"column":1,"nodeType":"Block","endLine":18,"endColumn":1},{"ruleId":"jsdoc/no-undefined-types","severity":1,"message":"The type 'GlobalWatchlistEntryBase' is undefined.","line":48,"column":1,"nodeType":"Block","endLine":48,"endColumn":1},{"ruleId":"jsdoc/no-undefined-types","severity":1,"message":"The type 'GlobalWatchlistEntryBase' is undefined.","line":233,"column":1,"nodeType":"Block","endLine":233,"endColumn":1}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":5,"fixableErrorCount":0,"fixableWarningCount":0,"source":"/*\n * Extended version of SiteBase.js for use in jQuery version of Special:GlobalWatchlist\n */\n\nconst GlobalWatchlistSiteBase = require( './SiteBase.js' );\n\n/**\n * Represents a specific site, including the display (used in jQuery display)\n *\n * @class GlobalWatchlistSiteDisplay\n * @extends GlobalWatchlistSiteBase\n *\n * @constructor\n * @param {GlobalWatchlistDebugger} globalWatchlistDebug Debugger instance to log to\n * @param {GlobalWatchlistLinker} linker Linker instance to use\n * @param {Object} config User configuration\n * @param {mw.ForeignApi} api Instance of mw.ForeignApi for this site\n * @param {GlobalWatchlistWatchlistUtils} watchlistUtils WatchlistUtils instance for this site\n * @param {string} urlFragment string for which site this represents\n */\nfunction GlobalWatchlistSiteDisplay(\n\tglobalWatchlistDebug,\n\tlinker,\n\tconfig,\n\tapi,\n\twatchlistUtils,\n\turlFragment\n) {\n\tGlobalWatchlistSiteDisplay.super.call(\n\t\tthis,\n\t\tglobalWatchlistDebug,\n\t\tlinker,\n\t\tconfig,\n\t\tapi,\n\t\twatchlistUtils,\n\t\turlFragment\n\t);\n\n\t// Actual output for this site\n\tthis.$feedDiv = '';\n}\n\nOO.inheritClass( GlobalWatchlistSiteDisplay, GlobalWatchlistSiteBase );\n\n/**\n * Make the links for a row in the watchlist\n *\n * @param {GlobalWatchlistEntryBase} entry Details of the list entry to create\n * @return {jQuery} list item\n */\nGlobalWatchlistSiteDisplay.prototype.makePageLink = function ( entry ) {\n\tconst pageTitle = encodeURIComponent( entry.title ).replace( /'/g, '%27' );\n\tconst $pageLink = $( '<a>' )\n\t\t.attr( 'href', this.linker.linkQuery( 'title=' + pageTitle + '&redirect=no' ) )\n\t\t.attr( 'target', '_blank' )\n\t\t.text( entry.titleMsg || entry.title );\n\tconst that = this;\n\n\t// Actually set up the $row to be returned\n\tconst $row = $( '<li>' );\n\n\t$row.attr( 'data-site', encodeURIComponent( this.siteID ) );\n\t$row.attr( 'data-title', encodeURIComponent( entry.title ) );\n\n\tif ( entry.timestamp ) {\n\t\t// entry.timestampTitle is either a string explaining grouped changes, or null to ignore\n\t\tconst $timestamp = $( '<span>' )\n\t\t\t.text( entry.timestamp )\n\t\t\t.attr( 'title', entry.timestampTitle );\n\t\t$row.append( $timestamp )\n\t\t\t.append( ' ' );\n\t}\n\tif ( entry.expiry ) {\n\t\tconst clockIcon = new OO.ui.IconWidget( {\n\t\t\tclasses: [ 'ext-globalwatchlist-expiry-icon' ],\n\t\t\ticon: 'clock',\n\t\t\ttitle: entry.expiry\n\t\t} );\n\t\t$row.append( clockIcon.$element )\n\t\t\t.append( ' ' );\n\t}\n\tif ( entry.flags ) {\n\t\t// New page / minor edit / bot flag\n\t\t$row.append( $( '<b>' ).text( entry.flags ) )\n\t\t\t.append( ' ' );\n\t}\n\tif ( entry.entryType === 'log' ) {\n\t\tconst logText = 'Log: ' + entry.logtype + '/' + entry.logaction + ': ';\n\t\t$row.append( $( '<em>' ).text( logText ) )\n\t\t\t.append( ' ' );\n\t}\n\n\t$row.append( $pageLink )\n\t\t.append( ' (' );\n\n\tif ( entry.entryType !== 'log' ) {\n\t\t// No history link for log entries, T273691\n\t\tconst $historyLink = $( '<a>' )\n\t\t\t.attr( 'href', this.linker.linkQuery( 'title=' + pageTitle + '&action=history' ) )\n\t\t\t.attr( 'target', '_blank' )\n\t\t\t.text( mw.msg( 'globalwatchlist-history' ) );\n\t\t$row.append( $historyLink )\n\t\t\t.append( ', ' );\n\t}\n\n\t// No diff links in fast mode, see T269728\n\tif ( entry.entryType === 'edit' && entry.newPage === false && this.config.fastMode === false ) {\n\t\tconst $diffLink = $( '<a>' )\n\t\t\t.attr( 'href', this.linker.linkQuery( 'diff=' + entry.toRev + '&oldid=' + entry.fromRev ) )\n\t\t\t.attr( 'target', '_blank' )\n\t\t\t.addClass( 'ext-globalwatchlist-diff' )\n\t\t\t.text(\n\t\t\t\tentry.editCount === 1 ? mw.msg( 'diff' ) : mw.msg( 'nchanges', entry.editCount )\n\t\t\t);\n\t\t$row.append( $diffLink )\n\t\t\t.append( ', ' );\n\t} else if ( entry.entryType === 'log' ) {\n\t\tconst $logPageLink = $( '<a>' )\n\t\t\t.attr( 'href', this.linker.linkQuery( 'title=Special:Log&page=' + pageTitle ) )\n\t\t\t.attr( 'target', '_blank' )\n\t\t\t.text( mw.msg( 'globalwatchlist-log-page' ) );\n\t\t$row.append( $logPageLink )\n\t\t\t.append( ', ' );\n\n\t\tconst $logEntryLink = $( '<a>' )\n\t\t\t.attr( 'href', this.linker.linkQuery( 'title=Special:Log&logid=' + entry.logid ) )\n\t\t\t.attr( 'target', '_blank' )\n\t\t\t.text( mw.msg( 'globalwatchlist-log-entry' ) );\n\t\t$row.append( $logEntryLink )\n\t\t\t.append( ', ' );\n\t}\n\n\tconst $unwatchLink = $( '<a>' )\n\t\t.addClass( 'ext-globalwatchlist-watchunwatch' )\n\t\t.text( mw.msg( 'globalwatchlist-unwatch' ) )\n\t\t.on( 'click', () => {\n\t\t\tthat.changeWatched( entry.title, 'unwatch' );\n\t\t} );\n\t$row.append( $unwatchLink )\n\t\t.append( ', ' );\n\n\tconst $markAsReadLink = $( '<a>' )\n\t\t.addClass( 'ext-globalwatchlist-markpageseen' )\n\t\t.text( mw.msg( 'globalwatchlist-markpageseen' ) )\n\t\t.on( 'click', () => {\n\t\t\tthat.markPageAsSeen( entry.title );\n\t\t} );\n\t$row.append( $markAsReadLink );\n\n\t$row.append( ')' );\n\n\tconst $user = ( this.config.fastMode ? '' : entry.userDisplay );\n\tlet $comment = '';\n\tif ( entry.commentDisplay ) {\n\t\t// Need to process links in the parsed comments as raw HTML\n\t\t$comment = $( '<span>' ).html( entry.commentDisplay );\n\t}\n\tif ( $user !== '' || $comment !== '' ) {\n\t\t$row.append( ' (' )\n\t\t\t.append( $user )\n\t\t\t.append( $comment )\n\t\t\t.append( ')' );\n\t}\n\n\tif ( entry.tagsDisplay ) {\n\t\t// Need to process links in the parsed description as raw HTML\n\t\tconst $tags = $( '<em>' ).html( entry.tagsDisplay );\n\n\t\t$row.append( ' ' )\n\t\t\t.append( $tags );\n\t}\n\n\treturn $row;\n};\n/* end GlobalWatchlistSiteDisplay.prototype.makePageLink */\n\n/**\n * Create the output for this.$feedDiv, either for success (via renderWatchlist) or\n * failure (via renderApiFailure)\n *\n * @param {jQuery} $content Content to show\n */\nGlobalWatchlistSiteDisplay.prototype.actuallyRenderWatchlist = function ( $content ) {\n\tconst headerTemplate = mw.template.get(\n\t\t'ext.globalwatchlist.specialglobalwatchlist',\n\t\t'templates/siteRowHeader.mustache'\n\t);\n\tconst headerParams = {\n\t\t'special-watchlist-url': this.linker.linkPage( 'Special:Watchlist' ),\n\t\t'site-name': this.site,\n\t\t'special-edit-watchlist-url': this.linker.linkPage( 'Special:EditWatchlist' ),\n\t\t'edit-watchlist-msg': mw.msg( 'globalwatchlist-editwatchlist' )\n\t};\n\n\t// Get RTL/LTR direction for the site. We can't use String.prototype.startsWith, since\n\t// that is unavailable in IE11, and doesn't take multiple values anyway. Use\n\t// String.prototype.match with a list of the language codes that should be RTL\n\t// this.siteID is based on the URL form of the wiki, and we assume that wikis that are\n\t// meant to be RTL are in the form `⧼rtl language code⧽.*`, and any URL that does not\n\t// match this is for an LTR wiki. See T274602 and T274313\n\tconst isRTL = this.siteID.match(\n\t\t/^(ar|azb|ckb|dv|fa|glk|he|ks|lrc|mzn|nqo|pnb|ps|sd|ug|ur|yi)_/\n\t);\n\t// mw-content-ltr and -rtl classes are not enough to ensure that the text is formatted\n\t// in the correct direction, so add a manual direction attribute. See T287649\n\t// We still add those classes because they are also used by jQuery.makeCollapsible\n\t// to know if the collapse button should be on the right or left.\n\tthis.$feedDiv = $( '<div>' )\n\t\t.attr( 'id', 'ext-globalwatchlist-feed-site-' + this.siteID )\n\t\t.attr( 'dir', isRTL ? 'rtl' : 'ltr' )\n\t\t.addClass( 'ext-globalwatchlist-feed-site' )\n\t\t.addClass( isRTL ? 'mw-content-rtl' : 'mw-content-ltr' )\n\t\t.append(\n\t\t\theaderTemplate.render( headerParams ),\n\t\t\t$content\n\t\t);\n};\n\n/**\n * Alert on API failures\n */\nGlobalWatchlistSiteDisplay.prototype.renderApiFailure = function () {\n\tconst $siteContent = $( '<p>' ).text(\n\t\tmw.msg( 'globalwatchlist-fetch-site-failure' )\n\t);\n\n\tthis.actuallyRenderWatchlist( $siteContent );\n};\n\n/**\n * Display the watchlist\n *\n * @param {GlobalWatchlistEntryBase[]} summary What should be rendered\n */\nGlobalWatchlistSiteDisplay.prototype.renderWatchlist = function ( summary ) {\n\tconst $ul = $( '<ul>' ),\n\t\tthat = this;\n\tsummary.forEach( ( element ) => {\n\t\t$ul.append( that.makePageLink( element ) );\n\t} );\n\n\tconst markSeenButton = new OO.ui.ButtonInputWidget( {\n\t\tclasses: [ 'ext-globalwatchlist-feed-markseen' ],\n\t\tflags: [ 'destructive' ],\n\t\ticon: 'check',\n\t\tlabel: mw.msg( 'globalwatchlist-markseen' )\n\t} ).on( 'click', () => {\n\t\tthat.markAllAsSeen();\n\t} );\n\n\tconst $outputContent = $( '<div>' )\n\t\t.addClass( 'ext-globalwatchlist-site' )\n\t\t.append(\n\t\t\tmarkSeenButton.$element,\n\t\t\t$ul\n\t\t)\n\t\t.makeCollapsible();\n\tthis.actuallyRenderWatchlist( $outputContent );\n};\n/* end GlobalWatchlistSiteDisplay.prototype.renderWatchlist */\n\n/**\n * Update display after marking a site as seen\n */\nGlobalWatchlistSiteDisplay.prototype.afterMarkAllAsSeen = function () {\n\tthis.debug( 'markSiteAsSeen - hiding site' );\n\tif ( this.$feedDiv ) {\n\t\t// Don't call .children() on the default empty string, T275078\n\t\t$( this.$feedDiv.children()[ 1 ] ).hide();\n\t}\n\n\t// FIXME\n\t// GlobalWatchlist.watchlists.checkChangesShown( true );\n};\n/* end GlobalWatchlistSiteDisplay.prototype.afterMarkAllAsSeen */\n\nGlobalWatchlistSiteBase.prototype.afterMarkPageAsSeen = function ( pageTitle ) {\n\tthis.debug( 'markPageAsSeen - hiding page' );\n\tif ( this.$feedDiv ) {\n\t\tconst dataTitle = encodeURIComponent( pageTitle );\n\t\t$( this.$feedDiv.find( '[data-title=\"' + dataTitle + '\"]' ) ).hide();\n\t}\n};\n\n/**\n * Update entry click handlers, text, and strikethrough for a specific title\n *\n * @param {string} pageTitle Title of the page that was unwatched/rewatched.\n * @param {boolean} unwatched Whether the page was unwatched\n */\nGlobalWatchlistSiteDisplay.prototype.processUpdateWatched = function ( pageTitle, unwatched ) {\n\tthis.debug(\n\t\t'Processing after ' + ( unwatched ? 'unwatching' : 'rewatching' ) + ': ' + pageTitle\n\t);\n\n\tconst encodedSite = encodeURIComponent( this.siteID );\n\tconst encodedTitle = encodeURIComponent( pageTitle );\n\tconst $entries = $( 'li[data-site=\"' + encodedSite + '\"][data-title=\"' + encodedTitle + '\"]' );\n\t$entries[ unwatched ? 'addClass' : 'removeClass' ]( 'ext-globalwatchlist-strike' );\n\n\t$entries.children( '.ext-globalwatchlist-expiry-icon' ).remove();\n\n\tconst $links = $entries.children( 'a.ext-globalwatchlist-watchunwatch' );\n\tconst newText = mw.msg( unwatched ? 'globalwatchlist-rewatch' : 'globalwatchlist-unwatch' );\n\tconst that = this;\n\n\t$links.each( function () {\n\t\t$( this ).off( 'click' );\n\t\t$( this ).on( 'click', () => {\n\t\t\tthat.changeWatched( pageTitle, unwatched ? 'watch' : 'unwatch' );\n\t\t} );\n\t\t$( this ).text( newText );\n\t} );\n};\n/* end GlobalWatchlistSiteDisplay.prototype.processUpdateWatched */\n\nmodule.exports = GlobalWatchlistSiteDisplay;\n","usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]},{"ruleId":"max-len","replacedBy":[]},{"ruleId":"arrow-parens","replacedBy":[]},{"ruleId":"arrow-spacing","replacedBy":[]},{"ruleId":"lines-between-class-members","replacedBy":[]},{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"template-curly-spacing","replacedBy":[]},{"ruleId":"implicit-arrow-linebreak","replacedBy":[]},{"ruleId":"array-bracket-spacing","replacedBy":[]},{"ruleId":"block-spacing","replacedBy":[]},{"ruleId":"brace-style","replacedBy":[]},{"ruleId":"comma-dangle","replacedBy":[]},{"ruleId":"comma-spacing","replacedBy":[]},{"ruleId":"comma-style","replacedBy":[]},{"ruleId":"computed-property-spacing","replacedBy":[]},{"ruleId":"dot-location","replacedBy":[]},{"ruleId":"eol-last","replacedBy":[]},{"ruleId":"func-call-spacing","replacedBy":[]},{"ruleId":"indent","replacedBy":[]},{"ruleId":"key-spacing","replacedBy":[]},{"ruleId":"keyword-spacing","replacedBy":[]},{"ruleId":"linebreak-style","replacedBy":[]},{"ruleId":"max-statements-per-line","replacedBy":[]},{"ruleId":"new-parens","replacedBy":[]},{"ruleId":"no-floating-decimal","replacedBy":[]},{"ruleId":"no-multi-spaces","replacedBy":[]},{"ruleId":"no-multiple-empty-lines","replacedBy":[]},{"ruleId":"no-new-object","replacedBy":["no-object-constructor"]},{"ruleId":"no-tabs","replacedBy":[]},{"ruleId":"no-trailing-spaces","replacedBy":[]},{"ruleId":"no-whitespace-before-property","replacedBy":[]},{"ruleId":"object-curly-spacing","replacedBy":[]},{"ruleId":"operator-linebreak","replacedBy":[]},{"ruleId":"quote-props","replacedBy":[]},{"ruleId":"quotes","replacedBy":[]},{"ruleId":"semi","replacedBy":[]},{"ruleId":"semi-spacing","replacedBy":[]},{"ruleId":"semi-style","replacedBy":[]},{"ruleId":"space-before-blocks","replacedBy":[]},{"ruleId":"space-before-function-paren","replacedBy":[]},{"ruleId":"space-in-parens","replacedBy":[]},{"ruleId":"space-infix-ops","replacedBy":[]},{"ruleId":"space-unary-ops","replacedBy":[]},{"ruleId":"spaced-comment","replacedBy":[]},{"ruleId":"switch-colon-spacing","replacedBy":[]},{"ruleId":"wrap-iife","replacedBy":[]},{"ruleId":"no-extra-semi","replacedBy":[]},{"ruleId":"no-mixed-spaces-and-tabs","replacedBy":[]}]},{"filePath":"/src/repo/modules/specialglobalwatchlist/SpecialGlobalWatchlist.display.js","messages":[],"suppressedMessages":[{"ruleId":"no-console","severity":2,"message":"Unexpected console statement.","line":164,"column":5,"nodeType":"MemberExpression","messageId":"unexpected","endLine":164,"endColumn":16,"suggestions":[{"messageId":"removeConsole","data":{"propertyName":"log"},"fix":{"range":[5182,5203],"text":""},"desc":"Remove the console.log()."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-jquery/no-global-selector","severity":2,"message":"Avoid queries which search the entire DOM. Keep DOM nodes in memory where possible.","line":241,"column":3,"nodeType":"CallExpression","endLine":241,"endColumn":38,"suppressions":[{"kind":"directive","justification":""}]}],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]},{"ruleId":"max-len","replacedBy":[]},{"ruleId":"arrow-parens","replacedBy":[]},{"ruleId":"arrow-spacing","replacedBy":[]},{"ruleId":"lines-between-class-members","replacedBy":[]},{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"template-curly-spacing","replacedBy":[]},{"ruleId":"implicit-arrow-linebreak","replacedBy":[]},{"ruleId":"array-bracket-spacing","replacedBy":[]},{"ruleId":"block-spacing","replacedBy":[]},{"ruleId":"brace-style","replacedBy":[]},{"ruleId":"comma-dangle","replacedBy":[]},{"ruleId":"comma-spacing","replacedBy":[]},{"ruleId":"comma-style","replacedBy":[]},{"ruleId":"computed-property-spacing","replacedBy":[]},{"ruleId":"dot-location","replacedBy":[]},{"ruleId":"eol-last","replacedBy":[]},{"ruleId":"func-call-spacing","replacedBy":[]},{"ruleId":"indent","replacedBy":[]},{"ruleId":"key-spacing","replacedBy":[]},{"ruleId":"keyword-spacing","replacedBy":[]},{"ruleId":"linebreak-style","replacedBy":[]},{"ruleId":"max-statements-per-line","replacedBy":[]},{"ruleId":"new-parens","replacedBy":[]},{"ruleId":"no-floating-decimal","replacedBy":[]},{"ruleId":"no-multi-spaces","replacedBy":[]},{"ruleId":"no-multiple-empty-lines","replacedBy":[]},{"ruleId":"no-new-object","replacedBy":["no-object-constructor"]},{"ruleId":"no-tabs","replacedBy":[]},{"ruleId":"no-trailing-spaces","replacedBy":[]},{"ruleId":"no-whitespace-before-property","replacedBy":[]},{"ruleId":"object-curly-spacing","replacedBy":[]},{"ruleId":"operator-linebreak","replacedBy":[]},{"ruleId":"quote-props","replacedBy":[]},{"ruleId":"quotes","replacedBy":[]},{"ruleId":"semi","replacedBy":[]},{"ruleId":"semi-spacing","replacedBy":[]},{"ruleId":"semi-style","replacedBy":[]},{"ruleId":"space-before-blocks","replacedBy":[]},{"ruleId":"space-before-function-paren","replacedBy":[]},{"ruleId":"space-in-parens","replacedBy":[]},{"ruleId":"space-infix-ops","replacedBy":[]},{"ruleId":"space-unary-ops","replacedBy":[]},{"ruleId":"spaced-comment","replacedBy":[]},{"ruleId":"switch-colon-spacing","replacedBy":[]},{"ruleId":"wrap-iife","replacedBy":[]},{"ruleId":"no-extra-semi","replacedBy":[]},{"ruleId":"no-mixed-spaces-and-tabs","replacedBy":[]}]},{"filePath":"/src/repo/modules/specialglobalwatchlist/WatchlistUtils.js","messages":[{"ruleId":"jsdoc/no-undefined-types","severity":1,"message":"The type 'GlobalWatchlistLinker' is undefined.","line":7,"column":1,"nodeType":"Block","endLine":7,"endColumn":1},{"ruleId":"max-len","severity":1,"message":"This line has a length of 105. Maximum allowed is 100.","line":70,"column":1,"nodeType":"Program","messageId":"max","endLine":70,"endColumn":100},{"ruleId":"jsdoc/no-undefined-types","severity":1,"message":"The type 'GlobalWatchlistSiteBase' is undefined.","line":300,"column":1,"nodeType":"Block","endLine":300,"endColumn":1},{"ruleId":"jsdoc/no-undefined-types","severity":1,"message":"The type 'GlobalWatchlistEntryBase' is undefined.","line":312,"column":1,"nodeType":"Block","endLine":312,"endColumn":1},{"ruleId":"jsdoc/no-undefined-types","severity":1,"message":"The type 'GlobalWatchlistEntryEdits' is undefined.","line":329,"column":1,"nodeType":"Block","endLine":329,"endColumn":1},{"ruleId":"jsdoc/no-undefined-types","severity":1,"message":"The type 'GlobalWatchlistEntryLog' is undefined.","line":329,"column":1,"nodeType":"Block","endLine":329,"endColumn":1},{"ruleId":"jsdoc/no-undefined-types","severity":1,"message":"The type 'GlobalWatchlistEntryEdits' is undefined.","line":332,"column":1,"nodeType":"Block","endLine":332,"endColumn":1},{"ruleId":"jsdoc/no-undefined-types","severity":1,"message":"The type 'GlobalWatchlistEntryLog' is undefined.","line":332,"column":1,"nodeType":"Block","endLine":332,"endColumn":1},{"ruleId":"jsdoc/no-undefined-types","severity":1,"message":"The type 'GlobalWatchlistEntryBase' is undefined.","line":412,"column":1,"nodeType":"Block","endLine":412,"endColumn":1},{"ruleId":"jsdoc/no-undefined-types","severity":1,"message":"The type 'GlobalWatchlistEntryEdits' is undefined.","line":418,"column":1,"nodeType":"Block","endLine":418,"endColumn":1},{"ruleId":"jsdoc/no-undefined-types","severity":1,"message":"The type 'GlobalWatchlistEntryLog' is undefined.","line":418,"column":1,"nodeType":"Block","endLine":418,"endColumn":1}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":11,"fixableErrorCount":0,"fixableWarningCount":0,"source":"/**\n * General helper for converting the api response data into the form we use to display\n *\n * @class GlobalWatchlistWatchlistUtils\n * @constructor\n *\n * @param {GlobalWatchlistLinker} linker Linker for the relevant site, used for\n *    Links to user pages for registered users\n *    Links to contributions pages for anonymous users\n *    Converting links in edit summaries to not be relative to the current site\n */\nfunction GlobalWatchlistWatchlistUtils( linker ) {\n\tthis.linker = linker;\n}\n\n/**\n * Convert an array of two or more objects for specific edits to the same page to one object\n * with the information grouped\n *\n * @param {Array} edits Edits to merge\n * @return {Object} Merged information\n */\nGlobalWatchlistWatchlistUtils.prototype.mergePageEdits = function ( edits ) {\n\tconst mergedEditInfo = {};\n\n\t// No comments are shown for the grouped changes\n\tmergedEditInfo.comment = false;\n\n\tmergedEditInfo.bot = edits\n\t\t.map( ( edit ) => edit.bot )\n\t\t.reduce(\n\t\t\t// The combined edits are only tagged as bot if all of the edits where bot edits\n\t\t\t( bot1, bot2 ) => bot1 && bot2\n\t\t);\n\n\tmergedEditInfo.editCount = edits.length;\n\n\t// Should all be the same\n\tmergedEditInfo.expiry = edits[ 0 ].expiry;\n\n\tmergedEditInfo.fromRev = edits\n\t\t.map( ( edit ) => edit.old_revid )\n\t\t.reduce(\n\t\t\t// Get the lower rev id, corresponding to the older revision\n\t\t\t( edit1, edit2 ) => ( edit1 > edit2 ? edit2 : edit1 )\n\t\t);\n\n\tmergedEditInfo.minor = edits\n\t\t.map( ( edit ) => edit.minor )\n\t\t.reduce(\n\t\t\t// The combined edits are only tagged as minor if all of the edits where minor\n\t\t\t( minor1, minor2 ) => minor1 && minor2\n\t\t);\n\n\tmergedEditInfo.newPage = edits\n\t\t.map( ( edit ) => edit.newPage )\n\t\t.reduce(\n\t\t\t// Page creation is stored as a flag on edit entries, instead of as\n\t\t\t// its own type of entry. If any of the entries are creations, the\n\t\t\t// overall group was a page creation\n\t\t\t( newPage1, newPage2 ) => newPage1 || newPage2\n\t\t);\n\n\t// No tags\n\tmergedEditInfo.tags = [];\n\n\t// Per T262176, and like the core watchlist, use the latest timestamp\n\tmergedEditInfo.timestamp = edits\n\t\t.map( ( edit ) => edit.timestamp )\n\t\t.reduce( ( time1, time2 ) => ( ( new Date( time1 ) ) > ( new Date( time2 ) ) ? time1 : time2 ) );\n\n\t// When there are multiple edits grouped, the timestamp has a tooltip (title attribute)\n\t// explaining that its the timestamp of the latest change. If it's not set here, it's\n\t// null, and the display ignores null attribute values. See T286268 and\n\t// * https://api.jquery.com/attr/#attr-attributeName-value\n\tmergedEditInfo.timestampTitle = mw.msg( 'globalwatchlist-grouped-timestamp' );\n\n\tmergedEditInfo.toRev = edits\n\t\t.map( ( edit ) => edit.revid )\n\t\t.reduce(\n\t\t\t// Get the higher rev id, corresponding to the newer revision\n\t\t\t( edit1, edit2 ) => ( edit1 > edit2 ? edit1 : edit2 )\n\t\t);\n\n\treturn mergedEditInfo;\n};\n\n/**\n * Create links based on one-or-more editors\n *\n * editsByUser has the information for the links to create. It is a map in the following format:\n *\n *   ⧼user name/ip address⧽\n *       ->\n *   {\n *       editCount: ⧼count⧽\n *       anon: ⧼true/false⧽\n *   }\n *\n * For edits where the user was hidden, the key is: ##hidden##\n *\n * WARNING: This method returns RAW HTML that is the displayed. jQuery isn't used because we need\n *          to handle creating multiple links and returning the same way a single link does, since\n *          the caller doesn't know if the entry row is for a single edit or multiple edits grouped\n * For each entry in editsByUser:\n *  - if the user was hidden, the output is hard-coded as the core message `rev-deleted-user`\n *      wrapped in a span for styling\n *  - if the user wasn't hidden, a link is shown. The text for the link is the username, and\n *      the target is the user page (for users) or the contributions page (for anonymous editors),\n *      just like at Special:Watchlist. See RCCacheEntryFactory::getUserLink and Linker::userLink.\n *  - if the user made multiple edits, or multiple edits were made by hidden users, the number of\n *      edits is appended after the link, using the `ntimes` core message. This is only the case\n *      when grouping results by page. See EnhancedChangesList::recentChangesBlockGroup\n *\n * @param {Object} editsByUser Edit information\n * @return {string} the raw HTML to display\n */\nGlobalWatchlistWatchlistUtils.prototype.makeUserLinks = function ( editsByUser ) {\n\tconst users = Object.keys( editsByUser );\n\n\tconst allLinks = [];\n\tlet userLink = '',\n\t\tuserLinkBase = '',\n\t\tuserLinkURL = '';\n\n\tconst that = this;\n\tusers.forEach( ( userMessage ) => {\n\t\tif ( userMessage === '##hidden##' ) {\n\t\t\t// Edits by hidden user(s)\n\t\t\tuserLink = '<span class=\"history-deleted\">' +\n\t\t\t\tmw.message( 'rev-deleted-user' ).escaped() +\n\t\t\t\t'</span>';\n\t\t} else {\n\t\t\tuserLinkBase = editsByUser[ userMessage ].anon ?\n\t\t\t\t'Special:Contributions/' :\n\t\t\t\t'User:';\n\t\t\tuserLinkURL = that.linker.linkPage( userLinkBase + userMessage );\n\t\t\tuserLink = '<a href=\"' + userLinkURL + '\" target=\"_blank\">' + userMessage + '</a>';\n\t\t}\n\t\tif ( editsByUser[ userMessage ].editCount > 1 ) {\n\t\t\tuserLink = userLink + ' ' +\n\t\t\t\tmw.message( 'ntimes', editsByUser[ userMessage ].editCount ).escaped();\n\t\t}\n\t\tallLinks.push( userLink );\n\t} );\n\n\treturn allLinks.join( ', ' );\n};\n\n/**\n * Shortcut for makeUserLinks when there is only one user (single edits, ungrouped edits,\n * or log entries) and no need for showing a message for the edit count\n *\n * @param {string} userMessage either name or ip address\n * @param {boolean} isAnon Whether the link is for an anonymous user\n * @return {string}\n */\nGlobalWatchlistWatchlistUtils.prototype.makeSingleUserLink = function ( userMessage, isAnon ) {\n\tif ( userMessage === '' ) {\n\t\t// Didn't fetch due to fast mode\n\t\treturn '';\n\t}\n\n\tconst editsByUser = {};\n\teditsByUser[ userMessage ] = {\n\t\teditCount: 1,\n\t\tanon: isAnon\n\t};\n\n\treturn this.makeUserLinks( editsByUser );\n};\n\n/**\n * Convert edit info, including adding links to user pages / anonymous users' contributions and\n * grouping results by page when called for\n *\n * @param {Object} editInfo\n * @param {boolean} groupPage Whether to group results by page\n * @return {Array} Converted edits\n */\nGlobalWatchlistWatchlistUtils.prototype.convertEdits = function ( editInfo, groupPage ) {\n\tconst finalEdits = [];\n\n\tconst edits = [];\n\tfor ( const key in editInfo ) {\n\t\tedits.push( editInfo[ key ] );\n\t}\n\n\tconst that = this;\n\tedits.forEach( ( page ) => {\n\t\tconst pagebase = {\n\t\t\tentryType: 'edit',\n\t\t\tns: page.ns,\n\t\t\ttitle: page.title\n\t\t};\n\t\tif ( !groupPage || page.each.length === 1 ) {\n\t\t\tpage.each.forEach( ( entry ) => {\n\t\t\t\tfinalEdits.push( Object.assign( {}, pagebase, {\n\t\t\t\t\tbot: entry.bot,\n\t\t\t\t\tcomment: entry.parsedcomment,\n\t\t\t\t\teditCount: 1,\n\t\t\t\t\texpiry: entry.expiry,\n\t\t\t\t\tfromRev: entry.old_revid,\n\t\t\t\t\tminor: entry.minor,\n\t\t\t\t\tnewPage: entry.newPage,\n\t\t\t\t\ttags: entry.tags,\n\t\t\t\t\ttimestamp: entry.timestamp,\n\t\t\t\t\ttimestampTitle: null,\n\t\t\t\t\ttoRev: entry.revid,\n\t\t\t\t\tuserDisplay: that.makeSingleUserLink(\n\t\t\t\t\t\tentry.user,\n\t\t\t\t\t\tentry.anon\n\t\t\t\t\t)\n\t\t\t\t} ) );\n\t\t\t} );\n\t\t} else {\n\t\t\tconst mergedEditInfo = that.mergePageEdits( page.each );\n\n\t\t\t// Map of edit counts\n\t\t\t// ⧼user name/ip address⧽\n\t\t\t//     ->\n\t\t\t// {\n\t\t\t//     editCount: ⧼count⧽\n\t\t\t//     anon: ⧼true/false⧽\n\t\t\t// }\n\t\t\t//\n\t\t\t// For edits where the user was hidden, the key is: ##hidden##\n\t\t\tconst editsByUser = {};\n\n\t\t\tpage.each.forEach( ( specificEdit ) => {\n\t\t\t\tif ( !( specificEdit.user in editsByUser ) ) {\n\t\t\t\t\teditsByUser[ specificEdit.user ] = {\n\t\t\t\t\t\teditCount: 0,\n\t\t\t\t\t\tanon: specificEdit.anon\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\teditsByUser[ specificEdit.user ].editCount =\n\t\t\t\t\teditsByUser[ specificEdit.user ].editCount + 1;\n\t\t\t} );\n\n\t\t\tmergedEditInfo.userDisplay = that.makeUserLinks( editsByUser );\n\n\t\t\tfinalEdits.push( Object.assign( {}, pagebase, mergedEditInfo ) );\n\t\t}\n\t} );\n\n\treturn finalEdits;\n};\n\n/**\n * @param {Array} entries Entries in the format returned by the api\n * @return {Array} Entries in a \"normalized\" format\n */\nGlobalWatchlistWatchlistUtils.prototype.normalizeEntries = function ( entries ) {\n\tentries.forEach( ( entry ) => {\n\t\tif ( entry.userhidden ) {\n\t\t\t// # is in wgLegalTitleChars so no conflict\n\t\t\tentry.user = '##hidden##';\n\t\t} else if ( typeof entry.user === 'undefined' ) {\n\t\t\t// Not fetching, fast mode\n\t\t\tentry.user = '';\n\t\t}\n\n\t\tif ( typeof entry.parsedcomment === 'undefined' ) {\n\t\t\tentry.parsedcomment = '';\n\t\t}\n\t\tif ( typeof entry.tags === 'undefined' ) {\n\t\t\tentry.tags = [];\n\t\t}\n\t\tif ( entry.type === 'new' ) {\n\t\t\t// Treat page creations as edits with a flag, so that they can be\n\t\t\t// grouped together when needed\n\t\t\tentry.type = 'edit';\n\t\t\tentry.newPage = true;\n\t\t} else {\n\t\t\tentry.newPage = false;\n\t\t}\n\n\t\tif ( typeof entry.timestamp === 'undefined' ) {\n\t\t\t// Not fetched in fast mode\n\t\t\tentry.timestamp = false;\n\t\t}\n\t} );\n\treturn entries;\n};\n\n/**\n * Do various cleanup of entries that goes after merging grouped edits and splitting\n * edits and log entries. This is where we will convert the plain objects to the new\n * classes in T288385.\n *\n * - Convert raw expiration strings into the tooltip to be shown.\n * - Add a \"flags\" property to each entry that will either be `false` or a string with the flags\n *     to show next to the entry (new page, minor edit, bot action).\n * - Truncate the timestamp to only show details down to the minute, see T262176. This needs to\n *     be done *after* the sorting of edits and log entries by timestamp, which should be done\n *     using the full untruncated version, see T286977.\n * - Create the HTML to show for the tags associated with an entry. For each tag, if there is\n *     a display configured onwiki, that is shown, otherwise its just the name. See\n *     {@link GlobalWatchlistSiteBase#getTagList SiteBase#getTagList} for where the info is\n *     retrieved.\n * - Set the comment display to include the updated links in edit summaries/log entries.\n *     In fast mode, or for grouped changes, there is no comment display. The commentDisplay\n *     set here is treated as raw html by the display. We use the `parsedcomment` result from\n *     the api, and MediaWiki core takes care of escaping.\n *\n * @param {Array} entries Entries to update\n * @param {Object} tagsInfo Keys are tag names, values are the html to display (either the\n *    display text with local links updated, or just the name)\n * @param {Function} EntryClass either {@link GlobalWatchlistEntryEdits} or\n *    {@link GlobalWatchlistEntryLog} to convert entries to\n * @return {GlobalWatchlistEntryBase[]} updated entries, each entry converted to either\n *    {@link GlobalWatchlistEntryEdits} or {@link GlobalWatchlistEntryLog}\n */\nGlobalWatchlistWatchlistUtils.prototype.getFinalEntries = function (\n\tentries,\n\ttagsInfo,\n\tEntryClass\n) {\n\t// Watchlist expiry\n\tlet expirationDate, daysLeft;\n\n\t// New page / minor / bot flags\n\t// Optimization: only fetch the messages a single time\n\t// Order to match the display of core\n\tconst newPageFlag = mw.msg( 'newpageletter' );\n\tconst minorFlag = mw.msg( 'minoreditletter' );\n\tconst botFlag = mw.msg( 'boteditletter' );\n\tlet entryFlags;\n\n\t// Tags display\n\tconst noTagsDisplay = Object.keys( tagsInfo ).length === 0;\n\tlet tagDescriptions, tagsWithLabel;\n\n\t// Comment display\n\tconst that = this;\n\n\treturn entries.map( ( entry ) => {\n\t\t// Watchlist expiry\n\t\tif ( entry.expiry ) {\n\t\t\texpirationDate = new Date( entry.expiry );\n\t\t\tdaysLeft = Math.ceil( ( expirationDate - Date.now() ) / 1000 / 86400 ) + 0;\n\t\t\tif ( daysLeft === 0 ) {\n\t\t\t\tentry.expiry = mw.msg( 'watchlist-expiring-hours-full-text' );\n\t\t\t} else {\n\t\t\t\tentry.expiry = mw.msg( 'watchlist-expiring-days-full-text', daysLeft );\n\t\t\t}\n\t\t}\n\n\t\t// New page / minor / bot flags\n\t\tentryFlags = '';\n\t\tif ( entry.newPage === true ) {\n\t\t\tentryFlags += newPageFlag;\n\t\t}\n\t\tif ( entry.minor ) {\n\t\t\tentryFlags += minorFlag;\n\t\t}\n\t\tif ( entry.bot ) {\n\t\t\tentryFlags += botFlag;\n\t\t}\n\t\tif ( entryFlags === '' ) {\n\t\t\tentry.flags = false;\n\t\t} else {\n\t\t\tentry.flags = entryFlags;\n\t\t}\n\n\t\t// Timestamp normalization\n\t\t// We set the timestamp to false in normalizeEntries if its not available\n\t\tif ( entry.timestamp ) {\n\t\t\t// Per T262176, display as\n\t\t\t// YYYY-MM-DD HH:MM\n\t\t\tentry.timestamp = entry.timestamp.replace( /T(\\d+:\\d+):\\d+Z/, ' $1' );\n\t\t}\n\n\t\t// Tags display\n\t\t// In fast mode no tag info was retrieved, so tagsInfo should be an empty object\n\t\t// and none of the entries should have tags that need displaying. We still need to\n\t\t// set the `tagsDisplay` property for each entry though, the display code checks it.\n\t\tif ( noTagsDisplay || entry.tags.length === 0 ) {\n\t\t\tentry.tagsDisplay = false;\n\t\t} else {\n\t\t\t// This is the actual building of the display\n\t\t\ttagDescriptions = entry.tags.map(\n\t\t\t\t( tagName ) => tagsInfo[ tagName ]\n\t\t\t).join( ', ' );\n\t\t\ttagsWithLabel = mw.msg( 'globalwatchlist-tags', entry.tags.length, tagDescriptions );\n\t\t\tentry.tagsDisplay = mw.msg( 'parentheses', tagsWithLabel );\n\t\t}\n\n\t\t// Comment display\n\t\tif ( entry.comment && entry.comment !== '' ) {\n\t\t\tentry.commentDisplay = ': ' + that.linker.fixLocalLinks( entry.comment );\n\t\t} else {\n\t\t\tentry.commentDisplay = false;\n\t\t}\n\n\t\t// Convert to relevant entry class, T288385\n\t\treturn new EntryClass( entry );\n\t} );\n};\n\n/**\n * Convert result from the API to format used by this extension\n *\n * This is the entry point for the JavaScript controlling Special:GlobalWatchlist and the\n * display of each site's changes.\n *\n * @param {Array} entries Entries to convert\n * @param {boolean} groupPage Whether to group results by page\n * @param {Object} tagsInfo See details at\n *    {@link GlobalWatchlistWatchlistUtils#getFinalEntries #getFinalEntries}\n * @return {GlobalWatchlistEntryBase[]} summary of changes, each change converted to either\n *    {@link GlobalWatchlistEntryEdits} or {@link GlobalWatchlistEntryLog}\n */\nGlobalWatchlistWatchlistUtils.prototype.rawToSummary = function ( entries, groupPage, tagsInfo ) {\n\tlet logEntries = [];\n\tconst edits = {},\n\t\tcleanedEntries = this.normalizeEntries( entries );\n\n\tconst that = this;\n\tcleanedEntries.forEach( ( entry ) => {\n\t\tif ( entry.type === 'edit' ) {\n\t\t\t// Also includes new pages\n\t\t\tif ( typeof edits[ entry.pageid ] === 'undefined' ) {\n\t\t\t\tedits[ entry.pageid ] = {\n\t\t\t\t\teach: [ entry ],\n\t\t\t\t\tns: entry.ns,\n\t\t\t\t\ttitle: entry.title\n\t\t\t\t};\n\t\t\t} else {\n\t\t\t\tedits[ entry.pageid ].each.push( entry );\n\t\t\t}\n\t\t} else if ( entry.type === 'log' ) {\n\t\t\tlogEntries.push( {\n\t\t\t\tbot: entry.bot,\n\t\t\t\tcomment: entry.parsedcomment,\n\t\t\t\tentryType: entry.type,\n\t\t\t\texpiry: entry.expiry,\n\t\t\t\tns: entry.ns,\n\t\t\t\ttags: entry.tags,\n\t\t\t\ttimestamp: entry.timestamp,\n\t\t\t\ttimestampTitle: null,\n\t\t\t\ttitle: entry.title,\n\t\t\t\tlogaction: entry.logaction,\n\t\t\t\tlogid: entry.logid,\n\t\t\t\tlogtype: entry.logtype,\n\t\t\t\tuserDisplay: that.makeSingleUserLink(\n\t\t\t\t\tentry.user,\n\t\t\t\t\tentry.anon\n\t\t\t\t)\n\t\t\t} );\n\t\t}\n\t} );\n\n\tlet convertedEdits = this.convertEdits( edits, groupPage );\n\n\t// Sorting: we want the newest edits and log entries at the top. But, the api\n\t// only tells us what minute the edit/log entry was made. So, if the timestamps\n\t// are the same, go by the revid and logid - we assume that newer edits have higher\n\t// revision ids, and newer log entries have higher log ids. Sort functions should\n\t// return negative if the order should not change, and positive if they should.\n\t// See T275303\n\tconvertedEdits.sort(\n\t\t( editA, editB ) => {\n\t\t\tif ( editA.timestamp !== editB.timestamp ) {\n\t\t\t\treturn ( ( new Date( editA.timestamp ) ) > ( new Date( editB.timestamp ) ) ?\n\t\t\t\t\t-1 :\n\t\t\t\t\t1\n\t\t\t\t);\n\t\t\t}\n\t\t\t// fallback to revision ids\n\t\t\treturn ( ( editA.toRev > editB.toRev ) ? -1 : 1 );\n\t\t}\n\t);\n\tlogEntries.sort(\n\t\t( logA, logB ) => {\n\t\t\tif ( logA.timestamp !== logB.timestamp ) {\n\t\t\t\treturn ( ( new Date( logA.timestamp ) ) > ( new Date( logB.timestamp ) ) ?\n\t\t\t\t\t-1 :\n\t\t\t\t\t1\n\t\t\t\t);\n\t\t\t}\n\t\t\t// fallback to log ids\n\t\t\treturn ( ( logA.logid > logB.logid ) ? -1 : 1 );\n\t\t}\n\t);\n\n\tconst GlobalWatchlistEntryEdits = require( './EntryEdits.js' );\n\tconvertedEdits = this.getFinalEntries( convertedEdits, tagsInfo, GlobalWatchlistEntryEdits );\n\n\tconst GlobalWatchlistEntryLog = require( './EntryLog.js' );\n\tlogEntries = this.getFinalEntries( logEntries, tagsInfo, GlobalWatchlistEntryLog );\n\n\treturn convertedEdits.concat( logEntries );\n};\n\nmodule.exports = GlobalWatchlistWatchlistUtils;\n","usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]},{"ruleId":"max-len","replacedBy":[]},{"ruleId":"arrow-parens","replacedBy":[]},{"ruleId":"arrow-spacing","replacedBy":[]},{"ruleId":"lines-between-class-members","replacedBy":[]},{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"template-curly-spacing","replacedBy":[]},{"ruleId":"implicit-arrow-linebreak","replacedBy":[]},{"ruleId":"array-bracket-spacing","replacedBy":[]},{"ruleId":"block-spacing","replacedBy":[]},{"ruleId":"brace-style","replacedBy":[]},{"ruleId":"comma-dangle","replacedBy":[]},{"ruleId":"comma-spacing","replacedBy":[]},{"ruleId":"comma-style","replacedBy":[]},{"ruleId":"computed-property-spacing","replacedBy":[]},{"ruleId":"dot-location","replacedBy":[]},{"ruleId":"eol-last","replacedBy":[]},{"ruleId":"func-call-spacing","replacedBy":[]},{"ruleId":"indent","replacedBy":[]},{"ruleId":"key-spacing","replacedBy":[]},{"ruleId":"keyword-spacing","replacedBy":[]},{"ruleId":"linebreak-style","replacedBy":[]},{"ruleId":"max-statements-per-line","replacedBy":[]},{"ruleId":"new-parens","replacedBy":[]},{"ruleId":"no-floating-decimal","replacedBy":[]},{"ruleId":"no-multi-spaces","replacedBy":[]},{"ruleId":"no-multiple-empty-lines","replacedBy":[]},{"ruleId":"no-new-object","replacedBy":["no-object-constructor"]},{"ruleId":"no-tabs","replacedBy":[]},{"ruleId":"no-trailing-spaces","replacedBy":[]},{"ruleId":"no-whitespace-before-property","replacedBy":[]},{"ruleId":"object-curly-spacing","replacedBy":[]},{"ruleId":"operator-linebreak","replacedBy":[]},{"ruleId":"quote-props","replacedBy":[]},{"ruleId":"quotes","replacedBy":[]},{"ruleId":"semi","replacedBy":[]},{"ruleId":"semi-spacing","replacedBy":[]},{"ruleId":"semi-style","replacedBy":[]},{"ruleId":"space-before-blocks","replacedBy":[]},{"ruleId":"space-before-function-paren","replacedBy":[]},{"ruleId":"space-in-parens","replacedBy":[]},{"ruleId":"space-infix-ops","replacedBy":[]},{"ruleId":"space-unary-ops","replacedBy":[]},{"ruleId":"spaced-comment","replacedBy":[]},{"ruleId":"switch-colon-spacing","replacedBy":[]},{"ruleId":"wrap-iife","replacedBy":[]},{"ruleId":"no-extra-semi","replacedBy":[]},{"ruleId":"no-mixed-spaces-and-tabs","replacedBy":[]}]},{"filePath":"/src/repo/modules/specialglobalwatchlist/WikibaseHandler.js","messages":[{"ruleId":"jsdoc/no-undefined-types","severity":1,"message":"The type 'GlobalWatchlistDebugger' is undefined.","line":9,"column":1,"nodeType":"Block","endLine":9,"endColumn":1},{"ruleId":"jsdoc/no-undefined-types","severity":1,"message":"The type 'GlobalWatchlistEntryBase' is undefined.","line":180,"column":1,"nodeType":"Block","endLine":180,"endColumn":1},{"ruleId":"jsdoc/no-undefined-types","severity":1,"message":"The type 'GlobalWatchlistEntryBase' is undefined.","line":214,"column":1,"nodeType":"Block","endLine":214,"endColumn":1}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":3,"fixableErrorCount":0,"fixableWarningCount":0,"source":"/**\n * Handle getting labels for wikibase items\n *\n * Caller is responsible for determining if this should be used\n *\n * @class GlobalWatchlistWikibaseHandler\n * @constructor\n *\n * @param {GlobalWatchlistDebugger} globalWatchlistDebug Debugger instance to log to\n * @param {mw.ForeignApi} api Instance of mw.ForeignApi to use\n * @param {string} userLang language to fetch labels in\n */\nfunction GlobalWatchlistWikibaseHandler( globalWatchlistDebug, api, userLang ) {\n\t// Logger to send debug info to\n\tthis.debugLogger = globalWatchlistDebug;\n\n\t// api for the wikibase repo site\n\tthis.api = api;\n\n\t// Language to fetch the labels in\n\tthis.userLang = userLang;\n}\n\n/**\n * Shortcut for sending information to the debug logger\n *\n * @param {string} msg Message for debug entry\n * @param {string} [extraInfo] Extra information for the debug entry\n */\nGlobalWatchlistWikibaseHandler.prototype.debug = function ( msg, extraInfo ) {\n\tthis.debugLogger.info( 'wikibase:' + msg, extraInfo );\n};\n\n/**\n * Fetch the labels for all of the ids given\n *\n * Since the api is usually limited to 50 ids at a time, called recursively\n * until all ids are processed. No special handling for users with `apihighlimits`,\n * still only fetch 50 at a time\n *\n * The returned promise resolves to an object with each of the entity ids being a key\n * to the relevant information. To help visualize, below is a partial result of the\n * wbgetentities query[1] performed on wikidata for Q5, P10, and L2, with the exception\n * that the `forms` and `senses` for L2 are not included.\n *\n * ```json\n *    {\n *        \"Q5\": {\n *            \"type\": \"item\",\n *            \"id\": \"Q5\",\n *            \"labels\": {\n *                \"en\": {\n *                    \"language\": \"en\",\n *                    \"value\": \"human\"\n *                }\n *            }\n *        },\n *        \"P10\": {\n *            \"type\": \"property\",\n *            \"datatype\": \"commonsMedia\",\n *            \"id\": \"P10\",\n *            \"labels\": {\n *                \"en\": {\n *                    \"language\": \"en\",\n *                    \"value\": \"video\"\n *                }\n *            }\n *        },\n *        \"L2\": {\n *            \"type\": \"lexeme\",\n *            \"id\": \"L2\",\n *            \"lemmas\": {\n *                \"en\": {\n *                    \"language\": \"en\",\n *                    \"value\": \"first\"\n *                }\n *            },\n *            \"lexicalCategory\": \"Q1084\",\n *            \"language\": \"Q1860\",\n *            \"forms\": [ ... ],\n *            \"senses\": [ ... ]\n *        }\n *    }\n * ```\n *\n *\n * [1] See:\n * https://www.wikidata.org/w/api.php?action=wbgetentities&ids=Q5|P10|L2&languages=en&props=labels&formatversion=2\n *\n * @see {@link GlobalWatchlistWikibaseHandler#cleanupRawLabels #cleanupRawLabels} for converting\n * to a more usable form\n *\n * @param {Array} entityIds The ids to get labels for\n * @return {Promise} Promise of api result\n */\nGlobalWatchlistWikibaseHandler.prototype.getRawLabels = function ( entityIds ) {\n\tconst that = this;\n\n\treturn new Promise( ( resolve ) => {\n\t\tconst query = {\n\t\t\taction: 'wbgetentities',\n\t\t\tformatversion: 2,\n\t\t\tids: entityIds.slice( 0, 50 ),\n\t\t\tlanguages: that.userLang,\n\t\t\tprops: 'labels'\n\t\t};\n\t\tthat.api.get( query ).then( ( response ) => {\n\t\t\tthat.debug( 'getRawLabels - api response', response );\n\t\t\tconst labels = response.entities;\n\t\t\tif ( entityIds.length > 50 ) {\n\t\t\t\t// Recursive processing\n\t\t\t\tthat.getRawLabels( entityIds.slice( 50 ) ).then( ( extraLabels ) => {\n\t\t\t\t\tconst bothLabels = Object.assign( {}, labels, extraLabels );\n\t\t\t\t\tthat.debug( 'getRawLabels - bothLabels', bothLabels );\n\t\t\t\t\tresolve( bothLabels );\n\t\t\t\t} );\n\t\t\t} else {\n\t\t\t\t// No need for further processing, either had less than 50 to\n\t\t\t\t// begin with or this was the final recursive call\n\t\t\t\tthat.debug( 'getRawLabels - last', labels );\n\t\t\t\tresolve( labels );\n\t\t\t}\n\t\t} );\n\t} );\n};\n\n/**\n * Convert the messy object returned from getRawLabels to something clearer\n *\n * Resulting object has the following form (see documentation in\n * {@link GlobalWatchlistWikibaseHandler#getRawLabels #getRawLabels} for the original)\n *\n *```json\n *    {\n *        \"Q5\": \"human\",\n *        \"P10\": \"video\",\n *        \"L2\": \"first\"\n *    }\n *```\n *\n * @param {Object} rawLabels Labels in the format returns from the api\n * @return {Object} Labels in a more usable format\n */\nGlobalWatchlistWikibaseHandler.prototype.cleanupRawLabels = function ( rawLabels ) {\n\tthis.debug( 'cleanupRawLabels - starting (raw)', rawLabels );\n\n\tconst cleanedLabels = {};\n\tconst entityIds = Object.keys( rawLabels );\n\tconst that = this;\n\tlet entityInfo,\n\t\tlabelKey;\n\n\tentityIds.forEach( ( entityId ) => {\n\t\t// Object.keys -> known to be a valid key\n\t\tentityInfo = rawLabels[ entityId ];\n\n\t\t// Lexemes have `lemmas`, items and properties have `labels`\n\t\tif ( entityInfo.type === 'lexeme' ) {\n\t\t\tlabelKey = 'lemmas';\n\t\t} else {\n\t\t\tlabelKey = 'labels';\n\t\t}\n\n\t\tif ( entityInfo[ labelKey ] &&\n\t\t\tentityInfo[ labelKey ][ that.userLang ] &&\n\t\t\tentityInfo[ labelKey ][ that.userLang ].value\n\t\t) {\n\t\t\tcleanedLabels[ entityId ] = entityInfo[ labelKey ][ that.userLang ].value;\n\t\t}\n\t} );\n\tthis.debug( 'cleanupRawLabels - ending (clean)', cleanedLabels );\n\n\treturn cleanedLabels;\n};\n\n/**\n * Set entities' titleMsg (title without the `Property:` or `Lexeme:` prefix) and\n * get a list of the ids to fetch in the form of Q1/P2/L3\n *\n * @param {GlobalWatchlistEntryBase[]} entries Original summary entries\n * @return {Object} updated entries and ids\n */\nGlobalWatchlistWikibaseHandler.prototype.getEntityIds = function ( entries ) {\n\tconst ids = [];\n\n\tentries.forEach( ( entry ) => {\n\t\tentry.titleMsg = entry.title.replace(\n\t\t\t/^(?:Property|Lexeme):/,\n\t\t\t''\n\t\t);\n\n\t\tif ( entry.ns === 0 || entry.title !== entry.titleMsg ) {\n\t\t\t// Either:\n\t\t\t// * main namespace, title doesn't have a prefix to remove\n\t\t\t// * property/lexeme, prefix removed\n\t\t\t// Add the Q/P/L id, without duplication\n\t\t\tif ( !ids.includes( entry.titleMsg ) ) {\n\t\t\t\tids.push( entry.titleMsg );\n\t\t\t}\n\t\t}\n\t} );\n\n\treturn {\n\t\tentries: entries,\n\t\tids: ids\n\t};\n};\n\n/**\n * Entry point - alter the entities given to have titleMsg that reflects the labels\n *\n * Promise resolves to the summary entries with updated info\n *\n * @param {GlobalWatchlistEntryBase[]} summaryEntries Original summary, entries have titleMsg as\n *   just the plain title (Q1, P2, L3, etc.)\n * @return {Promise} Promise of updated summary, with labels\n */\nGlobalWatchlistWikibaseHandler.prototype.addWikibaseLabels = function ( summaryEntries ) {\n\tconst that = this;\n\n\treturn new Promise( ( resolve ) => {\n\t\tconst extractedInfo = that.getEntityIds( summaryEntries );\n\t\tthat.debug( 'addLabels - extractedInfo', extractedInfo );\n\n\t\tconst updatedEntries = extractedInfo.entries;\n\t\tconst entityIds = extractedInfo.ids;\n\n\t\tif ( entityIds.length === 0 ) {\n\t\t\t// Nothing to fetch\n\t\t\tresolve( updatedEntries );\n\t\t\treturn;\n\t\t}\n\n\t\tthat.getRawLabels( entityIds ).then( ( rawLabels ) => {\n\t\t\tconst cleanedLabels = that.cleanupRawLabels( rawLabels );\n\n\t\t\tupdatedEntries.forEach( ( entry ) => {\n\t\t\t\tif ( cleanedLabels[ entry.titleMsg ] ) {\n\t\t\t\t\tentry.titleMsg += ' ' + mw.msg( 'parentheses', cleanedLabels[ entry.titleMsg ] );\n\t\t\t\t}\n\t\t\t} );\n\n\t\t\tresolve( updatedEntries );\n\t\t} );\n\t} );\n};\n\nmodule.exports = GlobalWatchlistWikibaseHandler;\n","usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]},{"ruleId":"max-len","replacedBy":[]},{"ruleId":"arrow-parens","replacedBy":[]},{"ruleId":"arrow-spacing","replacedBy":[]},{"ruleId":"lines-between-class-members","replacedBy":[]},{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"template-curly-spacing","replacedBy":[]},{"ruleId":"implicit-arrow-linebreak","replacedBy":[]},{"ruleId":"array-bracket-spacing","replacedBy":[]},{"ruleId":"block-spacing","replacedBy":[]},{"ruleId":"brace-style","replacedBy":[]},{"ruleId":"comma-dangle","replacedBy":[]},{"ruleId":"comma-spacing","replacedBy":[]},{"ruleId":"comma-style","replacedBy":[]},{"ruleId":"computed-property-spacing","replacedBy":[]},{"ruleId":"dot-location","replacedBy":[]},{"ruleId":"eol-last","replacedBy":[]},{"ruleId":"func-call-spacing","replacedBy":[]},{"ruleId":"indent","replacedBy":[]},{"ruleId":"key-spacing","replacedBy":[]},{"ruleId":"keyword-spacing","replacedBy":[]},{"ruleId":"linebreak-style","replacedBy":[]},{"ruleId":"max-statements-per-line","replacedBy":[]},{"ruleId":"new-parens","replacedBy":[]},{"ruleId":"no-floating-decimal","replacedBy":[]},{"ruleId":"no-multi-spaces","replacedBy":[]},{"ruleId":"no-multiple-empty-lines","replacedBy":[]},{"ruleId":"no-new-object","replacedBy":["no-object-constructor"]},{"ruleId":"no-tabs","replacedBy":[]},{"ruleId":"no-trailing-spaces","replacedBy":[]},{"ruleId":"no-whitespace-before-property","replacedBy":[]},{"ruleId":"object-curly-spacing","replacedBy":[]},{"ruleId":"operator-linebreak","replacedBy":[]},{"ruleId":"quote-props","replacedBy":[]},{"ruleId":"quotes","replacedBy":[]},{"ruleId":"semi","replacedBy":[]},{"ruleId":"semi-spacing","replacedBy":[]},{"ruleId":"semi-style","replacedBy":[]},{"ruleId":"space-before-blocks","replacedBy":[]},{"ruleId":"space-before-function-paren","replacedBy":[]},{"ruleId":"space-in-parens","replacedBy":[]},{"ruleId":"space-infix-ops","replacedBy":[]},{"ruleId":"space-unary-ops","replacedBy":[]},{"ruleId":"spaced-comment","replacedBy":[]},{"ruleId":"switch-colon-spacing","replacedBy":[]},{"ruleId":"wrap-iife","replacedBy":[]},{"ruleId":"no-extra-semi","replacedBy":[]},{"ruleId":"no-mixed-spaces-and-tabs","replacedBy":[]}]},{"filePath":"/src/repo/modules/specialglobalwatchlist/getSettings.js","messages":[{"ruleId":"jsdoc/no-undefined-types","severity":1,"message":"The type 'GlobalWatchlistDebugger' is undefined.","line":24,"column":1,"nodeType":"Block","endLine":24,"endColumn":1}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"/*\n * Retrieve current user settings to use, or the defaults\n *\n * Used for both versions of Special:GlobalWatchlist\n */\n/**\n * @private\n * @param {number} setting Current stored level\n * @param {string} flag\n * @return {string}\n */\nfunction getQueryFlag( setting, flag ) {\n\tif ( setting === 1 ) {\n\t\treturn ( '|' + flag );\n\t}\n\tif ( setting === 2 ) {\n\t\treturn ( '|!' + flag );\n\t}\n\treturn '';\n}\n\n/**\n * @private\n * @param {GlobalWatchlistDebugger} globalWatchlistDebug\n * @return {Object}\n */\nfunction globalWatchlistGetSettings( globalWatchlistDebug ) {\n\t// Note: this must be the same key as SettingsManager::PREFERENCE_NAME\n\tconst userOptions = mw.user.options.get( 'global-watchlist-options' ),\n\t\tdefaultConfig = {\n\t\t\tsiteList: [\n\t\t\t\tmw.config.get( 'wgServer' ).replace( /.*?\\/\\//, '' )\n\t\t\t],\n\t\t\tanon: 0,\n\t\t\tbot: 0,\n\t\t\tminor: 0,\n\t\t\tconfirmAllSites: true,\n\t\t\tfastMode: false,\n\t\t\tgroupPage: true,\n\t\t\tshowEdits: true,\n\t\t\tshowLogEntries: true,\n\t\t\tshowNewPages: true\n\t\t};\n\n\tlet config;\n\tif ( userOptions === null ) {\n\t\tconfig = defaultConfig;\n\t} else {\n\t\ttry {\n\t\t\tconst userSettings = JSON.parse( userOptions );\n\t\t\tconfig = {\n\t\t\t\tsiteList: userSettings.sites,\n\t\t\t\tanon: userSettings.anonfilter,\n\t\t\t\tbot: userSettings.botfilter,\n\t\t\t\tminor: userSettings.minorfilter,\n\t\t\t\tconfirmAllSites: userSettings.confirmallsites,\n\t\t\t\tfastMode: userSettings.fastmode,\n\t\t\t\tgroupPage: userSettings.grouppage,\n\t\t\t\tshowEdits: userSettings.showtypes.includes( 'edit' ),\n\t\t\t\tshowLogEntries: userSettings.showtypes.includes( 'log' ),\n\t\t\t\tshowNewPages: userSettings.showtypes.includes( 'new' )\n\t\t\t};\n\t\t} catch ( e ) {\n\t\t\t// Not using .error since it can be recovered from, and the user notice\n\t\t\t// should be the one in getsettingserror rather than a plain `alert` call\n\t\t\tglobalWatchlistDebug.info( 'GetSettings - error', e );\n\n\t\t\t// Alert the user\n\t\t\tmw.loader.load( 'ext.globalwatchlist.getsettingserror' );\n\n\t\t\tconfig = defaultConfig;\n\t\t}\n\t}\n\n\t// The following settings are extracted from the user's set configuration or the defaults\n\t// or are not set in the options\n\tconfig.lang = mw.config.get( 'wgUserLanguage' );\n\n\tconfig.watchlistQueryProps = config.fastMode ?\n\t\t'ids|title|flags|loginfo' :\n\t\t'ids|title|flags|loginfo|parsedcomment|timestamp|user|tags|expiry';\n\n\tconfig.watchlistQueryTypes = (\n\t\t( config.showEdits ? 'edit|' : '' ) +\n\t\t( config.showNewPages ? 'new|' : '' ) +\n\t\t( config.showLogEntries ? 'log|' : '' )\n\t).replace( /\\|+$/, '' );\n\n\tconfig.watchlistQueryShow = [\n\t\t'unread',\n\t\tgetQueryFlag( config.anon, 'anon' ),\n\t\tgetQueryFlag( config.bot, 'bot' ),\n\t\tgetQueryFlag( config.minor, 'minor' )\n\t].join( '' ).replace( /^\\|+/, '' );\n\n\tconfig.wikibaseSite = mw.config.get( 'wgGlobalWatchlistWikibaseSite' );\n\n\treturn config;\n}\n\nmodule.exports = globalWatchlistGetSettings;\n","usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]},{"ruleId":"max-len","replacedBy":[]},{"ruleId":"arrow-parens","replacedBy":[]},{"ruleId":"arrow-spacing","replacedBy":[]},{"ruleId":"lines-between-class-members","replacedBy":[]},{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"template-curly-spacing","replacedBy":[]},{"ruleId":"implicit-arrow-linebreak","replacedBy":[]},{"ruleId":"array-bracket-spacing","replacedBy":[]},{"ruleId":"block-spacing","replacedBy":[]},{"ruleId":"brace-style","replacedBy":[]},{"ruleId":"comma-dangle","replacedBy":[]},{"ruleId":"comma-spacing","replacedBy":[]},{"ruleId":"comma-style","replacedBy":[]},{"ruleId":"computed-property-spacing","replacedBy":[]},{"ruleId":"dot-location","replacedBy":[]},{"ruleId":"eol-last","replacedBy":[]},{"ruleId":"func-call-spacing","replacedBy":[]},{"ruleId":"indent","replacedBy":[]},{"ruleId":"key-spacing","replacedBy":[]},{"ruleId":"keyword-spacing","replacedBy":[]},{"ruleId":"linebreak-style","replacedBy":[]},{"ruleId":"max-statements-per-line","replacedBy":[]},{"ruleId":"new-parens","replacedBy":[]},{"ruleId":"no-floating-decimal","replacedBy":[]},{"ruleId":"no-multi-spaces","replacedBy":[]},{"ruleId":"no-multiple-empty-lines","replacedBy":[]},{"ruleId":"no-new-object","replacedBy":["no-object-constructor"]},{"ruleId":"no-tabs","replacedBy":[]},{"ruleId":"no-trailing-spaces","replacedBy":[]},{"ruleId":"no-whitespace-before-property","replacedBy":[]},{"ruleId":"object-curly-spacing","replacedBy":[]},{"ruleId":"operator-linebreak","replacedBy":[]},{"ruleId":"quote-props","replacedBy":[]},{"ruleId":"quotes","replacedBy":[]},{"ruleId":"semi","replacedBy":[]},{"ruleId":"semi-spacing","replacedBy":[]},{"ruleId":"semi-style","replacedBy":[]},{"ruleId":"space-before-blocks","replacedBy":[]},{"ruleId":"space-before-function-paren","replacedBy":[]},{"ruleId":"space-in-parens","replacedBy":[]},{"ruleId":"space-infix-ops","replacedBy":[]},{"ruleId":"space-unary-ops","replacedBy":[]},{"ruleId":"spaced-comment","replacedBy":[]},{"ruleId":"switch-colon-spacing","replacedBy":[]},{"ruleId":"wrap-iife","replacedBy":[]},{"ruleId":"no-extra-semi","replacedBy":[]},{"ruleId":"no-mixed-spaces-and-tabs","replacedBy":[]}]},{"filePath":"/src/repo/package-lock.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"arrow-parens","replacedBy":[]},{"ruleId":"arrow-spacing","replacedBy":[]},{"ruleId":"lines-between-class-members","replacedBy":[]},{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"template-curly-spacing","replacedBy":[]},{"ruleId":"implicit-arrow-linebreak","replacedBy":[]},{"ruleId":"indent","replacedBy":[]},{"ruleId":"comma-dangle","replacedBy":[]},{"ruleId":"no-extra-parens","replacedBy":[]},{"ruleId":"quotes","replacedBy":[]},{"ruleId":"quote-props","replacedBy":[]},{"ruleId":"array-bracket-spacing","replacedBy":[]},{"ruleId":"block-spacing","replacedBy":[]},{"ruleId":"brace-style","replacedBy":[]},{"ruleId":"comma-spacing","replacedBy":[]},{"ruleId":"comma-style","replacedBy":[]},{"ruleId":"computed-property-spacing","replacedBy":[]},{"ruleId":"dot-location","replacedBy":[]},{"ruleId":"eol-last","replacedBy":[]},{"ruleId":"func-call-spacing","replacedBy":[]},{"ruleId":"key-spacing","replacedBy":[]},{"ruleId":"keyword-spacing","replacedBy":[]},{"ruleId":"linebreak-style","replacedBy":[]},{"ruleId":"max-statements-per-line","replacedBy":[]},{"ruleId":"new-parens","replacedBy":[]},{"ruleId":"no-floating-decimal","replacedBy":[]},{"ruleId":"no-multi-spaces","replacedBy":[]},{"ruleId":"no-multiple-empty-lines","replacedBy":[]},{"ruleId":"no-new-object","replacedBy":["no-object-constructor"]},{"ruleId":"no-tabs","replacedBy":[]},{"ruleId":"no-trailing-spaces","replacedBy":[]},{"ruleId":"no-whitespace-before-property","replacedBy":[]},{"ruleId":"object-curly-spacing","replacedBy":[]},{"ruleId":"operator-linebreak","replacedBy":[]},{"ruleId":"semi","replacedBy":[]},{"ruleId":"semi-spacing","replacedBy":[]},{"ruleId":"semi-style","replacedBy":[]},{"ruleId":"space-before-blocks","replacedBy":[]},{"ruleId":"space-before-function-paren","replacedBy":[]},{"ruleId":"space-in-parens","replacedBy":[]},{"ruleId":"space-infix-ops","replacedBy":[]},{"ruleId":"space-unary-ops","replacedBy":[]},{"ruleId":"spaced-comment","replacedBy":[]},{"ruleId":"switch-colon-spacing","replacedBy":[]},{"ruleId":"wrap-iife","replacedBy":[]},{"ruleId":"no-extra-semi","replacedBy":[]},{"ruleId":"no-mixed-spaces-and-tabs","replacedBy":[]}]},{"filePath":"/src/repo/package.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"arrow-parens","replacedBy":[]},{"ruleId":"arrow-spacing","replacedBy":[]},{"ruleId":"lines-between-class-members","replacedBy":[]},{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"template-curly-spacing","replacedBy":[]},{"ruleId":"implicit-arrow-linebreak","replacedBy":[]},{"ruleId":"indent","replacedBy":[]},{"ruleId":"comma-dangle","replacedBy":[]},{"ruleId":"no-extra-parens","replacedBy":[]},{"ruleId":"quotes","replacedBy":[]},{"ruleId":"quote-props","replacedBy":[]},{"ruleId":"array-bracket-spacing","replacedBy":[]},{"ruleId":"block-spacing","replacedBy":[]},{"ruleId":"brace-style","replacedBy":[]},{"ruleId":"comma-spacing","replacedBy":[]},{"ruleId":"comma-style","replacedBy":[]},{"ruleId":"computed-property-spacing","replacedBy":[]},{"ruleId":"dot-location","replacedBy":[]},{"ruleId":"eol-last","replacedBy":[]},{"ruleId":"func-call-spacing","replacedBy":[]},{"ruleId":"key-spacing","replacedBy":[]},{"ruleId":"keyword-spacing","replacedBy":[]},{"ruleId":"linebreak-style","replacedBy":[]},{"ruleId":"max-statements-per-line","replacedBy":[]},{"ruleId":"new-parens","replacedBy":[]},{"ruleId":"no-floating-decimal","replacedBy":[]},{"ruleId":"no-multi-spaces","replacedBy":[]},{"ruleId":"no-multiple-empty-lines","replacedBy":[]},{"ruleId":"no-new-object","replacedBy":["no-object-constructor"]},{"ruleId":"no-tabs","replacedBy":[]},{"ruleId":"no-trailing-spaces","replacedBy":[]},{"ruleId":"no-whitespace-before-property","replacedBy":[]},{"ruleId":"object-curly-spacing","replacedBy":[]},{"ruleId":"operator-linebreak","replacedBy":[]},{"ruleId":"semi","replacedBy":[]},{"ruleId":"semi-spacing","replacedBy":[]},{"ruleId":"semi-style","replacedBy":[]},{"ruleId":"space-before-blocks","replacedBy":[]},{"ruleId":"space-before-function-paren","replacedBy":[]},{"ruleId":"space-in-parens","replacedBy":[]},{"ruleId":"space-infix-ops","replacedBy":[]},{"ruleId":"space-unary-ops","replacedBy":[]},{"ruleId":"spaced-comment","replacedBy":[]},{"ruleId":"switch-colon-spacing","replacedBy":[]},{"ruleId":"wrap-iife","replacedBy":[]},{"ruleId":"no-extra-semi","replacedBy":[]},{"ruleId":"no-mixed-spaces-and-tabs","replacedBy":[]}]},{"filePath":"/src/repo/tests/.eslintrc.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]},{"ruleId":"indent","replacedBy":[]},{"ruleId":"comma-dangle","replacedBy":[]},{"ruleId":"no-extra-parens","replacedBy":[]},{"ruleId":"quotes","replacedBy":[]},{"ruleId":"quote-props","replacedBy":[]},{"ruleId":"arrow-parens","replacedBy":[]},{"ruleId":"arrow-spacing","replacedBy":[]},{"ruleId":"lines-between-class-members","replacedBy":[]},{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"template-curly-spacing","replacedBy":[]},{"ruleId":"implicit-arrow-linebreak","replacedBy":[]},{"ruleId":"array-bracket-spacing","replacedBy":[]},{"ruleId":"block-spacing","replacedBy":[]},{"ruleId":"brace-style","replacedBy":[]},{"ruleId":"comma-spacing","replacedBy":[]},{"ruleId":"comma-style","replacedBy":[]},{"ruleId":"computed-property-spacing","replacedBy":[]},{"ruleId":"dot-location","replacedBy":[]},{"ruleId":"eol-last","replacedBy":[]},{"ruleId":"func-call-spacing","replacedBy":[]},{"ruleId":"key-spacing","replacedBy":[]},{"ruleId":"keyword-spacing","replacedBy":[]},{"ruleId":"linebreak-style","replacedBy":[]},{"ruleId":"max-statements-per-line","replacedBy":[]},{"ruleId":"new-parens","replacedBy":[]},{"ruleId":"no-floating-decimal","replacedBy":[]},{"ruleId":"no-multi-spaces","replacedBy":[]},{"ruleId":"no-multiple-empty-lines","replacedBy":[]},{"ruleId":"no-new-object","replacedBy":["no-object-constructor"]},{"ruleId":"no-tabs","replacedBy":[]},{"ruleId":"no-trailing-spaces","replacedBy":[]},{"ruleId":"no-whitespace-before-property","replacedBy":[]},{"ruleId":"object-curly-spacing","replacedBy":[]},{"ruleId":"operator-linebreak","replacedBy":[]},{"ruleId":"semi","replacedBy":[]},{"ruleId":"semi-spacing","replacedBy":[]},{"ruleId":"semi-style","replacedBy":[]},{"ruleId":"space-before-blocks","replacedBy":[]},{"ruleId":"space-before-function-paren","replacedBy":[]},{"ruleId":"space-in-parens","replacedBy":[]},{"ruleId":"space-infix-ops","replacedBy":[]},{"ruleId":"space-unary-ops","replacedBy":[]},{"ruleId":"spaced-comment","replacedBy":[]},{"ruleId":"switch-colon-spacing","replacedBy":[]},{"ruleId":"wrap-iife","replacedBy":[]},{"ruleId":"no-extra-semi","replacedBy":[]},{"ruleId":"no-mixed-spaces-and-tabs","replacedBy":[]}]},{"filePath":"/src/repo/tests/qunit/Debug.test.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]},{"ruleId":"max-len","replacedBy":[]},{"ruleId":"arrow-parens","replacedBy":[]},{"ruleId":"arrow-spacing","replacedBy":[]},{"ruleId":"lines-between-class-members","replacedBy":[]},{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"template-curly-spacing","replacedBy":[]},{"ruleId":"implicit-arrow-linebreak","replacedBy":[]},{"ruleId":"array-bracket-spacing","replacedBy":[]},{"ruleId":"block-spacing","replacedBy":[]},{"ruleId":"brace-style","replacedBy":[]},{"ruleId":"comma-dangle","replacedBy":[]},{"ruleId":"comma-spacing","replacedBy":[]},{"ruleId":"comma-style","replacedBy":[]},{"ruleId":"computed-property-spacing","replacedBy":[]},{"ruleId":"dot-location","replacedBy":[]},{"ruleId":"eol-last","replacedBy":[]},{"ruleId":"func-call-spacing","replacedBy":[]},{"ruleId":"indent","replacedBy":[]},{"ruleId":"key-spacing","replacedBy":[]},{"ruleId":"keyword-spacing","replacedBy":[]},{"ruleId":"linebreak-style","replacedBy":[]},{"ruleId":"max-statements-per-line","replacedBy":[]},{"ruleId":"new-parens","replacedBy":[]},{"ruleId":"no-floating-decimal","replacedBy":[]},{"ruleId":"no-multi-spaces","replacedBy":[]},{"ruleId":"no-multiple-empty-lines","replacedBy":[]},{"ruleId":"no-new-object","replacedBy":["no-object-constructor"]},{"ruleId":"no-tabs","replacedBy":[]},{"ruleId":"no-trailing-spaces","replacedBy":[]},{"ruleId":"no-whitespace-before-property","replacedBy":[]},{"ruleId":"object-curly-spacing","replacedBy":[]},{"ruleId":"operator-linebreak","replacedBy":[]},{"ruleId":"quote-props","replacedBy":[]},{"ruleId":"quotes","replacedBy":[]},{"ruleId":"semi","replacedBy":[]},{"ruleId":"semi-spacing","replacedBy":[]},{"ruleId":"semi-style","replacedBy":[]},{"ruleId":"space-before-blocks","replacedBy":[]},{"ruleId":"space-before-function-paren","replacedBy":[]},{"ruleId":"space-in-parens","replacedBy":[]},{"ruleId":"space-infix-ops","replacedBy":[]},{"ruleId":"space-unary-ops","replacedBy":[]},{"ruleId":"spaced-comment","replacedBy":[]},{"ruleId":"switch-colon-spacing","replacedBy":[]},{"ruleId":"wrap-iife","replacedBy":[]},{"ruleId":"no-extra-semi","replacedBy":[]},{"ruleId":"no-mixed-spaces-and-tabs","replacedBy":[]}]},{"filePath":"/src/repo/tests/qunit/Linker.test.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]},{"ruleId":"max-len","replacedBy":[]},{"ruleId":"arrow-parens","replacedBy":[]},{"ruleId":"arrow-spacing","replacedBy":[]},{"ruleId":"lines-between-class-members","replacedBy":[]},{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"template-curly-spacing","replacedBy":[]},{"ruleId":"implicit-arrow-linebreak","replacedBy":[]},{"ruleId":"array-bracket-spacing","replacedBy":[]},{"ruleId":"block-spacing","replacedBy":[]},{"ruleId":"brace-style","replacedBy":[]},{"ruleId":"comma-dangle","replacedBy":[]},{"ruleId":"comma-spacing","replacedBy":[]},{"ruleId":"comma-style","replacedBy":[]},{"ruleId":"computed-property-spacing","replacedBy":[]},{"ruleId":"dot-location","replacedBy":[]},{"ruleId":"eol-last","replacedBy":[]},{"ruleId":"func-call-spacing","replacedBy":[]},{"ruleId":"indent","replacedBy":[]},{"ruleId":"key-spacing","replacedBy":[]},{"ruleId":"keyword-spacing","replacedBy":[]},{"ruleId":"linebreak-style","replacedBy":[]},{"ruleId":"max-statements-per-line","replacedBy":[]},{"ruleId":"new-parens","replacedBy":[]},{"ruleId":"no-floating-decimal","replacedBy":[]},{"ruleId":"no-multi-spaces","replacedBy":[]},{"ruleId":"no-multiple-empty-lines","replacedBy":[]},{"ruleId":"no-new-object","replacedBy":["no-object-constructor"]},{"ruleId":"no-tabs","replacedBy":[]},{"ruleId":"no-trailing-spaces","replacedBy":[]},{"ruleId":"no-whitespace-before-property","replacedBy":[]},{"ruleId":"object-curly-spacing","replacedBy":[]},{"ruleId":"operator-linebreak","replacedBy":[]},{"ruleId":"quote-props","replacedBy":[]},{"ruleId":"quotes","replacedBy":[]},{"ruleId":"semi","replacedBy":[]},{"ruleId":"semi-spacing","replacedBy":[]},{"ruleId":"semi-style","replacedBy":[]},{"ruleId":"space-before-blocks","replacedBy":[]},{"ruleId":"space-before-function-paren","replacedBy":[]},{"ruleId":"space-in-parens","replacedBy":[]},{"ruleId":"space-infix-ops","replacedBy":[]},{"ruleId":"space-unary-ops","replacedBy":[]},{"ruleId":"spaced-comment","replacedBy":[]},{"ruleId":"switch-colon-spacing","replacedBy":[]},{"ruleId":"wrap-iife","replacedBy":[]},{"ruleId":"no-extra-semi","replacedBy":[]},{"ruleId":"no-mixed-spaces-and-tabs","replacedBy":[]}]},{"filePath":"/src/repo/tests/qunit/WatchlistUtils.test.js","messages":[{"ruleId":"jsdoc/no-undefined-types","severity":1,"message":"The type 'GlobalWatchlistEntryBase' is undefined.","line":8,"column":1,"nodeType":"Block","endLine":8,"endColumn":1}],"suppressedMessages":[{"ruleId":"camelcase","severity":2,"message":"Identifier 'old_revid' is not in camel case.","line":57,"column":4,"nodeType":"Identifier","messageId":"notCamelCase","endLine":57,"endColumn":13,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"camelcase","severity":2,"message":"Identifier 'edit2_a' is not in camel case.","line":63,"column":9,"nodeType":"Identifier","messageId":"notCamelCase","endLine":63,"endColumn":16,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"camelcase","severity":2,"message":"Identifier 'old_revid' is not in camel case.","line":67,"column":4,"nodeType":"Identifier","messageId":"notCamelCase","endLine":67,"endColumn":13,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"camelcase","severity":2,"message":"Identifier 'edit2_b' is not in camel case.","line":73,"column":9,"nodeType":"Identifier","messageId":"notCamelCase","endLine":73,"endColumn":16,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"camelcase","severity":2,"message":"Identifier 'old_revid' is not in camel case.","line":77,"column":4,"nodeType":"Identifier","messageId":"notCamelCase","endLine":77,"endColumn":13,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"camelcase","severity":2,"message":"Identifier 'edit2_c' is not in camel case.","line":83,"column":9,"nodeType":"Identifier","messageId":"notCamelCase","endLine":83,"endColumn":16,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"camelcase","severity":2,"message":"Identifier 'old_revid' is not in camel case.","line":87,"column":4,"nodeType":"Identifier","messageId":"notCamelCase","endLine":87,"endColumn":13,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"camelcase","severity":2,"message":"Identifier 'edit2_d' is not in camel case.","line":93,"column":9,"nodeType":"Identifier","messageId":"notCamelCase","endLine":93,"endColumn":16,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"camelcase","severity":2,"message":"Identifier 'old_revid' is not in camel case.","line":97,"column":4,"nodeType":"Identifier","messageId":"notCamelCase","endLine":97,"endColumn":13,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"camelcase","severity":2,"message":"Identifier 'edit2_e' is not in camel case.","line":103,"column":9,"nodeType":"Identifier","messageId":"notCamelCase","endLine":103,"endColumn":16,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"camelcase","severity":2,"message":"Identifier 'old_revid' is not in camel case.","line":107,"column":4,"nodeType":"Identifier","messageId":"notCamelCase","endLine":107,"endColumn":13,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"camelcase","severity":2,"message":"Identifier 'mergedEdits_a' is not in camel case.","line":114,"column":9,"nodeType":"Identifier","messageId":"notCamelCase","endLine":114,"endColumn":22,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"camelcase","severity":2,"message":"Identifier 'mergedEdits_b' is not in camel case.","line":128,"column":9,"nodeType":"Identifier","messageId":"notCamelCase","endLine":128,"endColumn":22,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"camelcase","severity":2,"message":"Identifier 'mergedEdits_c' is not in camel case.","line":142,"column":9,"nodeType":"Identifier","messageId":"notCamelCase","endLine":142,"endColumn":22,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"camelcase","severity":2,"message":"Identifier 'mergedEdits_d' is not in camel case.","line":156,"column":9,"nodeType":"Identifier","messageId":"notCamelCase","endLine":156,"endColumn":22,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"camelcase","severity":2,"message":"Identifier 'mergedEdits_e' is not in camel case.","line":170,"column":9,"nodeType":"Identifier","messageId":"notCamelCase","endLine":170,"endColumn":22,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"camelcase","severity":2,"message":"Identifier 'edit2_a' is not in camel case.","line":186,"column":44,"nodeType":"Identifier","messageId":"notCamelCase","endLine":186,"endColumn":51,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"camelcase","severity":2,"message":"Identifier 'edit2_b' is not in camel case.","line":191,"column":44,"nodeType":"Identifier","messageId":"notCamelCase","endLine":191,"endColumn":51,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"camelcase","severity":2,"message":"Identifier 'edit2_c' is not in camel case.","line":196,"column":44,"nodeType":"Identifier","messageId":"notCamelCase","endLine":196,"endColumn":51,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"camelcase","severity":2,"message":"Identifier 'edit2_d' is not in camel case.","line":201,"column":44,"nodeType":"Identifier","messageId":"notCamelCase","endLine":201,"endColumn":51,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"camelcase","severity":2,"message":"Identifier 'edit2_e' is not in camel case.","line":206,"column":44,"nodeType":"Identifier","messageId":"notCamelCase","endLine":206,"endColumn":51,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-bitwise","severity":2,"message":"Unexpected use of '&'.","line":339,"column":18,"nodeType":"BinaryExpression","messageId":"unexpected","endLine":339,"endColumn":25,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-bitwise","severity":2,"message":"Unexpected use of '&'.","line":340,"column":16,"nodeType":"BinaryExpression","messageId":"unexpected","endLine":340,"endColumn":23,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-bitwise","severity":2,"message":"Unexpected use of '&'.","line":341,"column":14,"nodeType":"BinaryExpression","messageId":"unexpected","endLine":341,"endColumn":21,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-bitwise","severity":2,"message":"Unexpected use of '&'.","line":354,"column":12,"nodeType":"BinaryExpression","messageId":"unexpected","endLine":354,"endColumn":19,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-bitwise","severity":2,"message":"Unexpected use of '&'.","line":355,"column":12,"nodeType":"BinaryExpression","messageId":"unexpected","endLine":355,"endColumn":19,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-bitwise","severity":2,"message":"Unexpected use of '&'.","line":356,"column":12,"nodeType":"BinaryExpression","messageId":"unexpected","endLine":356,"endColumn":19,"suppressions":[{"kind":"directive","justification":""}]}],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"( function () {\n\n\t/**\n\t * To simplify testing WatchlistUtils.getFinalEntries(), allow checking\n\t * part of the cleanup at a time by only checking some of the properties on\n\t * the updated entries\n\t *\n\t * @param {GlobalWatchlistEntryBase[]} fullEntries the result of getFinalEntries()\n\t * @param {Array} keysToInclude the keys of each entry that should be included\n\t * @return {Array} Objects corresponding to fullEntries but with only the keys\n\t *   that are included in keysToInclude\n\t */\n\tfunction getFilteredEntries( fullEntries, keysToInclude ) {\n\t\tlet filteredEntry;\n\t\treturn fullEntries.map(\n\t\t\t( fullEntry ) => {\n\t\t\t\tfilteredEntry = {};\n\t\t\t\tkeysToInclude.forEach(\n\t\t\t\t\t( keyToInclude ) => {\n\t\t\t\t\t\tfilteredEntry[ keyToInclude ] = fullEntry[ keyToInclude ];\n\t\t\t\t\t}\n\t\t\t\t);\n\t\t\t\treturn filteredEntry;\n\t\t\t}\n\t\t);\n\t}\n\tconst WatchlistUtils = require( 'ext.globalwatchlist.specialglobalwatchlist/WatchlistUtils.js' );\n\tconst Linker = require( 'ext.globalwatchlist.specialglobalwatchlist/Linker.js' );\n\n\t// For the getFinalEntries tests, all of the properties we care about are being set on\n\t// the base GlobalWatchlistEntryBase class, so even though that is meant to be abstract,\n\t// since that isn't enforced in JavaScript lets use that\n\tconst GlobalWatchlistEntryBase = require( 'ext.globalwatchlist.specialglobalwatchlist/EntryBase.js' );\n\n\t// Set config variables so that the linker can be created properly\n\tQUnit.module( 'ext.globalwatchlist.specialglobalwatchlist/WatchlistUtils', QUnit.newMwEnvironment( {\n\t\tconfig: {\n\t\t\twgArticlePath: '/wiki/$1/FooBar',\n\t\t\twgScript: '/w/baz/index.php'\n\t\t}\n\t} ) );\n\n\t// The linker is currently only needed for the addCommentDisplays() test\n\tconst watchlistUtils = new WatchlistUtils(\n\t\tnew Linker( 'en.wikipedia.org' )\n\t);\n\n\t/* eslint-disable camelcase */\n\tQUnit.test( 'watchlistUtils.mergePageEdits', ( assert ) => {\n\t\tconst expectTimestampTitle = mw.msg( 'globalwatchlist-grouped-timestamp' );\n\t\t// Not testing timestamps as part of this, all edits set to the same timestamp\n\t\t// of 2020-08-31 12:00.\n\t\tconst edit1 = {\n\t\t\t// Bot edit, minor edit, not a new page\n\t\t\tbot: true,\n\t\t\texpiry: false,\n\t\t\told_revid: 1,\n\t\t\tminor: true,\n\t\t\tnewPage: false,\n\t\t\ttimestamp: '2020-08-31 12:00',\n\t\t\trevid: 2\n\t\t};\n\t\tconst edit2_a = {\n\t\t\t// Bot edit, minor edit, not a new page\n\t\t\tbot: true,\n\t\t\texpiry: false,\n\t\t\told_revid: 2,\n\t\t\tminor: true,\n\t\t\tnewPage: false,\n\t\t\ttimestamp: '2020-08-31 12:00',\n\t\t\trevid: 3\n\t\t};\n\t\tconst edit2_b = {\n\t\t\t// Bot edit, not a minor edit, not a new page\n\t\t\tbot: true,\n\t\t\texpiry: false,\n\t\t\told_revid: 2,\n\t\t\tminor: false,\n\t\t\tnewPage: false,\n\t\t\ttimestamp: '2020-08-31 12:00',\n\t\t\trevid: 3\n\t\t};\n\t\tconst edit2_c = {\n\t\t\t// Not a bot edit, minor edit, not a new page\n\t\t\tbot: false,\n\t\t\texpiry: false,\n\t\t\told_revid: 2,\n\t\t\tminor: true,\n\t\t\tnewPage: false,\n\t\t\ttimestamp: '2020-08-31 12:00',\n\t\t\trevid: 3\n\t\t};\n\t\tconst edit2_d = {\n\t\t\t// Not a bot edit, not a minor edit, not a new page\n\t\t\tbot: false,\n\t\t\texpiry: false,\n\t\t\told_revid: 2,\n\t\t\tminor: false,\n\t\t\tnewPage: false,\n\t\t\ttimestamp: '2020-08-31 12:00',\n\t\t\trevid: 3\n\t\t};\n\t\tconst edit2_e = {\n\t\t\t// Not a bot edit, not a minor edit, new page\n\t\t\tbot: false,\n\t\t\texpiry: false,\n\t\t\told_revid: 0,\n\t\t\tminor: false,\n\t\t\tnewPage: true,\n\t\t\ttimestamp: '2020-08-31 12:00',\n\t\t\trevid: 0\n\t\t};\n\n\t\tconst mergedEdits_a = {\n\t\t\t// Both bot edits, both minor edits, neither new page\n\t\t\tbot: true,\n\t\t\tcomment: false,\n\t\t\teditCount: 2,\n\t\t\texpiry: false,\n\t\t\tfromRev: 1,\n\t\t\tminor: true,\n\t\t\tnewPage: false,\n\t\t\ttags: [],\n\t\t\ttimestamp: '2020-08-31 12:00',\n\t\t\ttimestampTitle: expectTimestampTitle,\n\t\t\ttoRev: 3\n\t\t};\n\t\tconst mergedEdits_b = {\n\t\t\t// Both bot edits, only one minor edit, neither new page\n\t\t\tbot: true,\n\t\t\tcomment: false,\n\t\t\teditCount: 2,\n\t\t\texpiry: false,\n\t\t\tfromRev: 1,\n\t\t\tminor: false,\n\t\t\tnewPage: false,\n\t\t\ttags: [],\n\t\t\ttimestamp: '2020-08-31 12:00',\n\t\t\ttimestampTitle: expectTimestampTitle,\n\t\t\ttoRev: 3\n\t\t};\n\t\tconst mergedEdits_c = {\n\t\t\t// Only one bot edit, both minor edits, neither new page\n\t\t\tbot: false,\n\t\t\tcomment: false,\n\t\t\teditCount: 2,\n\t\t\texpiry: false,\n\t\t\tfromRev: 1,\n\t\t\tminor: true,\n\t\t\tnewPage: false,\n\t\t\ttags: [],\n\t\t\ttimestamp: '2020-08-31 12:00',\n\t\t\ttimestampTitle: expectTimestampTitle,\n\t\t\ttoRev: 3\n\t\t};\n\t\tconst mergedEdits_d = {\n\t\t\t// Only one bot edit, only one minor edit, neither new page\n\t\t\tbot: false,\n\t\t\tcomment: false,\n\t\t\teditCount: 2,\n\t\t\texpiry: false,\n\t\t\tfromRev: 1,\n\t\t\tminor: false,\n\t\t\tnewPage: false,\n\t\t\ttags: [],\n\t\t\ttimestamp: '2020-08-31 12:00',\n\t\t\ttimestampTitle: expectTimestampTitle,\n\t\t\ttoRev: 3\n\t\t};\n\t\tconst mergedEdits_e = {\n\t\t\t// Only one bot edit, only one minor edit, one new page\n\t\t\tbot: false,\n\t\t\tcomment: false,\n\t\t\teditCount: 2,\n\t\t\texpiry: false,\n\t\t\tfromRev: 0,\n\t\t\tminor: false,\n\t\t\tnewPage: true,\n\t\t\ttags: [],\n\t\t\ttimestamp: '2020-08-31 12:00',\n\t\t\ttimestampTitle: expectTimestampTitle,\n\t\t\ttoRev: 2\n\t\t};\n\n\t\tassert.deepEqual(\n\t\t\twatchlistUtils.mergePageEdits( [ edit1, edit2_a ] ),\n\t\t\tmergedEdits_a,\n\t\t\t'Two minor bot edits -> minor bot edits'\n\t\t);\n\t\tassert.deepEqual(\n\t\t\twatchlistUtils.mergePageEdits( [ edit1, edit2_b ] ),\n\t\t\tmergedEdits_b,\n\t\t\t'Minor bot edit + bot edit -> bot edits'\n\t\t);\n\t\tassert.deepEqual(\n\t\t\twatchlistUtils.mergePageEdits( [ edit1, edit2_c ] ),\n\t\t\tmergedEdits_c,\n\t\t\t'Minor bot edit + minor edit -> minor edits'\n\t\t);\n\t\tassert.deepEqual(\n\t\t\twatchlistUtils.mergePageEdits( [ edit1, edit2_d ] ),\n\t\t\tmergedEdits_d,\n\t\t\t'Minor bot edit + normal edit -> normal edits'\n\t\t);\n\t\tassert.deepEqual(\n\t\t\twatchlistUtils.mergePageEdits( [ edit1, edit2_e ] ),\n\t\t\tmergedEdits_e,\n\t\t\t'Minor bot edit + new page -> normal new page'\n\t\t);\n\t} );\n\n\tQUnit.test( 'watchlistUtils.normalizeEntries', ( assert ) => {\n\t\t// Only fill in the parts that would otherwise be normalized\n\t\tconst edit = {\n\t\t\tanon: true,\n\t\t\tparsedcomment: 'comment',\n\t\t\ttags: [],\n\t\t\ttimestamp: '2020-08-31 12:00',\n\t\t\ttype: 'edit',\n\t\t\tuser: ''\n\t\t};\n\t\tconst normalizedEdit = {\n\t\t\tanon: true,\n\t\t\tparsedcomment: 'comment',\n\t\t\ttags: [],\n\t\t\ttimestamp: '2020-08-31 12:00',\n\t\t\ttype: 'edit',\n\t\t\tnewPage: false,\n\t\t\tuser: ''\n\t\t};\n\t\tassert.deepEqual(\n\t\t\twatchlistUtils.normalizeEntries( [ edit ] ),\n\t\t\t[ normalizedEdit ],\n\t\t\t'Edits are flagged as not new pages'\n\t\t);\n\n\t\tconst hiddenEditor = {\n\t\t\tanon: false,\n\t\t\tuserhidden: true,\n\t\t\tparsedcomment: 'comment',\n\t\t\ttags: [],\n\t\t\ttimestamp: '2020-08-31 12:00',\n\t\t\ttype: 'edit'\n\t\t};\n\t\tconst normalizedHiddenEditor = {\n\t\t\tanon: false,\n\t\t\tuserhidden: true,\n\t\t\tuser: '##hidden##',\n\t\t\tparsedcomment: 'comment',\n\t\t\ttags: [],\n\t\t\ttimestamp: '2020-08-31 12:00',\n\t\t\ttype: 'edit',\n\t\t\tnewPage: false\n\t\t};\n\t\tassert.deepEqual(\n\t\t\twatchlistUtils.normalizeEntries( [ hiddenEditor ] ),\n\t\t\t[ normalizedHiddenEditor ],\n\t\t\t'Edits by hidden users are flagged as user=##hidden##'\n\t\t);\n\n\t\tconst editWithNoSummary = {\n\t\t\tanon: false,\n\t\t\ttags: [],\n\t\t\ttimestamp: '2020-08-31 12:00',\n\t\t\ttype: 'edit'\n\t\t};\n\t\tconst normalizedEditWithNoSummary = {\n\t\t\tanon: false,\n\t\t\tparsedcomment: '',\n\t\t\ttags: [],\n\t\t\ttimestamp: '2020-08-31 12:00',\n\t\t\ttype: 'edit',\n\t\t\tnewPage: false,\n\t\t\tuser: ''\n\t\t};\n\t\tassert.deepEqual(\n\t\t\twatchlistUtils.normalizeEntries( [ editWithNoSummary ] ),\n\t\t\t[ normalizedEditWithNoSummary ],\n\t\t\t'Edits without comments are normalized to parsedcomment=\\'\\''\n\t\t);\n\n\t\tconst editWithNoTags = {\n\t\t\tanon: false,\n\t\t\tparsedcomment: '',\n\t\t\ttimestamp: '2020-08-31 12:00',\n\t\t\ttype: 'edit'\n\t\t};\n\t\tconst normalizedEditWithNoTags = {\n\t\t\tanon: false,\n\t\t\tparsedcomment: '',\n\t\t\ttags: [],\n\t\t\ttimestamp: '2020-08-31 12:00',\n\t\t\ttype: 'edit',\n\t\t\tnewPage: false,\n\t\t\tuser: ''\n\t\t};\n\t\tassert.deepEqual(\n\t\t\twatchlistUtils.normalizeEntries( [ editWithNoTags ] ),\n\t\t\t[ normalizedEditWithNoTags ],\n\t\t\t'Edits without tags are normalized to tags=[]'\n\t\t);\n\n\t\tconst newPage = {\n\t\t\tanon: true,\n\t\t\tparsedcomment: 'comment',\n\t\t\ttags: [],\n\t\t\ttimestamp: '2020-08-31 12:00',\n\t\t\ttype: 'new'\n\t\t};\n\t\tconst normalizedNewPage = {\n\t\t\tanon: true,\n\t\t\tparsedcomment: 'comment',\n\t\t\ttags: [],\n\t\t\ttimestamp: '2020-08-31 12:00',\n\t\t\ttype: 'edit',\n\t\t\tnewPage: true,\n\t\t\tuser: ''\n\t\t};\n\t\tassert.deepEqual(\n\t\t\twatchlistUtils.normalizeEntries( [ newPage ] ),\n\t\t\t[ normalizedNewPage ],\n\t\t\t'New pages are normalized to edits with a flag'\n\t\t);\n\t} );\n\n\tQUnit.test( 'WatchlistUtils.getFinalEntries (entry flags)', ( assert ) => {\n\t\tconst allEntries = [];\n\t\tconst expectedEntries = [];\n\t\tlet withoutFlags;\n\t\tlet withFlags;\n\t\tconst newPageFlag = mw.msg( 'newpageletter' );\n\t\tconst minorFlag = mw.msg( 'minoreditletter' );\n\t\tconst botFlag = mw.msg( 'boteditletter' );\n\n\t\t/* We use bitwise comparisons to simplify looping through all possible flag combinations */\n\t\t/* eslint-disable no-bitwise */\n\t\tfor ( let iii = 0; iii <= 7; iii++ ) {\n\t\t\twithoutFlags = {\n\t\t\t\tnewPage: ( ( iii & 1 ) === 1 ),\n\t\t\t\tminor: ( ( iii & 2 ) === 2 ),\n\t\t\t\tbot: ( ( iii & 4 ) === 4 )\n\t\t\t};\n\t\t\tallEntries.push( withoutFlags );\n\n\t\t\t// Expected result, only the `flags` is saved (GlobalWatchlistEntryEdits also\n\t\t\t// saves the `newPage` prop, but since it isn't changed we can just focus on\n\t\t\t// testing that the `flags` was computed properly;includes the original properties\n\t\t\twithFlags = {};\n\t\t\tif ( iii === 0 ) {\n\t\t\t\t// Would have no flags\n\t\t\t\twithFlags.flags = false;\n\t\t\t} else {\n\t\t\t\twithFlags.flags = (\n\t\t\t\t\t( ( ( iii & 1 ) === 1 ) ? newPageFlag : '' ) +\n\t\t\t\t\t( ( ( iii & 2 ) === 2 ) ? minorFlag : '' ) +\n\t\t\t\t\t( ( ( iii & 4 ) === 4 ) ? botFlag : '' )\n\t\t\t\t);\n\t\t\t}\n\t\t\texpectedEntries.push( withFlags );\n\t\t}\n\t\t/* eslint-enable no-bitwise */\n\n\t\t// Reduce the fully updated entries to just the parts we are checking\n\t\tconst result = getFilteredEntries(\n\t\t\twatchlistUtils.getFinalEntries( allEntries, {}, GlobalWatchlistEntryBase ),\n\t\t\t[ 'flags' ]\n\t\t);\n\t\tassert.deepEqual(\n\t\t\tresult,\n\t\t\texpectedEntries,\n\t\t\t'Flags are added to entries marked as new pages / minor edits / bot actions'\n\t\t);\n\t} );\n\n\tQUnit.test( 'WatchlistUtils.getFinalEntries (truncate timestamps)', ( assert ) => {\n\t\tconst originalEntries = [\n\t\t\t{ timestamp: false },\n\t\t\t{ timestamp: '2021-07-04T07:30:49Z' },\n\t\t\t{ timestamp: '2020-01-01T12:01:00Z' }\n\t\t];\n\t\tconst expectedUpdatedEntries = [\n\t\t\t{ timestamp: false },\n\t\t\t{ timestamp: '2021-07-04 07:30' },\n\t\t\t{ timestamp: '2020-01-01 12:01' }\n\t\t];\n\n\t\t// Reduce the fully updated entries to just the parts we are checking\n\t\tconst result = getFilteredEntries(\n\t\t\twatchlistUtils.getFinalEntries( originalEntries, {}, GlobalWatchlistEntryBase ),\n\t\t\t[ 'timestamp' ]\n\t\t);\n\t\tassert.deepEqual(\n\t\t\tresult,\n\t\t\texpectedUpdatedEntries,\n\t\t\t'Timestamps are truncated to display in the form YY-MM-DD HH:MM'\n\t\t);\n\t} );\n\n\tQUnit.test( 'WatchlistUtils.getFinalEntries (comment displays)', ( assert ) => {\n\t\t// First two are missing a comment, third doesn't have a link, third has\n\t\t// a link to [[PageName]]. This is for en.wikipedia.org, per configuration\n\t\t// of the linker above\n\t\tconst originalEntries = [\n\t\t\t{ comment: false },\n\t\t\t{ comment: '' },\n\t\t\t{ comment: 'foo' },\n\t\t\t{ comment: '<a href=\"/wiki/PageName\" title=\"PageName\">PageName</a>' }\n\t\t];\n\t\t// Expected result, only the `commentDisplay` is saved\n\t\tconst expectedUpdatedEntries = [\n\t\t\t{ commentDisplay: false },\n\t\t\t{ commentDisplay: false },\n\t\t\t{ commentDisplay: ': foo' },\n\t\t\t{ commentDisplay: ': <a href=\"//en.wikipedia.org/wiki/PageName\" title=\"PageName\">PageName</a>' }\n\t\t];\n\n\t\t// Reduce the fully updated entries to just the parts we are checking\n\t\tconst result = getFilteredEntries(\n\t\t\twatchlistUtils.getFinalEntries( originalEntries, {}, GlobalWatchlistEntryBase ),\n\t\t\t[ 'commentDisplay' ]\n\t\t);\n\t\tassert.deepEqual(\n\t\t\tresult,\n\t\t\texpectedUpdatedEntries,\n\t\t\t'leading \": \" are added to comments, and links are updated, when there is a comment'\n\t\t);\n\t} );\n\n\t/* eslint-enable camelcase */\n}() );\n","usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]},{"ruleId":"max-len","replacedBy":[]},{"ruleId":"arrow-parens","replacedBy":[]},{"ruleId":"arrow-spacing","replacedBy":[]},{"ruleId":"lines-between-class-members","replacedBy":[]},{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"template-curly-spacing","replacedBy":[]},{"ruleId":"implicit-arrow-linebreak","replacedBy":[]},{"ruleId":"array-bracket-spacing","replacedBy":[]},{"ruleId":"block-spacing","replacedBy":[]},{"ruleId":"brace-style","replacedBy":[]},{"ruleId":"comma-dangle","replacedBy":[]},{"ruleId":"comma-spacing","replacedBy":[]},{"ruleId":"comma-style","replacedBy":[]},{"ruleId":"computed-property-spacing","replacedBy":[]},{"ruleId":"dot-location","replacedBy":[]},{"ruleId":"eol-last","replacedBy":[]},{"ruleId":"func-call-spacing","replacedBy":[]},{"ruleId":"indent","replacedBy":[]},{"ruleId":"key-spacing","replacedBy":[]},{"ruleId":"keyword-spacing","replacedBy":[]},{"ruleId":"linebreak-style","replacedBy":[]},{"ruleId":"max-statements-per-line","replacedBy":[]},{"ruleId":"new-parens","replacedBy":[]},{"ruleId":"no-floating-decimal","replacedBy":[]},{"ruleId":"no-multi-spaces","replacedBy":[]},{"ruleId":"no-multiple-empty-lines","replacedBy":[]},{"ruleId":"no-new-object","replacedBy":["no-object-constructor"]},{"ruleId":"no-tabs","replacedBy":[]},{"ruleId":"no-trailing-spaces","replacedBy":[]},{"ruleId":"no-whitespace-before-property","replacedBy":[]},{"ruleId":"object-curly-spacing","replacedBy":[]},{"ruleId":"operator-linebreak","replacedBy":[]},{"ruleId":"quote-props","replacedBy":[]},{"ruleId":"quotes","replacedBy":[]},{"ruleId":"semi","replacedBy":[]},{"ruleId":"semi-spacing","replacedBy":[]},{"ruleId":"semi-style","replacedBy":[]},{"ruleId":"space-before-blocks","replacedBy":[]},{"ruleId":"space-before-function-paren","replacedBy":[]},{"ruleId":"space-in-parens","replacedBy":[]},{"ruleId":"space-infix-ops","replacedBy":[]},{"ruleId":"space-unary-ops","replacedBy":[]},{"ruleId":"spaced-comment","replacedBy":[]},{"ruleId":"switch-colon-spacing","replacedBy":[]},{"ruleId":"wrap-iife","replacedBy":[]},{"ruleId":"no-extra-semi","replacedBy":[]},{"ruleId":"no-mixed-spaces-and-tabs","replacedBy":[]}]},{"filePath":"/src/repo/tests/qunit/getSettings.test.js","messages":[],"suppressedMessages":[{"ruleId":"no-unused-vars","severity":2,"message":"'unused' is defined but never used.","line":8,"column":26,"nodeType":"Identifier","messageId":"unusedVar","endLine":8,"endColumn":32,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-unused-vars","severity":2,"message":"'alsoUnused' is defined but never used.","line":8,"column":34,"nodeType":"Identifier","messageId":"unusedVar","endLine":8,"endColumn":44,"suppressions":[{"kind":"directive","justification":""}]}],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]},{"ruleId":"max-len","replacedBy":[]},{"ruleId":"arrow-parens","replacedBy":[]},{"ruleId":"arrow-spacing","replacedBy":[]},{"ruleId":"lines-between-class-members","replacedBy":[]},{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"template-curly-spacing","replacedBy":[]},{"ruleId":"implicit-arrow-linebreak","replacedBy":[]},{"ruleId":"array-bracket-spacing","replacedBy":[]},{"ruleId":"block-spacing","replacedBy":[]},{"ruleId":"brace-style","replacedBy":[]},{"ruleId":"comma-dangle","replacedBy":[]},{"ruleId":"comma-spacing","replacedBy":[]},{"ruleId":"comma-style","replacedBy":[]},{"ruleId":"computed-property-spacing","replacedBy":[]},{"ruleId":"dot-location","replacedBy":[]},{"ruleId":"eol-last","replacedBy":[]},{"ruleId":"func-call-spacing","replacedBy":[]},{"ruleId":"indent","replacedBy":[]},{"ruleId":"key-spacing","replacedBy":[]},{"ruleId":"keyword-spacing","replacedBy":[]},{"ruleId":"linebreak-style","replacedBy":[]},{"ruleId":"max-statements-per-line","replacedBy":[]},{"ruleId":"new-parens","replacedBy":[]},{"ruleId":"no-floating-decimal","replacedBy":[]},{"ruleId":"no-multi-spaces","replacedBy":[]},{"ruleId":"no-multiple-empty-lines","replacedBy":[]},{"ruleId":"no-new-object","replacedBy":["no-object-constructor"]},{"ruleId":"no-tabs","replacedBy":[]},{"ruleId":"no-trailing-spaces","replacedBy":[]},{"ruleId":"no-whitespace-before-property","replacedBy":[]},{"ruleId":"object-curly-spacing","replacedBy":[]},{"ruleId":"operator-linebreak","replacedBy":[]},{"ruleId":"quote-props","replacedBy":[]},{"ruleId":"quotes","replacedBy":[]},{"ruleId":"semi","replacedBy":[]},{"ruleId":"semi-spacing","replacedBy":[]},{"ruleId":"semi-style","replacedBy":[]},{"ruleId":"space-before-blocks","replacedBy":[]},{"ruleId":"space-before-function-paren","replacedBy":[]},{"ruleId":"space-in-parens","replacedBy":[]},{"ruleId":"space-infix-ops","replacedBy":[]},{"ruleId":"space-unary-ops","replacedBy":[]},{"ruleId":"spaced-comment","replacedBy":[]},{"ruleId":"switch-colon-spacing","replacedBy":[]},{"ruleId":"wrap-iife","replacedBy":[]},{"ruleId":"no-extra-semi","replacedBy":[]},{"ruleId":"no-mixed-spaces-and-tabs","replacedBy":[]}]},{"filePath":"/src/repo/tests/selenium/.eslintrc.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"arrow-parens","replacedBy":[]},{"ruleId":"arrow-spacing","replacedBy":[]},{"ruleId":"lines-between-class-members","replacedBy":[]},{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"template-curly-spacing","replacedBy":[]},{"ruleId":"implicit-arrow-linebreak","replacedBy":[]},{"ruleId":"indent","replacedBy":[]},{"ruleId":"comma-dangle","replacedBy":[]},{"ruleId":"no-extra-parens","replacedBy":[]},{"ruleId":"quotes","replacedBy":[]},{"ruleId":"quote-props","replacedBy":[]},{"ruleId":"array-bracket-spacing","replacedBy":[]},{"ruleId":"block-spacing","replacedBy":[]},{"ruleId":"brace-style","replacedBy":[]},{"ruleId":"comma-spacing","replacedBy":[]},{"ruleId":"comma-style","replacedBy":[]},{"ruleId":"computed-property-spacing","replacedBy":[]},{"ruleId":"dot-location","replacedBy":[]},{"ruleId":"eol-last","replacedBy":[]},{"ruleId":"func-call-spacing","replacedBy":[]},{"ruleId":"key-spacing","replacedBy":[]},{"ruleId":"keyword-spacing","replacedBy":[]},{"ruleId":"linebreak-style","replacedBy":[]},{"ruleId":"max-statements-per-line","replacedBy":[]},{"ruleId":"new-parens","replacedBy":[]},{"ruleId":"no-floating-decimal","replacedBy":[]},{"ruleId":"no-multi-spaces","replacedBy":[]},{"ruleId":"no-multiple-empty-lines","replacedBy":[]},{"ruleId":"no-new-object","replacedBy":["no-object-constructor"]},{"ruleId":"no-tabs","replacedBy":[]},{"ruleId":"no-trailing-spaces","replacedBy":[]},{"ruleId":"no-whitespace-before-property","replacedBy":[]},{"ruleId":"object-curly-spacing","replacedBy":[]},{"ruleId":"operator-linebreak","replacedBy":[]},{"ruleId":"semi","replacedBy":[]},{"ruleId":"semi-spacing","replacedBy":[]},{"ruleId":"semi-style","replacedBy":[]},{"ruleId":"space-before-blocks","replacedBy":[]},{"ruleId":"space-before-function-paren","replacedBy":[]},{"ruleId":"space-in-parens","replacedBy":[]},{"ruleId":"space-infix-ops","replacedBy":[]},{"ruleId":"space-unary-ops","replacedBy":[]},{"ruleId":"spaced-comment","replacedBy":[]},{"ruleId":"switch-colon-spacing","replacedBy":[]},{"ruleId":"wrap-iife","replacedBy":[]},{"ruleId":"no-extra-semi","replacedBy":[]},{"ruleId":"no-mixed-spaces-and-tabs","replacedBy":[]}]},{"filePath":"/src/repo/tests/selenium/pageobjects/GlobalWatchlist.page.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"arrow-parens","replacedBy":[]},{"ruleId":"arrow-spacing","replacedBy":[]},{"ruleId":"lines-between-class-members","replacedBy":[]},{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"template-curly-spacing","replacedBy":[]},{"ruleId":"implicit-arrow-linebreak","replacedBy":[]},{"ruleId":"array-bracket-spacing","replacedBy":[]},{"ruleId":"block-spacing","replacedBy":[]},{"ruleId":"brace-style","replacedBy":[]},{"ruleId":"comma-dangle","replacedBy":[]},{"ruleId":"comma-spacing","replacedBy":[]},{"ruleId":"comma-style","replacedBy":[]},{"ruleId":"computed-property-spacing","replacedBy":[]},{"ruleId":"dot-location","replacedBy":[]},{"ruleId":"eol-last","replacedBy":[]},{"ruleId":"func-call-spacing","replacedBy":[]},{"ruleId":"indent","replacedBy":[]},{"ruleId":"key-spacing","replacedBy":[]},{"ruleId":"keyword-spacing","replacedBy":[]},{"ruleId":"linebreak-style","replacedBy":[]},{"ruleId":"max-len","replacedBy":[]},{"ruleId":"max-statements-per-line","replacedBy":[]},{"ruleId":"new-parens","replacedBy":[]},{"ruleId":"no-floating-decimal","replacedBy":[]},{"ruleId":"no-multi-spaces","replacedBy":[]},{"ruleId":"no-multiple-empty-lines","replacedBy":[]},{"ruleId":"no-new-object","replacedBy":["no-object-constructor"]},{"ruleId":"no-tabs","replacedBy":[]},{"ruleId":"no-trailing-spaces","replacedBy":[]},{"ruleId":"no-whitespace-before-property","replacedBy":[]},{"ruleId":"object-curly-spacing","replacedBy":[]},{"ruleId":"operator-linebreak","replacedBy":[]},{"ruleId":"quote-props","replacedBy":[]},{"ruleId":"quotes","replacedBy":[]},{"ruleId":"semi","replacedBy":[]},{"ruleId":"semi-spacing","replacedBy":[]},{"ruleId":"semi-style","replacedBy":[]},{"ruleId":"space-before-blocks","replacedBy":[]},{"ruleId":"space-before-function-paren","replacedBy":[]},{"ruleId":"space-in-parens","replacedBy":[]},{"ruleId":"space-infix-ops","replacedBy":[]},{"ruleId":"space-unary-ops","replacedBy":[]},{"ruleId":"spaced-comment","replacedBy":[]},{"ruleId":"switch-colon-spacing","replacedBy":[]},{"ruleId":"wrap-iife","replacedBy":[]},{"ruleId":"no-extra-semi","replacedBy":[]},{"ruleId":"no-mixed-spaces-and-tabs","replacedBy":[]}]},{"filePath":"/src/repo/tests/selenium/specs/SpecialGlobalWatchlist.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"arrow-parens","replacedBy":[]},{"ruleId":"arrow-spacing","replacedBy":[]},{"ruleId":"lines-between-class-members","replacedBy":[]},{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"template-curly-spacing","replacedBy":[]},{"ruleId":"implicit-arrow-linebreak","replacedBy":[]},{"ruleId":"array-bracket-spacing","replacedBy":[]},{"ruleId":"block-spacing","replacedBy":[]},{"ruleId":"brace-style","replacedBy":[]},{"ruleId":"comma-dangle","replacedBy":[]},{"ruleId":"comma-spacing","replacedBy":[]},{"ruleId":"comma-style","replacedBy":[]},{"ruleId":"computed-property-spacing","replacedBy":[]},{"ruleId":"dot-location","replacedBy":[]},{"ruleId":"eol-last","replacedBy":[]},{"ruleId":"func-call-spacing","replacedBy":[]},{"ruleId":"indent","replacedBy":[]},{"ruleId":"key-spacing","replacedBy":[]},{"ruleId":"keyword-spacing","replacedBy":[]},{"ruleId":"linebreak-style","replacedBy":[]},{"ruleId":"max-len","replacedBy":[]},{"ruleId":"max-statements-per-line","replacedBy":[]},{"ruleId":"new-parens","replacedBy":[]},{"ruleId":"no-floating-decimal","replacedBy":[]},{"ruleId":"no-multi-spaces","replacedBy":[]},{"ruleId":"no-multiple-empty-lines","replacedBy":[]},{"ruleId":"no-new-object","replacedBy":["no-object-constructor"]},{"ruleId":"no-tabs","replacedBy":[]},{"ruleId":"no-trailing-spaces","replacedBy":[]},{"ruleId":"no-whitespace-before-property","replacedBy":[]},{"ruleId":"object-curly-spacing","replacedBy":[]},{"ruleId":"operator-linebreak","replacedBy":[]},{"ruleId":"quote-props","replacedBy":[]},{"ruleId":"quotes","replacedBy":[]},{"ruleId":"semi","replacedBy":[]},{"ruleId":"semi-spacing","replacedBy":[]},{"ruleId":"semi-style","replacedBy":[]},{"ruleId":"space-before-blocks","replacedBy":[]},{"ruleId":"space-before-function-paren","replacedBy":[]},{"ruleId":"space-in-parens","replacedBy":[]},{"ruleId":"space-infix-ops","replacedBy":[]},{"ruleId":"space-unary-ops","replacedBy":[]},{"ruleId":"spaced-comment","replacedBy":[]},{"ruleId":"switch-colon-spacing","replacedBy":[]},{"ruleId":"wrap-iife","replacedBy":[]},{"ruleId":"no-extra-semi","replacedBy":[]},{"ruleId":"no-mixed-spaces-and-tabs","replacedBy":[]}]},{"filePath":"/src/repo/tests/selenium/wdio.conf.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"arrow-parens","replacedBy":[]},{"ruleId":"arrow-spacing","replacedBy":[]},{"ruleId":"lines-between-class-members","replacedBy":[]},{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"template-curly-spacing","replacedBy":[]},{"ruleId":"implicit-arrow-linebreak","replacedBy":[]},{"ruleId":"array-bracket-spacing","replacedBy":[]},{"ruleId":"block-spacing","replacedBy":[]},{"ruleId":"brace-style","replacedBy":[]},{"ruleId":"comma-dangle","replacedBy":[]},{"ruleId":"comma-spacing","replacedBy":[]},{"ruleId":"comma-style","replacedBy":[]},{"ruleId":"computed-property-spacing","replacedBy":[]},{"ruleId":"dot-location","replacedBy":[]},{"ruleId":"eol-last","replacedBy":[]},{"ruleId":"func-call-spacing","replacedBy":[]},{"ruleId":"indent","replacedBy":[]},{"ruleId":"key-spacing","replacedBy":[]},{"ruleId":"keyword-spacing","replacedBy":[]},{"ruleId":"linebreak-style","replacedBy":[]},{"ruleId":"max-len","replacedBy":[]},{"ruleId":"max-statements-per-line","replacedBy":[]},{"ruleId":"new-parens","replacedBy":[]},{"ruleId":"no-floating-decimal","replacedBy":[]},{"ruleId":"no-multi-spaces","replacedBy":[]},{"ruleId":"no-multiple-empty-lines","replacedBy":[]},{"ruleId":"no-new-object","replacedBy":["no-object-constructor"]},{"ruleId":"no-tabs","replacedBy":[]},{"ruleId":"no-trailing-spaces","replacedBy":[]},{"ruleId":"no-whitespace-before-property","replacedBy":[]},{"ruleId":"object-curly-spacing","replacedBy":[]},{"ruleId":"operator-linebreak","replacedBy":[]},{"ruleId":"quote-props","replacedBy":[]},{"ruleId":"quotes","replacedBy":[]},{"ruleId":"semi","replacedBy":[]},{"ruleId":"semi-spacing","replacedBy":[]},{"ruleId":"semi-style","replacedBy":[]},{"ruleId":"space-before-blocks","replacedBy":[]},{"ruleId":"space-before-function-paren","replacedBy":[]},{"ruleId":"space-in-parens","replacedBy":[]},{"ruleId":"space-infix-ops","replacedBy":[]},{"ruleId":"space-unary-ops","replacedBy":[]},{"ruleId":"spaced-comment","replacedBy":[]},{"ruleId":"switch-colon-spacing","replacedBy":[]},{"ruleId":"wrap-iife","replacedBy":[]},{"ruleId":"no-extra-semi","replacedBy":[]},{"ruleId":"no-mixed-spaces-and-tabs","replacedBy":[]}]}]

--- end ---
$ /usr/bin/npm ci
--- stderr ---
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/cli@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/config@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/dot-reporter@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/globals@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/junit-reporter@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/local-runner@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/logger@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/mocha-framework@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/repl@9.4.4',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/reporter@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/runner@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/spec-reporter@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/types@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/utils@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: 'undici@7.10.0',
npm WARN EBADENGINE   required: { node: '>=20.18.1' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: 'webdriver@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: 'webdriverio@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN deprecated lodash.isequal@4.5.0: This package is deprecated. Use require('node:util').isDeepStrictEqual instead.
npm WARN deprecated har-validator@5.1.5: this library is no longer supported
npm WARN deprecated glob@8.1.0: Glob versions prior to v9 are no longer supported
npm WARN deprecated uuid@3.4.0: Please upgrade  to version 7 or higher.  Older versions may use Math.random() in certain circumstances, which is known to be problematic.  See https://v8.dev/blog/math-random for details.
npm WARN deprecated request@2.88.2: request has been deprecated, see https://github.com/request/request/issues/3142
npm WARN deprecated node-domexception@1.0.0: Use your platform's native DOMException instead
--- stdout ---

added 855 packages, and audited 856 packages in 16s

211 packages are looking for funding
  run `npm fund` for details

5 vulnerabilities (3 moderate, 2 critical)

Some issues need review, and may require choosing
a different dependency.

Run `npm audit` for details.

--- end ---
$ /usr/bin/npm test
--- stdout ---

> test
> npm run lint


> lint
> npm run lint:eslint && npm run lint:styles && npm run lint:i18n


> lint:eslint
> eslint --cache .


/src/repo/modules/specialglobalwatchlist/EntryBase.js
  50:1  warning  The type 'GlobalWatchlistWikibaseHandler' is undefined  jsdoc/no-undefined-types
  56:1  warning  The type 'GlobalWatchlistWikibaseHandler' is undefined  jsdoc/no-undefined-types

/src/repo/modules/specialglobalwatchlist/MultiSiteWrapper.js
   9:1  warning  The type 'GlobalWatchlistSiteDisplay' is undefined  jsdoc/no-undefined-types
  10:1  warning  The type 'GlobalWatchlistDebugger' is undefined     jsdoc/no-undefined-types

/src/repo/modules/specialglobalwatchlist/SiteBase.js
    8:1  warning  The type 'GlobalWatchlistDebugger' is undefined        jsdoc/no-undefined-types
    9:1  warning  The type 'GlobalWatchlistLinker' is undefined          jsdoc/no-undefined-types
   12:1  warning  The type 'GlobalWatchlistWatchlistUtils' is undefined  jsdoc/no-undefined-types
  244:1  warning  The type 'GlobalWatchlistSiteDisplay' is undefined     jsdoc/no-undefined-types
  368:1  warning  The type 'GlobalWatchlistSiteDisplay' is undefined     jsdoc/no-undefined-types
  370:1  warning  The type 'GlobalWatchlistEntryBase' is undefined       jsdoc/no-undefined-types
  379:1  warning  The type 'GlobalWatchlistEntryBase' is undefined       jsdoc/no-undefined-types
  424:1  warning  The type 'GlobalWatchlistSiteDisplay' is undefined     jsdoc/no-undefined-types
  433:1  warning  The type 'GlobalWatchlistSiteDisplay' is undefined     jsdoc/no-undefined-types
  443:1  warning  The type 'GlobalWatchlistSiteDisplay' is undefined     jsdoc/no-undefined-types

/src/repo/modules/specialglobalwatchlist/SiteDisplay.js
   14:1  warning  The type 'GlobalWatchlistDebugger' is undefined        jsdoc/no-undefined-types
   15:1  warning  The type 'GlobalWatchlistLinker' is undefined          jsdoc/no-undefined-types
   18:1  warning  The type 'GlobalWatchlistWatchlistUtils' is undefined  jsdoc/no-undefined-types
   48:1  warning  The type 'GlobalWatchlistEntryBase' is undefined       jsdoc/no-undefined-types
  233:1  warning  The type 'GlobalWatchlistEntryBase' is undefined       jsdoc/no-undefined-types

/src/repo/modules/specialglobalwatchlist/WatchlistUtils.js
    7:1  warning  The type 'GlobalWatchlistLinker' is undefined          jsdoc/no-undefined-types
   70:1  warning  This line has a length of 105. Maximum allowed is 100  max-len
  300:1  warning  The type 'GlobalWatchlistSiteBase' is undefined        jsdoc/no-undefined-types
  312:1  warning  The type 'GlobalWatchlistEntryBase' is undefined       jsdoc/no-undefined-types
  329:1  warning  The type 'GlobalWatchlistEntryEdits' is undefined      jsdoc/no-undefined-types
  329:1  warning  The type 'GlobalWatchlistEntryLog' is undefined        jsdoc/no-undefined-types
  332:1  warning  The type 'GlobalWatchlistEntryEdits' is undefined      jsdoc/no-undefined-types
  332:1  warning  The type 'GlobalWatchlistEntryLog' is undefined        jsdoc/no-undefined-types
  412:1  warning  The type 'GlobalWatchlistEntryBase' is undefined       jsdoc/no-undefined-types
  418:1  warning  The type 'GlobalWatchlistEntryEdits' is undefined      jsdoc/no-undefined-types
  418:1  warning  The type 'GlobalWatchlistEntryLog' is undefined        jsdoc/no-undefined-types

/src/repo/modules/specialglobalwatchlist/WikibaseHandler.js
    9:1  warning  The type 'GlobalWatchlistDebugger' is undefined   jsdoc/no-undefined-types
  180:1  warning  The type 'GlobalWatchlistEntryBase' is undefined  jsdoc/no-undefined-types
  214:1  warning  The type 'GlobalWatchlistEntryBase' is undefined  jsdoc/no-undefined-types

/src/repo/modules/specialglobalwatchlist/getSettings.js
  24:1  warning  The type 'GlobalWatchlistDebugger' is undefined  jsdoc/no-undefined-types

/src/repo/tests/qunit/WatchlistUtils.test.js
  8:1  warning  The type 'GlobalWatchlistEntryBase' is undefined  jsdoc/no-undefined-types

✖ 35 problems (0 errors, 35 warnings)


> lint:styles
> stylelint --cache "**/*.{css,less}"


> lint:i18n
> banana-checker --requireLowerCase=0 i18n/

Checked 1 message directory.

--- end ---
$ /usr/bin/npm audit --json
--- stdout ---
{
  "auditReportVersion": 2,
  "vulnerabilities": {
    "form-data": {
      "name": "form-data",
      "severity": "critical",
      "isDirect": false,
      "via": [
        {
          "source": 1106509,
          "name": "form-data",
          "dependency": "form-data",
          "title": "form-data uses unsafe random function in form-data for choosing boundary",
          "url": "https://github.com/advisories/GHSA-fjxv-7rqg-78g4",
          "severity": "critical",
          "cwe": [
            "CWE-330"
          ],
          "cvss": {
            "score": 0,
            "vectorString": null
          },
          "range": "<2.5.4"
        }
      ],
      "effects": [
        "request"
      ],
      "range": "<2.5.4",
      "nodes": [
        "node_modules/form-data"
      ],
      "fixAvailable": false
    },
    "mwbot": {
      "name": "mwbot",
      "severity": "moderate",
      "isDirect": false,
      "via": [
        "request"
      ],
      "effects": [
        "wdio-mediawiki"
      ],
      "range": ">=0.1.6",
      "nodes": [
        "node_modules/mwbot"
      ],
      "fixAvailable": false
    },
    "request": {
      "name": "request",
      "severity": "critical",
      "isDirect": false,
      "via": [
        {
          "source": 1096727,
          "name": "request",
          "dependency": "request",
          "title": "Server-Side Request Forgery in Request",
          "url": "https://github.com/advisories/GHSA-p8p7-x288-28g6",
          "severity": "moderate",
          "cwe": [
            "CWE-918"
          ],
          "cvss": {
            "score": 6.1,
            "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N"
          },
          "range": "<=2.88.2"
        },
        "form-data",
        "tough-cookie"
      ],
      "effects": [
        "mwbot"
      ],
      "range": "*",
      "nodes": [
        "node_modules/request"
      ],
      "fixAvailable": false
    },
    "tough-cookie": {
      "name": "tough-cookie",
      "severity": "moderate",
      "isDirect": false,
      "via": [
        {
          "source": 1097682,
          "name": "tough-cookie",
          "dependency": "tough-cookie",
          "title": "tough-cookie Prototype Pollution vulnerability",
          "url": "https://github.com/advisories/GHSA-72xf-g2v4-qvf3",
          "severity": "moderate",
          "cwe": [
            "CWE-1321"
          ],
          "cvss": {
            "score": 6.5,
            "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N"
          },
          "range": "<4.1.3"
        }
      ],
      "effects": [
        "request"
      ],
      "range": "<4.1.3",
      "nodes": [
        "node_modules/tough-cookie"
      ],
      "fixAvailable": false
    },
    "wdio-mediawiki": {
      "name": "wdio-mediawiki",
      "severity": "moderate",
      "isDirect": true,
      "via": [
        "mwbot"
      ],
      "effects": [],
      "range": "*",
      "nodes": [
        "node_modules/wdio-mediawiki"
      ],
      "fixAvailable": false
    }
  },
  "metadata": {
    "vulnerabilities": {
      "info": 0,
      "low": 0,
      "moderate": 3,
      "high": 0,
      "critical": 2,
      "total": 5
    },
    "dependencies": {
      "prod": 1,
      "dev": 880,
      "optional": 41,
      "peer": 5,
      "peerOptional": 0,
      "total": 880
    }
  }
}

--- end ---
Attempting to npm audit fix
$ /usr/bin/npm audit fix --dry-run --only=dev --json
--- stderr ---
npm WARN invalid config only="dev" set in command line options
npm WARN invalid config Must be one of: null, prod, production
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/cli@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/config@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/dot-reporter@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/globals@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/junit-reporter@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/local-runner@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/logger@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/mocha-framework@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/repl@9.4.4',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/reporter@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/runner@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/spec-reporter@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/types@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/utils@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: 'undici@7.10.0',
npm WARN EBADENGINE   required: { node: '>=20.18.1' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: 'webdriver@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: 'webdriverio@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
--- stdout ---
{
  "added": 25,
  "removed": 0,
  "changed": 0,
  "audited": 881,
  "funding": 211,
  "audit": {
    "auditReportVersion": 2,
    "vulnerabilities": {
      "form-data": {
        "name": "form-data",
        "severity": "critical",
        "isDirect": false,
        "via": [
          {
            "source": 1106509,
            "name": "form-data",
            "dependency": "form-data",
            "title": "form-data uses unsafe random function in form-data for choosing boundary",
            "url": "https://github.com/advisories/GHSA-fjxv-7rqg-78g4",
            "severity": "critical",
            "cwe": [
              "CWE-330"
            ],
            "cvss": {
              "score": 0,
              "vectorString": null
            },
            "range": "<2.5.4"
          }
        ],
        "effects": [
          "request"
        ],
        "range": "<2.5.4",
        "nodes": [
          "node_modules/form-data"
        ],
        "fixAvailable": false
      },
      "mwbot": {
        "name": "mwbot",
        "severity": "moderate",
        "isDirect": false,
        "via": [
          "request"
        ],
        "effects": [
          "wdio-mediawiki"
        ],
        "range": ">=0.1.6",
        "nodes": [
          "node_modules/mwbot"
        ],
        "fixAvailable": false
      },
      "request": {
        "name": "request",
        "severity": "critical",
        "isDirect": false,
        "via": [
          {
            "source": 1096727,
            "name": "request",
            "dependency": "request",
            "title": "Server-Side Request Forgery in Request",
            "url": "https://github.com/advisories/GHSA-p8p7-x288-28g6",
            "severity": "moderate",
            "cwe": [
              "CWE-918"
            ],
            "cvss": {
              "score": 6.1,
              "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N"
            },
            "range": "<=2.88.2"
          },
          "form-data",
          "tough-cookie"
        ],
        "effects": [
          "mwbot"
        ],
        "range": "*",
        "nodes": [
          "node_modules/request"
        ],
        "fixAvailable": false
      },
      "tough-cookie": {
        "name": "tough-cookie",
        "severity": "moderate",
        "isDirect": false,
        "via": [
          {
            "source": 1097682,
            "name": "tough-cookie",
            "dependency": "tough-cookie",
            "title": "tough-cookie Prototype Pollution vulnerability",
            "url": "https://github.com/advisories/GHSA-72xf-g2v4-qvf3",
            "severity": "moderate",
            "cwe": [
              "CWE-1321"
            ],
            "cvss": {
              "score": 6.5,
              "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N"
            },
            "range": "<4.1.3"
          }
        ],
        "effects": [
          "request"
        ],
        "range": "<4.1.3",
        "nodes": [
          "node_modules/tough-cookie"
        ],
        "fixAvailable": false
      },
      "wdio-mediawiki": {
        "name": "wdio-mediawiki",
        "severity": "moderate",
        "isDirect": true,
        "via": [
          "mwbot"
        ],
        "effects": [],
        "range": "*",
        "nodes": [
          "node_modules/wdio-mediawiki"
        ],
        "fixAvailable": false
      }
    },
    "metadata": {
      "vulnerabilities": {
        "info": 0,
        "low": 0,
        "moderate": 3,
        "high": 0,
        "critical": 2,
        "total": 5
      },
      "dependencies": {
        "prod": 1,
        "dev": 880,
        "optional": 41,
        "peer": 5,
        "peerOptional": 0,
        "total": 880
      }
    }
  }
}

--- end ---
{"added": 25, "removed": 0, "changed": 0, "audited": 881, "funding": 211, "audit": {"auditReportVersion": 2, "vulnerabilities": {"form-data": {"name": "form-data", "severity": "critical", "isDirect": false, "via": [{"source": 1106509, "name": "form-data", "dependency": "form-data", "title": "form-data uses unsafe random function in form-data for choosing boundary", "url": "https://github.com/advisories/GHSA-fjxv-7rqg-78g4", "severity": "critical", "cwe": ["CWE-330"], "cvss": {"score": 0, "vectorString": null}, "range": "<2.5.4"}], "effects": ["request"], "range": "<2.5.4", "nodes": ["node_modules/form-data"], "fixAvailable": false}, "mwbot": {"name": "mwbot", "severity": "moderate", "isDirect": false, "via": ["request"], "effects": ["wdio-mediawiki"], "range": ">=0.1.6", "nodes": ["node_modules/mwbot"], "fixAvailable": false}, "request": {"name": "request", "severity": "critical", "isDirect": false, "via": [{"source": 1096727, "name": "request", "dependency": "request", "title": "Server-Side Request Forgery in Request", "url": "https://github.com/advisories/GHSA-p8p7-x288-28g6", "severity": "moderate", "cwe": ["CWE-918"], "cvss": {"score": 6.1, "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N"}, "range": "<=2.88.2"}, "form-data", "tough-cookie"], "effects": ["mwbot"], "range": "*", "nodes": ["node_modules/request"], "fixAvailable": false}, "tough-cookie": {"name": "tough-cookie", "severity": "moderate", "isDirect": false, "via": [{"source": 1097682, "name": "tough-cookie", "dependency": "tough-cookie", "title": "tough-cookie Prototype Pollution vulnerability", "url": "https://github.com/advisories/GHSA-72xf-g2v4-qvf3", "severity": "moderate", "cwe": ["CWE-1321"], "cvss": {"score": 6.5, "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N"}, "range": "<4.1.3"}], "effects": ["request"], "range": "<4.1.3", "nodes": ["node_modules/tough-cookie"], "fixAvailable": false}, "wdio-mediawiki": {"name": "wdio-mediawiki", "severity": "moderate", "isDirect": true, "via": ["mwbot"], "effects": [], "range": "*", "nodes": ["node_modules/wdio-mediawiki"], "fixAvailable": false}}, "metadata": {"vulnerabilities": {"info": 0, "low": 0, "moderate": 3, "high": 0, "critical": 2, "total": 5}, "dependencies": {"prod": 1, "dev": 880, "optional": 41, "peer": 5, "peerOptional": 0, "total": 880}}}}
$ /usr/bin/npm audit fix --only=dev
--- stderr ---
npm WARN invalid config only="dev" set in command line options
npm WARN invalid config Must be one of: null, prod, production
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/cli@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/config@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/dot-reporter@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/globals@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/junit-reporter@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/local-runner@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/logger@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/mocha-framework@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/repl@9.4.4',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/reporter@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/runner@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/spec-reporter@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/types@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/utils@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: 'undici@7.10.0',
npm WARN EBADENGINE   required: { node: '>=20.18.1' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: 'webdriver@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: 'webdriverio@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
--- stdout ---

up to date, audited 856 packages in 3s

211 packages are looking for funding
  run `npm fund` for details

# npm audit report

form-data  <2.5.4
Severity: critical
form-data uses unsafe random function in form-data for choosing boundary - https://github.com/advisories/GHSA-fjxv-7rqg-78g4
No fix available
node_modules/form-data
  request  *
  Depends on vulnerable versions of form-data
  Depends on vulnerable versions of tough-cookie
  node_modules/request
    mwbot  >=0.1.6
    Depends on vulnerable versions of request
    node_modules/mwbot
      wdio-mediawiki  *
      Depends on vulnerable versions of mwbot
      node_modules/wdio-mediawiki


tough-cookie  <4.1.3
Severity: moderate
tough-cookie Prototype Pollution vulnerability - https://github.com/advisories/GHSA-72xf-g2v4-qvf3
No fix available
node_modules/tough-cookie

5 vulnerabilities (3 moderate, 2 critical)

Some issues need review, and may require choosing
a different dependency.

--- end ---
Verifying that tests still pass
$ /usr/bin/npm ci
--- stderr ---
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/cli@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/config@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/dot-reporter@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/globals@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/junit-reporter@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/local-runner@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/logger@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/mocha-framework@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/repl@9.4.4',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/reporter@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/runner@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/spec-reporter@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/types@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@wdio/utils@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: 'undici@7.10.0',
npm WARN EBADENGINE   required: { node: '>=20.18.1' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: 'webdriver@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: 'webdriverio@9.15.0',
npm WARN EBADENGINE   required: { node: '>=18.20.0' },
npm WARN EBADENGINE   current: { node: 'v18.19.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN deprecated lodash.isequal@4.5.0: This package is deprecated. Use require('node:util').isDeepStrictEqual instead.
npm WARN deprecated har-validator@5.1.5: this library is no longer supported
npm WARN deprecated glob@8.1.0: Glob versions prior to v9 are no longer supported
npm WARN deprecated uuid@3.4.0: Please upgrade  to version 7 or higher.  Older versions may use Math.random() in certain circumstances, which is known to be problematic.  See https://v8.dev/blog/math-random for details.
npm WARN deprecated request@2.88.2: request has been deprecated, see https://github.com/request/request/issues/3142
npm WARN deprecated node-domexception@1.0.0: Use your platform's native DOMException instead
--- stdout ---

added 855 packages, and audited 856 packages in 16s

211 packages are looking for funding
  run `npm fund` for details

5 vulnerabilities (3 moderate, 2 critical)

Some issues need review, and may require choosing
a different dependency.

Run `npm audit` for details.

--- end ---
$ /usr/bin/npm test
--- stdout ---

> test
> npm run lint


> lint
> npm run lint:eslint && npm run lint:styles && npm run lint:i18n


> lint:eslint
> eslint --cache .


/src/repo/modules/specialglobalwatchlist/EntryBase.js
  50:1  warning  The type 'GlobalWatchlistWikibaseHandler' is undefined  jsdoc/no-undefined-types
  56:1  warning  The type 'GlobalWatchlistWikibaseHandler' is undefined  jsdoc/no-undefined-types

/src/repo/modules/specialglobalwatchlist/MultiSiteWrapper.js
   9:1  warning  The type 'GlobalWatchlistSiteDisplay' is undefined  jsdoc/no-undefined-types
  10:1  warning  The type 'GlobalWatchlistDebugger' is undefined     jsdoc/no-undefined-types

/src/repo/modules/specialglobalwatchlist/SiteBase.js
    8:1  warning  The type 'GlobalWatchlistDebugger' is undefined        jsdoc/no-undefined-types
    9:1  warning  The type 'GlobalWatchlistLinker' is undefined          jsdoc/no-undefined-types
   12:1  warning  The type 'GlobalWatchlistWatchlistUtils' is undefined  jsdoc/no-undefined-types
  244:1  warning  The type 'GlobalWatchlistSiteDisplay' is undefined     jsdoc/no-undefined-types
  368:1  warning  The type 'GlobalWatchlistSiteDisplay' is undefined     jsdoc/no-undefined-types
  370:1  warning  The type 'GlobalWatchlistEntryBase' is undefined       jsdoc/no-undefined-types
  379:1  warning  The type 'GlobalWatchlistEntryBase' is undefined       jsdoc/no-undefined-types
  424:1  warning  The type 'GlobalWatchlistSiteDisplay' is undefined     jsdoc/no-undefined-types
  433:1  warning  The type 'GlobalWatchlistSiteDisplay' is undefined     jsdoc/no-undefined-types
  443:1  warning  The type 'GlobalWatchlistSiteDisplay' is undefined     jsdoc/no-undefined-types

/src/repo/modules/specialglobalwatchlist/SiteDisplay.js
   14:1  warning  The type 'GlobalWatchlistDebugger' is undefined        jsdoc/no-undefined-types
   15:1  warning  The type 'GlobalWatchlistLinker' is undefined          jsdoc/no-undefined-types
   18:1  warning  The type 'GlobalWatchlistWatchlistUtils' is undefined  jsdoc/no-undefined-types
   48:1  warning  The type 'GlobalWatchlistEntryBase' is undefined       jsdoc/no-undefined-types
  233:1  warning  The type 'GlobalWatchlistEntryBase' is undefined       jsdoc/no-undefined-types

/src/repo/modules/specialglobalwatchlist/WatchlistUtils.js
    7:1  warning  The type 'GlobalWatchlistLinker' is undefined          jsdoc/no-undefined-types
   70:1  warning  This line has a length of 105. Maximum allowed is 100  max-len
  300:1  warning  The type 'GlobalWatchlistSiteBase' is undefined        jsdoc/no-undefined-types
  312:1  warning  The type 'GlobalWatchlistEntryBase' is undefined       jsdoc/no-undefined-types
  329:1  warning  The type 'GlobalWatchlistEntryEdits' is undefined      jsdoc/no-undefined-types
  329:1  warning  The type 'GlobalWatchlistEntryLog' is undefined        jsdoc/no-undefined-types
  332:1  warning  The type 'GlobalWatchlistEntryEdits' is undefined      jsdoc/no-undefined-types
  332:1  warning  The type 'GlobalWatchlistEntryLog' is undefined        jsdoc/no-undefined-types
  412:1  warning  The type 'GlobalWatchlistEntryBase' is undefined       jsdoc/no-undefined-types
  418:1  warning  The type 'GlobalWatchlistEntryEdits' is undefined      jsdoc/no-undefined-types
  418:1  warning  The type 'GlobalWatchlistEntryLog' is undefined        jsdoc/no-undefined-types

/src/repo/modules/specialglobalwatchlist/WikibaseHandler.js
    9:1  warning  The type 'GlobalWatchlistDebugger' is undefined   jsdoc/no-undefined-types
  180:1  warning  The type 'GlobalWatchlistEntryBase' is undefined  jsdoc/no-undefined-types
  214:1  warning  The type 'GlobalWatchlistEntryBase' is undefined  jsdoc/no-undefined-types

/src/repo/modules/specialglobalwatchlist/getSettings.js
  24:1  warning  The type 'GlobalWatchlistDebugger' is undefined  jsdoc/no-undefined-types

/src/repo/tests/qunit/WatchlistUtils.test.js
  8:1  warning  The type 'GlobalWatchlistEntryBase' is undefined  jsdoc/no-undefined-types

✖ 35 problems (0 errors, 35 warnings)


> lint:styles
> stylelint --cache "**/*.{css,less}"


> lint:i18n
> banana-checker --requireLowerCase=0 i18n/

Checked 1 message directory.

--- end ---
$ package-lock-lint package-lock.json
--- stdout ---
Checking package-lock.json

--- end ---
build: Updating eslint-config-wikimedia to 0.31.0

$ git add .
--- stdout ---

--- end ---
$ git commit -F /tmp/tmpa0emcfbo
--- stdout ---
[master e6c9bf3] build: Updating eslint-config-wikimedia to 0.31.0
 2 files changed, 1145 insertions(+), 61 deletions(-)

--- end ---
$ git format-patch HEAD~1 --stdout
--- stdout ---
From e6c9bf3bccf4b1d7017886e51e52ace42043c4b9 Mon Sep 17 00:00:00 2001
From: libraryupgrader <tools.libraryupgrader@tools.wmflabs.org>
Date: Mon, 28 Jul 2025 04:21:07 +0000
Subject: [PATCH] build: Updating eslint-config-wikimedia to 0.31.0

Change-Id: Iabd057ca15d4a91744c529e99db3c4d88e744f82
---
 package-lock.json | 1204 ++++++++++++++++++++++++++++++++++++++++++---
 package.json      |    2 +-
 2 files changed, 1145 insertions(+), 61 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index f166d2a..96f32ef 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,7 +11,7 @@
 				"@wdio/local-runner": "9.15.0",
 				"@wdio/mocha-framework": "9.15.0",
 				"@wdio/spec-reporter": "9.15.0",
-				"eslint-config-wikimedia": "0.30.0",
+				"eslint-config-wikimedia": "0.31.0",
 				"grunt-banana-checker": "0.13.0",
 				"jsdoc": "4.0.4",
 				"jsdoc-wmf-theme": "1.1.0",
@@ -1227,6 +1227,249 @@
 				"url": "https://github.com/sponsors/sindresorhus"
 			}
 		},
+		"node_modules/@stylistic/eslint-plugin": {
+			"version": "3.1.0",
+			"resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-3.1.0.tgz",
+			"integrity": "sha512-pA6VOrOqk0+S8toJYhQGv2MWpQQR0QpeUo9AhNkC49Y26nxBQ/nH1rta9bUU1rPw2fJ1zZEMV5oCX5AazT7J2g==",
+			"dev": true,
+			"dependencies": {
+				"@typescript-eslint/utils": "^8.13.0",
+				"eslint-visitor-keys": "^4.2.0",
+				"espree": "^10.3.0",
+				"estraverse": "^5.3.0",
+				"picomatch": "^4.0.2"
+			},
+			"engines": {
+				"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+			},
+			"peerDependencies": {
+				"eslint": ">=8.40.0"
+			}
+		},
+		"node_modules/@stylistic/eslint-plugin/node_modules/@typescript-eslint/project-service": {
+			"version": "8.38.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.38.0.tgz",
+			"integrity": "sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==",
+			"dev": true,
+			"dependencies": {
+				"@typescript-eslint/tsconfig-utils": "^8.38.0",
+				"@typescript-eslint/types": "^8.38.0",
+				"debug": "^4.3.4"
+			},
+			"engines": {
+				"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			},
+			"peerDependencies": {
+				"typescript": ">=4.8.4 <5.9.0"
+			}
+		},
+		"node_modules/@stylistic/eslint-plugin/node_modules/@typescript-eslint/scope-manager": {
+			"version": "8.38.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.38.0.tgz",
+			"integrity": "sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==",
+			"dev": true,
+			"dependencies": {
+				"@typescript-eslint/types": "8.38.0",
+				"@typescript-eslint/visitor-keys": "8.38.0"
+			},
+			"engines": {
+				"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			}
+		},
+		"node_modules/@stylistic/eslint-plugin/node_modules/@typescript-eslint/tsconfig-utils": {
+			"version": "8.38.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.38.0.tgz",
+			"integrity": "sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==",
+			"dev": true,
+			"engines": {
+				"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			},
+			"peerDependencies": {
+				"typescript": ">=4.8.4 <5.9.0"
+			}
+		},
+		"node_modules/@stylistic/eslint-plugin/node_modules/@typescript-eslint/types": {
+			"version": "8.38.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.38.0.tgz",
+			"integrity": "sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==",
+			"dev": true,
+			"engines": {
+				"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			}
+		},
+		"node_modules/@stylistic/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": {
+			"version": "8.38.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.38.0.tgz",
+			"integrity": "sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==",
+			"dev": true,
+			"dependencies": {
+				"@typescript-eslint/project-service": "8.38.0",
+				"@typescript-eslint/tsconfig-utils": "8.38.0",
+				"@typescript-eslint/types": "8.38.0",
+				"@typescript-eslint/visitor-keys": "8.38.0",
+				"debug": "^4.3.4",
+				"fast-glob": "^3.3.2",
+				"is-glob": "^4.0.3",
+				"minimatch": "^9.0.4",
+				"semver": "^7.6.0",
+				"ts-api-utils": "^2.1.0"
+			},
+			"engines": {
+				"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			},
+			"peerDependencies": {
+				"typescript": ">=4.8.4 <5.9.0"
+			}
+		},
+		"node_modules/@stylistic/eslint-plugin/node_modules/@typescript-eslint/utils": {
+			"version": "8.38.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.38.0.tgz",
+			"integrity": "sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==",
+			"dev": true,
+			"dependencies": {
+				"@eslint-community/eslint-utils": "^4.7.0",
+				"@typescript-eslint/scope-manager": "8.38.0",
+				"@typescript-eslint/types": "8.38.0",
+				"@typescript-eslint/typescript-estree": "8.38.0"
+			},
+			"engines": {
+				"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			},
+			"peerDependencies": {
+				"eslint": "^8.57.0 || ^9.0.0",
+				"typescript": ">=4.8.4 <5.9.0"
+			}
+		},
+		"node_modules/@stylistic/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": {
+			"version": "8.38.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.38.0.tgz",
+			"integrity": "sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==",
+			"dev": true,
+			"dependencies": {
+				"@typescript-eslint/types": "8.38.0",
+				"eslint-visitor-keys": "^4.2.1"
+			},
+			"engines": {
+				"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			}
+		},
+		"node_modules/@stylistic/eslint-plugin/node_modules/brace-expansion": {
+			"version": "2.0.2",
+			"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+			"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+			"dev": true,
+			"dependencies": {
+				"balanced-match": "^1.0.0"
+			}
+		},
+		"node_modules/@stylistic/eslint-plugin/node_modules/eslint-visitor-keys": {
+			"version": "4.2.1",
+			"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+			"integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+			"dev": true,
+			"engines": {
+				"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+			},
+			"funding": {
+				"url": "https://opencollective.com/eslint"
+			}
+		},
+		"node_modules/@stylistic/eslint-plugin/node_modules/espree": {
+			"version": "10.4.0",
+			"resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
+			"integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
+			"dev": true,
+			"dependencies": {
+				"acorn": "^8.15.0",
+				"acorn-jsx": "^5.3.2",
+				"eslint-visitor-keys": "^4.2.1"
+			},
+			"engines": {
+				"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+			},
+			"funding": {
+				"url": "https://opencollective.com/eslint"
+			}
+		},
+		"node_modules/@stylistic/eslint-plugin/node_modules/minimatch": {
+			"version": "9.0.5",
+			"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+			"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+			"dev": true,
+			"dependencies": {
+				"brace-expansion": "^2.0.1"
+			},
+			"engines": {
+				"node": ">=16 || 14 >=14.17"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/isaacs"
+			}
+		},
+		"node_modules/@stylistic/eslint-plugin/node_modules/picomatch": {
+			"version": "4.0.3",
+			"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
+			"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+			"dev": true,
+			"engines": {
+				"node": ">=12"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/jonschlinkert"
+			}
+		},
+		"node_modules/@stylistic/eslint-plugin/node_modules/semver": {
+			"version": "7.7.2",
+			"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
+			"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
+			"dev": true,
+			"bin": {
+				"semver": "bin/semver.js"
+			},
+			"engines": {
+				"node": ">=10"
+			}
+		},
+		"node_modules/@stylistic/eslint-plugin/node_modules/ts-api-utils": {
+			"version": "2.1.0",
+			"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
+			"integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==",
+			"dev": true,
+			"engines": {
+				"node": ">=18.12"
+			},
+			"peerDependencies": {
+				"typescript": ">=4.8.4"
+			}
+		},
 		"node_modules/@stylistic/stylelint-config": {
 			"version": "2.0.0",
 			"resolved": "https://registry.npmjs.org/@stylistic/stylelint-config/-/stylelint-config-2.0.0.tgz",
@@ -1445,6 +1688,86 @@
 				"@types/node": "*"
 			}
 		},
+		"node_modules/@typescript-eslint/eslint-plugin": {
+			"version": "7.18.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz",
+			"integrity": "sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==",
+			"dev": true,
+			"optional": true,
+			"peer": true,
+			"dependencies": {
+				"@eslint-community/regexpp": "^4.10.0",
+				"@typescript-eslint/scope-manager": "7.18.0",
+				"@typescript-eslint/type-utils": "7.18.0",
+				"@typescript-eslint/utils": "7.18.0",
+				"@typescript-eslint/visitor-keys": "7.18.0",
+				"graphemer": "^1.4.0",
+				"ignore": "^5.3.1",
+				"natural-compare": "^1.4.0",
+				"ts-api-utils": "^1.3.0"
+			},
+			"engines": {
+				"node": "^18.18.0 || >=20.0.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			},
+			"peerDependencies": {
+				"@typescript-eslint/parser": "^7.0.0",
+				"eslint": "^8.56.0"
+			},
+			"peerDependenciesMeta": {
+				"typescript": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/@typescript-eslint/parser": {
+			"version": "7.18.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.18.0.tgz",
+			"integrity": "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==",
+			"dev": true,
+			"optional": true,
+			"peer": true,
+			"dependencies": {
+				"@typescript-eslint/scope-manager": "7.18.0",
+				"@typescript-eslint/types": "7.18.0",
+				"@typescript-eslint/typescript-estree": "7.18.0",
+				"@typescript-eslint/visitor-keys": "7.18.0",
+				"debug": "^4.3.4"
+			},
+			"engines": {
+				"node": "^18.18.0 || >=20.0.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			},
+			"peerDependencies": {
+				"eslint": "^8.56.0"
+			},
+			"peerDependenciesMeta": {
+				"typescript": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": {
+			"version": "7.18.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz",
+			"integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==",
+			"dev": true,
+			"optional": true,
+			"peer": true,
+			"engines": {
+				"node": "^18.18.0 || >=20.0.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			}
+		},
 		"node_modules/@typescript-eslint/project-service": {
 			"version": "8.34.0",
 			"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.34.0.tgz",
@@ -1480,13 +1803,13 @@
 			}
 		},
 		"node_modules/@typescript-eslint/scope-manager": {
-			"version": "7.12.0",
-			"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.12.0.tgz",
-			"integrity": "sha512-itF1pTnN6F3unPak+kutH9raIkL3lhH1YRPGgt7QQOh43DQKVJXmWkpb+vpc/TiDHs6RSd9CTbDsc/Y+Ygq7kg==",
+			"version": "7.18.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz",
+			"integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==",
 			"dev": true,
 			"dependencies": {
-				"@typescript-eslint/types": "7.12.0",
-				"@typescript-eslint/visitor-keys": "7.12.0"
+				"@typescript-eslint/types": "7.18.0",
+				"@typescript-eslint/visitor-keys": "7.18.0"
 			},
 			"engines": {
 				"node": "^18.18.0 || >=20.0.0"
@@ -1496,6 +1819,19 @@
 				"url": "https://opencollective.com/typescript-eslint"
 			}
 		},
+		"node_modules/@typescript-eslint/scope-manager/node_modules/@typescript-eslint/types": {
+			"version": "7.18.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz",
+			"integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==",
+			"dev": true,
+			"engines": {
+				"node": "^18.18.0 || >=20.0.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			}
+		},
 		"node_modules/@typescript-eslint/tsconfig-utils": {
 			"version": "8.34.0",
 			"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.34.0.tgz",
@@ -1512,6 +1848,35 @@
 				"typescript": ">=4.8.4 <5.9.0"
 			}
 		},
+		"node_modules/@typescript-eslint/type-utils": {
+			"version": "7.18.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.18.0.tgz",
+			"integrity": "sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==",
+			"dev": true,
+			"optional": true,
+			"peer": true,
+			"dependencies": {
+				"@typescript-eslint/typescript-estree": "7.18.0",
+				"@typescript-eslint/utils": "7.18.0",
+				"debug": "^4.3.4",
+				"ts-api-utils": "^1.3.0"
+			},
+			"engines": {
+				"node": "^18.18.0 || >=20.0.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			},
+			"peerDependencies": {
+				"eslint": "^8.56.0"
+			},
+			"peerDependenciesMeta": {
+				"typescript": {
+					"optional": true
+				}
+			}
+		},
 		"node_modules/@typescript-eslint/types": {
 			"version": "7.12.0",
 			"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.12.0.tgz",
@@ -1526,13 +1891,13 @@
 			}
 		},
 		"node_modules/@typescript-eslint/typescript-estree": {
-			"version": "7.12.0",
-			"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.12.0.tgz",
-			"integrity": "sha512-5bwqLsWBULv1h6pn7cMW5dXX/Y2amRqLaKqsASVwbBHMZSnHqE/HN4vT4fE0aFsiwxYvr98kqOWh1a8ZKXalCQ==",
+			"version": "7.18.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz",
+			"integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==",
 			"dev": true,
 			"dependencies": {
-				"@typescript-eslint/types": "7.12.0",
-				"@typescript-eslint/visitor-keys": "7.12.0",
+				"@typescript-eslint/types": "7.18.0",
+				"@typescript-eslint/visitor-keys": "7.18.0",
 				"debug": "^4.3.4",
 				"globby": "^11.1.0",
 				"is-glob": "^4.0.3",
@@ -1553,6 +1918,19 @@
 				}
 			}
 		},
+		"node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/types": {
+			"version": "7.18.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz",
+			"integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==",
+			"dev": true,
+			"engines": {
+				"node": "^18.18.0 || >=20.0.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			}
+		},
 		"node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
 			"version": "2.0.2",
 			"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
@@ -1578,9 +1956,9 @@
 			}
 		},
 		"node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
-			"version": "7.6.2",
-			"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz",
-			"integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==",
+			"version": "7.7.2",
+			"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
+			"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
 			"dev": true,
 			"bin": {
 				"semver": "bin/semver.js"
@@ -1590,15 +1968,15 @@
 			}
 		},
 		"node_modules/@typescript-eslint/utils": {
-			"version": "7.12.0",
-			"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.12.0.tgz",
-			"integrity": "sha512-Y6hhwxwDx41HNpjuYswYp6gDbkiZ8Hin9Bf5aJQn1bpTs3afYY4GX+MPYxma8jtoIV2GRwTM/UJm/2uGCVv+DQ==",
+			"version": "7.18.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz",
+			"integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==",
 			"dev": true,
 			"dependencies": {
 				"@eslint-community/eslint-utils": "^4.4.0",
-				"@typescript-eslint/scope-manager": "7.12.0",
-				"@typescript-eslint/types": "7.12.0",
-				"@typescript-eslint/typescript-estree": "7.12.0"
+				"@typescript-eslint/scope-manager": "7.18.0",
+				"@typescript-eslint/types": "7.18.0",
+				"@typescript-eslint/typescript-estree": "7.18.0"
 			},
 			"engines": {
 				"node": "^18.18.0 || >=20.0.0"
@@ -1611,13 +1989,26 @@
 				"eslint": "^8.56.0"
 			}
 		},
+		"node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": {
+			"version": "7.18.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz",
+			"integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==",
+			"dev": true,
+			"engines": {
+				"node": "^18.18.0 || >=20.0.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			}
+		},
 		"node_modules/@typescript-eslint/visitor-keys": {
-			"version": "7.12.0",
-			"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.12.0.tgz",
-			"integrity": "sha512-uZk7DevrQLL3vSnfFl5bj4sL75qC9D6EdjemIdbtkuUmIheWpuiiylSY01JxJE7+zGrOWDZrp1WxOuDntvKrHQ==",
+			"version": "7.18.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz",
+			"integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==",
 			"dev": true,
 			"dependencies": {
-				"@typescript-eslint/types": "7.12.0",
+				"@typescript-eslint/types": "7.18.0",
 				"eslint-visitor-keys": "^3.4.3"
 			},
 			"engines": {
@@ -1628,6 +2019,19 @@
 				"url": "https://opencollective.com/typescript-eslint"
 			}
 		},
+		"node_modules/@typescript-eslint/visitor-keys/node_modules/@typescript-eslint/types": {
+			"version": "7.18.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz",
+			"integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==",
+			"dev": true,
+			"engines": {
+				"node": "^18.18.0 || >=20.0.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			}
+		},
 		"node_modules/@ungap/structured-clone": {
 			"version": "1.2.0",
 			"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
@@ -2369,9 +2773,9 @@
 			}
 		},
 		"node_modules/acorn": {
-			"version": "8.11.3",
-			"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
-			"integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
+			"version": "8.15.0",
+			"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
+			"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
 			"dev": true,
 			"bin": {
 				"acorn": "bin/acorn"
@@ -4244,11 +4648,14 @@
 			}
 		},
 		"node_modules/eslint-config-wikimedia": {
-			"version": "0.30.0",
-			"resolved": "https://registry.npmjs.org/eslint-config-wikimedia/-/eslint-config-wikimedia-0.30.0.tgz",
-			"integrity": "sha512-i8ESzSoo0x3Jur/0JhAgCVPxbV51zfdI3MN3MVQPnjiFdmo21CNKmiBBmw8JnJ3fx/d5zHDrBa+yDjxSLpnDlA==",
+			"version": "0.31.0",
+			"resolved": "https://registry.npmjs.org/eslint-config-wikimedia/-/eslint-config-wikimedia-0.31.0.tgz",
+			"integrity": "sha512-Z/t/zGPdxs/ehxb0EM6THNWAzueT7GtuqzjUvmBpkxcTKzZPJEXWnnpswdj/hgv8Ce8PIeDp0zwQxR4e3c9CIw==",
 			"dev": true,
 			"dependencies": {
+				"@stylistic/eslint-plugin": "^3.1.0",
+				"@typescript-eslint/eslint-plugin": "8.35.1",
+				"@typescript-eslint/parser": "8.35.1",
 				"browserslist-config-wikimedia": "^0.7.0",
 				"eslint": "^8.57.0",
 				"eslint-plugin-compat": "^4.2.0",
@@ -4268,7 +4675,287 @@
 				"eslint-plugin-yml": "^1.14.0"
 			},
 			"engines": {
-				"node": ">=18 <23"
+				"node": ">=18 <25"
+			}
+		},
+		"node_modules/eslint-config-wikimedia/node_modules/@typescript-eslint/eslint-plugin": {
+			"version": "8.35.1",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.35.1.tgz",
+			"integrity": "sha512-9XNTlo7P7RJxbVeICaIIIEipqxLKguyh+3UbXuT2XQuFp6d8VOeDEGuz5IiX0dgZo8CiI6aOFLg4e8cF71SFVg==",
+			"dev": true,
+			"dependencies": {
+				"@eslint-community/regexpp": "^4.10.0",
+				"@typescript-eslint/scope-manager": "8.35.1",
+				"@typescript-eslint/type-utils": "8.35.1",
+				"@typescript-eslint/utils": "8.35.1",
+				"@typescript-eslint/visitor-keys": "8.35.1",
+				"graphemer": "^1.4.0",
+				"ignore": "^7.0.0",
+				"natural-compare": "^1.4.0",
+				"ts-api-utils": "^2.1.0"
+			},
+			"engines": {
+				"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			},
+			"peerDependencies": {
+				"@typescript-eslint/parser": "^8.35.1",
+				"eslint": "^8.57.0 || ^9.0.0",
+				"typescript": ">=4.8.4 <5.9.0"
+			}
+		},
+		"node_modules/eslint-config-wikimedia/node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/type-utils": {
+			"version": "8.35.1",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.35.1.tgz",
+			"integrity": "sha512-HOrUBlfVRz5W2LIKpXzZoy6VTZzMu2n8q9C2V/cFngIC5U1nStJgv0tMV4sZPzdf4wQm9/ToWUFPMN9Vq9VJQQ==",
+			"dev": true,
+			"dependencies": {
+				"@typescript-eslint/typescript-estree": "8.35.1",
+				"@typescript-eslint/utils": "8.35.1",
+				"debug": "^4.3.4",
+				"ts-api-utils": "^2.1.0"
+			},
+			"engines": {
+				"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			},
+			"peerDependencies": {
+				"eslint": "^8.57.0 || ^9.0.0",
+				"typescript": ">=4.8.4 <5.9.0"
+			}
+		},
+		"node_modules/eslint-config-wikimedia/node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": {
+			"version": "8.35.1",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.35.1.tgz",
+			"integrity": "sha512-lhnwatFmOFcazAsUm3ZnZFpXSxiwoa1Lj50HphnDe1Et01NF4+hrdXONSUHIcbVu2eFb1bAf+5yjXkGVkXBKAQ==",
+			"dev": true,
+			"dependencies": {
+				"@eslint-community/eslint-utils": "^4.7.0",
+				"@typescript-eslint/scope-manager": "8.35.1",
+				"@typescript-eslint/types": "8.35.1",
+				"@typescript-eslint/typescript-estree": "8.35.1"
+			},
+			"engines": {
+				"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			},
+			"peerDependencies": {
+				"eslint": "^8.57.0 || ^9.0.0",
+				"typescript": ">=4.8.4 <5.9.0"
+			}
+		},
+		"node_modules/eslint-config-wikimedia/node_modules/@typescript-eslint/parser": {
+			"version": "8.35.1",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.35.1.tgz",
+			"integrity": "sha512-3MyiDfrfLeK06bi/g9DqJxP5pV74LNv4rFTyvGDmT3x2p1yp1lOd+qYZfiRPIOf/oON+WRZR5wxxuF85qOar+w==",
+			"dev": true,
+			"dependencies": {
+				"@typescript-eslint/scope-manager": "8.35.1",
+				"@typescript-eslint/types": "8.35.1",
+				"@typescript-eslint/typescript-estree": "8.35.1",
+				"@typescript-eslint/visitor-keys": "8.35.1",
+				"debug": "^4.3.4"
+			},
+			"engines": {
+				"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			},
+			"peerDependencies": {
+				"eslint": "^8.57.0 || ^9.0.0",
+				"typescript": ">=4.8.4 <5.9.0"
+			}
+		},
+		"node_modules/eslint-config-wikimedia/node_modules/@typescript-eslint/project-service": {
+			"version": "8.35.1",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.35.1.tgz",
+			"integrity": "sha512-VYxn/5LOpVxADAuP3NrnxxHYfzVtQzLKeldIhDhzC8UHaiQvYlXvKuVho1qLduFbJjjy5U5bkGwa3rUGUb1Q6Q==",
+			"dev": true,
+			"dependencies": {
+				"@typescript-eslint/tsconfig-utils": "^8.35.1",
+				"@typescript-eslint/types": "^8.35.1",
+				"debug": "^4.3.4"
+			},
+			"engines": {
+				"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			},
+			"peerDependencies": {
+				"typescript": ">=4.8.4 <5.9.0"
+			}
+		},
+		"node_modules/eslint-config-wikimedia/node_modules/@typescript-eslint/scope-manager": {
+			"version": "8.35.1",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.35.1.tgz",
+			"integrity": "sha512-s/Bpd4i7ht2934nG+UoSPlYXd08KYz3bmjLEb7Ye1UVob0d1ENiT3lY8bsCmik4RqfSbPw9xJJHbugpPpP5JUg==",
+			"dev": true,
+			"dependencies": {
+				"@typescript-eslint/types": "8.35.1",
+				"@typescript-eslint/visitor-keys": "8.35.1"
+			},
+			"engines": {
+				"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			}
+		},
+		"node_modules/eslint-config-wikimedia/node_modules/@typescript-eslint/tsconfig-utils": {
+			"version": "8.35.1",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.35.1.tgz",
+			"integrity": "sha512-K5/U9VmT9dTHoNowWZpz+/TObS3xqC5h0xAIjXPw+MNcKV9qg6eSatEnmeAwkjHijhACH0/N7bkhKvbt1+DXWQ==",
+			"dev": true,
+			"engines": {
+				"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			},
+			"peerDependencies": {
+				"typescript": ">=4.8.4 <5.9.0"
+			}
+		},
+		"node_modules/eslint-config-wikimedia/node_modules/@typescript-eslint/types": {
+			"version": "8.35.1",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.35.1.tgz",
+			"integrity": "sha512-q/O04vVnKHfrrhNAscndAn1tuQhIkwqnaW+eu5waD5IPts2eX1dgJxgqcPx5BX109/qAz7IG6VrEPTOYKCNfRQ==",
+			"dev": true,
+			"engines": {
+				"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			}
+		},
+		"node_modules/eslint-config-wikimedia/node_modules/@typescript-eslint/typescript-estree": {
+			"version": "8.35.1",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.35.1.tgz",
+			"integrity": "sha512-Vvpuvj4tBxIka7cPs6Y1uvM7gJgdF5Uu9F+mBJBPY4MhvjrjWGK4H0lVgLJd/8PWZ23FTqsaJaLEkBCFUk8Y9g==",
+			"dev": true,
+			"dependencies": {
+				"@typescript-eslint/project-service": "8.35.1",
+				"@typescript-eslint/tsconfig-utils": "8.35.1",
+				"@typescript-eslint/types": "8.35.1",
+				"@typescript-eslint/visitor-keys": "8.35.1",
+				"debug": "^4.3.4",
+				"fast-glob": "^3.3.2",
+				"is-glob": "^4.0.3",
+				"minimatch": "^9.0.4",
+				"semver": "^7.6.0",
+				"ts-api-utils": "^2.1.0"
+			},
+			"engines": {
+				"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			},
+			"peerDependencies": {
+				"typescript": ">=4.8.4 <5.9.0"
+			}
+		},
+		"node_modules/eslint-config-wikimedia/node_modules/@typescript-eslint/visitor-keys": {
+			"version": "8.35.1",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.35.1.tgz",
+			"integrity": "sha512-VRwixir4zBWCSTP/ljEo091lbpypz57PoeAQ9imjG+vbeof9LplljsL1mos4ccG6H9IjfrVGM359RozUnuFhpw==",
+			"dev": true,
+			"dependencies": {
+				"@typescript-eslint/types": "8.35.1",
+				"eslint-visitor-keys": "^4.2.1"
+			},
+			"engines": {
+				"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			}
+		},
+		"node_modules/eslint-config-wikimedia/node_modules/brace-expansion": {
+			"version": "2.0.2",
+			"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+			"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+			"dev": true,
+			"dependencies": {
+				"balanced-match": "^1.0.0"
+			}
+		},
+		"node_modules/eslint-config-wikimedia/node_modules/eslint-visitor-keys": {
+			"version": "4.2.1",
+			"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+			"integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+			"dev": true,
+			"engines": {
+				"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+			},
+			"funding": {
+				"url": "https://opencollective.com/eslint"
+			}
+		},
+		"node_modules/eslint-config-wikimedia/node_modules/ignore": {
+			"version": "7.0.5",
+			"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
+			"integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
+			"dev": true,
+			"engines": {
+				"node": ">= 4"
+			}
+		},
+		"node_modules/eslint-config-wikimedia/node_modules/minimatch": {
+			"version": "9.0.5",
+			"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+			"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+			"dev": true,
+			"dependencies": {
+				"brace-expansion": "^2.0.1"
+			},
+			"engines": {
+				"node": ">=16 || 14 >=14.17"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/isaacs"
+			}
+		},
+		"node_modules/eslint-config-wikimedia/node_modules/semver": {
+			"version": "7.7.2",
+			"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
+			"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
+			"dev": true,
+			"bin": {
+				"semver": "bin/semver.js"
+			},
+			"engines": {
+				"node": ">=10"
+			}
+		},
+		"node_modules/eslint-config-wikimedia/node_modules/ts-api-utils": {
+			"version": "2.1.0",
+			"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
+			"integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==",
+			"dev": true,
+			"engines": {
+				"node": ">=18.12"
+			},
+			"peerDependencies": {
+				"typescript": ">=4.8.4"
 			}
 		},
 		"node_modules/eslint-plugin-compat": {
@@ -11224,6 +11911,149 @@
 			"integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==",
 			"dev": true
 		},
+		"@stylistic/eslint-plugin": {
+			"version": "3.1.0",
+			"resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-3.1.0.tgz",
+			"integrity": "sha512-pA6VOrOqk0+S8toJYhQGv2MWpQQR0QpeUo9AhNkC49Y26nxBQ/nH1rta9bUU1rPw2fJ1zZEMV5oCX5AazT7J2g==",
+			"dev": true,
+			"requires": {
+				"@typescript-eslint/utils": "^8.13.0",
+				"eslint-visitor-keys": "^4.2.0",
+				"espree": "^10.3.0",
+				"estraverse": "^5.3.0",
+				"picomatch": "^4.0.2"
+			},
+			"dependencies": {
+				"@typescript-eslint/project-service": {
+					"version": "8.38.0",
+					"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.38.0.tgz",
+					"integrity": "sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==",
+					"dev": true,
+					"requires": {
+						"@typescript-eslint/tsconfig-utils": "^8.38.0",
+						"@typescript-eslint/types": "^8.38.0",
+						"debug": "^4.3.4"
+					}
+				},
+				"@typescript-eslint/scope-manager": {
+					"version": "8.38.0",
+					"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.38.0.tgz",
+					"integrity": "sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==",
+					"dev": true,
+					"requires": {
+						"@typescript-eslint/types": "8.38.0",
+						"@typescript-eslint/visitor-keys": "8.38.0"
+					}
+				},
+				"@typescript-eslint/tsconfig-utils": {
+					"version": "8.38.0",
+					"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.38.0.tgz",
+					"integrity": "sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==",
+					"dev": true,
+					"requires": {}
+				},
+				"@typescript-eslint/types": {
+					"version": "8.38.0",
+					"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.38.0.tgz",
+					"integrity": "sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==",
+					"dev": true
+				},
+				"@typescript-eslint/typescript-estree": {
+					"version": "8.38.0",
+					"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.38.0.tgz",
+					"integrity": "sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==",
+					"dev": true,
+					"requires": {
+						"@typescript-eslint/project-service": "8.38.0",
+						"@typescript-eslint/tsconfig-utils": "8.38.0",
+						"@typescript-eslint/types": "8.38.0",
+						"@typescript-eslint/visitor-keys": "8.38.0",
+						"debug": "^4.3.4",
+						"fast-glob": "^3.3.2",
+						"is-glob": "^4.0.3",
+						"minimatch": "^9.0.4",
+						"semver": "^7.6.0",
+						"ts-api-utils": "^2.1.0"
+					}
+				},
+				"@typescript-eslint/utils": {
+					"version": "8.38.0",
+					"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.38.0.tgz",
+					"integrity": "sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==",
+					"dev": true,
+					"requires": {
+						"@eslint-community/eslint-utils": "^4.7.0",
+						"@typescript-eslint/scope-manager": "8.38.0",
+						"@typescript-eslint/types": "8.38.0",
+						"@typescript-eslint/typescript-estree": "8.38.0"
+					}
+				},
+				"@typescript-eslint/visitor-keys": {
+					"version": "8.38.0",
+					"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.38.0.tgz",
+					"integrity": "sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==",
+					"dev": true,
+					"requires": {
+						"@typescript-eslint/types": "8.38.0",
+						"eslint-visitor-keys": "^4.2.1"
+					}
+				},
+				"brace-expansion": {
+					"version": "2.0.2",
+					"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+					"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+					"dev": true,
+					"requires": {
+						"balanced-match": "^1.0.0"
+					}
+				},
+				"eslint-visitor-keys": {
+					"version": "4.2.1",
+					"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+					"integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+					"dev": true
+				},
+				"espree": {
+					"version": "10.4.0",
+					"resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
+					"integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
+					"dev": true,
+					"requires": {
+						"acorn": "^8.15.0",
+						"acorn-jsx": "^5.3.2",
+						"eslint-visitor-keys": "^4.2.1"
+					}
+				},
+				"minimatch": {
+					"version": "9.0.5",
+					"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+					"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+					"dev": true,
+					"requires": {
+						"brace-expansion": "^2.0.1"
+					}
+				},
+				"picomatch": {
+					"version": "4.0.3",
+					"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
+					"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+					"dev": true
+				},
+				"semver": {
+					"version": "7.7.2",
+					"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
+					"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
+					"dev": true
+				},
+				"ts-api-utils": {
+					"version": "2.1.0",
+					"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
+					"integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==",
+					"dev": true,
+					"requires": {}
+				}
+			}
+		},
 		"@stylistic/stylelint-config": {
 			"version": "2.0.0",
 			"resolved": "https://registry.npmjs.org/@stylistic/stylelint-config/-/stylelint-config-2.0.0.tgz",
@@ -11429,6 +12259,50 @@
 				"@types/node": "*"
 			}
 		},
+		"@typescript-eslint/eslint-plugin": {
+			"version": "7.18.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz",
+			"integrity": "sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==",
+			"dev": true,
+			"optional": true,
+			"peer": true,
+			"requires": {
+				"@eslint-community/regexpp": "^4.10.0",
+				"@typescript-eslint/scope-manager": "7.18.0",
+				"@typescript-eslint/type-utils": "7.18.0",
+				"@typescript-eslint/utils": "7.18.0",
+				"@typescript-eslint/visitor-keys": "7.18.0",
+				"graphemer": "^1.4.0",
+				"ignore": "^5.3.1",
+				"natural-compare": "^1.4.0",
+				"ts-api-utils": "^1.3.0"
+			}
+		},
+		"@typescript-eslint/parser": {
+			"version": "7.18.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.18.0.tgz",
+			"integrity": "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==",
+			"dev": true,
+			"optional": true,
+			"peer": true,
+			"requires": {
+				"@typescript-eslint/scope-manager": "7.18.0",
+				"@typescript-eslint/types": "7.18.0",
+				"@typescript-eslint/typescript-estree": "7.18.0",
+				"@typescript-eslint/visitor-keys": "7.18.0",
+				"debug": "^4.3.4"
+			},
+			"dependencies": {
+				"@typescript-eslint/types": {
+					"version": "7.18.0",
+					"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz",
+					"integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==",
+					"dev": true,
+					"optional": true,
+					"peer": true
+				}
+			}
+		},
 		"@typescript-eslint/project-service": {
 			"version": "8.34.0",
 			"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.34.0.tgz",
@@ -11449,13 +12323,21 @@
 			}
 		},
 		"@typescript-eslint/scope-manager": {
-			"version": "7.12.0",
-			"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.12.0.tgz",
-			"integrity": "sha512-itF1pTnN6F3unPak+kutH9raIkL3lhH1YRPGgt7QQOh43DQKVJXmWkpb+vpc/TiDHs6RSd9CTbDsc/Y+Ygq7kg==",
+			"version": "7.18.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz",
+			"integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==",
 			"dev": true,
 			"requires": {
-				"@typescript-eslint/types": "7.12.0",
-				"@typescript-eslint/visitor-keys": "7.12.0"
+				"@typescript-eslint/types": "7.18.0",
+				"@typescript-eslint/visitor-keys": "7.18.0"
+			},
+			"dependencies": {
+				"@typescript-eslint/types": {
+					"version": "7.18.0",
+					"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz",
+					"integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==",
+					"dev": true
+				}
 			}
 		},
 		"@typescript-eslint/tsconfig-utils": {
@@ -11465,6 +12347,20 @@
 			"dev": true,
 			"requires": {}
 		},
+		"@typescript-eslint/type-utils": {
+			"version": "7.18.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.18.0.tgz",
+			"integrity": "sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==",
+			"dev": true,
+			"optional": true,
+			"peer": true,
+			"requires": {
+				"@typescript-eslint/typescript-estree": "7.18.0",
+				"@typescript-eslint/utils": "7.18.0",
+				"debug": "^4.3.4",
+				"ts-api-utils": "^1.3.0"
+			}
+		},
 		"@typescript-eslint/types": {
 			"version": "7.12.0",
 			"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.12.0.tgz",
@@ -11472,13 +12368,13 @@
 			"dev": true
 		},
 		"@typescript-eslint/typescript-estree": {
-			"version": "7.12.0",
-			"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.12.0.tgz",
-			"integrity": "sha512-5bwqLsWBULv1h6pn7cMW5dXX/Y2amRqLaKqsASVwbBHMZSnHqE/HN4vT4fE0aFsiwxYvr98kqOWh1a8ZKXalCQ==",
+			"version": "7.18.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz",
+			"integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==",
 			"dev": true,
 			"requires": {
-				"@typescript-eslint/types": "7.12.0",
-				"@typescript-eslint/visitor-keys": "7.12.0",
+				"@typescript-eslint/types": "7.18.0",
+				"@typescript-eslint/visitor-keys": "7.18.0",
 				"debug": "^4.3.4",
 				"globby": "^11.1.0",
 				"is-glob": "^4.0.3",
@@ -11487,6 +12383,12 @@
 				"ts-api-utils": "^1.3.0"
 			},
 			"dependencies": {
+				"@typescript-eslint/types": {
+					"version": "7.18.0",
+					"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz",
+					"integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==",
+					"dev": true
+				},
 				"brace-expansion": {
 					"version": "2.0.2",
 					"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
@@ -11506,33 +12408,49 @@
 					}
 				},
 				"semver": {
-					"version": "7.6.2",
-					"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz",
-					"integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==",
+					"version": "7.7.2",
+					"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
+					"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
 					"dev": true
 				}
 			}
 		},
 		"@typescript-eslint/utils": {
-			"version": "7.12.0",
-			"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.12.0.tgz",
-			"integrity": "sha512-Y6hhwxwDx41HNpjuYswYp6gDbkiZ8Hin9Bf5aJQn1bpTs3afYY4GX+MPYxma8jtoIV2GRwTM/UJm/2uGCVv+DQ==",
+			"version": "7.18.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz",
+			"integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==",
 			"dev": true,
 			"requires": {
 				"@eslint-community/eslint-utils": "^4.4.0",
-				"@typescript-eslint/scope-manager": "7.12.0",
-				"@typescript-eslint/types": "7.12.0",
-				"@typescript-eslint/typescript-estree": "7.12.0"
+				"@typescript-eslint/scope-manager": "7.18.0",
+				"@typescript-eslint/types": "7.18.0",
+				"@typescript-eslint/typescript-estree": "7.18.0"
+			},
+			"dependencies": {
+				"@typescript-eslint/types": {
+					"version": "7.18.0",
+					"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz",
+					"integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==",
+					"dev": true
+				}
 			}
 		},
 		"@typescript-eslint/visitor-keys": {
-			"version": "7.12.0",
-			"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.12.0.tgz",
-			"integrity": "sha512-uZk7DevrQLL3vSnfFl5bj4sL75qC9D6EdjemIdbtkuUmIheWpuiiylSY01JxJE7+zGrOWDZrp1WxOuDntvKrHQ==",
+			"version": "7.18.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz",
+			"integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==",
 			"dev": true,
 			"requires": {
-				"@typescript-eslint/types": "7.12.0",
+				"@typescript-eslint/types": "7.18.0",
 				"eslint-visitor-keys": "^3.4.3"
+			},
+			"dependencies": {
+				"@typescript-eslint/types": {
+					"version": "7.18.0",
+					"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz",
+					"integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==",
+					"dev": true
+				}
 			}
 		},
 		"@ungap/structured-clone": {
@@ -12073,9 +12991,9 @@
 			}
 		},
 		"acorn": {
-			"version": "8.11.3",
-			"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
-			"integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
+			"version": "8.15.0",
+			"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
+			"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
 			"dev": true
 		},
 		"acorn-jsx": {
@@ -13505,11 +14423,14 @@
 			}
 		},
 		"eslint-config-wikimedia": {
-			"version": "0.30.0",
-			"resolved": "https://registry.npmjs.org/eslint-config-wikimedia/-/eslint-config-wikimedia-0.30.0.tgz",
-			"integrity": "sha512-i8ESzSoo0x3Jur/0JhAgCVPxbV51zfdI3MN3MVQPnjiFdmo21CNKmiBBmw8JnJ3fx/d5zHDrBa+yDjxSLpnDlA==",
+			"version": "0.31.0",
+			"resolved": "https://registry.npmjs.org/eslint-config-wikimedia/-/eslint-config-wikimedia-0.31.0.tgz",
+			"integrity": "sha512-Z/t/zGPdxs/ehxb0EM6THNWAzueT7GtuqzjUvmBpkxcTKzZPJEXWnnpswdj/hgv8Ce8PIeDp0zwQxR4e3c9CIw==",
 			"dev": true,
 			"requires": {
+				"@stylistic/eslint-plugin": "^3.1.0",
+				"@typescript-eslint/eslint-plugin": "8.35.1",
+				"@typescript-eslint/parser": "8.35.1",
 				"browserslist-config-wikimedia": "^0.7.0",
 				"eslint": "^8.57.0",
 				"eslint-plugin-compat": "^4.2.0",
@@ -13527,6 +14448,169 @@
 				"eslint-plugin-vue": "^9.26.0",
 				"eslint-plugin-wdio": "^8.24.12",
 				"eslint-plugin-yml": "^1.14.0"
+			},
+			"dependencies": {
+				"@typescript-eslint/eslint-plugin": {
+					"version": "8.35.1",
+					"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.35.1.tgz",
+					"integrity": "sha512-9XNTlo7P7RJxbVeICaIIIEipqxLKguyh+3UbXuT2XQuFp6d8VOeDEGuz5IiX0dgZo8CiI6aOFLg4e8cF71SFVg==",
+					"dev": true,
+					"requires": {
+						"@eslint-community/regexpp": "^4.10.0",
+						"@typescript-eslint/scope-manager": "8.35.1",
+						"@typescript-eslint/type-utils": "8.35.1",
+						"@typescript-eslint/utils": "8.35.1",
+						"@typescript-eslint/visitor-keys": "8.35.1",
+						"graphemer": "^1.4.0",
+						"ignore": "^7.0.0",
+						"natural-compare": "^1.4.0",
+						"ts-api-utils": "^2.1.0"
+					},
+					"dependencies": {
+						"@typescript-eslint/type-utils": {
+							"version": "8.35.1",
+							"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.35.1.tgz",
+							"integrity": "sha512-HOrUBlfVRz5W2LIKpXzZoy6VTZzMu2n8q9C2V/cFngIC5U1nStJgv0tMV4sZPzdf4wQm9/ToWUFPMN9Vq9VJQQ==",
+							"dev": true,
+							"requires": {
+								"@typescript-eslint/typescript-estree": "8.35.1",
+								"@typescript-eslint/utils": "8.35.1",
+								"debug": "^4.3.4",
+								"ts-api-utils": "^2.1.0"
+							}
+						},
+						"@typescript-eslint/utils": {
+							"version": "8.35.1",
+							"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.35.1.tgz",
+							"integrity": "sha512-lhnwatFmOFcazAsUm3ZnZFpXSxiwoa1Lj50HphnDe1Et01NF4+hrdXONSUHIcbVu2eFb1bAf+5yjXkGVkXBKAQ==",
+							"dev": true,
+							"requires": {
+								"@eslint-community/eslint-utils": "^4.7.0",
+								"@typescript-eslint/scope-manager": "8.35.1",
+								"@typescript-eslint/types": "8.35.1",
+								"@typescript-eslint/typescript-estree": "8.35.1"
+							}
+						}
+					}
+				},
+				"@typescript-eslint/parser": {
+					"version": "8.35.1",
+					"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.35.1.tgz",
+					"integrity": "sha512-3MyiDfrfLeK06bi/g9DqJxP5pV74LNv4rFTyvGDmT3x2p1yp1lOd+qYZfiRPIOf/oON+WRZR5wxxuF85qOar+w==",
+					"dev": true,
+					"requires": {
+						"@typescript-eslint/scope-manager": "8.35.1",
+						"@typescript-eslint/types": "8.35.1",
+						"@typescript-eslint/typescript-estree": "8.35.1",
+						"@typescript-eslint/visitor-keys": "8.35.1",
+						"debug": "^4.3.4"
+					}
+				},
+				"@typescript-eslint/project-service": {
+					"version": "8.35.1",
+					"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.35.1.tgz",
+					"integrity": "sha512-VYxn/5LOpVxADAuP3NrnxxHYfzVtQzLKeldIhDhzC8UHaiQvYlXvKuVho1qLduFbJjjy5U5bkGwa3rUGUb1Q6Q==",
+					"dev": true,
+					"requires": {
+						"@typescript-eslint/tsconfig-utils": "^8.35.1",
+						"@typescript-eslint/types": "^8.35.1",
+						"debug": "^4.3.4"
+					}
+				},
+				"@typescript-eslint/scope-manager": {
+					"version": "8.35.1",
+					"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.35.1.tgz",
+					"integrity": "sha512-s/Bpd4i7ht2934nG+UoSPlYXd08KYz3bmjLEb7Ye1UVob0d1ENiT3lY8bsCmik4RqfSbPw9xJJHbugpPpP5JUg==",
+					"dev": true,
+					"requires": {
+						"@typescript-eslint/types": "8.35.1",
+						"@typescript-eslint/visitor-keys": "8.35.1"
+					}
+				},
+				"@typescript-eslint/tsconfig-utils": {
+					"version": "8.35.1",
+					"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.35.1.tgz",
+					"integrity": "sha512-K5/U9VmT9dTHoNowWZpz+/TObS3xqC5h0xAIjXPw+MNcKV9qg6eSatEnmeAwkjHijhACH0/N7bkhKvbt1+DXWQ==",
+					"dev": true,
+					"requires": {}
+				},
+				"@typescript-eslint/types": {
+					"version": "8.35.1",
+					"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.35.1.tgz",
+					"integrity": "sha512-q/O04vVnKHfrrhNAscndAn1tuQhIkwqnaW+eu5waD5IPts2eX1dgJxgqcPx5BX109/qAz7IG6VrEPTOYKCNfRQ==",
+					"dev": true
+				},
+				"@typescript-eslint/typescript-estree": {
+					"version": "8.35.1",
+					"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.35.1.tgz",
+					"integrity": "sha512-Vvpuvj4tBxIka7cPs6Y1uvM7gJgdF5Uu9F+mBJBPY4MhvjrjWGK4H0lVgLJd/8PWZ23FTqsaJaLEkBCFUk8Y9g==",
+					"dev": true,
+					"requires": {
+						"@typescript-eslint/project-service": "8.35.1",
+						"@typescript-eslint/tsconfig-utils": "8.35.1",
+						"@typescript-eslint/types": "8.35.1",
+						"@typescript-eslint/visitor-keys": "8.35.1",
+						"debug": "^4.3.4",
+						"fast-glob": "^3.3.2",
+						"is-glob": "^4.0.3",
+						"minimatch": "^9.0.4",
+						"semver": "^7.6.0",
+						"ts-api-utils": "^2.1.0"
+					}
+				},
+				"@typescript-eslint/visitor-keys": {
+					"version": "8.35.1",
+					"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.35.1.tgz",
+					"integrity": "sha512-VRwixir4zBWCSTP/ljEo091lbpypz57PoeAQ9imjG+vbeof9LplljsL1mos4ccG6H9IjfrVGM359RozUnuFhpw==",
+					"dev": true,
+					"requires": {
+						"@typescript-eslint/types": "8.35.1",
+						"eslint-visitor-keys": "^4.2.1"
+					}
+				},
+				"brace-expansion": {
+					"version": "2.0.2",
+					"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+					"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+					"dev": true,
+					"requires": {
+						"balanced-match": "^1.0.0"
+					}
+				},
+				"eslint-visitor-keys": {
+					"version": "4.2.1",
+					"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+					"integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+					"dev": true
+				},
+				"ignore": {
+					"version": "7.0.5",
+					"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
+					"integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
+					"dev": true
+				},
+				"minimatch": {
+					"version": "9.0.5",
+					"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+					"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+					"dev": true,
+					"requires": {
+						"brace-expansion": "^2.0.1"
+					}
+				},
+				"semver": {
+					"version": "7.7.2",
+					"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
+					"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
+					"dev": true
+				},
+				"ts-api-utils": {
+					"version": "2.1.0",
+					"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
+					"integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==",
+					"dev": true,
+					"requires": {}
+				}
 			}
 		},
 		"eslint-plugin-compat": {
diff --git a/package.json b/package.json
index 830be39..5024cae 100644
--- a/package.json
+++ b/package.json
@@ -18,7 +18,7 @@
 		"@wdio/local-runner": "9.15.0",
 		"@wdio/mocha-framework": "9.15.0",
 		"@wdio/spec-reporter": "9.15.0",
-		"eslint-config-wikimedia": "0.30.0",
+		"eslint-config-wikimedia": "0.31.0",
 		"grunt-banana-checker": "0.13.0",
 		"jsdoc": "4.0.4",
 		"jsdoc-wmf-theme": "1.1.0",
-- 
2.39.5


--- end ---
Source code is licensed under the AGPL.