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

sourcepatches

This run took 160 seconds.

$ date
--- stdout ---
Mon Mar 20 19:57:47 UTC 2023

--- 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 ---
8e1d315f40c3133654ace17f3be51902beb1f67d refs/heads/master

--- end ---
$ /usr/bin/npm audit --json --legacy-peer-deps
--- stdout ---
{
  "auditReportVersion": 2,
  "vulnerabilities": {
    "@mdx-js/mdx": {
      "name": "@mdx-js/mdx",
      "severity": "high",
      "isDirect": false,
      "via": [
        "remark-mdx",
        "remark-parse"
      ],
      "effects": [
        "@storybook/codemod",
        "@storybook/csf-tools",
        "@storybook/mdx1-csf"
      ],
      "range": "<=1.6.22",
      "nodes": [
        "node_modules/@mdx-js/mdx"
      ],
      "fixAvailable": {
        "name": "@storybook/addon-docs",
        "version": "6.4.22",
        "isSemVerMajor": true
      }
    },
    "@storybook/addon-docs": {
      "name": "@storybook/addon-docs",
      "severity": "high",
      "isDirect": true,
      "via": [
        "@storybook/mdx1-csf"
      ],
      "effects": [],
      "range": ">=6.5.0-alpha.1",
      "nodes": [
        "node_modules/@storybook/addon-docs"
      ],
      "fixAvailable": {
        "name": "@storybook/addon-docs",
        "version": "6.4.22",
        "isSemVerMajor": true
      }
    },
    "@storybook/cli": {
      "name": "@storybook/cli",
      "severity": "high",
      "isDirect": false,
      "via": [
        "@storybook/codemod",
        "update-notifier"
      ],
      "effects": [
        "storybook"
      ],
      "range": "<=7.0.0-alpha.40",
      "nodes": [
        "node_modules/@storybook/cli"
      ],
      "fixAvailable": {
        "name": "storybook",
        "version": "1.0.0",
        "isSemVerMajor": true
      }
    },
    "@storybook/codemod": {
      "name": "@storybook/codemod",
      "severity": "high",
      "isDirect": false,
      "via": [
        "@mdx-js/mdx",
        "@storybook/csf-tools"
      ],
      "effects": [
        "@storybook/cli"
      ],
      "range": "5.2.0-alpha.0 - 7.0.0-alpha.39",
      "nodes": [
        "node_modules/@storybook/codemod"
      ],
      "fixAvailable": {
        "name": "storybook",
        "version": "1.0.0",
        "isSemVerMajor": true
      }
    },
    "@storybook/core": {
      "name": "@storybook/core",
      "severity": "high",
      "isDirect": false,
      "via": [
        "@storybook/core-server"
      ],
      "effects": [
        "@storybook/vue"
      ],
      "range": ">=6.2.0-alpha.0",
      "nodes": [
        "node_modules/@storybook/core"
      ],
      "fixAvailable": {
        "name": "@storybook/vue",
        "version": "6.1.21",
        "isSemVerMajor": true
      }
    },
    "@storybook/core-server": {
      "name": "@storybook/core-server",
      "severity": "high",
      "isDirect": false,
      "via": [
        "@storybook/csf-tools",
        "cpy"
      ],
      "effects": [
        "@storybook/core"
      ],
      "range": "<=7.0.0-alpha.6",
      "nodes": [
        "node_modules/@storybook/core-server"
      ],
      "fixAvailable": {
        "name": "@storybook/vue",
        "version": "6.1.21",
        "isSemVerMajor": true
      }
    },
    "@storybook/csf-tools": {
      "name": "@storybook/csf-tools",
      "severity": "high",
      "isDirect": false,
      "via": [
        "@mdx-js/mdx"
      ],
      "effects": [
        "@storybook/codemod",
        "@storybook/core-server"
      ],
      "range": "<=6.5.0-rc.1",
      "nodes": [
        "node_modules/@storybook/core-server/node_modules/@storybook/csf-tools",
        "node_modules/@storybook/csf-tools"
      ],
      "fixAvailable": {
        "name": "storybook",
        "version": "1.0.0",
        "isSemVerMajor": true
      }
    },
    "@storybook/mdx1-csf": {
      "name": "@storybook/mdx1-csf",
      "severity": "high",
      "isDirect": false,
      "via": [
        "@mdx-js/mdx"
      ],
      "effects": [
        "@storybook/addon-docs"
      ],
      "range": "*",
      "nodes": [
        "node_modules/@storybook/mdx1-csf"
      ],
      "fixAvailable": {
        "name": "@storybook/addon-docs",
        "version": "6.4.22",
        "isSemVerMajor": true
      }
    },
    "@storybook/vue": {
      "name": "@storybook/vue",
      "severity": "high",
      "isDirect": true,
      "via": [
        "@storybook/core"
      ],
      "effects": [],
      "range": "6.2.0-alpha.0 - 6.5.16",
      "nodes": [
        "node_modules/@storybook/vue"
      ],
      "fixAvailable": {
        "name": "@storybook/vue",
        "version": "6.1.21",
        "isSemVerMajor": true
      }
    },
    "chokidar": {
      "name": "chokidar",
      "severity": "high",
      "isDirect": false,
      "via": [
        "glob-parent"
      ],
      "effects": [
        "watchpack-chokidar2"
      ],
      "range": "1.0.0-rc1 - 2.1.8",
      "nodes": [
        "node_modules/watchpack-chokidar2/node_modules/chokidar"
      ],
      "fixAvailable": true
    },
    "cpy": {
      "name": "cpy",
      "severity": "high",
      "isDirect": false,
      "via": [
        "globby"
      ],
      "effects": [
        "@storybook/core-server"
      ],
      "range": "7.0.0 - 8.1.2",
      "nodes": [
        "node_modules/cpy"
      ],
      "fixAvailable": {
        "name": "@storybook/vue",
        "version": "6.1.21",
        "isSemVerMajor": true
      }
    },
    "fast-glob": {
      "name": "fast-glob",
      "severity": "high",
      "isDirect": false,
      "via": [
        "glob-parent"
      ],
      "effects": [
        "globby"
      ],
      "range": "<=2.2.7",
      "nodes": [
        "node_modules/cpy/node_modules/fast-glob"
      ],
      "fixAvailable": {
        "name": "@storybook/vue",
        "version": "6.1.21",
        "isSemVerMajor": true
      }
    },
    "glob-parent": {
      "name": "glob-parent",
      "severity": "high",
      "isDirect": false,
      "via": [
        {
          "source": 1091181,
          "name": "glob-parent",
          "dependency": "glob-parent",
          "title": "glob-parent before 5.1.2 vulnerable to Regular Expression Denial of Service in enclosure regex",
          "url": "https://github.com/advisories/GHSA-ww39-953v-wcq6",
          "severity": "high",
          "cwe": [
            "CWE-400"
          ],
          "cvss": {
            "score": 7.5,
            "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H"
          },
          "range": "<5.1.2"
        }
      ],
      "effects": [
        "chokidar",
        "fast-glob"
      ],
      "range": "<5.1.2",
      "nodes": [
        "node_modules/cpy/node_modules/glob-parent",
        "node_modules/watchpack-chokidar2/node_modules/glob-parent"
      ],
      "fixAvailable": {
        "name": "@storybook/vue",
        "version": "6.1.21",
        "isSemVerMajor": true
      }
    },
    "globby": {
      "name": "globby",
      "severity": "high",
      "isDirect": false,
      "via": [
        "fast-glob"
      ],
      "effects": [
        "cpy"
      ],
      "range": "8.0.0 - 9.2.0",
      "nodes": [
        "node_modules/cpy/node_modules/globby"
      ],
      "fixAvailable": {
        "name": "@storybook/vue",
        "version": "6.1.21",
        "isSemVerMajor": true
      }
    },
    "got": {
      "name": "got",
      "severity": "moderate",
      "isDirect": false,
      "via": [
        {
          "source": 1088948,
          "name": "got",
          "dependency": "got",
          "title": "Got allows a redirect to a UNIX socket",
          "url": "https://github.com/advisories/GHSA-pfrx-2q88-qq97",
          "severity": "moderate",
          "cwe": [],
          "cvss": {
            "score": 5.3,
            "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N"
          },
          "range": "<11.8.5"
        }
      ],
      "effects": [
        "package-json"
      ],
      "range": "<11.8.5",
      "nodes": [
        "node_modules/package-json/node_modules/got"
      ],
      "fixAvailable": {
        "name": "storybook",
        "version": "1.0.0",
        "isSemVerMajor": true
      }
    },
    "jsdoc": {
      "name": "jsdoc",
      "severity": "high",
      "isDirect": true,
      "via": [
        "taffydb"
      ],
      "effects": [],
      "range": "3.2.0-dev - 3.6.11",
      "nodes": [
        "node_modules/jsdoc"
      ],
      "fixAvailable": {
        "name": "jsdoc",
        "version": "4.0.2",
        "isSemVerMajor": true
      }
    },
    "jsdoc-wmf-theme": {
      "name": "jsdoc-wmf-theme",
      "severity": "high",
      "isDirect": true,
      "via": [
        "taffydb"
      ],
      "effects": [],
      "range": "*",
      "nodes": [
        "node_modules/jsdoc-wmf-theme"
      ],
      "fixAvailable": false
    },
    "latest-version": {
      "name": "latest-version",
      "severity": "moderate",
      "isDirect": false,
      "via": [
        "package-json"
      ],
      "effects": [
        "update-notifier"
      ],
      "range": "0.2.0 - 5.1.0",
      "nodes": [
        "node_modules/latest-version"
      ],
      "fixAvailable": {
        "name": "storybook",
        "version": "1.0.0",
        "isSemVerMajor": true
      }
    },
    "mwbot": {
      "name": "mwbot",
      "severity": "moderate",
      "isDirect": false,
      "via": [
        "request"
      ],
      "effects": [
        "wdio-mediawiki"
      ],
      "range": ">=0.1.6",
      "nodes": [
        "node_modules/mwbot"
      ],
      "fixAvailable": false
    },
    "package-json": {
      "name": "package-json",
      "severity": "moderate",
      "isDirect": false,
      "via": [
        "got"
      ],
      "effects": [
        "latest-version"
      ],
      "range": "<=6.5.0",
      "nodes": [
        "node_modules/package-json"
      ],
      "fixAvailable": {
        "name": "storybook",
        "version": "1.0.0",
        "isSemVerMajor": true
      }
    },
    "remark-mdx": {
      "name": "remark-mdx",
      "severity": "high",
      "isDirect": false,
      "via": [
        "remark-parse"
      ],
      "effects": [
        "@mdx-js/mdx"
      ],
      "range": "<=1.6.22",
      "nodes": [
        "node_modules/remark-mdx"
      ],
      "fixAvailable": {
        "name": "@storybook/addon-docs",
        "version": "6.4.22",
        "isSemVerMajor": true
      }
    },
    "remark-parse": {
      "name": "remark-parse",
      "severity": "high",
      "isDirect": false,
      "via": [
        "trim"
      ],
      "effects": [
        "@mdx-js/mdx",
        "remark-mdx"
      ],
      "range": "<=8.0.3",
      "nodes": [
        "node_modules/remark-parse"
      ],
      "fixAvailable": {
        "name": "@storybook/addon-docs",
        "version": "6.4.22",
        "isSemVerMajor": true
      }
    },
    "request": {
      "name": "request",
      "severity": "moderate",
      "isDirect": false,
      "via": [
        {
          "source": 1091410,
          "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": 0,
            "vectorString": null
          },
          "range": "<=2.88.2"
        }
      ],
      "effects": [
        "mwbot"
      ],
      "range": "*",
      "nodes": [
        "node_modules/request"
      ],
      "fixAvailable": false
    },
    "storybook": {
      "name": "storybook",
      "severity": "moderate",
      "isDirect": true,
      "via": [
        "@storybook/cli"
      ],
      "effects": [],
      "range": "5.1.11 - 7.0.0-alpha.40",
      "nodes": [
        "node_modules/storybook"
      ],
      "fixAvailable": {
        "name": "storybook",
        "version": "1.0.0",
        "isSemVerMajor": true
      }
    },
    "taffydb": {
      "name": "taffydb",
      "severity": "high",
      "isDirect": false,
      "via": [
        {
          "source": 1089386,
          "name": "taffydb",
          "dependency": "taffydb",
          "title": "TaffyDB can allow access to any data items in the DB",
          "url": "https://github.com/advisories/GHSA-mxhp-79qh-mcx6",
          "severity": "high",
          "cwe": [
            "CWE-20",
            "CWE-668"
          ],
          "cvss": {
            "score": 7.5,
            "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N"
          },
          "range": "<=2.7.3"
        }
      ],
      "effects": [
        "jsdoc",
        "jsdoc-wmf-theme"
      ],
      "range": "*",
      "nodes": [
        "node_modules/taffydb"
      ],
      "fixAvailable": {
        "name": "jsdoc",
        "version": "4.0.2",
        "isSemVerMajor": true
      }
    },
    "trim": {
      "name": "trim",
      "severity": "high",
      "isDirect": false,
      "via": [
        {
          "source": 1089867,
          "name": "trim",
          "dependency": "trim",
          "title": "Regular Expression Denial of Service in trim",
          "url": "https://github.com/advisories/GHSA-w5p7-h5w8-2hfq",
          "severity": "high",
          "cwe": [
            "CWE-400"
          ],
          "cvss": {
            "score": 7.5,
            "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H"
          },
          "range": "<0.0.3"
        }
      ],
      "effects": [
        "remark-parse"
      ],
      "range": "<0.0.3",
      "nodes": [
        "node_modules/trim"
      ],
      "fixAvailable": {
        "name": "@storybook/addon-docs",
        "version": "6.4.22",
        "isSemVerMajor": true
      }
    },
    "update-notifier": {
      "name": "update-notifier",
      "severity": "moderate",
      "isDirect": false,
      "via": [
        "latest-version"
      ],
      "effects": [
        "@storybook/cli"
      ],
      "range": "0.2.0 - 5.1.0",
      "nodes": [
        "node_modules/update-notifier"
      ],
      "fixAvailable": {
        "name": "storybook",
        "version": "1.0.0",
        "isSemVerMajor": true
      }
    },
    "watchpack": {
      "name": "watchpack",
      "severity": "high",
      "isDirect": false,
      "via": [
        "watchpack-chokidar2"
      ],
      "effects": [
        "webpack"
      ],
      "range": "1.7.2 - 1.7.5",
      "nodes": [
        "node_modules/watchpack"
      ],
      "fixAvailable": true
    },
    "watchpack-chokidar2": {
      "name": "watchpack-chokidar2",
      "severity": "high",
      "isDirect": false,
      "via": [
        "chokidar"
      ],
      "effects": [
        "watchpack"
      ],
      "range": "*",
      "nodes": [
        "node_modules/watchpack-chokidar2"
      ],
      "fixAvailable": true
    },
    "wdio-mediawiki": {
      "name": "wdio-mediawiki",
      "severity": "moderate",
      "isDirect": true,
      "via": [
        "mwbot"
      ],
      "effects": [],
      "range": "*",
      "nodes": [
        "node_modules/wdio-mediawiki"
      ],
      "fixAvailable": false
    },
    "webpack": {
      "name": "webpack",
      "severity": "high",
      "isDirect": false,
      "via": [
        "watchpack"
      ],
      "effects": [],
      "range": "4.44.0 - 4.46.0",
      "nodes": [
        "node_modules/webpack"
      ],
      "fixAvailable": true
    }
  },
  "metadata": {
    "vulnerabilities": {
      "info": 0,
      "low": 0,
      "moderate": 8,
      "high": 23,
      "critical": 0,
      "total": 31
    },
    "dependencies": {
      "prod": 1,
      "dev": 2574,
      "optional": 31,
      "peer": 0,
      "peerOptional": 0,
      "total": 2574
    }
  }
}

--- end ---
$ /usr/bin/composer install
--- stderr ---
No lock file found. Updating dependencies instead of installing from lock file. Use composer update over composer install if you do not have a lock file.
Loading composer repositories with package information
Info from https://repo.packagist.org: #StandWithUkraine
Updating dependencies
Lock file operations: 36 installs, 0 updates, 0 removals
  - Locking composer/pcre (3.1.0)
  - Locking composer/semver (3.3.2)
  - Locking composer/spdx-licenses (1.5.7)
  - Locking composer/xdebug-handler (3.0.3)
  - Locking doctrine/deprecations (v1.0.0)
  - Locking felixfbecker/advanced-json-rpc (v3.2.1)
  - Locking mediawiki/mediawiki-codesniffer (v41.0.0)
  - Locking mediawiki/mediawiki-phan-config (0.12.0)
  - Locking mediawiki/minus-x (1.1.1)
  - Locking mediawiki/phan-taint-check-plugin (4.0.0)
  - Locking microsoft/tolerant-php-parser (v0.1.1)
  - Locking netresearch/jsonmapper (v4.1.0)
  - Locking phan/phan (5.4.1)
  - 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.3.2)
  - Locking phpdocumentor/reflection-common (2.2.0)
  - Locking phpdocumentor/reflection-docblock (5.3.0)
  - Locking phpdocumentor/type-resolver (1.7.0)
  - Locking phpstan/phpdoc-parser (1.16.1)
  - Locking psr/container (1.1.2)
  - Locking psr/log (1.1.4)
  - Locking sabre/event (5.1.4)
  - Locking squizlabs/php_codesniffer (3.7.2)
  - Locking symfony/console (v5.4.21)
  - Locking symfony/deprecation-contracts (v2.5.2)
  - Locking symfony/polyfill-ctype (v1.27.0)
  - Locking symfony/polyfill-intl-grapheme (v1.27.0)
  - Locking symfony/polyfill-intl-normalizer (v1.27.0)
  - Locking symfony/polyfill-mbstring (v1.27.0)
  - Locking symfony/polyfill-php73 (v1.27.0)
  - Locking symfony/polyfill-php80 (v1.27.0)
  - Locking symfony/service-contracts (v2.5.2)
  - Locking symfony/string (v5.4.21)
  - 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: 36 installs, 0 updates, 0 removals
    0 [>---------------------------]    0 [->--------------------------]    0 [--->------------------------]  - Installing composer/pcre (3.1.0): Extracting archive
  - Installing symfony/polyfill-php80 (v1.27.0): Extracting archive
  - Installing squizlabs/php_codesniffer (3.7.2): Extracting archive
  - Installing symfony/polyfill-mbstring (v1.27.0): Extracting archive
  - Installing composer/spdx-licenses (1.5.7): Extracting archive
  - Installing composer/semver (3.3.2): Extracting archive
  - Installing mediawiki/mediawiki-codesniffer (v41.0.0): Extracting archive
  - Installing tysonandre/var_representation_polyfill (0.1.3): Extracting archive
  - Installing symfony/polyfill-intl-normalizer (v1.27.0): Extracting archive
  - Installing symfony/polyfill-intl-grapheme (v1.27.0): Extracting archive
  - Installing symfony/polyfill-ctype (v1.27.0): Extracting archive
  - Installing symfony/string (v5.4.21): Extracting archive
  - Installing symfony/deprecation-contracts (v2.5.2): Extracting archive
  - Installing psr/container (1.1.2): Extracting archive
  - Installing symfony/service-contracts (v2.5.2): Extracting archive
  - Installing symfony/polyfill-php73 (v1.27.0): Extracting archive
  - Installing symfony/console (v5.4.21): Extracting archive
  - Installing sabre/event (5.1.4): Extracting archive
  - Installing netresearch/jsonmapper (v4.1.0): Extracting archive
  - Installing microsoft/tolerant-php-parser (v0.1.1): Extracting archive
  - Installing webmozart/assert (1.11.0): Extracting archive
  - Installing phpstan/phpdoc-parser (1.16.1): Extracting archive
  - Installing phpdocumentor/reflection-common (2.2.0): Extracting archive
  - Installing doctrine/deprecations (v1.0.0): Extracting archive
  - Installing phpdocumentor/type-resolver (1.7.0): Extracting archive
  - Installing phpdocumentor/reflection-docblock (5.3.0): Extracting archive
  - Installing felixfbecker/advanced-json-rpc (v3.2.1): Extracting archive
  - Installing psr/log (1.1.4): Extracting archive
  - Installing composer/xdebug-handler (3.0.3): Extracting archive
  - Installing phan/phan (5.4.1): Extracting archive
  - Installing mediawiki/phan-taint-check-plugin (4.0.0): Extracting archive
  - Installing mediawiki/mediawiki-phan-config (0.12.0): Extracting archive
  - Installing mediawiki/minus-x (1.1.1): 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.3.2): Extracting archive
  0/27 [>---------------------------]   0%
 10/27 [==========>-----------------]  37%
 20/27 [====================>-------]  74%
 27/27 [============================] 100%4 package suggestions were added by new dependencies, use `composer suggest` to see details.
Generating autoload files
14 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
--- stdout ---

--- end ---
Upgrading n:eslint-config-wikimedia from 0.22.1 -> 0.24.0
Upgrading n:stylelint-config-wikimedia from 0.13.1 -> 0.14.0
$ /usr/bin/npm install
--- stderr ---
npm WARN ERESOLVE overriding peer dependency
npm WARN While resolving: @babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.14.5
npm WARN Found: @babel/core@7.11.6
npm WARN node_modules/@babel/core
npm WARN   dev @babel/core@"7.11.6" from the root project
npm WARN   92 more (@babel/helper-compilation-targets, ...)
npm WARN 
npm WARN Could not resolve dependency:
npm WARN peer @babel/core@"^7.13.0" from @babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.14.5
npm WARN node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining
npm WARN   @babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@"^7.14.5" from @babel/preset-env@7.15.0
npm WARN   node_modules/@babel/preset-env
npm WARN 
npm WARN Conflicting peer dependency: @babel/core@7.21.3
npm WARN node_modules/@babel/core
npm WARN   peer @babel/core@"^7.13.0" from @babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.14.5
npm WARN   node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining
npm WARN     @babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@"^7.14.5" from @babel/preset-env@7.15.0
npm WARN     node_modules/@babel/preset-env
npm WARN ERESOLVE overriding peer dependency
npm WARN While resolving: @babel/plugin-proposal-class-static-block@7.14.5
npm WARN Found: @babel/core@7.11.6
npm WARN node_modules/@babel/core
npm WARN   dev @babel/core@"7.11.6" from the root project
npm WARN   92 more (@babel/helper-compilation-targets, ...)
npm WARN 
npm WARN Could not resolve dependency:
npm WARN peer @babel/core@"^7.12.0" from @babel/plugin-proposal-class-static-block@7.14.5
npm WARN node_modules/@babel/plugin-proposal-class-static-block
npm WARN   @babel/plugin-proposal-class-static-block@"^7.14.5" from @babel/preset-env@7.15.0
npm WARN   node_modules/@babel/preset-env
npm WARN 
npm WARN Conflicting peer dependency: @babel/core@7.21.3
npm WARN node_modules/@babel/core
npm WARN   peer @babel/core@"^7.12.0" from @babel/plugin-proposal-class-static-block@7.14.5
npm WARN   node_modules/@babel/plugin-proposal-class-static-block
npm WARN     @babel/plugin-proposal-class-static-block@"^7.14.5" from @babel/preset-env@7.15.0
npm WARN     node_modules/@babel/preset-env
npm WARN ERESOLVE overriding peer dependency
npm WARN While resolving: @storybook/vue@6.4.18
npm WARN Found: @babel/core@7.17.2
npm WARN node_modules/@storybook/vue/node_modules/@babel/core
npm WARN   peer @babel/core@"^7.4.0-0" from @babel/helper-define-polyfill-provider@0.1.5
npm WARN   node_modules/@storybook/vue/node_modules/@babel/helper-define-polyfill-provider
npm WARN     @babel/helper-define-polyfill-provider@"^0.1.5" from babel-plugin-polyfill-corejs3@0.1.7
npm WARN     node_modules/@storybook/vue/node_modules/babel-plugin-polyfill-corejs3
npm WARN   2 more (@storybook/core-common, babel-plugin-polyfill-corejs3)
npm WARN 
npm WARN Could not resolve dependency:
npm WARN peer @babel/core@"*" from @storybook/vue@6.4.18
npm WARN node_modules/@storybook/vue
npm WARN   dev @storybook/vue@"6.4.18" from the root project
npm WARN deprecated @types/easy-table@1.2.0: This is a stub types definition. easy-table provides its own type definitions, so you do not need this installed.
npm WARN deprecated source-map-url@0.4.1: See https://github.com/lydell/source-map-url#deprecated
npm WARN deprecated urix@0.1.0: Please see https://github.com/lydell/urix#deprecated
npm WARN deprecated har-validator@5.1.5: this library is no longer supported
npm WARN deprecated resolve-url@0.2.1: https://github.com/lydell/resolve-url#deprecated
npm WARN deprecated source-map-resolve@0.5.3: See https://github.com/lydell/source-map-resolve#deprecated
npm WARN deprecated sane@4.1.0: some dependency vulnerabilities fixed, support for node < 10 dropped, and newer ECMAScript syntax/features added
npm WARN deprecated chokidar@2.1.8: Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies
npm WARN deprecated querystring@0.2.0: The querystring API is considered Legacy. new code should use the URLSearchParams API instead.
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 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
--- stdout ---

added 3006 packages, and audited 3007 packages in 52s

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

31 vulnerabilities (8 moderate, 23 high)

To address issues that do not require attention, run:
  npm audit fix

To address all issues possible (including breaking changes), run:
  npm audit fix --force

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/EntryBase.js
  36:2   error  ES2015 'RegExp.prototype.flags' property is forbidden  es-x/no-regexp-prototype-flags
  36:15  error  ES2015 'RegExp.prototype.flags' property is forbidden  es-x/no-regexp-prototype-flags

/src/repo/modules/MultiSiteWrapper.js
   61:9   error  ES2015 'Promise' class is forbidden  es-x/no-promise
   79:13  error  ES2015 'Promise' class is forbidden  es-x/no-promise
   87:22  error  ES2015 'Promise' class is forbidden  es-x/no-promise
  102:6   error  ES2015 'Promise' class is forbidden  es-x/no-promise

/src/repo/modules/SiteBase.js
   98:13  error  ES2015 'Promise' class is forbidden  es-x/no-promise
  145:13  error  ES2015 'Promise' class is forbidden  es-x/no-promise
  222:13  error  ES2015 'Promise' class is forbidden  es-x/no-promise
  247:13  error  ES2015 'Promise' class is forbidden  es-x/no-promise
  285:13  error  ES2015 'Promise' class is forbidden  es-x/no-promise
  345:13  error  ES2015 'Promise' class is forbidden  es-x/no-promise
  366:13  error  ES2015 'Promise' class is forbidden  es-x/no-promise

/src/repo/modules/SiteDisplay.js
  82:7   error  ES2015 'RegExp.prototype.flags' property is forbidden  es-x/no-regexp-prototype-flags
  84:33  error  ES2015 'RegExp.prototype.flags' property is forbidden  es-x/no-regexp-prototype-flags

/src/repo/modules/SiteVue.js
  40:2  error  ES2015 'Array.prototype.entries' method is forbidden  es-x/no-array-prototype-entries
  51:2  error  ES2015 'Array.prototype.entries' method is forbidden  es-x/no-array-prototype-entries
  52:2  error  ES2015 'Array.prototype.entries' method is forbidden  es-x/no-array-prototype-entries
  62:2  error  ES2015 'Array.prototype.entries' method is forbidden  es-x/no-array-prototype-entries
  78:2  error  ES2015 'Array.prototype.entries' method is forbidden  es-x/no-array-prototype-entries

/src/repo/modules/SpecialGlobalWatchlist.display.js
  105:14  error  ES2015 'Promise' class is forbidden  es-x/no-promise

/src/repo/modules/WatchlistUtils.js
  377:4  error  ES2015 'RegExp.prototype.flags' property is forbidden  es-x/no-regexp-prototype-flags
  379:4  error  ES2015 'RegExp.prototype.flags' property is forbidden  es-x/no-regexp-prototype-flags

/src/repo/modules/WikibaseHandler.js
   99:13  error  ES2015 'Promise' class is forbidden                   es-x/no-promise
  221:13  error  ES2015 'Promise' class is forbidden                   es-x/no-promise
  225:24  error  ES2015 'Array.prototype.entries' method is forbidden  es-x/no-array-prototype-entries

/src/repo/modules/vue/Site.vue
  118:4  error  ES2015 'Array.prototype.entries' method is forbidden  es-x/no-array-prototype-entries
  129:4  error  ES2015 'Array.prototype.entries' method is forbidden  es-x/no-array-prototype-entries
  143:8  error  ES2015 'Array.prototype.entries' method is forbidden  es-x/no-array-prototype-entries

/src/repo/modules/vue/SpecialGlobalWatchlist.vue
  182:15  error  ES2015 'Promise' class is forbidden                   es-x/no-promise
  193:18  error  ES2015 'Array.prototype.entries' method is forbidden  es-x/no-array-prototype-entries
  234:6   error  ES2015 'Array.prototype.entries' method is forbidden  es-x/no-array-prototype-entries
  234:32  error  ES2015 'Array.prototype.entries' method is forbidden  es-x/no-array-prototype-entries

/src/repo/tests/qunit/WatchlistUtils.tests.js
  351:5  error  ES2015 'RegExp.prototype.flags' property is forbidden  es-x/no-regexp-prototype-flags
  353:5  error  ES2015 'RegExp.prototype.flags' property is forbidden  es-x/no-regexp-prototype-flags

✖ 35 problems (35 errors, 0 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":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/.stylelintrc.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/composer.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/extension.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/ace.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/ar.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/av.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/az.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/ban.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/be.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/bjn.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/blk.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/bn.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/br.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/bs.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/ca.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/ckb.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/cs.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/cy.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/da.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/de.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/el.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/en.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/eo.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/es-formal.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/es.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/et.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/fa.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/fi.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/fr.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/frc.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/frr.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/gu.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/he.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/hi.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/hr.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/hsb.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/hu.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/hy.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/hyw.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/ia.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/id.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/is.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/it.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/ja.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/kn.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/ko.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/ksw.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/ku-latn.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/lb.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/lmo.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/luz.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/lv.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/mk.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/mnw.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/mrh.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/ms-arab.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/ms.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/my.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/nb.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/ne.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/nia.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/nl.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/nqo.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/ojb.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/pl.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/pnb.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/pt-br.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/pt.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/qqq.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/roa-tara.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/ru.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/rue.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/scn.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/sd.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/sdc.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/se.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/sh.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/sje.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/sl.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/smn.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/sms.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/sr-ec.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/sr-el.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/sv.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/sw.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/ta.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/te.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/th.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/ti.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/tly.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/tr.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/uk.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/uz.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/vi.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/war.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/xmf.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/yue.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/zgh.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/zh-hans.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/i18n/zh-hant.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/jsdoc.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/modules/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,"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,"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,"suppressions":[{"kind":"directive","justification":""}]}],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/modules/EntryBase.js","messages":[{"ruleId":"es-x/no-regexp-prototype-flags","severity":2,"message":"ES2015 'RegExp.prototype.flags' property is forbidden.","line":36,"column":2,"nodeType":"MemberExpression","messageId":"forbidden","endLine":36,"endColumn":12},{"ruleId":"es-x/no-regexp-prototype-flags","severity":2,"message":"ES2015 'RegExp.prototype.flags' property is forbidden.","line":36,"column":15,"nodeType":"MemberExpression","messageId":"forbidden","endLine":36,"endColumn":25}],"suppressedMessages":[],"errorCount":2,"fatalErrorCount":0,"warningCount":0,"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":[]}]},{"filePath":"/src/repo/modules/EntryEdits.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/modules/EntryLog.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/modules/Linker.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/modules/MultiSiteWrapper.js","messages":[{"ruleId":"es-x/no-promise","severity":2,"message":"ES2015 'Promise' class is forbidden.","line":61,"column":9,"nodeType":"Identifier","messageId":"forbidden","endLine":61,"endColumn":16},{"ruleId":"es-x/no-promise","severity":2,"message":"ES2015 'Promise' class is forbidden.","line":79,"column":13,"nodeType":"Identifier","messageId":"forbidden","endLine":79,"endColumn":20},{"ruleId":"es-x/no-promise","severity":2,"message":"ES2015 'Promise' class is forbidden.","line":87,"column":22,"nodeType":"Identifier","messageId":"forbidden","endLine":87,"endColumn":29},{"ruleId":"es-x/no-promise","severity":2,"message":"ES2015 'Promise' class is forbidden.","line":102,"column":6,"nodeType":"Identifier","messageId":"forbidden","endLine":102,"endColumn":13}],"suppressedMessages":[],"errorCount":4,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"/**\n * Shared helper to reduce code duplication between Vue and non-Vue versions\n * of Special:GlobalWatchlist\n *\n * @class GlobalWatchlistMultiSiteWrapper\n * @constructor\n *\n * @param {Function} SiteClass either {@link GlobalWatchlistSiteDisplay} or\n *    {@link GlobalWatchlistSiteVue}, used to create 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\tvar GlobalWatchlistLinker = require( './Linker.js' );\n\tvar 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\tvar 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\tvar linker;\n\t/**\n\t * @property {Array} siteList The individual sites\n\t */\n\tthis.siteList = config.siteList.map( function ( 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( function ( 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 markAsSeen\n *\n * @return {Promise} Promise that all sites were marked as seen\n */\nGlobalWatchlistMultiSiteWrapper.prototype.markAllSitesSeen = function () {\n\tvar that = this;\n\n\treturn new Promise( function ( resolve, reject ) {\n\t\tvar 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\tfunction ( confirmed ) {\n\t\t\t\tif ( confirmed ) {\n\t\t\t\t\tPromise.all(\n\t\t\t\t\t\tthat.siteList.map( function ( site ) {\n\t\t\t\t\t\t\treturn site.markAsSeen();\n\t\t\t\t\t\t} )\n\t\t\t\t\t).then( function () {\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":[]}]},{"filePath":"/src/repo/modules/SettingsTour.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/modules/SiteBase.js","messages":[{"ruleId":"es-x/no-promise","severity":2,"message":"ES2015 'Promise' class is forbidden.","line":98,"column":13,"nodeType":"Identifier","messageId":"forbidden","endLine":98,"endColumn":20},{"ruleId":"es-x/no-promise","severity":2,"message":"ES2015 'Promise' class is forbidden.","line":145,"column":13,"nodeType":"Identifier","messageId":"forbidden","endLine":145,"endColumn":20},{"ruleId":"es-x/no-promise","severity":2,"message":"ES2015 'Promise' class is forbidden.","line":222,"column":13,"nodeType":"Identifier","messageId":"forbidden","endLine":222,"endColumn":20},{"ruleId":"es-x/no-promise","severity":2,"message":"ES2015 'Promise' class is forbidden.","line":247,"column":13,"nodeType":"Identifier","messageId":"forbidden","endLine":247,"endColumn":20},{"ruleId":"es-x/no-promise","severity":2,"message":"ES2015 'Promise' class is forbidden.","line":285,"column":13,"nodeType":"Identifier","messageId":"forbidden","endLine":285,"endColumn":20},{"ruleId":"es-x/no-promise","severity":2,"message":"ES2015 'Promise' class is forbidden.","line":345,"column":13,"nodeType":"Identifier","messageId":"forbidden","endLine":345,"endColumn":20},{"ruleId":"es-x/no-promise","severity":2,"message":"ES2015 'Promise' class is forbidden.","line":366,"column":13,"nodeType":"Identifier","messageId":"forbidden","endLine":366,"endColumn":20}],"suppressedMessages":[{"ruleId":"no-unused-vars","severity":2,"message":"'reject' is defined but never used.","line":98,"column":42,"nodeType":"Identifier","messageId":"unusedVar","endLine":98,"endColumn":48,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-unused-vars","severity":2,"message":"'summary' is defined but never used.","line":333,"column":64,"nodeType":"Identifier","messageId":"unusedVar","endLine":333,"endColumn":71,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-unused-vars","severity":2,"message":"'pageTitle' is defined but never used.","line":399,"column":69,"nodeType":"Identifier","messageId":"unusedVar","endLine":399,"endColumn":78,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-unused-vars","severity":2,"message":"'unwatched' is defined but never used.","line":399,"column":80,"nodeType":"Identifier","messageId":"unusedVar","endLine":399,"endColumn":89,"suppressions":[{"kind":"directive","justification":""}]}],"errorCount":7,"fatalErrorCount":0,"warningCount":0,"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\tvar 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\tvar that = this;\n\n\treturn new Promise( function ( resolve, reject ) {\n\t\tthat.debug( 'API.' + name + ' (called), with func & content:', [ func, content ] );\n\t\tthat.apiObject[ func ]( content ).then( function ( 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( function ( code, data ) {\n\t\t\tthat.error( 'API.' + name + ' ' + code, data );\n\t\t\tthat.apiError = true;\n\n\t\t\tvar $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\tvar that = this;\n\n\treturn new Promise( function ( resolve ) {\n\t\tvar 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( function ( response ) {\n\t\t\tif ( response === 'ERROR' ) {\n\t\t\t\tresolve( [] );\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tvar 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( function ( 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\tvar 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( function ( 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 * 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\tvar that = this;\n\treturn new Promise( function ( resolve ) {\n\t\tvar 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( function ( 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\tvar that = this;\n\treturn new Promise( function ( 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\tvar 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( function ( response ) {\n\t\t\t\tvar asObject = {};\n\t\t\t\tresponse.query.tags.forEach( function ( 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\tvar that = this;\n\treturn new Promise( function ( resolve ) {\n\t\tthat.actuallyGetWatchlist( 1, 0 ).then( function ( 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( function ( tagsInfo ) {\n\t\t\t\tvar 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( function ( 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} and {@link GlobalWatchlistSiteVue}\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\tvar that = this;\n\treturn new Promise( function ( 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( function ( 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 `afterMarkAsSeen`\n *   is called, not necessarily after the api call is finished.\n */\nGlobalWatchlistSiteBase.prototype.markAsSeen = function () {\n\tthis.debug( 'markSiteAsSeen - marking' );\n\tvar that = this;\n\n\treturn new Promise( function ( resolve ) {\n\t\tvar 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.afterMarkAsSeen();\n\n\t\t// Done within a promise so that Vue 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} and {@link GlobalWatchlistSiteVue}\n */\nGlobalWatchlistSiteBase.prototype.afterMarkAsSeen = 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} and {@link GlobalWatchlistSiteVue}\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":[]}]},{"filePath":"/src/repo/modules/SiteDisplay.js","messages":[{"ruleId":"es-x/no-regexp-prototype-flags","severity":2,"message":"ES2015 'RegExp.prototype.flags' property is forbidden.","line":82,"column":7,"nodeType":"MemberExpression","messageId":"forbidden","endLine":82,"endColumn":18},{"ruleId":"es-x/no-regexp-prototype-flags","severity":2,"message":"ES2015 'RegExp.prototype.flags' property is forbidden.","line":84,"column":33,"nodeType":"MemberExpression","messageId":"forbidden","endLine":84,"endColumn":44}],"suppressedMessages":[],"errorCount":2,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"/*\n * Extended version of SiteBase.js for use in jQuery version of Special:GlobalWatchlist\n */\n\nvar GlobalWatchlistSiteBase = require( './SiteBase.js' );\n\n/**\n * Represents a specific site, including the display (used in jQuery / non-Vue 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\tvar pageTitle = encodeURIComponent( entry.title ).replace( /'/g, '%27' );\n\tvar $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\tvar that = this;\n\n\t// Actually set up the $row to be returned\n\tvar $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\tvar $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\tvar 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\tvar 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\tvar $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\tvar $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\tvar $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\tvar $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\tvar $unwatchLink = $( '<a>' )\n\t\t.addClass( 'ext-globalwatchlist-watchunwatch' )\n\t\t.text( mw.msg( 'globalwatchlist-unwatch' ) )\n\t\t.on( 'click', function () {\n\t\t\tthat.changeWatched( entry.title, 'unwatch' );\n\t\t} );\n\t$row.append( $unwatchLink )\n\t\t.append( ')' );\n\n\tvar $user = ( this.config.fastMode ? '' : entry.userDisplay );\n\tvar $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\tvar $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\tvar headerTemplate = mw.template.get(\n\t\t'ext.globalwatchlist.specialglobalwatchlist',\n\t\t'templates/siteRowHeader.mustache'\n\t);\n\tvar 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\tvar 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\tvar $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\tvar $ul = $( '<ul>' ),\n\t\tthat = this;\n\tsummary.forEach( function ( element ) {\n\t\t$ul.append( that.makePageLink( element ) );\n\t} );\n\n\tvar 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', function () {\n\t\tthat.markAsSeen();\n\t} );\n\n\tvar $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.afterMarkAsSeen = 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.afterMarkAsSeen */\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\tvar encodedSite = encodeURIComponent( this.siteID );\n\tvar encodedTitle = encodeURIComponent( pageTitle );\n\tvar $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\tvar $links = $entries.children( 'a.ext-globalwatchlist-watchunwatch' );\n\tvar newText = mw.msg( unwatched ? 'globalwatchlist-rewatch' : 'globalwatchlist-unwatch' );\n\tvar that = this;\n\n\t$links.each( function () {\n\t\t$( this ).off( 'click' );\n\t\t$( this ).on( 'click', function () {\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":[]}]},{"filePath":"/src/repo/modules/SiteVue.js","messages":[{"ruleId":"es-x/no-array-prototype-entries","severity":2,"message":"ES2015 'Array.prototype.entries' method is forbidden.","line":40,"column":2,"nodeType":"MemberExpression","messageId":"forbidden","endLine":40,"endColumn":14},{"ruleId":"es-x/no-array-prototype-entries","severity":2,"message":"ES2015 'Array.prototype.entries' method is forbidden.","line":51,"column":2,"nodeType":"MemberExpression","messageId":"forbidden","endLine":51,"endColumn":14},{"ruleId":"es-x/no-array-prototype-entries","severity":2,"message":"ES2015 'Array.prototype.entries' method is forbidden.","line":52,"column":2,"nodeType":"MemberExpression","messageId":"forbidden","endLine":52,"endColumn":14},{"ruleId":"es-x/no-array-prototype-entries","severity":2,"message":"ES2015 'Array.prototype.entries' method is forbidden.","line":62,"column":2,"nodeType":"MemberExpression","messageId":"forbidden","endLine":62,"endColumn":14},{"ruleId":"es-x/no-array-prototype-entries","severity":2,"message":"ES2015 'Array.prototype.entries' method is forbidden.","line":78,"column":2,"nodeType":"MemberExpression","messageId":"forbidden","endLine":78,"endColumn":14}],"suppressedMessages":[],"errorCount":5,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"/*\n * Extended version of SiteBase.js for use in Vue version of Special:GlobalWatchlist\n */\n\nvar GlobalWatchlistSiteBase = require( './SiteBase.js' );\n\n/**\n * Represents a specific site, excluding the display (used in Vue display)\n *\n * @class GlobalWatchlistSiteVue\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 GlobalWatchlistSiteVue(\n\tglobalWatchlistDebug,\n\tlinker,\n\tconfig,\n\tapi,\n\twatchlistUtils,\n\turlFragment\n) {\n\tGlobalWatchlistSiteVue.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// Entries to be used for EntryRow.vue\n\tthis.entries = [];\n}\n\nOO.inheritClass( GlobalWatchlistSiteVue, GlobalWatchlistSiteBase );\n\n/**\n * Update this.entries for the latest entries to show\n *\n * @param {GlobalWatchlistEntryBase[]} summary What should be rendered\n */\nGlobalWatchlistSiteVue.prototype.renderWatchlist = function ( summary ) {\n\tthis.entries = summary;\n\tthis.entries.forEach( function ( entry ) {\n\t\tentry.pageWatched = true;\n\t} );\n};\n\n/**\n * Update display after marking a site as seen\n */\nGlobalWatchlistSiteVue.prototype.afterMarkAsSeen = function () {\n\tthis.debug( 'afterMarkAsSeen - Finished for: ' + this.site );\n\tthis.entries = [];\n};\n\n/**\n * Update entry.pageWatched 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 */\nGlobalWatchlistSiteVue.prototype.processUpdateWatched = function ( pageTitle, unwatched ) {\n\tthis.debug(\n\t\t'Processing after ' + ( unwatched ? 'unwatching' : 'rewatching' ) + ': ' + pageTitle\n\t);\n\n\tvar pageWatched = !unwatched;\n\n\tthis.entries.forEach( function ( entry ) {\n\t\tif ( entry.title === pageTitle ) {\n\t\t\tentry.pageWatched = pageWatched;\n\t\t}\n\t} );\n};\n\nmodule.exports = GlobalWatchlistSiteVue;\n","usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/modules/SpecialGlobalWatchlist.display.js","messages":[{"ruleId":"es-x/no-promise","severity":2,"message":"ES2015 'Promise' class is forbidden.","line":105,"column":14,"nodeType":"Identifier","messageId":"forbidden","endLine":105,"endColumn":21}],"suppressedMessages":[{"ruleId":"no-console","severity":2,"message":"Unexpected console statement.","line":161,"column":5,"nodeType":"MemberExpression","messageId":"unexpected","endLine":161,"endColumn":16,"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":238,"column":3,"nodeType":"CallExpression","endLine":238,"endColumn":38,"suppressions":[{"kind":"directive","justification":""}]}],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"/* eslint-disable no-jquery/no-global-selector */\n/*\n * Javascript for Special:GlobalWatchlist\n */\n( function () {\n\t'use strict';\n\n\tvar GlobalWatchlistDebugger = require( './Debug.js' ),\n\t\tgetSettings = require( './getSettings.js' ),\n\t\tconfig = {},\n\t\tMultiSiteWrapper = require( './MultiSiteWrapper.js' ),\n\t\tWatchedSite = require( './SiteDisplay.js' ),\n\t\tviewElements = {},\n\t\tviewManager = {};\n\tvar globalWatchlistDebug = new GlobalWatchlistDebugger();\n\n\tconfig = getSettings( globalWatchlistDebug );\n\tconfig.inLive = false;\n\n\tvar watchedSites = new MultiSiteWrapper(\n\t\tWatchedSite,\n\t\tconfig,\n\t\tglobalWatchlistDebug\n\t);\n\n\tviewElements.groupPage = new OO.ui.ToggleButtonWidget( {\n\t\tdisabled: config.fastMode,\n\t\tlabel: mw.msg( 'globalwatchlist-option-grouppage' ),\n\t\tvalue: config.groupPage && !config.fastMode\n\t} ).on( 'click', function () {\n\t\tconfig.groupPage = viewElements.groupPage.value;\n\t\tviewManager.renderFeed();\n\t} );\n\tviewElements.liveToggle = new OO.ui.ToggleButtonWidget( {\n\t\tdisabled: config.fastMode,\n\t\tlabel: mw.msg( 'globalwatchlist-option-live' ),\n\t\tvalue: false\n\t} ).on( 'click', function () {\n\t\tif ( viewElements.liveToggle.value ) {\n\t\t\tviewManager.startLiveUpdates();\n\t\t} else {\n\t\t\tviewManager.showFeed();\n\t\t}\n\t} );\n\tviewElements.settingsLink = new OO.ui.ButtonWidget( {\n\t\tflags: [ 'progressive' ],\n\t\thref: mw.config.get( 'wgArticlePath' ).replace( '$1', 'Special:GlobalWatchlistSettings' ),\n\t\ticon: 'settings',\n\t\tlabel: mw.msg( 'globalwatchlist-globalwatchlistsettingslink' )\n\t} );\n\tviewElements.markAllSeen = new OO.ui.ButtonInputWidget( {\n\t\tflags: [ 'primary', 'destructive' ],\n\t\ticon: 'checkAll',\n\t\tid: 'ext-globalwatchlist-markseen-all',\n\t\tlabel: mw.msg( 'globalwatchlist-markseen-all' )\n\t} ).on( 'click', function () {\n\t\twatchedSites.markAllSitesSeen();\n\t} );\n\tviewElements.refresh = new OO.ui.ButtonInputWidget( {\n\t\tflags: [ 'primary', 'progressive' ],\n\t\ticon: 'reload',\n\t\tid: 'ext-globalwatchlist-refresh',\n\t\tlabel: mw.msg( 'globalwatchlist-refresh' )\n\t} ).on( 'click', function () {\n\t\tviewManager.renderFeed();\n\t} );\n\tviewElements.progressBar = new OO.ui.ProgressBarWidget( {\n\t\tid: 'ext-globalwatchlist-watchlistsloading'\n\t} );\n\tviewElements.progressBar.$element.hide();\n\tviewElements.$asOf = $( '<label>' )\n\t\t.attr( 'id', 'ext-globalwatchlist-asof' );\n\tviewElements.$sharedFeed = $( '<div>' )\n\t\t.attr( 'id', 'ext-globalwatchlist-watchlistsfeed' );\n\tviewElements.$toolbar = $( '<div>' )\n\t\t.attr( 'id', 'ext-globalwatchlist-toolbar' )\n\t\t.append(\n\t\t\tviewElements.liveToggle.$element,\n\t\t\tviewElements.groupPage.$element,\n\t\t\tviewElements.refresh.$element,\n\t\t\tviewElements.settingsLink.$element,\n\t\t\tviewElements.markAllSeen.$element\n\t\t);\n\t// The \"Sites with changes\" label\n\tviewElements.$feedHeader = $( '<label>' )\n\t\t.text( mw.msg( 'globalwatchlist-changesfeed' ) );\n\tviewElements.$feedHeader.hide();\n\n\tviewManager.newEmptySiteRow = function ( site ) {\n\t\tvar template = mw.template.get(\n\t\t\t'ext.globalwatchlist.specialglobalwatchlist',\n\t\t\t'templates/newEmptySiteRow.mustache'\n\t\t);\n\t\tvar params = {\n\t\t\t'special-watchlist-url': '//' + site + mw.config.get( 'wgArticlePath' ).replace( '$1', 'Special:Watchlist' ),\n\t\t\t'site-name': site,\n\t\t\t'special-edit-watchlist-url': '//' + site + mw.config.get( 'wgArticlePath' ).replace( '$1', 'Special:EditWatchlist' ),\n\t\t\t'edit-watchlist-msg': mw.msg( 'globalwatchlist-editwatchlist' )\n\t\t};\n\t\treturn template.render( params );\n\t};\n\tviewManager.refresh = function () {\n\t\tglobalWatchlistDebug.info( 'watchlists.refresh - starting' );\n\t\tconfig.time = new Date();\n\t\treturn new Promise( function ( resolve ) {\n\t\t\twatchedSites.getAllWatchlists( config ).then( function () {\n\t\t\t\tvar $div = $( '<div>' ).attr( 'id', 'ext-globalwatchlist-feedcollector' ),\n\t\t\t\t\temptySites = [],\n\t\t\t\t\thaveChangesToShow = false;\n\n\t\t\t\twatchedSites.siteList.forEach( function ( site ) {\n\t\t\t\t\tif ( site.isEmpty ) {\n\t\t\t\t\t\temptySites.push( site.site );\n\t\t\t\t\t} else {\n\t\t\t\t\t\thaveChangesToShow = true;\n\t\t\t\t\t\t$div.append( site.$feedDiv );\n\t\t\t\t\t}\n\t\t\t\t} );\n\n\t\t\t\t// Only show the \"Sites with changes\" message if there\n\t\t\t\t// are any sites without changes, otherwise its not helpful\n\t\t\t\t// See T274720\n\t\t\t\tif ( haveChangesToShow && emptySites[ 0 ] ) {\n\t\t\t\t\tviewElements.$feedHeader.show();\n\t\t\t\t\t$div.append( $( '<hr>' ) );\n\t\t\t\t} else {\n\t\t\t\t\tviewElements.$feedHeader.hide();\n\t\t\t\t}\n\n\t\t\t\tif ( emptySites[ 0 ] ) {\n\t\t\t\t\tvar $ul = $( '<ul>' );\n\t\t\t\t\temptySites.forEach( function ( site ) {\n\t\t\t\t\t\t$ul.append(\n\t\t\t\t\t\t\tviewManager.newEmptySiteRow( site )\n\t\t\t\t\t\t);\n\t\t\t\t\t} );\n\t\t\t\t\tvar $emptySitesDiv = mw.template.get(\n\t\t\t\t\t\t'ext.globalwatchlist.specialglobalwatchlist',\n\t\t\t\t\t\t'templates/allEmptySites.mustache'\n\t\t\t\t\t).render( {\n\t\t\t\t\t\t'empty-sites': $ul[ 0 ].outerHTML\n\t\t\t\t\t} )\n\t\t\t\t\t\t.makeCollapsible();\n\n\t\t\t\t\t$div.append(\n\t\t\t\t\t\t$( '<label>' ).text( mw.msg( 'globalwatchlist-emptyfeed' ) ),\n\t\t\t\t\t\t$emptySitesDiv\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tviewElements.$sharedFeed.empty()\n\t\t\t\t\t.append( $div );\n\t\t\t\tviewManager.runLive();\n\t\t\t\tviewElements.$asOf[ 0 ].innerText = mw.msg(\n\t\t\t\t\t'globalwatchlist-asof',\n\t\t\t\t\tconfig.time.toUTCString()\n\t\t\t\t);\n\t\t\t\tresolve();\n\t\t\t} ).catch( function ( error ) {\n\t\t\t\t/* eslint-disable-next-line no-console */\n\t\t\t\tconsole.log( error );\n\t\t\t\tglobalWatchlistDebug.info( 'watchlists.refresh ERROR', error );\n\t\t\t\tresolve();\n\t\t\t} );\n\t\t} );\n\t};\n\tviewManager.renderFeed = function () {\n\t\tglobalWatchlistDebug.info( 'renderFeed - called' );\n\t\tif ( config.inLive === true ) {\n\t\t\treturn;\n\t\t}\n\n\t\tconfig.inLive = false;\n\n\t\tviewElements.liveToggle.setDisabled( true );\n\t\tviewElements.groupPage.setDisabled( true );\n\t\tviewElements.progressBar.$element.show();\n\t\tviewElements.$feedHeader.hide();\n\t\tviewElements.$sharedFeed.hide();\n\t\tviewElements.$asOf[ 0 ].innerText = '';\n\n\t\tviewManager.refresh().then( function () {\n\t\t\tviewManager.showFeed();\n\t\t} );\n\t};\n\n\tviewManager.maybeLiveRefresh = function () {\n\t\t// In case the live updates were disabled between when the refresh\n\t\t// was queued and when it was called, double check current config\n\t\t// before proceeding to refresh()\n\t\tif ( config.inLive ) {\n\t\t\tviewManager.refresh();\n\t\t}\n\t};\n\n\tviewManager.runLive = function () {\n\t\tif ( config.inLive === true ) {\n\t\t\tsetTimeout( viewManager.maybeLiveRefresh, 7500 );\n\t\t}\n\t};\n\n\t// Displaying the global watchlist\n\tviewManager.showFeed = function () {\n\t\tglobalWatchlistDebug.info( 'mode - displaying watchlist' );\n\t\tconfig.inLive = false;\n\n\t\tviewElements.liveToggle.setDisabled( config.fastMode );\n\t\tviewElements.refresh.setDisabled( false );\n\t\tviewElements.groupPage.setDisabled( config.fastMode );\n\t\tviewElements.liveToggle.setIcon( 'play' );\n\t\tviewElements.progressBar.$element.hide();\n\t\tviewElements.$sharedFeed.show();\n\t};\n\n\t// Running in live updates mode\n\tviewManager.startLiveUpdates = function () {\n\t\tglobalWatchlistDebug.info( 'mode - starting live updates' );\n\t\tconfig.inLive = true;\n\n\t\tviewElements.refresh.setDisabled( true );\n\t\tviewElements.groupPage.setDisabled( true );\n\t\tviewElements.liveToggle.setIcon( 'pause' );\n\t\tviewManager.runLive();\n\t};\n\n\tmw.globalwatchlist = {\n\t\tconfig: config,\n\t\tdebug: globalWatchlistDebug,\n\t\telements: viewElements,\n\t\tview: viewManager,\n\t\twatchedSites: watchedSites\n\t};\n\n\t// On ready initialization\n\t$( function () {\n\t\tglobalWatchlistDebug.info( 'GlobalWatchlist - javascript loaded!' );\n\n\t\t$( '.ext-globalwatchlist-content' )\n\t\t\t.empty()\n\t\t\t.append(\n\t\t\t\tviewElements.$toolbar,\n\t\t\t\tviewElements.$asOf,\n\t\t\t\tviewElements.progressBar.$element,\n\t\t\t\tviewElements.$feedHeader,\n\t\t\t\tviewElements.$sharedFeed\n\t\t\t);\n\n\t\t// Based on viewManager.renderFeed but with timing\n\t\tvar loadStartTime = mw.now();\n\t\tviewElements.liveToggle.setDisabled( true );\n\t\tviewElements.groupPage.setDisabled( true );\n\t\tviewElements.$sharedFeed.hide();\n\t\tviewElements.$asOf[ 0 ].innerText = '';\n\n\t\t// Wait a bit before showing the progress bar, hopefully if the user's\n\t\t// internet is fast enough clearTimeout will be called before the loading bar\n\t\t// is ever shown. See T268268\n\t\tvar timer = setTimeout( function () {\n\t\t\tviewElements.progressBar.$element.show();\n\t\t}, 1500 );\n\n\t\tviewManager.refresh().then( function () {\n\t\t\t// If the progress bar wasn't shown, prevent timer from finishing\n\t\t\tclearTimeout( timer );\n\n\t\t\tviewManager.showFeed();\n\n\t\t\tvar metricName = config.fastMode ?\n\t\t\t\t'timing.MediaWiki.GlobalWatchlist.firstload.display.fastmode' :\n\t\t\t\t'timing.MediaWiki.GlobalWatchlist.firstload.display.normal';\n\t\t\tvar loadEndTime = mw.now();\n\t\t\tvar loadElapsedTime = loadEndTime - loadStartTime;\n\t\t\tmw.track( metricName, loadElapsedTime );\n\t\t} );\n\n\t\t// Only run live updates when the special page is being displayed\n\t\t// Note: the page visibility api isn't available for some of the\n\t\t// older versions of mobile browsers that MediaWiki still provides\n\t\t// Grade A support for, but its better than nothing. See T268266\n\t\tdocument.addEventListener( 'visibilitychange', function () {\n\t\t\tif ( document.visibilityState === 'hidden' ) {\n\t\t\t\t// Pause live updates\n\t\t\t\tif ( config.inLive === true ) {\n\t\t\t\t\tconfig.inLive = 'paused';\n\t\t\t\t}\n\t\t\t} else if ( document.visibilityState === 'visible' ) {\n\t\t\t\t// Unpause\n\t\t\t\tif ( config.inLive === 'paused' ) {\n\t\t\t\t\t// Set back to true in the method, as well as requeueing\n\t\t\t\t\t// the actual updates\n\t\t\t\t\tviewManager.startLiveUpdates();\n\t\t\t\t}\n\t\t\t}\n\t\t} );\n\t} );\n}() );\n","usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/modules/SpecialGlobalWatchlist.vue.js","messages":[],"suppressedMessages":[{"ruleId":"no-jquery/no-global-selector","severity":2,"message":"Avoid queries which search the entire DOM. Keep DOM nodes in memory where possible.","line":10,"column":3,"nodeType":"CallExpression","endLine":10,"endColumn":38,"suppressions":[{"kind":"directive","justification":""}]}],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/modules/WatchlistUtils.js","messages":[{"ruleId":"es-x/no-regexp-prototype-flags","severity":2,"message":"ES2015 'RegExp.prototype.flags' property is forbidden.","line":377,"column":4,"nodeType":"MemberExpression","messageId":"forbidden","endLine":377,"endColumn":15},{"ruleId":"es-x/no-regexp-prototype-flags","severity":2,"message":"ES2015 'RegExp.prototype.flags' property is forbidden.","line":379,"column":4,"nodeType":"MemberExpression","messageId":"forbidden","endLine":379,"endColumn":15}],"suppressedMessages":[],"errorCount":2,"fatalErrorCount":0,"warningCount":0,"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\tvar 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( function ( edit ) {\n\t\t\treturn edit.bot;\n\t\t} )\n\t\t.reduce( function ( bot1, bot2 ) {\n\t\t\t// The combined edits are only tagged as bot if all of the edits where bot edits\n\t\t\treturn 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( function ( edit ) {\n\t\t\treturn edit.old_revid;\n\t\t} )\n\t\t.reduce( function ( edit1, edit2 ) {\n\t\t\t// Get the lower rev id, corresponding to the older revision\n\t\t\treturn ( edit1 > edit2 ? edit2 : edit1 );\n\t\t} );\n\n\tmergedEditInfo.minor = edits\n\t\t.map( function ( edit ) {\n\t\t\treturn edit.minor;\n\t\t} )\n\t\t.reduce( function ( minor1, minor2 ) {\n\t\t\t// The combined edits are only tagged as minor if all of the edits where minor\n\t\t\treturn minor1 && minor2;\n\t\t} );\n\n\tmergedEditInfo.newPage = edits\n\t\t.map( function ( edit ) {\n\t\t\treturn edit.newPage;\n\t\t} )\n\t\t.reduce( function ( newPage1, newPage2 ) {\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\treturn 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( function ( edit ) {\n\t\t\treturn edit.timestamp;\n\t\t} )\n\t\t.reduce( function ( time1, time2 ) {\n\t\t\treturn ( ( new Date( time1 ) ) > ( new Date( time2 ) ) ? time1 : time2 );\n\t\t} );\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 its not set here, its null,\n\t// and both the jQuery and Vue displays ignore null attribute values. See T286268 and\n\t// * https://v3.vuejs.org/guide/migration/attribute-coercion.html#overview\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( function ( edit ) {\n\t\t\treturn edit.revid;\n\t\t} )\n\t\t.reduce( function ( edit1, edit2 ) {\n\t\t\t// Get the higher rev id, corresponding to the newer revision\n\t\t\treturn ( 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\tvar users = Object.keys( editsByUser );\n\n\tvar allLinks = [],\n\t\tuserLink = '',\n\t\tuserLinkBase = '',\n\t\tuserLinkURL = '';\n\n\tvar that = this;\n\tusers.forEach( function ( 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\tvar 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\tvar finalEdits = [];\n\n\tvar edits = [];\n\tfor ( var key in editInfo ) {\n\t\tedits.push( editInfo[ key ] );\n\t}\n\n\tvar that = this;\n\tedits.forEach( function ( page ) {\n\t\tvar 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( function ( entry ) {\n\t\t\t\tfinalEdits.push( $.extend( {}, 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\tvar 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\tvar editsByUser = {};\n\n\t\t\tpage.each.forEach( function ( 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( $.extend( {}, 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( function ( 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 both the jQuery and Vue displays. We use the\n *     `parsedcomment` result from 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\tvar 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\tvar newPageFlag = mw.msg( 'newpageletter' );\n\tvar minorFlag = mw.msg( 'minoreditletter' );\n\tvar botFlag = mw.msg( 'boteditletter' );\n\tvar entryFlags;\n\n\t// Tags display\n\tvar noTagsDisplay = Object.keys( tagsInfo ).length === 0;\n\tvar tagDescriptions, tagsWithLabel;\n\n\t// Comment display\n\tvar that = this;\n\n\treturn entries.map( function ( 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\tfunction ( tagName ) {\n\t\t\t\t\treturn tagsInfo[ tagName ];\n\t\t\t\t}\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\tvar convertedEdits = [],\n\t\tedits = {},\n\t\tlogEntries = [],\n\t\tcleanedEntries = this.normalizeEntries( entries );\n\n\tvar that = this;\n\tcleanedEntries.forEach( function ( 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\tconvertedEdits = 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\tfunction ( 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\tfunction ( 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\tvar GlobalWatchlistEntryEdits = require( './EntryEdits.js' );\n\tconvertedEdits = this.getFinalEntries( convertedEdits, tagsInfo, GlobalWatchlistEntryEdits );\n\n\tvar 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":[]}]},{"filePath":"/src/repo/modules/WikibaseHandler.js","messages":[{"ruleId":"es-x/no-promise","severity":2,"message":"ES2015 'Promise' class is forbidden.","line":99,"column":13,"nodeType":"Identifier","messageId":"forbidden","endLine":99,"endColumn":20},{"ruleId":"es-x/no-promise","severity":2,"message":"ES2015 'Promise' class is forbidden.","line":221,"column":13,"nodeType":"Identifier","messageId":"forbidden","endLine":221,"endColumn":20},{"ruleId":"es-x/no-array-prototype-entries","severity":2,"message":"ES2015 'Array.prototype.entries' method is forbidden.","line":225,"column":24,"nodeType":"MemberExpression","messageId":"forbidden","endLine":225,"endColumn":45}],"suppressedMessages":[],"errorCount":3,"fatalErrorCount":0,"warningCount":0,"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\tvar that = this;\n\n\treturn new Promise( function ( resolve ) {\n\t\tvar 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( function ( response ) {\n\t\t\tthat.debug( 'getRawLabels - api response', response );\n\t\t\tvar 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( function ( extraLabels ) {\n\t\t\t\t\tvar bothLabels = $.extend( {}, 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\tvar cleanedLabels = {};\n\tvar entityIds = Object.keys( rawLabels );\n\tvar that = this;\n\tvar entityInfo,\n\t\tlabelKey;\n\n\tentityIds.forEach( function ( 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\tvar ids = [];\n\n\tentries.forEach( function ( 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.indexOf( entry.titleMsg ) === -1 ) {\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\tvar that = this;\n\n\treturn new Promise( function ( resolve ) {\n\t\tvar extractedInfo = that.getEntityIds( summaryEntries );\n\t\tthat.debug( 'addLabels - extractedInfo', extractedInfo );\n\n\t\tvar updatedEntries = extractedInfo.entries;\n\t\tvar 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( function ( rawLabels ) {\n\t\t\tvar cleanedLabels = that.cleanupRawLabels( rawLabels );\n\n\t\t\tupdatedEntries.forEach( function ( 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":[]}]},{"filePath":"/src/repo/modules/getSettings.error.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/modules/getSettings.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/modules/vue/EntryRow.vue","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/modules/vue/Site.vue","messages":[{"ruleId":"es-x/no-array-prototype-entries","severity":2,"message":"ES2015 'Array.prototype.entries' method is forbidden.","line":118,"column":4,"nodeType":"MemberExpression","messageId":"forbidden","endLine":118,"endColumn":16},{"ruleId":"es-x/no-array-prototype-entries","severity":2,"message":"ES2015 'Array.prototype.entries' method is forbidden.","line":129,"column":4,"nodeType":"MemberExpression","messageId":"forbidden","endLine":129,"endColumn":16},{"ruleId":"es-x/no-array-prototype-entries","severity":2,"message":"ES2015 'Array.prototype.entries' method is forbidden.","line":143,"column":8,"nodeType":"MemberExpression","messageId":"forbidden","endLine":143,"endColumn":20}],"suppressedMessages":[],"errorCount":3,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"<template>\n\t<div class=\"ext-globalwatchlist-vue-site\">\n\t\t<h3>\n\t\t\t<a\n\t\t\t\tv-bind:href=\"specialWatchlistUrl\"\n\t\t\t\ttarget=\"_blank\"\n\t\t\t>{{ site }}</a>\n\t\t\t(<!--\n\t\t\t\tAvoid a space\n\t\t\t--><a\n\t\t\t\tv-bind:href=\"specialEditWatchlistUrl\"\n\t\t\t\ttarget=\"_blank\"\n\t\t\t>{{ $i18n( 'globalwatchlist-editwatchlist' ).text() }}</a><!--\n\t\t\t\tAvoid a space\n\t\t\t-->)\n\t\t</h3>\n\t\t<p v-if=\"hasApiError\">\n\t\t\t{{ $i18n( 'globalwatchlist-fetch-site-failure' ).text() }}\n\t\t</p>\n\t\t<global-watchlist-collapsible-wrapper v-else>\n\t\t\t<wvui-button\n\t\t\t\taction=\"destructive\"\n\t\t\t\tv-on:click=\"markChangesSeen\"\n\t\t\t>\n\t\t\t\t<span>\n\t\t\t\t\t<wvui-icon\n\t\t\t\t\t\tclass=\"ext-globalwatchlist-button-icon\"\n\t\t\t\t\t\tv-bind:icon=\"checkIcon\"\n\t\t\t\t\t>\n\t\t\t\t\t</wvui-icon>\n\t\t\t\t\t{{ $i18n( 'globalwatchlist-markseen' ).text() }}\n\t\t\t\t</span>\n\t\t\t</wvui-button>\n\t\t\t<ul>\n\t\t\t\t<global-watchlist-entry-row\n\t\t\t\t\tv-for=\"( rowInfo, index ) in entries\"\n\t\t\t\t\tv-bind:key=\"index\"\n\t\t\t\t\tv-bind:entry=\"rowInfo\"\n\t\t\t\t\tv-bind:pagewatched=\"rowInfo.pageWatched\"\n\t\t\t\t\tv-bind:site=\"site\"\n\t\t\t\t\tv-on:unwatch-page=\"onUnwatchPage\"\n\t\t\t\t\tv-on:rewatch-page=\"onRewatchPage\"\n\t\t\t\t>\n\t\t\t\t</global-watchlist-entry-row>\n\t\t\t</ul>\n\t\t</global-watchlist-collapsible-wrapper>\n\t</div>\n</template>\n\n<script>\nvar GlobalWatchlistLinker = require( './../Linker.js' );\n\nvar CollapsibleWrapper = require( './base/CollapsibleWrapper.vue' ),\n\tEntryRow = require( './EntryRow.vue' );\n\nvar WvuiButton = require( 'wvui' ).WvuiButton;\nvar WvuiIcon = require( 'wvui' ).WvuiIcon;\n\n/**\n * Output for a specific site\n *\n * Inputs:\n *  - entries, array of objects for EntryRow\n *  - site, string (url) for the site in question\n *\n * Emits:\n *  - `unwatch-site-page` when unwatching a page.\n *     Parameters: site (url), title\n *  - `rewatch-site-page` when rewatching a page.\n *     Parameters: site (url), title\n *  - `mark-site-seen` when marking a site as seen.\n *     Parameters: site (url)\n */\n// @vue/component\nmodule.exports = {\n\tcomponents: {\n\t\t'global-watchlist-collapsible-wrapper': CollapsibleWrapper,\n\t\t'global-watchlist-entry-row': EntryRow,\n\t\t'wvui-button': WvuiButton,\n\t\t'wvui-icon': WvuiIcon\n\t},\n\n\tprops: {\n\t\tentries: {\n\t\t\ttype: Array,\n\t\t\trequired: true\n\t\t},\n\t\tsite: {\n\t\t\ttype: String,\n\t\t\trequired: true\n\t\t}\n\t},\n\n\tdata: function () {\n\t\treturn {\n\t\t\thasApiError: false\n\t\t};\n\t},\n\n\tcomputed: {\n\t\tlinker: function () {\n\t\t\treturn new GlobalWatchlistLinker( this.site );\n\t\t},\n\t\tspecialWatchlistUrl: function () {\n\t\t\treturn this.linker.linkPage( 'Special:Watchlist' );\n\t\t},\n\t\tspecialEditWatchlistUrl: function () {\n\t\t\treturn this.linker.linkPage( 'Special:EditWatchlist' );\n\t\t},\n\t\tcheckIcon: function () {\n\t\t\treturn require( './icons.json' ).check;\n\t\t}\n\t},\n\n\tmethods: {\n\t\tonUnwatchPage: function ( eventTitle ) {\n\t\t\tthis.$emit( 'unwatch-site-page', this.site, eventTitle );\n\t\t\tthis.entries.forEach( function ( entryInfo ) {\n\t\t\t\tif ( entryInfo.title === eventTitle ) {\n\t\t\t\t\tentryInfo.pageWatched = false;\n\t\t\t\t\t// To remove the clock\n\t\t\t\t\tentryInfo.expiry = false;\n\t\t\t\t}\n\t\t\t} );\n\t\t\tthis.$forceUpdate();\n\t\t},\n\t\tonRewatchPage: function ( eventTitle ) {\n\t\t\tthis.$emit( 'rewatch-site-page', this.site, eventTitle );\n\t\t\tthis.entries.forEach( function ( entryInfo ) {\n\t\t\t\tif ( entryInfo.title === eventTitle ) {\n\t\t\t\t\tentryInfo.pageWatched = true;\n\t\t\t\t}\n\t\t\t} );\n\t\t\tthis.$forceUpdate();\n\t\t},\n\t\tmarkChangesSeen: function () {\n\t\t\tthis.$emit( 'mark-site-seen', this.site );\n\t\t}\n\t},\n\n\tcreated: function () {\n\t\t// If this is created but has no entries, its because something went wrong\n\t\tif ( this.entries.length === 0 ) {\n\t\t\tthis.hasApiError = true;\n\t\t}\n\t}\n};\n</script>\n","usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/modules/vue/SitesWithoutChanges.vue","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/modules/vue/SpecialGlobalWatchlist.vue","messages":[{"ruleId":"es-x/no-promise","severity":2,"message":"ES2015 'Promise' class is forbidden.","line":182,"column":15,"nodeType":"Identifier","messageId":"forbidden","endLine":182,"endColumn":22},{"ruleId":"es-x/no-array-prototype-entries","severity":2,"message":"ES2015 'Array.prototype.entries' method is forbidden.","line":193,"column":18,"nodeType":"MemberExpression","messageId":"forbidden","endLine":193,"endColumn":30},{"ruleId":"es-x/no-array-prototype-entries","severity":2,"message":"ES2015 'Array.prototype.entries' method is forbidden.","line":234,"column":6,"nodeType":"MemberExpression","messageId":"forbidden","endLine":234,"endColumn":29},{"ruleId":"es-x/no-array-prototype-entries","severity":2,"message":"ES2015 'Array.prototype.entries' method is forbidden.","line":234,"column":32,"nodeType":"MemberExpression","messageId":"forbidden","endLine":234,"endColumn":82}],"suppressedMessages":[],"errorCount":4,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"<template>\n\t<div id=\"ext-globalwatchlist-vue-specialpage\">\n\t\t<global-watchlist-toolbar\n\t\t\tv-bind:live-updates-active=\"liveUpdatesActive\"\n\t\t\tv-bind:group-page-active=\"resultsGrouped\"\n\t\t\tv-bind:liveupdatesdisabled=\"disableLiveUpdates\"\n\t\t\tv-bind:grouppagedisabled=\"disableGroupPage\"\n\t\t\tv-bind:refreshdisabled=\"disableRefresh\"\n\t\t\tv-bind:markalldisabled=\"disableMarkAll\"\n\t\t\tv-on:toggle-live-updates=\"toggleLiveUpdates\"\n\t\t\tv-on:toggle-group-page=\"toggleGroupPage\"\n\t\t\tv-on:click-refresh=\"refreshSites\"\n\t\t\tv-on:mark-all-sites-seen=\"markAllSitesSeen\"\n\t\t>\n\t\t</global-watchlist-toolbar>\n\t\t<hr>\n\n\t\t<div\n\t\t\tv-if=\"inLoading\"\n\t\t\tclass=\"ext-globalwatchlist-vue-loading\"\n\t\t>\n\t\t\t<wvui-progress-bar></wvui-progress-bar>\n\t\t</div>\n\n\t\t<div\n\t\t\tv-else\n\t\t\tclass=\"ext-globalwatchlist-sitelist\"\n\t\t>\n\t\t\t<label id=\"ext-globalwatchlist-vue-asof\">{{ asOfLabelText }}</label>\n\t\t\t<div v-if=\"haveChangesToShow\">\n\t\t\t\t<!-- Only show label if there are empty sites, T274720 -->\n\t\t\t\t<label v-if=\"haveEmptySites\">\n\t\t\t\t\t{{ $i18n( 'globalwatchlist-changesfeed' ).text() }}\n\t\t\t\t</label>\n\t\t\t\t<global-watchlist-sites-with-changes\n\t\t\t\t\tv-for=\"withChanges in sitesWithChangesList\"\n\t\t\t\t\tv-bind:key=\"withChanges.site\"\n\t\t\t\t\tv-bind:site=\"withChanges.site\"\n\t\t\t\t\tv-bind:entries=\"withChanges.entries\"\n\t\t\t\t\tv-on:unwatch-site-page=\"onUnwatchSitePage\"\n\t\t\t\t\tv-on:rewatch-site-page=\"onRewatchSitePage\"\n\t\t\t\t\tv-on:mark-site-seen=\"markSiteAsSeen\"\n\t\t\t\t>\n\t\t\t\t</global-watchlist-sites-with-changes>\n\t\t\t</div>\n\t\t\t<div v-if=\"haveEmptySites\">\n\t\t\t\t<global-watchlist-sites-without-changes\n\t\t\t\t\tv-bind:emptysitelist=\"sitesWithoutChangesList\"\n\t\t\t\t>\n\t\t\t\t</global-watchlist-sites-without-changes>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\nvar Toolbar = require( './Toolbar.vue' ),\n\tWvuiProgressBar = require( 'wvui' ).WvuiProgressBar,\n\tSitesWithoutChanges = require( './SitesWithoutChanges.vue' ),\n\tSite = require( './Site.vue' );\n\nvar GlobalWatchlistDebugger = require( './../Debug.js' ),\n\tgetSettings = require( './../getSettings.js' ),\n\tWatchedSite = require( './../SiteVue.js' ),\n\tMultiSiteWrapper = require( './../MultiSiteWrapper.js' );\n\nvar globalWatchlistDebug = new GlobalWatchlistDebugger();\nvar config = getSettings( globalWatchlistDebug );\nconfig.time = new Date();\n\nvar watchedSites = new MultiSiteWrapper(\n\tWatchedSite,\n\tconfig,\n\tglobalWatchlistDebug\n);\n\nvar watchedSitesBySite = {};\nwatchedSites.siteList.forEach( function ( watchedSite ) {\n\twatchedSitesBySite[ watchedSite.site ] = watchedSite;\n} );\n\n// @vue/component\nmodule.exports = {\n\tcomponents: {\n\t\t'wvui-progress-bar': WvuiProgressBar,\n\t\t'global-watchlist-toolbar': Toolbar,\n\t\t'global-watchlist-sites-with-changes': Site,\n\t\t'global-watchlist-sites-without-changes': SitesWithoutChanges\n\t},\n\n\tdata: function () {\n\t\t// For debugging purposes, we may need to access the debug log\n\t\t// attach the debugger here so that it can be accessed from the console\n\t\treturn {\n\t\t\tinLoading: false,\n\t\t\tsitesWithChangesList: [],\n\t\t\tsitesWithoutChangesList: [],\n\t\t\tconfig: config,\n\t\t\tglobalWatchlistDebug: globalWatchlistDebug,\n\t\t\tliveUpdatesActive: false\n\t\t};\n\t},\n\n\tcomputed: {\n\t\thaveChangesToShow: function () {\n\t\t\treturn this.sitesWithChangesList.length > 0;\n\t\t},\n\t\thaveEmptySites: function () {\n\t\t\treturn this.sitesWithoutChangesList.length > 0;\n\t\t},\n\t\tasOfLabelText: function () {\n\t\t\treturn this.$i18n(\n\t\t\t\t'globalwatchlist-asof',\n\t\t\t\tthis.config.time.toUTCString()\n\t\t\t).text();\n\t\t},\n\t\tresultsGrouped: function () {\n\t\t\treturn this.config.groupPage;\n\t\t},\n\t\tdisableLiveUpdates: function () {\n\t\t\treturn this.inLoading;\n\t\t},\n\t\tdisableGroupPage: function () {\n\t\t\treturn ( this.liveUpdatesActive === true ) || this.inLoading;\n\t\t},\n\t\tdisableRefresh: function () {\n\t\t\treturn ( this.liveUpdatesActive === true ) || this.inLoading;\n\t\t},\n\t\tdisableMarkAll: function () {\n\t\t\treturn ( this.liveUpdatesActive === true ) || this.inLoading;\n\t\t}\n\t},\n\n\tmethods: {\n\t\ttoggleLiveUpdates: function ( isActive ) {\n\t\t\tthis.liveUpdatesActive = isActive;\n\t\t\tthis.globalWatchlistDebug.info( isActive ? 'Now running live updates' : 'Done running live updates' );\n\n\t\t\t// updateLive will only do anything if liveUpdatesActive is true\n\t\t\tthis.updateLive();\n\t\t},\n\t\ttoggleGroupPage: function ( isActive ) {\n\t\t\tthis.config.groupPage = isActive; // To be passed in getWatchlist\n\t\t\tthis.globalWatchlistDebug.info( isActive ? 'Now grouping by page' : 'Done grouping by page' );\n\n\t\t\tthis.refreshSites();\n\t\t},\n\t\tupdateLive: function () {\n\t\t\tif ( this.liveUpdatesActive === true ) {\n\t\t\t\tvar that = this;\n\t\t\t\tthis.backgroundRefresh().then( function ( results ) {\n\t\t\t\t\tif ( that.liveUpdatesActive === true ) {\n\t\t\t\t\t\t// Might have been turned off while the update\n\t\t\t\t\t\t// was being prepared\n\t\t\t\t\t\tthat.sitesWithChangesList = results.withChanges;\n\t\t\t\t\t\tthat.sitesWithoutChangesList = results.withoutChanges;\n\n\t\t\t\t\t\t// Call again in 7.5 seconds\n\t\t\t\t\t\tsetTimeout( that.updateLive, 7500 );\n\t\t\t\t\t}\n\t\t\t\t} );\n\t\t\t}\n\t\t},\n\t\trefreshSites: function () {\n\t\t\tthis.globalWatchlistDebug.info( 'Refreshing sites' );\n\t\t\tthis.inLoading = true;\n\t\t\tthis.sitesWithChangesList = [];\n\t\t\tthis.sitesWithoutChangesList = [];\n\n\t\t\tvar that = this;\n\t\t\tthis.backgroundRefresh().then( function ( results ) {\n\t\t\t\tthat.sitesWithChangesList = results.withChanges;\n\t\t\t\tthat.sitesWithoutChangesList = results.withoutChanges;\n\t\t\t} ).then( function () {\n\t\t\t\tthat.inLoading = false;\n\t\t\t} );\n\t\t},\n\t\tbackgroundRefresh: function () {\n\t\t\tvar that = this;\n\t\t\tthis.config.time = new Date();\n\n\t\t\treturn new Promise( function ( resolve ) {\n\t\t\t\twatchedSites.getAllWatchlists( that.config ).then( function () {\n\t\t\t\t\tvar newSitesWithChanges = [];\n\t\t\t\t\tvar newSitesWithoutChanges = [];\n\n\t\t\t\t\twatchedSites.siteList.forEach( function ( site ) {\n\t\t\t\t\t\tif ( site.isEmpty ) {\n\t\t\t\t\t\t\tnewSitesWithoutChanges.push( site.site );\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tnewSitesWithChanges.push( {\n\t\t\t\t\t\t\t\tsite: site.site,\n\t\t\t\t\t\t\t\tentries: site.entries\n\t\t\t\t\t\t\t} );\n\t\t\t\t\t\t}\n\t\t\t\t\t} );\n\t\t\t\t\tvar results = {\n\t\t\t\t\t\twithChanges: newSitesWithChanges,\n\t\t\t\t\t\twithoutChanges: newSitesWithoutChanges\n\t\t\t\t\t};\n\t\t\t\t\tresolve( results );\n\t\t\t\t\treturn;\n\t\t\t\t} );\n\t\t\t} );\n\t\t},\n\t\tonUnwatchSitePage: function ( site, pageTitle ) {\n\t\t\twatchedSitesBySite[ site ].changeWatched( pageTitle, 'unwatch' );\n\t\t},\n\t\tonRewatchSitePage: function ( site, pageTitle ) {\n\t\t\twatchedSitesBySite[ site ].changeWatched( pageTitle, 'watch' );\n\t\t},\n\t\tmarkAllSitesSeen: function () {\n\t\t\tthis.globalWatchlistDebug.info( 'Marking all sites as seen' );\n\t\t\tvar that = this;\n\n\t\t\twatchedSites.markAllSitesSeen().then(\n\t\t\t\tfunction () {\n\t\t\t\t\t// Resolved, either confirmation wasn't needed or was given\n\t\t\t\t\tthat.refreshSites();\n\t\t\t\t},\n\t\t\t\tfunction () {\n\t\t\t\t\t// Confirmation wasn't given\n\t\t\t\t\tthat.globalWatchlistDebug.info( 'Not confirmed' );\n\t\t\t\t}\n\t\t\t);\n\t\t},\n\t\tmarkSiteAsSeen: function ( site ) {\n\t\t\tvar that = this;\n\n\t\t\twatchedSitesBySite[ site ].markAsSeen().then( function () {\n\t\t\t\t// Re sync sitesWithChangesList, only the site that was marked as seen should change\n\t\t\t\t// we don't separately index sitesWithChangesList by site\n\t\t\t\tthat.sitesWithChangesList.forEach( function ( siteWithChanges ) {\n\t\t\t\t\tsiteWithChanges.entries = watchedSitesBySite[ siteWithChanges.site ].entries;\n\t\t\t\t} );\n\t\t\t} );\n\t\t}\n\t},\n\n\tmounted: function () {\n\t\t// Trigger initial refresh once mounted\n\t\t// Based on this.refreshSites() but with timing\n\n\t\tvar loadStartTime = mw.now();\n\t\tthis.inLoading = true;\n\t\tthis.sitesWithChangesList = [];\n\t\tthis.sitesWithoutChangesList = [];\n\n\t\tvar that = this;\n\t\tthis.backgroundRefresh().then( function ( results ) {\n\t\t\tthat.sitesWithChangesList = results.withChanges;\n\t\t\tthat.sitesWithoutChangesList = results.withoutChanges;\n\t\t} ).then( function () {\n\t\t\tthat.inLoading = false;\n\n\t\t\tvar metricName = that.config.fastMode ?\n\t\t\t\t'timing.MediaWiki.GlobalWatchlist.firstload.vue.fastmode' :\n\t\t\t\t'timing.MediaWiki.GlobalWatchlist.firstload.vue.normal';\n\t\t\tvar loadEndTime = mw.now();\n\t\t\tvar loadElapsedTime = loadEndTime - loadStartTime;\n\t\t\tmw.track( metricName, loadElapsedTime );\n\t\t} );\n\n\t\t// Only run live updates when the special page is being displayed\n\t\t// Note: the page visibility api isn't available for some of the\n\t\t// older versions of mobile browsers that MediaWiki still provides\n\t\t// Grade A support for, but its better than nothing. See T268266\n\t\tdocument.addEventListener( 'visibilitychange', function () {\n\t\t\tif ( document.visibilityState === 'hidden' ) {\n\t\t\t\t// Pause live updates\n\t\t\t\tif ( that.liveUpdatesActive === true ) {\n\t\t\t\t\tthat.liveUpdatesActive = 'paused';\n\t\t\t\t}\n\t\t\t} else if ( document.visibilityState === 'visible' ) {\n\t\t\t\t// Unpause\n\t\t\t\tif ( that.liveUpdatesActive === 'paused' ) {\n\t\t\t\t\tthat.liveUpdatesActive = true;\n\t\t\t\t\tthat.updateLive();\n\t\t\t\t}\n\t\t\t}\n\t\t} );\n\t}\n};\n</script>\n\n<style>\n/* Ensure that this applies to links, see T245104 */\n.ext-globalwatchlist-strike,\n.ext-globalwatchlist-strike a {\n\ttext-decoration: line-through;\n}\n\n#ext-globalwatchlist-vue-asof {\n\ttext-align: center;\n}\n\n.ext-globalwatchlist-content hr {\n\theight: 2px;\n\tclear: both;\n}\n\n.ext-globalwatchlist-expiry-icon svg {\n\t/* Based on core watchlistexpiry.less */\n\tmin-height: 13px;\n\theight: 13px;\n\tposition: relative;\n\topacity: 0.51;\n}\n\n/* Make sure the icon color is the same as the text */\n.ext-globalwatchlist-button-icon {\n\tcolor: currentColor;\n}\n\n.ext-globalwatchlist-vue-loading .wvui-progress-bar {\n\tmargin: auto;\n\tmargin-top: 5px;\n}\n\n/*\n * Copy some core styles from mediawiki.interface.helpers.styles.less to match the normal watchlist:\n *\n * - auto-generated edit summaries\n * - formatting of message when a username is hidden from an edit\n *\n * See T288733\n */\n.autocomment,\n.autocomment a,\n.autocomment a:visited {\n\tcolor: #72777d;\n}\n\nspan.history-deleted {\n\ttext-decoration: line-through;\n\tcolor: #72777d;\n\tfont-style: italic;\n}\n</style>\n","usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/modules/vue/Toolbar.vue","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/modules/vue/base/CollapsibleWrapper.vue","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/modules/vue/icons.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/package-lock.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/package.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/tests/.eslintrc.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/tests/qunit/Debug.tests.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/tests/qunit/Linker.tests.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/tests/qunit/WatchlistUtils.tests.js","messages":[{"ruleId":"es-x/no-regexp-prototype-flags","severity":2,"message":"ES2015 'RegExp.prototype.flags' property is forbidden.","line":351,"column":5,"nodeType":"MemberExpression","messageId":"forbidden","endLine":351,"endColumn":20},{"ruleId":"es-x/no-regexp-prototype-flags","severity":2,"message":"ES2015 'RegExp.prototype.flags' property is forbidden.","line":353,"column":5,"nodeType":"MemberExpression","messageId":"forbidden","endLine":353,"endColumn":20}],"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":7,"nodeType":"Identifier","messageId":"notCamelCase","endLine":63,"endColumn":14,"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":7,"nodeType":"Identifier","messageId":"notCamelCase","endLine":73,"endColumn":14,"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":7,"nodeType":"Identifier","messageId":"notCamelCase","endLine":83,"endColumn":14,"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":7,"nodeType":"Identifier","messageId":"notCamelCase","endLine":93,"endColumn":14,"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":7,"nodeType":"Identifier","messageId":"notCamelCase","endLine":103,"endColumn":14,"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":7,"nodeType":"Identifier","messageId":"notCamelCase","endLine":114,"endColumn":20,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"camelcase","severity":2,"message":"Identifier 'mergedEdits_b' is not in camel case.","line":128,"column":7,"nodeType":"Identifier","messageId":"notCamelCase","endLine":128,"endColumn":20,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"camelcase","severity":2,"message":"Identifier 'mergedEdits_c' is not in camel case.","line":142,"column":7,"nodeType":"Identifier","messageId":"notCamelCase","endLine":142,"endColumn":20,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"camelcase","severity":2,"message":"Identifier 'mergedEdits_d' is not in camel case.","line":156,"column":7,"nodeType":"Identifier","messageId":"notCamelCase","endLine":156,"endColumn":20,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"camelcase","severity":2,"message":"Identifier 'mergedEdits_e' is not in camel case.","line":170,"column":7,"nodeType":"Identifier","messageId":"notCamelCase","endLine":170,"endColumn":20,"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":2,"fatalErrorCount":0,"warningCount":0,"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\tvar filteredEntry;\n\t\treturn fullEntries.map(\n\t\t\tfunction ( fullEntry ) {\n\t\t\t\tfilteredEntry = {};\n\t\t\t\tkeysToInclude.forEach(\n\t\t\t\t\tfunction ( 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\tvar GlobalWatchlistWatchlistUtils = require( '../../../modules/WatchlistUtils.js' );\n\tvar GlobalWatchlistLinker = require( '../../../modules/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\tvar GlobalWatchlistEntryBase = require( '../../../modules/EntryBase.js' );\n\n\t// Set config variables so that the linker can be created properly\n\tQUnit.module( 'ext.globalwatchlist.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\tvar watchlistUtils = new GlobalWatchlistWatchlistUtils(\n\t\tnew GlobalWatchlistLinker( 'en.wikipedia.org' )\n\t);\n\n\t/* eslint-disable camelcase */\n\tQUnit.test( 'watchlistUtils.mergePageEdits', function ( assert ) {\n\t\tvar 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\tvar 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\tvar 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\tvar 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\tvar 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\tvar 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\tvar 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\tvar 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\tvar 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\tvar 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\tvar 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\tvar 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', function ( assert ) {\n\t\t// Only fill in the parts that would otherwise be normalized\n\t\tvar 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\tvar 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\tvar 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\tvar 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\tvar 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\tvar 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\tvar 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\tvar 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\tvar 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\tvar 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)', function ( assert ) {\n\t\tvar allEntries = [];\n\t\tvar expectedEntries = [];\n\t\tvar withoutFlags;\n\t\tvar withFlags;\n\t\tvar newPageFlag = mw.msg( 'newpageletter' );\n\t\tvar minorFlag = mw.msg( 'minoreditletter' );\n\t\tvar 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 ( var 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\tvar 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)', function ( assert ) {\n\t\tvar 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\tvar 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\tvar 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)', function ( 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\tvar 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\tvar 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\tvar 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":[]}]},{"filePath":"/src/repo/tests/qunit/getSettings.tests.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":[]}]},{"filePath":"/src/repo/tests/qunit/tests.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"valid-jsdoc","replacedBy":[]}]},{"filePath":"/src/repo/tests/selenium/.eslintrc.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"no-process-exit","replacedBy":[]}]},{"filePath":"/src/repo/tests/selenium/pageobjects/GlobalWatchlist.page.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"no-process-exit","replacedBy":[]}]},{"filePath":"/src/repo/tests/selenium/specs/SpecialGlobalWatchlist.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"no-process-exit","replacedBy":[]}]},{"filePath":"/src/repo/tests/selenium/wdio.conf.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"no-process-exit","replacedBy":[]}]}]

--- end ---
$ ./node_modules/.bin/stylelint modules/SpecialGlobalWatchlistSettings.less modules/SpecialGlobalWatchlist.display.css -f json
--- stdout ---
[{"source":"/src/repo/modules/SpecialGlobalWatchlistSettings.less","deprecations":[],"invalidOptionWarnings":[],"parseErrors":[],"errored":false,"warnings":[]},{"source":"/src/repo/modules/SpecialGlobalWatchlist.display.css","deprecations":[],"invalidOptionWarnings":[],"parseErrors":[],"errored":false,"warnings":[]}]
--- end ---
$ /usr/bin/npm ci --legacy-peer-deps
--- stderr ---
npm WARN deprecated @types/easy-table@1.2.0: This is a stub types definition. easy-table provides its own type definitions, so you do not need this installed.
npm WARN deprecated source-map-url@0.4.1: See https://github.com/lydell/source-map-url#deprecated
npm WARN deprecated urix@0.1.0: Please see https://github.com/lydell/urix#deprecated
npm WARN deprecated har-validator@5.1.5: this library is no longer supported
npm WARN deprecated resolve-url@0.2.1: https://github.com/lydell/resolve-url#deprecated
npm WARN deprecated source-map-resolve@0.5.3: See https://github.com/lydell/source-map-resolve#deprecated
npm WARN deprecated sane@4.1.0: some dependency vulnerabilities fixed, support for node < 10 dropped, and newer ECMAScript syntax/features added
npm WARN deprecated chokidar@2.1.8: Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies
npm WARN deprecated querystring@0.2.0: The querystring API is considered Legacy. new code should use the URLSearchParams API instead.
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 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
--- stdout ---

added 3006 packages, and audited 3007 packages in 47s

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

31 vulnerabilities (8 moderate, 23 high)

To address issues that do not require attention, run:
  npm audit fix

To address all issues possible (including breaking changes), run:
  npm audit fix --force

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/EntryBase.js
  36:2   warning  ES2015 'RegExp.prototype.flags' property is forbidden  es-x/no-regexp-prototype-flags
  36:15  warning  ES2015 'RegExp.prototype.flags' property is forbidden  es-x/no-regexp-prototype-flags

/src/repo/modules/MultiSiteWrapper.js
   61:9   warning  ES2015 'Promise' class is forbidden  es-x/no-promise
   79:13  warning  ES2015 'Promise' class is forbidden  es-x/no-promise
   87:22  warning  ES2015 'Promise' class is forbidden  es-x/no-promise
  102:6   warning  ES2015 'Promise' class is forbidden  es-x/no-promise

/src/repo/modules/SiteBase.js
   98:13  warning  ES2015 'Promise' class is forbidden  es-x/no-promise
  145:13  warning  ES2015 'Promise' class is forbidden  es-x/no-promise
  222:13  warning  ES2015 'Promise' class is forbidden  es-x/no-promise
  247:13  warning  ES2015 'Promise' class is forbidden  es-x/no-promise
  285:13  warning  ES2015 'Promise' class is forbidden  es-x/no-promise
  345:13  warning  ES2015 'Promise' class is forbidden  es-x/no-promise
  366:13  warning  ES2015 'Promise' class is forbidden  es-x/no-promise

/src/repo/modules/SiteDisplay.js
  82:7   warning  ES2015 'RegExp.prototype.flags' property is forbidden  es-x/no-regexp-prototype-flags
  84:33  warning  ES2015 'RegExp.prototype.flags' property is forbidden  es-x/no-regexp-prototype-flags

/src/repo/modules/SiteVue.js
  40:2  warning  ES2015 'Array.prototype.entries' method is forbidden  es-x/no-array-prototype-entries
  51:2  warning  ES2015 'Array.prototype.entries' method is forbidden  es-x/no-array-prototype-entries
  52:2  warning  ES2015 'Array.prototype.entries' method is forbidden  es-x/no-array-prototype-entries
  62:2  warning  ES2015 'Array.prototype.entries' method is forbidden  es-x/no-array-prototype-entries
  78:2  warning  ES2015 'Array.prototype.entries' method is forbidden  es-x/no-array-prototype-entries

/src/repo/modules/SpecialGlobalWatchlist.display.js
  105:14  warning  ES2015 'Promise' class is forbidden  es-x/no-promise

/src/repo/modules/WatchlistUtils.js
  377:4  warning  ES2015 'RegExp.prototype.flags' property is forbidden  es-x/no-regexp-prototype-flags
  379:4  warning  ES2015 'RegExp.prototype.flags' property is forbidden  es-x/no-regexp-prototype-flags

/src/repo/modules/WikibaseHandler.js
   99:13  warning  ES2015 'Promise' class is forbidden                   es-x/no-promise
  221:13  warning  ES2015 'Promise' class is forbidden                   es-x/no-promise
  225:24  warning  ES2015 'Array.prototype.entries' method is forbidden  es-x/no-array-prototype-entries

/src/repo/modules/vue/Site.vue
  118:4  error  ES2015 'Array.prototype.entries' method is forbidden  es-x/no-array-prototype-entries
  129:4  error  ES2015 'Array.prototype.entries' method is forbidden  es-x/no-array-prototype-entries
  143:8  error  ES2015 'Array.prototype.entries' method is forbidden  es-x/no-array-prototype-entries

/src/repo/modules/vue/SpecialGlobalWatchlist.vue
  182:15  error  ES2015 'Promise' class is forbidden                   es-x/no-promise
  193:18  error  ES2015 'Array.prototype.entries' method is forbidden  es-x/no-array-prototype-entries
  234:6   error  ES2015 'Array.prototype.entries' method is forbidden  es-x/no-array-prototype-entries
  234:32  error  ES2015 'Array.prototype.entries' method is forbidden  es-x/no-array-prototype-entries

/src/repo/tests/qunit/WatchlistUtils.tests.js
  351:5  warning  ES2015 'RegExp.prototype.flags' property is forbidden  es-x/no-regexp-prototype-flags
  353:5  warning  ES2015 'RegExp.prototype.flags' property is forbidden  es-x/no-regexp-prototype-flags

✖ 35 problems (7 errors, 28 warnings)


--- end ---
Traceback (most recent call last):
  File "/venv/lib/python3.9/site-packages/runner-0.1.0-py3.9.egg/runner/__init__.py", line 1400, in main
    libup.run(args.repo, args.output, args.branch)
  File "/venv/lib/python3.9/site-packages/runner-0.1.0-py3.9.egg/runner/__init__.py", line 1338, in run
    self.npm_upgrade(plan)
  File "/venv/lib/python3.9/site-packages/runner-0.1.0-py3.9.egg/runner/__init__.py", line 1049, in npm_upgrade
    self.npm_test()
  File "/venv/lib/python3.9/site-packages/runner-0.1.0-py3.9.egg/runner/__init__.py", line 287, in npm_test
    self.check_call(['npm', 'test'])
  File "/venv/lib/python3.9/site-packages/runner-0.1.0-py3.9.egg/runner/shell2.py", line 54, in check_call
    res.check_returncode()
  File "/usr/lib/python3.9/subprocess.py", line 460, in check_returncode
    raise CalledProcessError(self.returncode, self.args, self.stdout,
subprocess.CalledProcessError: Command '['/usr/bin/npm', 'test']' returned non-zero exit status 1.
Source code is licensed under the AGPL.