From 7f26b37ee90f056ee07bfa4f760a2a084cb6d9c5 Mon Sep 17 00:00:00 2001 From: Ed Clement Date: Fri, 21 Jan 2022 09:37:17 -0500 Subject: [PATCH] feat(metrics): collect http request metrics and package download metrics - add metric `registry_http_requests` that captures metrics related to all incoming http requests - rename metric `registry_requests` to `registry_package_downloads` for clarity - breaking refactor to plugin configuration schema - allow metric names to be overridden BREAKING CHANGE: plugin configuration schema overhauled, default metric name of `registry_requests` renamed to `registry_package_downloads` fix #9 --- README.md | 177 ++- package-lock.json | 1191 ++++++++++------- package.json | 20 +- src/index.ts | 5 +- src/metrics.ts | 147 +- src/plugin.ts | 180 +++ src/types/index.ts | 32 +- tests/pluginMetricsDefault.spec.ts | 101 ++ ....spec.ts => pluginMetricsPackages.spec.ts} | 158 +-- tests/pluginMetricsRequests.spec.ts | 188 +++ tests/testUtils.ts | 25 +- tests/utils.spec.ts | 8 +- tsconfig.json | 2 - 13 files changed, 1463 insertions(+), 771 deletions(-) create mode 100644 src/plugin.ts create mode 100644 tests/pluginMetricsDefault.spec.ts rename tests/{metrics.spec.ts => pluginMetricsPackages.spec.ts} (66%) create mode 100644 tests/pluginMetricsRequests.spec.ts diff --git a/README.md b/README.md index 5cf07ca..7dc0d71 100644 --- a/README.md +++ b/README.md @@ -1,64 +1,150 @@ # verdaccio-prometheus-middleware -Metrics middleware plugin for Verdaccio. Collects metrics specifically for package tarball install/download requests and -exposes them at a configurable metrics endpoint (defaults to `/-/metrics`). The metrics are produced in the standard +A Prometheus metrics middleware plugin for Verdaccio v5. Collects several types of metrics and exposes them at a +configurable metrics endpoint (defaults to `/-/metrics`). The metrics are produced in the standard [prometheus metrics text format](https://prometheus.io/docs/instrumenting/exposition_formats/#text-format-example). -A [counter](https://prometheus.io/docs/concepts/metric_types/#counter) metric is used to track the number of package -tarball installs/downloads. The following [labels](https://prometheus.io/docs/practices/naming/#labels) are applied to -_every_ request: +## Table of contents + +- [Metric Types](#metric-types) + - [Prometheus Default Metrics](#prometheus-default-metrics) + - [HTTP Request Metrics](#http-request-metrics) + - [Package Download Metrics](#package-download-metrics) +- [Installation](#installation) +- [Configuration](#configuration) +- [Package Groups](#package-groups) +- [Contributing](#contributing) + - [Pull Requests](#pull-requests) + +### Metric Types + +This plugin is capable of collecting three (3) different types of metrics. Further explanation is provided in the +sections below. + +#### Prometheus Default Metrics + +This plugin will collect default (standard) metrics when the `defaultMetrics.enabled` configuration option is set to +`true`. More information about default metrics can be found at: + +- https://prometheus.io/docs/instrumenting/writing_clientlibs/#standard-and-runtime-collectors +- https://github.com/siimon/prom-client/tree/v14.0.1#default-metrics + +#### HTTP Request Metrics + +This plugin will collect metrics related to incoming HTTP requests when the `requestMetrics.enabled` configuration +option is set to `true`. A [counter](https://prometheus.io/docs/concepts/metric_types/#counter) metric is used to track +the number of HTTP requests. The following [labels](https://prometheus.io/docs/practices/naming/#labels) are applied to +_every_ request for this metric: - `username` - The Verdaccio username of the user attempting to install/download a package. If the request is unauthenticated then the value `UNKNOWN` is used. - `userAgentName` - The name of the user agent the client used to make the request. It is derived from the `user-agent` header on the request. If no `user-agent` header is provided it defaults to `UNKNOWN`. +- `httpMethod` - The HTTP method used in the request. - `statusCode` - The status code of the response from Verdaccio. -Optionally, an additional `packageGroup` label can be applied _if_ a `packageGroups` option is added to the plugin -configuration. +#### Package Download Metrics -## Installation +This plugin will collect metrics related to package tarball downloads when the `packageMetrics.enabled` configuration +option is set to `true`. A [counter](https://prometheus.io/docs/concepts/metric_types/#counter) metric is used to track +the number of package tarball installs/downloads. The following [labels](https://prometheus.io/docs/practices/naming/#labels) +are applied to _every_ request for this metric: -Because of how Verdaccio loads plugins, you will need to install this package using an `npm` alias in your `package.json`: +- `username` - The Verdaccio username of the user attempting to install/download a package. If the request is + unauthenticated then the value `UNKNOWN` is used. +- `userAgentName` - The name of the user agent the client used to make the request. It is derived from the `user-agent` + header on the request. If no `user-agent` header is provided it defaults to `UNKNOWN`. +- `statusCode` - The status code of the response from Verdaccio. -```json -"dependencies": { - "verdaccio-metrics": "npm:@xlts.dev/verdaccio-prometheus-middleware" -} +Optionally, an additional `packageGroup` label can be applied _if_ a `packageMetrics.packageGroups` option is added to +the plugin configuration. Refer to the [Package Groups](#package-groups) section of this README for more information. + +### Installation + +Because Verdaccio automatically prefixes the string `verdaccio-` to the directory it looks for when loading a plugin, +this plugin will need to be installed to a directory named `verdaccio-metrics` _IF_ your `middlewares` configuration +uses `metrics` as the configuration key for this plugin (as shown in the [Configuration](#configuration) example). Below +is an example `Dockerfile` that can be used to build a custom image containing this plugin. The Verdaccio +[`plugins`](https://verdaccio.org/docs/configuration#plugins) configuration option is assumed to be set to +`/verdaccio/plugins`. + +```Dockerfile +# Docker multi-stage build - https://docs.docker.com/develop/develop-images/multistage-build/ +# Use an alpine node image to install the plugin +FROM node:lts-alpine as builder + +# Install the metrics middleware plugin +RUN mkdir -p /verdaccio/plugins \ + && cd /verdaccio/plugins \ + && npm install --global-style --no-bin-links --no-optional @xlts.dev/verdaccio-prometheus-middleware@1.0.0 + +# The final built image will be based on the standard Verdaccio docker image. +FROM verdaccio/verdaccio:5.2.0 + +# Copy the plugin files over from the 'builder' node image. +# The `$VERDACCIO_USER_UID` env variable is defined in the base `verdaccio/verdaccio` image. +# Refer to: https://github.com/verdaccio/verdaccio/blob/v5.2.0/Dockerfile#L32 +COPY --chown=$VERDACCIO_USER_UID:root --from=builder \ + /verdaccio/plugins/node_modules/@xlts.dev/verdaccio-prometheus-middleware \ + /verdaccio/plugins/verdaccio-metrics ``` -## Configuration +### Configuration -Complete configuration example: +Complete configuration example with comments: ```yaml middlewares: metrics: - ## Optional. Defaults to `false` so make sure to set this to `true` if you want to collect metrics. - metricsEnabled: true - - ## Optional. Collect default prometheus metrics. Defaults to `false`. - ## Refer to: https://github.com/siimon/prom-client/tree/v14.0.1#default-metrics - collectDefaultMetrics: false - ## Optional. Defaults to `/-/metrics`. metricsPath: /custom/path/metrics - ## Optional. A map of regular expressions to package grouping names. - packageGroups: - ## NOTE: The order of items below matters. The first matched regex is the package grouping that will be applied - ## to the metric generated for a request. - '@angular[/][^/]*': angular - 'react': react - '.*': other + ## Optional. If not specified, no default metrics will be collected. + ## Refer to: https://github.com/siimon/prom-client/tree/v14.0.1#default-metrics + defaultMetrics: + ## Optional. Defaults to `false`. Make sure to set this to `true` if you want to collect default metrics. + enabled: true + + ## Optional. If not specified, no package download metrics will be collected. + requestMetrics: + ## Optional. Defaults to `false`. Make sure to set this to `true` if you want to collect request metrics. + enabled: true + + ## Optional. Defaults to 'verdaccio_package_downloads'. + metricName: 'registry_http_requests' + + ## Optional. An array of regular expressions used to match and exclude request paths. The default list of paths to + ## exclude are shown below. If you override this array of values these default paths will NOT be included and will + ## need to be added manually if you still wish to exclude them. The `metricsPath` is **ALWAYS** excluded. + pathExclusions: + - '^/$' # root path to web ui + - '^/[-]/ping' # health endpoint + - '^/[-]/(static|verdaccio|web)' # web ui related paths + - '[.]ico$' # requests for icons (e.g. `favicon.ico`) + + ## Optional. If not specified, no package download metrics will be collected. + packageMetrics: + ## Optional. Defaults to `false`. Make sure to set this to `true` if you want to collect package download metrics. + enabled: true + + ## Optional. Defaults to 'verdaccio_package_downloads'. + metricName: 'registry_package_downloads' + + ## Optional. A map of regular expressions to package grouping names. + packageGroups: + ## NOTE: The order of items below matters. The first matched regex is the package grouping that will be applied + ## to the metric generated for a request. + '@angular/[^/]*': angular + 'react': react + '.*': other ``` -## Package Groups +### Package Groups -The `packageGroups` configuration option accepts an object map whose keys are expected to be a regular expression string -and the value the name of the package group that should be used for the `packageGroup` metric label. If no -`packageGroups` are defined, a `packageGroup` [label](https://prometheus.io/docs/practices/naming/#labels) will NOT be -applied to the [counter](https://prometheus.io/docs/concepts/metric_types/#counter) metric. +The `packageMetrics.packageGroups` configuration option accepts an object map whose keys are expected to be a regular +expression string and the value the name of the package group that should be used for the `packageGroup` metric label. +If no `packageMetrics.packageGroups` are defined, a `packageGroup` [label](https://prometheus.io/docs/practices/naming/#labels) +will NOT be applied to the `packageMetrics` [counter](https://prometheus.io/docs/concepts/metric_types/#counter) metric. The regular expression key is evaluated against the request path in the following manner: @@ -68,8 +154,8 @@ new RegExp(packageGroupRegex).test(decodeURIComponent(request.path)); ``` The order of the keys/values in the object map matters. The regular expressions are evaluated from first to last in the -order they are listed under the `packageGroups` configuration option and the first matching regex will have the -corresponding package grouping value applied when the install/download counter metric is collected. +order they are listed under the `packageMetrics.packageGroups` configuration option and the first matching regex will +have the corresponding package grouping value applied when the `packageMetrics` counter metric is collected. Given the following example configuration: @@ -77,13 +163,14 @@ Given the following example configuration: # Verdaccio config file middlewares: metrics: - metricsEnabled: true - packageGroups: - '@babel[/]plugin': 'babel-plugin' - 'babel[-]plugin': 'babel-plugin' - 'babel': 'babel' - '@.*': scoped - '.*': other + packageMetrics: + enabled: true + packageGroups: + '@babel/plugin': 'babel-plugin' + 'babel[-]plugin': 'babel-plugin' + 'babel': 'babel' + '@.*': scoped + '.*': other ``` ... the following packages would resolve to the `packageGroup` as listed in the table: @@ -100,7 +187,7 @@ middlewares: | `react` | other | | `apollo-server-express` | other | -## Contributing +### Contributing This project enforces the [Angular commit message format](https://github.com/angular/angular/blob/13.1.1/CONTRIBUTING.md#-commit-message-format). @@ -112,7 +199,7 @@ is linted using [commitlint](https://commitlint.js.org/#/) and a [husky](https:/ `commit-msg` hook to ensure the [Angular commit message format](https://github.com/angular/angular/blob/13.1.1/CONTRIBUTING.md#-commit-message-format) is followed. -## Pull Requests +#### Pull Requests Prior to opening a pull request, please ensure that you run the command: diff --git a/package-lock.json b/package-lock.json index bed8c75..6890055 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,39 +7,52 @@ "": { "name": "@xlts.dev/verdaccio-prometheus-middleware", "version": "1.0.0", - "license": "UNLICENSED", + "license": "MIT", "dependencies": { "prom-client": "14.0.1" }, "devDependencies": { - "@commitlint/cli": "15.0.0", - "@commitlint/config-conventional": "15.0.0", + "@commitlint/cli": "16.1.0", + "@commitlint/config-conventional": "16.0.0", "@types/chance": "1.1.3", "@types/express": "4.17.13", - "@types/jest": "27.0.3", - "@types/node": "17.0.4", - "@typescript-eslint/eslint-plugin": "5.10.0", - "@verdaccio/types": "10.1.0", + "@types/jest": "27.4.0", + "@types/node": "17.0.14", + "@typescript-eslint/eslint-plugin": "5.10.2", + "@verdaccio/types": "10.2.2", "chance": "1.1.8", "commitizen": "4.2.4", "cz-conventional-changelog": "3.3.0", - "eslint": "8.7.0", + "eslint": "8.8.0", "eslint-config-prettier": "8.3.0", "eslint-plugin-prettier": "4.0.0", "express": "4.17.2", "husky": "7.0.4", - "jest": "27.4.5", + "jest": "27.4.7", "jest-junit": "13.0.0", "prettier": "2.5.1", "pretty-quick": "3.1.3", "standard-version": "9.3.2", - "ts-jest": "27.1.2", - "typescript": "4.5.4" + "ts-jest": "27.1.3", + "typescript": "4.5.5" }, "engines": { "node": ">=14" } }, + "node_modules/@ampproject/remapping": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.0.2.tgz", + "integrity": "sha512-sE8Gx+qSDMLoJvb3QarJJlDQK7SSY4rK3hxp4XsiANeFOmjU46ZI7Y9adAQRJrmbz8zbtZkp3mJTT+rGxtF0XA==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.2.2", + "sourcemap-codec": "1.4.8" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@babel/code-frame": { "version": "7.16.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", @@ -53,35 +66,35 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.16.8.tgz", - "integrity": "sha512-m7OkX0IdKLKPpBlJtF561YJal5y/jyI5fNfWbPxh2D/nbzzGI4qRyrD8xO2jB24u7l+5I2a43scCG2IrfjC50Q==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.0.tgz", + "integrity": "sha512-392byTlpGWXMv4FbyWw3sAZ/FrW/DrwqLGXpy0mbyNe9Taqv1mg9yON5/o0cnr8XYCkFTZbC1eV+c+LAROgrng==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.7.tgz", - "integrity": "sha512-aeLaqcqThRNZYmbMqtulsetOQZ/5gbR/dWruUCJcpas4Qoyy+QeagfDsPdMrqwsPRDNxJvBlRiZxxX7THO7qtA==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.0.tgz", + "integrity": "sha512-x/5Ea+RO5MvF9ize5DeVICJoVrNv0Mi2RnIABrZEKYvPEpldXwauPkgvYA17cKa6WpU3LoYvYbuEMFtSNFsarA==", "dev": true, "dependencies": { + "@ampproject/remapping": "^2.0.0", "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.16.7", + "@babel/generator": "^7.17.0", "@babel/helper-compilation-targets": "^7.16.7", "@babel/helper-module-transforms": "^7.16.7", - "@babel/helpers": "^7.16.7", - "@babel/parser": "^7.16.7", + "@babel/helpers": "^7.17.0", + "@babel/parser": "^7.17.0", "@babel/template": "^7.16.7", - "@babel/traverse": "^7.16.7", - "@babel/types": "^7.16.7", + "@babel/traverse": "^7.17.0", + "@babel/types": "^7.17.0", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.1.2", - "semver": "^6.3.0", - "source-map": "^0.5.0" + "semver": "^6.3.0" }, "engines": { "node": ">=6.9.0" @@ -100,22 +113,13 @@ "semver": "bin/semver.js" } }, - "node_modules/@babel/core/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/@babel/generator": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.8.tgz", - "integrity": "sha512-1ojZwE9+lOXzcWdWmO6TbUzDfqLD39CmEhN8+2cX9XkDo5yW1OpgfejfliysR2AWLpMamTiOiAp/mtroaymhpw==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.0.tgz", + "integrity": "sha512-I3Omiv6FGOC29dtlZhkfXO6pgkmukJSlT26QjVvS1DGZe/NzSVCPG41X0tS21oZkJYlovfj9qDWgKP+Cn4bXxw==", "dev": true, "dependencies": { - "@babel/types": "^7.16.8", + "@babel/types": "^7.17.0", "jsesc": "^2.5.1", "source-map": "^0.5.0" }, @@ -292,23 +296,23 @@ } }, "node_modules/@babel/helpers": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.16.7.tgz", - "integrity": "sha512-9ZDoqtfY7AuEOt3cxchfii6C7GDyyMBffktR5B2jvWv8u2+efwvpnVKXMWzNehqy68tKgAfSwfdw/lWpthS2bw==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.0.tgz", + "integrity": "sha512-Xe/9NFxjPwELUvW2dsukcMZIp6XwPSbI4ojFBJuX5ramHuVE22SVcZIwqzdWo5uCgeTXW8qV97lMvSOjq+1+nQ==", "dev": true, "dependencies": { "@babel/template": "^7.16.7", - "@babel/traverse": "^7.16.7", - "@babel/types": "^7.16.7" + "@babel/traverse": "^7.17.0", + "@babel/types": "^7.17.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.7.tgz", - "integrity": "sha512-aKpPMfLvGO3Q97V0qhw/V2SWNWlwfJknuwAunU7wZLSfrM4xTBvg7E5opUVi1kJTBKihE38CPg4nBiqX83PWYw==", + "version": "7.16.10", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz", + "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==", "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.16.7", @@ -391,9 +395,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.8.tgz", - "integrity": "sha512-i7jDUfrVBWc+7OKcBzEe5n7fbv3i2fWtxKzzCvOjnzSxMfWMigAhtfJ7qzZNGFNMsCCd67+uz553dYKWXPvCKw==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.0.tgz", + "integrity": "sha512-VKXSCQx5D8S04ej+Dqsr1CzYvvWgf20jIw2D+YhQCrIlr2UZGaDds23Y0xg75/skOxpLCRpUZvk/1EAVkGoDOw==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -579,19 +583,19 @@ } }, "node_modules/@babel/traverse": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.8.tgz", - "integrity": "sha512-xe+H7JlvKsDQwXRsBhSnq1/+9c+LlQcCK3Tn/l5sbx02HYns/cn7ibp9+RV1sIUqu7hKg91NWsgHurO9dowITQ==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.0.tgz", + "integrity": "sha512-fpFIXvqD6kC7c7PUNnZ0Z8cQXlarCLtCUpt2S1Dx7PjoRtCFffvOkHHSom+m5HIxMZn5bIBVb71lhabcmjEsqg==", "dev": true, "dependencies": { "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.16.8", + "@babel/generator": "^7.17.0", "@babel/helper-environment-visitor": "^7.16.7", "@babel/helper-function-name": "^7.16.7", "@babel/helper-hoist-variables": "^7.16.7", "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/parser": "^7.16.8", - "@babel/types": "^7.16.8", + "@babel/parser": "^7.17.0", + "@babel/types": "^7.17.0", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -609,9 +613,9 @@ } }, "node_modules/@babel/types": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.8.tgz", - "integrity": "sha512-smN2DQc5s4M7fntyjGtyIPbRJv6wW4rU/94fmYJ7PKQuZkC0qGMHXJbg6sNGt12JmVr4k5YaptI/XtiLJBnmIg==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", + "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==", "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.16.7", @@ -628,16 +632,16 @@ "dev": true }, "node_modules/@commitlint/cli": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-15.0.0.tgz", - "integrity": "sha512-Y5xmDCweytqzo4N4lOI2YRiuX35xTjcs8n5hUceBH8eyK0YbwtgWX50BJOH2XbkwEmII9blNhlBog6AdQsqicg==", + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-16.1.0.tgz", + "integrity": "sha512-x5L1knvA3isRWBRVQx+Q6D45pA9139a2aZQYpxkljMG0dj4UHZkCnsYWpnGalxPxASI7nrI0KedKfS2YeQ55cQ==", "dev": true, "dependencies": { - "@commitlint/format": "^15.0.0", - "@commitlint/lint": "^15.0.0", - "@commitlint/load": "^15.0.0", - "@commitlint/read": "^15.0.0", - "@commitlint/types": "^15.0.0", + "@commitlint/format": "^16.0.0", + "@commitlint/lint": "^16.0.0", + "@commitlint/load": "^16.1.0", + "@commitlint/read": "^16.0.0", + "@commitlint/types": "^16.0.0", "lodash": "^4.17.19", "resolve-from": "5.0.0", "resolve-global": "1.0.0", @@ -651,9 +655,9 @@ } }, "node_modules/@commitlint/config-conventional": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-15.0.0.tgz", - "integrity": "sha512-eZBRL8Lk3hMNHp1wUMYj0qrZQEsST1ai7KHR8J1IDD9aHgT7L2giciibuQ+Og7vxVhR5WtYDvh9xirXFVPaSkQ==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-16.0.0.tgz", + "integrity": "sha512-mN7J8KlKFn0kROd+q9PB01sfDx/8K/R25yITspL1No8PB4oj9M1p77xWjP80hPydqZG9OvQq+anXK3ZWeR7s3g==", "dev": true, "dependencies": { "conventional-changelog-conventionalcommits": "^4.3.1" @@ -662,13 +666,26 @@ "node": ">=v12" } }, + "node_modules/@commitlint/config-validator": { + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-16.1.0.tgz", + "integrity": "sha512-2cHeZPNTuf1JWbMqyA46MkExor5HMSgv8JrdmzEakUbJHUreh35/wN00FJf57qGs134exQW2thiSQ1IJUsVx2Q==", + "dev": true, + "dependencies": { + "@commitlint/types": "^16.0.0", + "ajv": "^6.12.6" + }, + "engines": { + "node": ">=v12" + } + }, "node_modules/@commitlint/ensure": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-15.0.0.tgz", - "integrity": "sha512-7DV4iNIald3vycwaWBNGk5FbonaNzOlU8nBe5m5AgU2dIeNKuXwLm+zzJzG27j0Ho56rgz//3F6RIvmsoxY9ZA==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-16.0.0.tgz", + "integrity": "sha512-WdMySU8DCTaq3JPf0tZFCKIUhqxaL54mjduNhu8v4D2AMUVIIQKYMGyvXn94k8begeW6iJkTf9cXBArayskE7Q==", "dev": true, "dependencies": { - "@commitlint/types": "^15.0.0", + "@commitlint/types": "^16.0.0", "lodash": "^4.17.19" }, "engines": { @@ -676,21 +693,21 @@ } }, "node_modules/@commitlint/execute-rule": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-15.0.0.tgz", - "integrity": "sha512-pyE4ApxjbWhb1TXz5vRiGwI2ssdMMgZbaaheZq1/7WC0xRnqnIhE1yUC1D2q20qPtvkZPstTYvMiRVtF+DvjUg==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-16.0.0.tgz", + "integrity": "sha512-8edcCibmBb386x5JTHSPHINwA5L0xPkHQFY8TAuDEt5QyRZY/o5DF8OPHSa5Hx2xJvGaxxuIz4UtAT6IiRDYkw==", "dev": true, "engines": { "node": ">=v12" } }, "node_modules/@commitlint/format": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-15.0.0.tgz", - "integrity": "sha512-bPhAfqwRhPk92WiuY0ktEJNpRRHSCd+Eg1MdhGyL9Bl3U25E5zvuInA+dNctnzZiOBSH/37ZaD0eOKCpQE6acg==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-16.0.0.tgz", + "integrity": "sha512-9yp5NCquXL1jVMKL0ZkRwJf/UHdebvCcMvICuZV00NQGYSAL89O398nhqrqxlbjBhM5EZVq0VGcV5+7r3D4zAA==", "dev": true, "dependencies": { - "@commitlint/types": "^15.0.0", + "@commitlint/types": "^16.0.0", "chalk": "^4.0.0" }, "engines": { @@ -698,12 +715,12 @@ } }, "node_modules/@commitlint/is-ignored": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-15.0.0.tgz", - "integrity": "sha512-edtnkf2QZ/7e/YCJDgn1WDw9wfF1WfOitW5YEoSOb4SxjJEb/oE87kxNPZ2j8mnDMuunspcMfGHeg6fRlwaEWg==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-16.0.0.tgz", + "integrity": "sha512-gmAQcwIGC/R/Lp0CEb2b5bfGC7MT5rPe09N8kOGjO/NcdNmfFSZMquwrvNJsq9hnAP0skRdHIsqwlkENkN4Lag==", "dev": true, "dependencies": { - "@commitlint/types": "^15.0.0", + "@commitlint/types": "^16.0.0", "semver": "7.3.5" }, "engines": { @@ -711,32 +728,33 @@ } }, "node_modules/@commitlint/lint": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-15.0.0.tgz", - "integrity": "sha512-hUi2+Im/2dJ5FBvWnodypTkg+5haCgsDzB0fyMApWLUA1IucYUAqRCQCW5em1Mhk9Crw1pd5YzFNikhIclkqCw==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-16.0.0.tgz", + "integrity": "sha512-HNl15bRC0h+pLzbMzQC3tM0j1aESXsLYhElqKnXcf5mnCBkBkHzu6WwJW8rZbfxX+YwJmNljN62cPhmdBo8x0A==", "dev": true, "dependencies": { - "@commitlint/is-ignored": "^15.0.0", - "@commitlint/parse": "^15.0.0", - "@commitlint/rules": "^15.0.0", - "@commitlint/types": "^15.0.0" + "@commitlint/is-ignored": "^16.0.0", + "@commitlint/parse": "^16.0.0", + "@commitlint/rules": "^16.0.0", + "@commitlint/types": "^16.0.0" }, "engines": { "node": ">=v12" } }, "node_modules/@commitlint/load": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-15.0.0.tgz", - "integrity": "sha512-Ak1YPeOhvxmY3ioe0o6m1yLGvUAYb4BdfGgShU8jiTCmU3Mnmms0Xh/kfQz8AybhezCC3AmVTyBLaBZxOHR8kg==", + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-16.1.0.tgz", + "integrity": "sha512-MtlEhKjP8jAF85jjX4mw8DUUwCxKsCgAc865hhpnwxjrfBcmGP7Up2AFE/M3ZMGDmSl1X1TMybQk/zohj8Cqdg==", "dev": true, "dependencies": { - "@commitlint/execute-rule": "^15.0.0", - "@commitlint/resolve-extends": "^15.0.0", - "@commitlint/types": "^15.0.0", - "@endemolshinegroup/cosmiconfig-typescript-loader": "^3.0.2", + "@commitlint/config-validator": "^16.1.0", + "@commitlint/execute-rule": "^16.0.0", + "@commitlint/resolve-extends": "^16.1.0", + "@commitlint/types": "^16.0.0", "chalk": "^4.0.0", "cosmiconfig": "^7.0.0", + "cosmiconfig-typescript-loader": "^1.0.0", "lodash": "^4.17.19", "resolve-from": "^5.0.0", "typescript": "^4.4.3" @@ -746,21 +764,21 @@ } }, "node_modules/@commitlint/message": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-15.0.0.tgz", - "integrity": "sha512-L8euabzboKavPuDJsdIYAY2wx97LbiGEYsckMo6NmV8pOun50c8hQx6ouXFSAx4pp+mX9yUGmMiVqfrk2LKDJQ==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-16.0.0.tgz", + "integrity": "sha512-CmK2074SH1Ws6kFMEKOKH/7hMekGVbOD6vb4alCOo2+33ZSLUIX8iNkDYyrw38Jwg6yWUhLjyQLUxREeV+QIUA==", "dev": true, "engines": { "node": ">=v12" } }, "node_modules/@commitlint/parse": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-15.0.0.tgz", - "integrity": "sha512-7fweM67tZfBNS7zw1KTuuT5K2u9nGytUJqFqT/1Ln3Na9cBCsoAqR47mfsNOTlRCgGwakm4xiQ7BpS2gN0OGuw==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-16.0.0.tgz", + "integrity": "sha512-F9EjFlMw4MYgBEqoRrWZZKQBzdiJzPBI0qFDFqwUvfQsMmXEREZ242T4R5bFwLINWaALFLHEIa/FXEPa6QxCag==", "dev": true, "dependencies": { - "@commitlint/types": "^15.0.0", + "@commitlint/types": "^16.0.0", "conventional-changelog-angular": "^5.0.11", "conventional-commits-parser": "^3.2.2" }, @@ -769,13 +787,13 @@ } }, "node_modules/@commitlint/read": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-15.0.0.tgz", - "integrity": "sha512-5yI1o2HKZFVe7RTjL7IhuhHMKar/MDNY34vEHqqz9gMI7BK/rdP8uVb4Di1efl2V0UPnwID0nPKWESjQ8Ti0gw==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-16.0.0.tgz", + "integrity": "sha512-H4T2zsfmYQK9B+JtoQaCXWBHUhgIJyOzWZjSfuIV9Ce69/OgHoffNpLZPF2lX6yKuDrS1SQFhI/kUCjVc/e4ew==", "dev": true, "dependencies": { - "@commitlint/top-level": "^15.0.0", - "@commitlint/types": "^15.0.0", + "@commitlint/top-level": "^16.0.0", + "@commitlint/types": "^16.0.0", "fs-extra": "^10.0.0", "git-raw-commits": "^2.0.0" }, @@ -784,11 +802,13 @@ } }, "node_modules/@commitlint/resolve-extends": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-15.0.0.tgz", - "integrity": "sha512-7apfRJjgJsKja7lHsPfEFixKjA/fk/UeD3owkOw1174yYu4u8xBDLSeU3IinGPdMuF9m245eX8wo7vLUy+EBSg==", + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-16.1.0.tgz", + "integrity": "sha512-8182s6AFoUFX6+FT1PgQDt15nO2ogdR/EN8SYVAdhNXw1rLz8kT5saB/ICw567GuRAUgFTUMGCXy3ctMOXPEDg==", "dev": true, "dependencies": { + "@commitlint/config-validator": "^16.1.0", + "@commitlint/types": "^16.0.0", "import-fresh": "^3.0.0", "lodash": "^4.17.19", "resolve-from": "^5.0.0", @@ -799,15 +819,15 @@ } }, "node_modules/@commitlint/rules": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-15.0.0.tgz", - "integrity": "sha512-SqXfp6QUlwBS+0IZm4FEA/NmmAwcFQIkG3B05BtemOVWXQdZ8j1vV6hDwvA9oMPCmUSrrGpHOtZK7HaHhng2yA==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-16.0.0.tgz", + "integrity": "sha512-AOl0y2SBTdJ1bvIv8nwHvQKRT/jC1xb09C5VZwzHoT8sE8F54KDeEzPCwHQFgUcWdGLyS10kkOTAH2MyA8EIlg==", "dev": true, "dependencies": { - "@commitlint/ensure": "^15.0.0", - "@commitlint/message": "^15.0.0", - "@commitlint/to-lines": "^15.0.0", - "@commitlint/types": "^15.0.0", + "@commitlint/ensure": "^16.0.0", + "@commitlint/message": "^16.0.0", + "@commitlint/to-lines": "^16.0.0", + "@commitlint/types": "^16.0.0", "execa": "^5.0.0" }, "engines": { @@ -815,18 +835,18 @@ } }, "node_modules/@commitlint/to-lines": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-15.0.0.tgz", - "integrity": "sha512-mY3MNA9ujPqVpiJjTYG9MDsYCobue5PJFO0MfcIzS1mCVvngH8ZFTPAh1fT5t+t1h876boS88+9WgqjRvbYItw==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-16.0.0.tgz", + "integrity": "sha512-iN/qU38TCKU7uKOg6RXLpD49wNiuI0TqMqybHbjefUeP/Jmzxa8ishryj0uLyVdrAl1ZjGeD1ukXGMTtvqz8iA==", "dev": true, "engines": { "node": ">=v12" } }, "node_modules/@commitlint/top-level": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-15.0.0.tgz", - "integrity": "sha512-7Gz3t7xcuuUw1d1Nou6YLaztzp2Em+qZ6YdCzrqYc+aquca3Vt0O696nuiBDU/oE+tls4Hx2CNpAbWhTgEwB5A==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-16.0.0.tgz", + "integrity": "sha512-/Jt6NLxyFkpjL5O0jxurZPCHURZAm7cQCqikgPCwqPAH0TLgwqdHjnYipl8J+AGnAMGDip4FNLoYrtgIpZGBYw==", "dev": true, "dependencies": { "find-up": "^5.0.0" @@ -836,9 +856,9 @@ } }, "node_modules/@commitlint/types": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-15.0.0.tgz", - "integrity": "sha512-OMSLX+QJnyNoTwws54ULv9sOvuw9GdVezln76oyUd4YbMMJyaav62aSXDuCdWyL2sm9hTkSzyEi52PNaIj/vqw==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-16.0.0.tgz", + "integrity": "sha512-+0FvYOAS39bJ4aKjnYn/7FD4DfWkmQ6G/06I4F0Gvu4KS5twirEg8mIcLhmeRDOOKn4Tp8PwpLwBiSA6npEMQA==", "dev": true, "dependencies": { "chalk": "^4.0.0" @@ -847,22 +867,25 @@ "node": ">=v12" } }, - "node_modules/@endemolshinegroup/cosmiconfig-typescript-loader": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@endemolshinegroup/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-3.0.2.tgz", - "integrity": "sha512-QRVtqJuS1mcT56oHpVegkKBlgtWjXw/gHNWO3eL9oyB5Sc7HBoc2OLG/nYpVfT/Jejvo3NUrD0Udk7XgoyDKkA==", + "node_modules/@cspotcode/source-map-consumer": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", + "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", + "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", "dev": true, "dependencies": { - "lodash.get": "^4", - "make-error": "^1", - "ts-node": "^9", - "tslib": "^2" + "@cspotcode/source-map-consumer": "0.8.0" }, "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "cosmiconfig": ">=6" + "node": ">=12" } }, "node_modules/@eslint/eslintrc": { @@ -907,9 +930,9 @@ } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.2.tgz", - "integrity": "sha512-UXOuFCGcwciWckOpmfKDq/GyhlTf9pN/BzG//x8p8zTOFEcGuA68ANXheFS0AGvy3qgZqLBUkMs7hqzqCKOVwA==", + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.3.tgz", + "integrity": "sha512-3xSMlXHh03hCcCmFc0rbKp3Ivt2PFEJnQUJDDMTJQ2wkECZWdq4GePs2ctc5H8zV+cHPaq8k2vU8mrQjA6iHdQ==", "dev": true, "dependencies": { "@humanwhocodes/object-schema": "^1.2.1", @@ -1301,6 +1324,25 @@ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.4.tgz", + "integrity": "sha512-cz8HFjOFfUBtvN+NXYSFMHYRdxZMaEl0XypVrhzxBgadKIXhIkRd8aMeHhmF56Sl7SuS8OnUpQ73/k9LE4VnLg==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.2.5.tgz", + "integrity": "sha512-K+Eths78fXDFOvQ2hgJhCiI5s+g81r2yXmACBpbn+f2+Qt94PNoTgUcAXPT8DZkhXCsZRsHVWVtY5KIBMcpDqQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "sourcemap-codec": "1.4.8" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1363,6 +1405,30 @@ "node": ">= 6" } }, + "node_modules/@tsconfig/node10": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", + "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", + "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", + "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", + "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", + "dev": true + }, "node_modules/@types/babel__core": { "version": "7.1.18", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.18.tgz", @@ -1486,9 +1552,9 @@ } }, "node_modules/@types/jest": { - "version": "27.0.3", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-27.0.3.tgz", - "integrity": "sha512-cmmwv9t7gBYt7hNKH5Spu7Kuu/DotGa+Ff+JGRKZ4db5eh8PnKS4LuebJ3YLUoyOyIHraTGyULn23YtEAm0VSg==", + "version": "27.4.0", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-27.4.0.tgz", + "integrity": "sha512-gHl8XuC1RZ8H2j5sHv/JqsaxXkDDM9iDOgu0Wp8sjs4u/snb2PVehyWXJPr+ORA0RPpgw231mnutWI1+0hgjIQ==", "dev": true, "dependencies": { "jest-diff": "^27.0.0", @@ -1520,9 +1586,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "17.0.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.4.tgz", - "integrity": "sha512-6xwbrW4JJiJLgF+zNypN5wr2ykM9/jHcL7rQ8fZe2vuftggjzZeRSM4OwRc6Xk8qWjwJ99qVHo/JgOGmomWRog==", + "version": "17.0.14", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.14.tgz", + "integrity": "sha512-SbjLmERksKOGzWzPNuW7fJM7fk3YXVTFiZWB/Hs99gwhk+/dnrQRPBQjPW9aO+fi1tAffi9PrwFvsmOKmDTyng==", "dev": true }, "node_modules/@types/normalize-package-data": { @@ -1587,14 +1653,14 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.10.0.tgz", - "integrity": "sha512-XXVKnMsq2fuu9K2KsIxPUGqb6xAImz8MEChClbXmE3VbveFtBUU5bzM6IPVWqzyADIgdkS2Ws/6Xo7W2TeZWjQ==", + "version": "5.10.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.10.2.tgz", + "integrity": "sha512-4W/9lLuE+v27O/oe7hXJKjNtBLnZE8tQAFpapdxwSVHqtmIoPB1gph3+ahNwVuNL37BX7YQHyGF9Xv6XCnIX2Q==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.10.0", - "@typescript-eslint/type-utils": "5.10.0", - "@typescript-eslint/utils": "5.10.0", + "@typescript-eslint/scope-manager": "5.10.2", + "@typescript-eslint/type-utils": "5.10.2", + "@typescript-eslint/utils": "5.10.2", "debug": "^4.3.2", "functional-red-black-tree": "^1.0.1", "ignore": "^5.1.8", @@ -1620,15 +1686,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.10.0.tgz", - "integrity": "sha512-pJB2CCeHWtwOAeIxv8CHVGJhI5FNyJAIpx5Pt72YkK3QfEzt6qAlXZuyaBmyfOdM62qU0rbxJzNToPTVeJGrQw==", + "version": "5.10.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.10.2.tgz", + "integrity": "sha512-JaNYGkaQVhP6HNF+lkdOr2cAs2wdSZBoalE22uYWq8IEv/OVH0RksSGydk+sW8cLoSeYmC+OHvRyv2i4AQ7Czg==", "dev": true, "peer": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.10.0", - "@typescript-eslint/types": "5.10.0", - "@typescript-eslint/typescript-estree": "5.10.0", + "@typescript-eslint/scope-manager": "5.10.2", + "@typescript-eslint/types": "5.10.2", + "@typescript-eslint/typescript-estree": "5.10.2", "debug": "^4.3.2" }, "engines": { @@ -1648,13 +1714,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.10.0.tgz", - "integrity": "sha512-tgNgUgb4MhqK6DoKn3RBhyZ9aJga7EQrw+2/OiDk5hKf3pTVZWyqBi7ukP+Z0iEEDMF5FDa64LqODzlfE4O/Dg==", + "version": "5.10.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.10.2.tgz", + "integrity": "sha512-39Tm6f4RoZoVUWBYr3ekS75TYgpr5Y+X0xLZxXqcZNDWZdJdYbKd3q2IR4V9y5NxxiPu/jxJ8XP7EgHiEQtFnw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.10.0", - "@typescript-eslint/visitor-keys": "5.10.0" + "@typescript-eslint/types": "5.10.2", + "@typescript-eslint/visitor-keys": "5.10.2" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1665,12 +1731,12 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.10.0.tgz", - "integrity": "sha512-TzlyTmufJO5V886N+hTJBGIfnjQDQ32rJYxPaeiyWKdjsv2Ld5l8cbS7pxim4DeNs62fKzRSt8Q14Evs4JnZyQ==", + "version": "5.10.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.10.2.tgz", + "integrity": "sha512-uRKSvw/Ccs5FYEoXW04Z5VfzF2iiZcx8Fu7DGIB7RHozuP0VbKNzP1KfZkHBTM75pCpsWxIthEH1B33dmGBKHw==", "dev": true, "dependencies": { - "@typescript-eslint/utils": "5.10.0", + "@typescript-eslint/utils": "5.10.2", "debug": "^4.3.2", "tsutils": "^3.21.0" }, @@ -1691,9 +1757,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.10.0.tgz", - "integrity": "sha512-wUljCgkqHsMZbw60IbOqT/puLfyqqD5PquGiBo1u1IS3PLxdi3RDGlyf032IJyh+eQoGhz9kzhtZa+VC4eWTlQ==", + "version": "5.10.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.10.2.tgz", + "integrity": "sha512-Qfp0qk/5j2Rz3p3/WhWgu4S1JtMcPgFLnmAKAW061uXxKSa7VWKZsDXVaMXh2N60CX9h6YLaBoy9PJAfCOjk3w==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1704,13 +1770,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.10.0.tgz", - "integrity": "sha512-x+7e5IqfwLwsxTdliHRtlIYkgdtYXzE0CkFeV6ytAqq431ZyxCFzNMNR5sr3WOlIG/ihVZr9K/y71VHTF/DUQA==", + "version": "5.10.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.10.2.tgz", + "integrity": "sha512-WHHw6a9vvZls6JkTgGljwCsMkv8wu8XU8WaYKeYhxhWXH/atZeiMW6uDFPLZOvzNOGmuSMvHtZKd6AuC8PrwKQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.10.0", - "@typescript-eslint/visitor-keys": "5.10.0", + "@typescript-eslint/types": "5.10.2", + "@typescript-eslint/visitor-keys": "5.10.2", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", @@ -1731,15 +1797,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.10.0.tgz", - "integrity": "sha512-IGYwlt1CVcFoE2ueW4/ioEwybR60RAdGeiJX/iDAw0t5w0wK3S7QncDwpmsM70nKgGTuVchEWB8lwZwHqPAWRg==", + "version": "5.10.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.10.2.tgz", + "integrity": "sha512-vuJaBeig1NnBRkf7q9tgMLREiYD7zsMrsN1DA3wcoMDvr3BTFiIpKjGiYZoKPllfEwN7spUjv7ZqD+JhbVjEPg==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.10.0", - "@typescript-eslint/types": "5.10.0", - "@typescript-eslint/typescript-estree": "5.10.0", + "@typescript-eslint/scope-manager": "5.10.2", + "@typescript-eslint/types": "5.10.2", + "@typescript-eslint/typescript-estree": "5.10.2", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" }, @@ -1755,12 +1821,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.10.0.tgz", - "integrity": "sha512-GMxj0K1uyrFLPKASLmZzCuSddmjZVbVj3Ouy5QVuIGKZopxvOr24JsS7gruz6C3GExE01mublZ3mIBOaon9zuQ==", + "version": "5.10.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.10.2.tgz", + "integrity": "sha512-zHIhYGGGrFJvvyfwHk5M08C5B5K4bewkm+rrvNTKk1/S15YHR+SA/QUF8ZWscXSfEaB8Nn2puZj+iHcoxVOD/Q==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.10.0", + "@typescript-eslint/types": "5.10.2", "eslint-visitor-keys": "^3.0.0" }, "engines": { @@ -1772,9 +1838,9 @@ } }, "node_modules/@verdaccio/types": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/@verdaccio/types/-/types-10.1.0.tgz", - "integrity": "sha512-lKycP0Bp5ZURIxsau/jZSNPQy7mLIKilK5pBk1fUPu9recpfw5G9/DF5oOzwUvBkgfmLFzOV896dHSAiasKYbA==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@verdaccio/types/-/types-10.2.2.tgz", + "integrity": "sha512-9V+MujwCsreox27d30Ny3dq/2sLJKhNGOVU38i+CWFQIl2VOCRFQPGTkJNHLjEUC5XHEpeXPI5mtHHAO+ePiEw==", "dev": true, "funding": { "type": "opencollective", @@ -1788,13 +1854,13 @@ "dev": true }, "node_modules/accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", "dev": true, "dependencies": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" + "mime-types": "~2.1.34", + "negotiator": "0.6.3" }, "engines": { "node": ">= 0.6" @@ -2260,9 +2326,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001300", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001300.tgz", - "integrity": "sha512-cVjiJHWGcNlJi8TZVKNMnvMid3Z3TTdDHmLDzlOdIiZq138Exvo0G+G0wTdVYolxKb4AYwC+38pxodiInVtJSA==", + "version": "1.0.30001306", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001306.tgz", + "integrity": "sha512-Wd1OuggRzg1rbnM5hv1wXs2VkxJH/AA+LuudlIqvZiCvivF+wJJe2mgBZC8gPMgI7D76PP5CTx8Luvaqc1V6OQ==", "dev": true, "funding": { "type": "opencollective", @@ -3082,6 +3148,25 @@ "node": ">=10" } }, + "node_modules/cosmiconfig-typescript-loader": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-1.0.4.tgz", + "integrity": "sha512-ulv2dvwurP/MZAIthXm69bO7EzzIUThZ6RJ1qXhdlXM6to3F+IKBL/17EnhYSG52A5N1KcAUu66vSG/3/77KrA==", + "dev": true, + "dependencies": { + "cosmiconfig": "^7", + "ts-node": "^10.4.0" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "peerDependencies": { + "@types/node": "*", + "cosmiconfig": ">=7", + "typescript": ">=3" + } + }, "node_modules/create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", @@ -3531,9 +3616,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.48", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.48.tgz", - "integrity": "sha512-RT3SEmpv7XUA+tKXrZGudAWLDpa7f8qmhjcLaM6OD/ERxjQ/zAojT8/Vvo0BSzbArkElFZ1WyZ9FuwAYbkdBNA==", + "version": "1.4.63", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.63.tgz", + "integrity": "sha512-e0PX/LRJPFRU4kzJKLvTobxyFdnANCvcoDCe8XcyTqP58nTWIwdsHvXLIl1RkB39X5yaosLaroMASWB0oIsgCA==", "dev": true }, "node_modules/emittery": { @@ -3691,9 +3776,9 @@ } }, "node_modules/eslint": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.7.0.tgz", - "integrity": "sha512-ifHYzkBGrzS2iDU7KjhCAVMGCvF6M3Xfs8X8b37cgrUlDt6bWRTpRh6T/gtSXv1HJ/BUGgmjvNvOEGu85Iif7w==", + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.8.0.tgz", + "integrity": "sha512-H3KXAzQGBH1plhYS3okDix2ZthuYJlQQEGE5k0IKuEqUSiyu4AmxxlJ2MtTYeJ3xB4jDhcYCwGOg2TXYdnDXlQ==", "dev": true, "dependencies": { "@eslint/eslintrc": "^1.0.5", @@ -4292,9 +4377,9 @@ } }, "node_modules/flatted": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.4.tgz", - "integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", + "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", "dev": true }, "node_modules/form-data": { @@ -4675,9 +4760,9 @@ } }, "node_modules/globals": { - "version": "13.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", - "integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==", + "version": "13.12.1", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.1.tgz", + "integrity": "sha512-317dFlgY2pdJZ9rspXDks7073GpDmXdfbM3vYYp0HAMKGDh1FfWPleI2ljVNLQX5M5lXcAslTcPTrOrMEFOjyw==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -5328,14 +5413,14 @@ } }, "node_modules/jest": { - "version": "27.4.5", - "resolved": "https://registry.npmjs.org/jest/-/jest-27.4.5.tgz", - "integrity": "sha512-uT5MiVN3Jppt314kidCk47MYIRilJjA/l2mxwiuzzxGUeJIvA8/pDaJOAX5KWvjAo7SCydcW0/4WEtgbLMiJkg==", + "version": "27.4.7", + "resolved": "https://registry.npmjs.org/jest/-/jest-27.4.7.tgz", + "integrity": "sha512-8heYvsx7nV/m8m24Vk26Y87g73Ba6ueUd0MWed/NXMhSZIm62U/llVbS0PJe1SHunbyXjJ/BqG1z9bFjGUIvTg==", "dev": true, "dependencies": { - "@jest/core": "^27.4.5", + "@jest/core": "^27.4.7", "import-local": "^3.0.2", - "jest-cli": "^27.4.5" + "jest-cli": "^27.4.7" }, "bin": { "jest": "bin/jest.js" @@ -6293,12 +6378,6 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, - "node_modules/lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", - "dev": true - }, "node_modules/lodash.ismatch": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz", @@ -6647,9 +6726,9 @@ "dev": true }, "node_modules/negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "dev": true, "engines": { "node": ">= 0.6" @@ -6955,9 +7034,9 @@ } }, "node_modules/pirates": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.4.tgz", - "integrity": "sha512-ZIrVPH+A52Dw84R0L3/VS9Op04PuQ2SEoJL6bkshmiTic/HldyW9Tf7oH5mhJZBK7NmDx27vSMrYEXPXclpDKw==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", + "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", "dev": true, "engines": { "node": ">= 6" @@ -7548,12 +7627,12 @@ } }, "node_modules/resolve": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.21.0.tgz", - "integrity": "sha512-3wCbTpk5WJlyE4mSOtDLhqQmGFi0/TD9VPwmiolnk8U0wRgMEktqCXd3vy5buTO3tljvalNvKrjHEfrd2WpEKA==", + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", + "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", "dev": true, "dependencies": { - "is-core-module": "^2.8.0", + "is-core-module": "^2.8.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -7722,12 +7801,6 @@ "npm": ">=2.0.0" } }, - "node_modules/rxjs/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -7908,6 +7981,12 @@ "source-map": "^0.6.0" } }, + "node_modules/sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "dev": true + }, "node_modules/spdx-correct": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", @@ -8517,9 +8596,9 @@ } }, "node_modules/ts-jest": { - "version": "27.1.2", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.2.tgz", - "integrity": "sha512-eSOiJOWq6Hhs6Khzk5wKC5sgWIXgXqOCiIl1+3lfnearu58Hj4QpE5tUhQcA3xtZrELbcvAGCsd6HB8OsaVaTA==", + "version": "27.1.3", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.3.tgz", + "integrity": "sha512-6Nlura7s6uM9BVUAoqLH7JHyMXjz8gluryjpPXxr3IxZdAXnU6FhjvVLHFtfd1vsE1p8zD1OJfskkc0jhTSnkA==", "dev": true, "dependencies": { "bs-logger": "0.x", @@ -8561,35 +8640,59 @@ } }, "node_modules/ts-node": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", - "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", - "dev": true, - "dependencies": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.4.0.tgz", + "integrity": "sha512-g0FlPvvCXSIO1JDF6S232P5jPYqBkRL9qly81ZgAOSU7rwI0stphCgd2kLiCrU9DjQCrJMWEqcNSjQL02s6d8A==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "0.7.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", "arg": "^4.1.0", "create-require": "^1.1.0", "diff": "^4.0.1", "make-error": "^1.1.1", - "source-map-support": "^0.5.17", "yn": "3.1.1" }, "bin": { "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", "ts-node-script": "dist/bin-script.js", "ts-node-transpile-only": "dist/bin-transpile.js", "ts-script": "dist/bin-script-deprecated.js" }, - "engines": { - "node": ">=10.0.0" - }, "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node/node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true, + "engines": { + "node": ">=0.4.0" } }, "node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true }, "node_modules/tsutils": { @@ -8607,12 +8710,6 @@ "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" } }, - "node_modules/tsutils/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -8675,9 +8772,9 @@ } }, "node_modules/typescript": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.4.tgz", - "integrity": "sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg==", + "version": "4.5.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz", + "integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -8688,9 +8785,9 @@ } }, "node_modules/uglify-js": { - "version": "3.14.5", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.5.tgz", - "integrity": "sha512-qZukoSxOG0urUTvjc2ERMTcAy+BiFh3weWAkeurLwjrCba73poHmG3E36XEjd/JGukMzwTL7uCxZiAexj8ppvQ==", + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.15.0.tgz", + "integrity": "sha512-x+xdeDWq7FiORDvyIJ0q/waWd4PhjBNOm5dQUOq2AKC0IEjxOS66Ha9tctiVDGcRQuh69K7fgU5oRuTK4cysSg==", "dev": true, "optional": true, "bin": { @@ -9109,6 +9206,16 @@ } }, "dependencies": { + "@ampproject/remapping": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.0.2.tgz", + "integrity": "sha512-sE8Gx+qSDMLoJvb3QarJJlDQK7SSY4rK3hxp4XsiANeFOmjU46ZI7Y9adAQRJrmbz8zbtZkp3mJTT+rGxtF0XA==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.2.2", + "sourcemap-codec": "1.4.8" + } + }, "@babel/code-frame": { "version": "7.16.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", @@ -9119,32 +9226,32 @@ } }, "@babel/compat-data": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.16.8.tgz", - "integrity": "sha512-m7OkX0IdKLKPpBlJtF561YJal5y/jyI5fNfWbPxh2D/nbzzGI4qRyrD8xO2jB24u7l+5I2a43scCG2IrfjC50Q==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.0.tgz", + "integrity": "sha512-392byTlpGWXMv4FbyWw3sAZ/FrW/DrwqLGXpy0mbyNe9Taqv1mg9yON5/o0cnr8XYCkFTZbC1eV+c+LAROgrng==", "dev": true }, "@babel/core": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.7.tgz", - "integrity": "sha512-aeLaqcqThRNZYmbMqtulsetOQZ/5gbR/dWruUCJcpas4Qoyy+QeagfDsPdMrqwsPRDNxJvBlRiZxxX7THO7qtA==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.0.tgz", + "integrity": "sha512-x/5Ea+RO5MvF9ize5DeVICJoVrNv0Mi2RnIABrZEKYvPEpldXwauPkgvYA17cKa6WpU3LoYvYbuEMFtSNFsarA==", "dev": true, "requires": { + "@ampproject/remapping": "^2.0.0", "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.16.7", + "@babel/generator": "^7.17.0", "@babel/helper-compilation-targets": "^7.16.7", "@babel/helper-module-transforms": "^7.16.7", - "@babel/helpers": "^7.16.7", - "@babel/parser": "^7.16.7", + "@babel/helpers": "^7.17.0", + "@babel/parser": "^7.17.0", "@babel/template": "^7.16.7", - "@babel/traverse": "^7.16.7", - "@babel/types": "^7.16.7", + "@babel/traverse": "^7.17.0", + "@babel/types": "^7.17.0", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.1.2", - "semver": "^6.3.0", - "source-map": "^0.5.0" + "semver": "^6.3.0" }, "dependencies": { "semver": { @@ -9152,22 +9259,16 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true } } }, "@babel/generator": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.8.tgz", - "integrity": "sha512-1ojZwE9+lOXzcWdWmO6TbUzDfqLD39CmEhN8+2cX9XkDo5yW1OpgfejfliysR2AWLpMamTiOiAp/mtroaymhpw==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.0.tgz", + "integrity": "sha512-I3Omiv6FGOC29dtlZhkfXO6pgkmukJSlT26QjVvS1DGZe/NzSVCPG41X0tS21oZkJYlovfj9qDWgKP+Cn4bXxw==", "dev": true, "requires": { - "@babel/types": "^7.16.8", + "@babel/types": "^7.17.0", "jsesc": "^2.5.1", "source-map": "^0.5.0" }, @@ -9300,20 +9401,20 @@ "dev": true }, "@babel/helpers": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.16.7.tgz", - "integrity": "sha512-9ZDoqtfY7AuEOt3cxchfii6C7GDyyMBffktR5B2jvWv8u2+efwvpnVKXMWzNehqy68tKgAfSwfdw/lWpthS2bw==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.0.tgz", + "integrity": "sha512-Xe/9NFxjPwELUvW2dsukcMZIp6XwPSbI4ojFBJuX5ramHuVE22SVcZIwqzdWo5uCgeTXW8qV97lMvSOjq+1+nQ==", "dev": true, "requires": { "@babel/template": "^7.16.7", - "@babel/traverse": "^7.16.7", - "@babel/types": "^7.16.7" + "@babel/traverse": "^7.17.0", + "@babel/types": "^7.17.0" } }, "@babel/highlight": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.7.tgz", - "integrity": "sha512-aKpPMfLvGO3Q97V0qhw/V2SWNWlwfJknuwAunU7wZLSfrM4xTBvg7E5opUVi1kJTBKihE38CPg4nBiqX83PWYw==", + "version": "7.16.10", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz", + "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.16.7", @@ -9380,9 +9481,9 @@ } }, "@babel/parser": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.8.tgz", - "integrity": "sha512-i7jDUfrVBWc+7OKcBzEe5n7fbv3i2fWtxKzzCvOjnzSxMfWMigAhtfJ7qzZNGFNMsCCd67+uz553dYKWXPvCKw==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.0.tgz", + "integrity": "sha512-VKXSCQx5D8S04ej+Dqsr1CzYvvWgf20jIw2D+YhQCrIlr2UZGaDds23Y0xg75/skOxpLCRpUZvk/1EAVkGoDOw==", "dev": true }, "@babel/plugin-syntax-async-generators": { @@ -9514,19 +9615,19 @@ } }, "@babel/traverse": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.8.tgz", - "integrity": "sha512-xe+H7JlvKsDQwXRsBhSnq1/+9c+LlQcCK3Tn/l5sbx02HYns/cn7ibp9+RV1sIUqu7hKg91NWsgHurO9dowITQ==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.0.tgz", + "integrity": "sha512-fpFIXvqD6kC7c7PUNnZ0Z8cQXlarCLtCUpt2S1Dx7PjoRtCFffvOkHHSom+m5HIxMZn5bIBVb71lhabcmjEsqg==", "dev": true, "requires": { "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.16.8", + "@babel/generator": "^7.17.0", "@babel/helper-environment-visitor": "^7.16.7", "@babel/helper-function-name": "^7.16.7", "@babel/helper-hoist-variables": "^7.16.7", "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/parser": "^7.16.8", - "@babel/types": "^7.16.8", + "@babel/parser": "^7.17.0", + "@babel/types": "^7.17.0", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -9540,9 +9641,9 @@ } }, "@babel/types": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.8.tgz", - "integrity": "sha512-smN2DQc5s4M7fntyjGtyIPbRJv6wW4rU/94fmYJ7PKQuZkC0qGMHXJbg6sNGt12JmVr4k5YaptI/XtiLJBnmIg==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", + "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.16.7", @@ -9556,16 +9657,16 @@ "dev": true }, "@commitlint/cli": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-15.0.0.tgz", - "integrity": "sha512-Y5xmDCweytqzo4N4lOI2YRiuX35xTjcs8n5hUceBH8eyK0YbwtgWX50BJOH2XbkwEmII9blNhlBog6AdQsqicg==", + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-16.1.0.tgz", + "integrity": "sha512-x5L1knvA3isRWBRVQx+Q6D45pA9139a2aZQYpxkljMG0dj4UHZkCnsYWpnGalxPxASI7nrI0KedKfS2YeQ55cQ==", "dev": true, "requires": { - "@commitlint/format": "^15.0.0", - "@commitlint/lint": "^15.0.0", - "@commitlint/load": "^15.0.0", - "@commitlint/read": "^15.0.0", - "@commitlint/types": "^15.0.0", + "@commitlint/format": "^16.0.0", + "@commitlint/lint": "^16.0.0", + "@commitlint/load": "^16.1.0", + "@commitlint/read": "^16.0.0", + "@commitlint/types": "^16.0.0", "lodash": "^4.17.19", "resolve-from": "5.0.0", "resolve-global": "1.0.0", @@ -9573,114 +9674,127 @@ } }, "@commitlint/config-conventional": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-15.0.0.tgz", - "integrity": "sha512-eZBRL8Lk3hMNHp1wUMYj0qrZQEsST1ai7KHR8J1IDD9aHgT7L2giciibuQ+Og7vxVhR5WtYDvh9xirXFVPaSkQ==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-16.0.0.tgz", + "integrity": "sha512-mN7J8KlKFn0kROd+q9PB01sfDx/8K/R25yITspL1No8PB4oj9M1p77xWjP80hPydqZG9OvQq+anXK3ZWeR7s3g==", "dev": true, "requires": { "conventional-changelog-conventionalcommits": "^4.3.1" } }, + "@commitlint/config-validator": { + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-16.1.0.tgz", + "integrity": "sha512-2cHeZPNTuf1JWbMqyA46MkExor5HMSgv8JrdmzEakUbJHUreh35/wN00FJf57qGs134exQW2thiSQ1IJUsVx2Q==", + "dev": true, + "requires": { + "@commitlint/types": "^16.0.0", + "ajv": "^6.12.6" + } + }, "@commitlint/ensure": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-15.0.0.tgz", - "integrity": "sha512-7DV4iNIald3vycwaWBNGk5FbonaNzOlU8nBe5m5AgU2dIeNKuXwLm+zzJzG27j0Ho56rgz//3F6RIvmsoxY9ZA==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-16.0.0.tgz", + "integrity": "sha512-WdMySU8DCTaq3JPf0tZFCKIUhqxaL54mjduNhu8v4D2AMUVIIQKYMGyvXn94k8begeW6iJkTf9cXBArayskE7Q==", "dev": true, "requires": { - "@commitlint/types": "^15.0.0", + "@commitlint/types": "^16.0.0", "lodash": "^4.17.19" } }, "@commitlint/execute-rule": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-15.0.0.tgz", - "integrity": "sha512-pyE4ApxjbWhb1TXz5vRiGwI2ssdMMgZbaaheZq1/7WC0xRnqnIhE1yUC1D2q20qPtvkZPstTYvMiRVtF+DvjUg==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-16.0.0.tgz", + "integrity": "sha512-8edcCibmBb386x5JTHSPHINwA5L0xPkHQFY8TAuDEt5QyRZY/o5DF8OPHSa5Hx2xJvGaxxuIz4UtAT6IiRDYkw==", "dev": true }, "@commitlint/format": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-15.0.0.tgz", - "integrity": "sha512-bPhAfqwRhPk92WiuY0ktEJNpRRHSCd+Eg1MdhGyL9Bl3U25E5zvuInA+dNctnzZiOBSH/37ZaD0eOKCpQE6acg==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-16.0.0.tgz", + "integrity": "sha512-9yp5NCquXL1jVMKL0ZkRwJf/UHdebvCcMvICuZV00NQGYSAL89O398nhqrqxlbjBhM5EZVq0VGcV5+7r3D4zAA==", "dev": true, "requires": { - "@commitlint/types": "^15.0.0", + "@commitlint/types": "^16.0.0", "chalk": "^4.0.0" } }, "@commitlint/is-ignored": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-15.0.0.tgz", - "integrity": "sha512-edtnkf2QZ/7e/YCJDgn1WDw9wfF1WfOitW5YEoSOb4SxjJEb/oE87kxNPZ2j8mnDMuunspcMfGHeg6fRlwaEWg==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-16.0.0.tgz", + "integrity": "sha512-gmAQcwIGC/R/Lp0CEb2b5bfGC7MT5rPe09N8kOGjO/NcdNmfFSZMquwrvNJsq9hnAP0skRdHIsqwlkENkN4Lag==", "dev": true, "requires": { - "@commitlint/types": "^15.0.0", + "@commitlint/types": "^16.0.0", "semver": "7.3.5" } }, "@commitlint/lint": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-15.0.0.tgz", - "integrity": "sha512-hUi2+Im/2dJ5FBvWnodypTkg+5haCgsDzB0fyMApWLUA1IucYUAqRCQCW5em1Mhk9Crw1pd5YzFNikhIclkqCw==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-16.0.0.tgz", + "integrity": "sha512-HNl15bRC0h+pLzbMzQC3tM0j1aESXsLYhElqKnXcf5mnCBkBkHzu6WwJW8rZbfxX+YwJmNljN62cPhmdBo8x0A==", "dev": true, "requires": { - "@commitlint/is-ignored": "^15.0.0", - "@commitlint/parse": "^15.0.0", - "@commitlint/rules": "^15.0.0", - "@commitlint/types": "^15.0.0" + "@commitlint/is-ignored": "^16.0.0", + "@commitlint/parse": "^16.0.0", + "@commitlint/rules": "^16.0.0", + "@commitlint/types": "^16.0.0" } }, "@commitlint/load": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-15.0.0.tgz", - "integrity": "sha512-Ak1YPeOhvxmY3ioe0o6m1yLGvUAYb4BdfGgShU8jiTCmU3Mnmms0Xh/kfQz8AybhezCC3AmVTyBLaBZxOHR8kg==", + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-16.1.0.tgz", + "integrity": "sha512-MtlEhKjP8jAF85jjX4mw8DUUwCxKsCgAc865hhpnwxjrfBcmGP7Up2AFE/M3ZMGDmSl1X1TMybQk/zohj8Cqdg==", "dev": true, "requires": { - "@commitlint/execute-rule": "^15.0.0", - "@commitlint/resolve-extends": "^15.0.0", - "@commitlint/types": "^15.0.0", - "@endemolshinegroup/cosmiconfig-typescript-loader": "^3.0.2", + "@commitlint/config-validator": "^16.1.0", + "@commitlint/execute-rule": "^16.0.0", + "@commitlint/resolve-extends": "^16.1.0", + "@commitlint/types": "^16.0.0", "chalk": "^4.0.0", "cosmiconfig": "^7.0.0", + "cosmiconfig-typescript-loader": "^1.0.0", "lodash": "^4.17.19", "resolve-from": "^5.0.0", "typescript": "^4.4.3" } }, "@commitlint/message": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-15.0.0.tgz", - "integrity": "sha512-L8euabzboKavPuDJsdIYAY2wx97LbiGEYsckMo6NmV8pOun50c8hQx6ouXFSAx4pp+mX9yUGmMiVqfrk2LKDJQ==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-16.0.0.tgz", + "integrity": "sha512-CmK2074SH1Ws6kFMEKOKH/7hMekGVbOD6vb4alCOo2+33ZSLUIX8iNkDYyrw38Jwg6yWUhLjyQLUxREeV+QIUA==", "dev": true }, "@commitlint/parse": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-15.0.0.tgz", - "integrity": "sha512-7fweM67tZfBNS7zw1KTuuT5K2u9nGytUJqFqT/1Ln3Na9cBCsoAqR47mfsNOTlRCgGwakm4xiQ7BpS2gN0OGuw==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-16.0.0.tgz", + "integrity": "sha512-F9EjFlMw4MYgBEqoRrWZZKQBzdiJzPBI0qFDFqwUvfQsMmXEREZ242T4R5bFwLINWaALFLHEIa/FXEPa6QxCag==", "dev": true, "requires": { - "@commitlint/types": "^15.0.0", + "@commitlint/types": "^16.0.0", "conventional-changelog-angular": "^5.0.11", "conventional-commits-parser": "^3.2.2" } }, "@commitlint/read": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-15.0.0.tgz", - "integrity": "sha512-5yI1o2HKZFVe7RTjL7IhuhHMKar/MDNY34vEHqqz9gMI7BK/rdP8uVb4Di1efl2V0UPnwID0nPKWESjQ8Ti0gw==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-16.0.0.tgz", + "integrity": "sha512-H4T2zsfmYQK9B+JtoQaCXWBHUhgIJyOzWZjSfuIV9Ce69/OgHoffNpLZPF2lX6yKuDrS1SQFhI/kUCjVc/e4ew==", "dev": true, "requires": { - "@commitlint/top-level": "^15.0.0", - "@commitlint/types": "^15.0.0", + "@commitlint/top-level": "^16.0.0", + "@commitlint/types": "^16.0.0", "fs-extra": "^10.0.0", "git-raw-commits": "^2.0.0" } }, "@commitlint/resolve-extends": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-15.0.0.tgz", - "integrity": "sha512-7apfRJjgJsKja7lHsPfEFixKjA/fk/UeD3owkOw1174yYu4u8xBDLSeU3IinGPdMuF9m245eX8wo7vLUy+EBSg==", + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-16.1.0.tgz", + "integrity": "sha512-8182s6AFoUFX6+FT1PgQDt15nO2ogdR/EN8SYVAdhNXw1rLz8kT5saB/ICw567GuRAUgFTUMGCXy3ctMOXPEDg==", "dev": true, "requires": { + "@commitlint/config-validator": "^16.1.0", + "@commitlint/types": "^16.0.0", "import-fresh": "^3.0.0", "lodash": "^4.17.19", "resolve-from": "^5.0.0", @@ -9688,52 +9802,55 @@ } }, "@commitlint/rules": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-15.0.0.tgz", - "integrity": "sha512-SqXfp6QUlwBS+0IZm4FEA/NmmAwcFQIkG3B05BtemOVWXQdZ8j1vV6hDwvA9oMPCmUSrrGpHOtZK7HaHhng2yA==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-16.0.0.tgz", + "integrity": "sha512-AOl0y2SBTdJ1bvIv8nwHvQKRT/jC1xb09C5VZwzHoT8sE8F54KDeEzPCwHQFgUcWdGLyS10kkOTAH2MyA8EIlg==", "dev": true, "requires": { - "@commitlint/ensure": "^15.0.0", - "@commitlint/message": "^15.0.0", - "@commitlint/to-lines": "^15.0.0", - "@commitlint/types": "^15.0.0", + "@commitlint/ensure": "^16.0.0", + "@commitlint/message": "^16.0.0", + "@commitlint/to-lines": "^16.0.0", + "@commitlint/types": "^16.0.0", "execa": "^5.0.0" } }, "@commitlint/to-lines": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-15.0.0.tgz", - "integrity": "sha512-mY3MNA9ujPqVpiJjTYG9MDsYCobue5PJFO0MfcIzS1mCVvngH8ZFTPAh1fT5t+t1h876boS88+9WgqjRvbYItw==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-16.0.0.tgz", + "integrity": "sha512-iN/qU38TCKU7uKOg6RXLpD49wNiuI0TqMqybHbjefUeP/Jmzxa8ishryj0uLyVdrAl1ZjGeD1ukXGMTtvqz8iA==", "dev": true }, "@commitlint/top-level": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-15.0.0.tgz", - "integrity": "sha512-7Gz3t7xcuuUw1d1Nou6YLaztzp2Em+qZ6YdCzrqYc+aquca3Vt0O696nuiBDU/oE+tls4Hx2CNpAbWhTgEwB5A==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-16.0.0.tgz", + "integrity": "sha512-/Jt6NLxyFkpjL5O0jxurZPCHURZAm7cQCqikgPCwqPAH0TLgwqdHjnYipl8J+AGnAMGDip4FNLoYrtgIpZGBYw==", "dev": true, "requires": { "find-up": "^5.0.0" } }, "@commitlint/types": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-15.0.0.tgz", - "integrity": "sha512-OMSLX+QJnyNoTwws54ULv9sOvuw9GdVezln76oyUd4YbMMJyaav62aSXDuCdWyL2sm9hTkSzyEi52PNaIj/vqw==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-16.0.0.tgz", + "integrity": "sha512-+0FvYOAS39bJ4aKjnYn/7FD4DfWkmQ6G/06I4F0Gvu4KS5twirEg8mIcLhmeRDOOKn4Tp8PwpLwBiSA6npEMQA==", "dev": true, "requires": { "chalk": "^4.0.0" } }, - "@endemolshinegroup/cosmiconfig-typescript-loader": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@endemolshinegroup/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-3.0.2.tgz", - "integrity": "sha512-QRVtqJuS1mcT56oHpVegkKBlgtWjXw/gHNWO3eL9oyB5Sc7HBoc2OLG/nYpVfT/Jejvo3NUrD0Udk7XgoyDKkA==", + "@cspotcode/source-map-consumer": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", + "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", + "dev": true + }, + "@cspotcode/source-map-support": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", + "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", "dev": true, "requires": { - "lodash.get": "^4", - "make-error": "^1", - "ts-node": "^9", - "tslib": "^2" + "@cspotcode/source-map-consumer": "0.8.0" } }, "@eslint/eslintrc": { @@ -9768,9 +9885,9 @@ } }, "@humanwhocodes/config-array": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.2.tgz", - "integrity": "sha512-UXOuFCGcwciWckOpmfKDq/GyhlTf9pN/BzG//x8p8zTOFEcGuA68ANXheFS0AGvy3qgZqLBUkMs7hqzqCKOVwA==", + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.3.tgz", + "integrity": "sha512-3xSMlXHh03hCcCmFc0rbKp3Ivt2PFEJnQUJDDMTJQ2wkECZWdq4GePs2ctc5H8zV+cHPaq8k2vU8mrQjA6iHdQ==", "dev": true, "requires": { "@humanwhocodes/object-schema": "^1.2.1", @@ -10075,6 +10192,22 @@ "chalk": "^4.0.0" } }, + "@jridgewell/resolve-uri": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.4.tgz", + "integrity": "sha512-cz8HFjOFfUBtvN+NXYSFMHYRdxZMaEl0XypVrhzxBgadKIXhIkRd8aMeHhmF56Sl7SuS8OnUpQ73/k9LE4VnLg==", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.2.5.tgz", + "integrity": "sha512-K+Eths78fXDFOvQ2hgJhCiI5s+g81r2yXmACBpbn+f2+Qt94PNoTgUcAXPT8DZkhXCsZRsHVWVtY5KIBMcpDqQ==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "sourcemap-codec": "1.4.8" + } + }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -10125,6 +10258,30 @@ "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", "dev": true }, + "@tsconfig/node10": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", + "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==", + "dev": true + }, + "@tsconfig/node12": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", + "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==", + "dev": true + }, + "@tsconfig/node14": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", + "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", + "dev": true + }, + "@tsconfig/node16": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", + "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", + "dev": true + }, "@types/babel__core": { "version": "7.1.18", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.18.tgz", @@ -10248,9 +10405,9 @@ } }, "@types/jest": { - "version": "27.0.3", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-27.0.3.tgz", - "integrity": "sha512-cmmwv9t7gBYt7hNKH5Spu7Kuu/DotGa+Ff+JGRKZ4db5eh8PnKS4LuebJ3YLUoyOyIHraTGyULn23YtEAm0VSg==", + "version": "27.4.0", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-27.4.0.tgz", + "integrity": "sha512-gHl8XuC1RZ8H2j5sHv/JqsaxXkDDM9iDOgu0Wp8sjs4u/snb2PVehyWXJPr+ORA0RPpgw231mnutWI1+0hgjIQ==", "dev": true, "requires": { "jest-diff": "^27.0.0", @@ -10282,9 +10439,9 @@ "dev": true }, "@types/node": { - "version": "17.0.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.4.tgz", - "integrity": "sha512-6xwbrW4JJiJLgF+zNypN5wr2ykM9/jHcL7rQ8fZe2vuftggjzZeRSM4OwRc6Xk8qWjwJ99qVHo/JgOGmomWRog==", + "version": "17.0.14", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.14.tgz", + "integrity": "sha512-SbjLmERksKOGzWzPNuW7fJM7fk3YXVTFiZWB/Hs99gwhk+/dnrQRPBQjPW9aO+fi1tAffi9PrwFvsmOKmDTyng==", "dev": true }, "@types/normalize-package-data": { @@ -10349,14 +10506,14 @@ "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.10.0.tgz", - "integrity": "sha512-XXVKnMsq2fuu9K2KsIxPUGqb6xAImz8MEChClbXmE3VbveFtBUU5bzM6IPVWqzyADIgdkS2Ws/6Xo7W2TeZWjQ==", + "version": "5.10.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.10.2.tgz", + "integrity": "sha512-4W/9lLuE+v27O/oe7hXJKjNtBLnZE8tQAFpapdxwSVHqtmIoPB1gph3+ahNwVuNL37BX7YQHyGF9Xv6XCnIX2Q==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.10.0", - "@typescript-eslint/type-utils": "5.10.0", - "@typescript-eslint/utils": "5.10.0", + "@typescript-eslint/scope-manager": "5.10.2", + "@typescript-eslint/type-utils": "5.10.2", + "@typescript-eslint/utils": "5.10.2", "debug": "^4.3.2", "functional-red-black-tree": "^1.0.1", "ignore": "^5.1.8", @@ -10366,53 +10523,53 @@ } }, "@typescript-eslint/parser": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.10.0.tgz", - "integrity": "sha512-pJB2CCeHWtwOAeIxv8CHVGJhI5FNyJAIpx5Pt72YkK3QfEzt6qAlXZuyaBmyfOdM62qU0rbxJzNToPTVeJGrQw==", + "version": "5.10.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.10.2.tgz", + "integrity": "sha512-JaNYGkaQVhP6HNF+lkdOr2cAs2wdSZBoalE22uYWq8IEv/OVH0RksSGydk+sW8cLoSeYmC+OHvRyv2i4AQ7Czg==", "dev": true, "peer": true, "requires": { - "@typescript-eslint/scope-manager": "5.10.0", - "@typescript-eslint/types": "5.10.0", - "@typescript-eslint/typescript-estree": "5.10.0", + "@typescript-eslint/scope-manager": "5.10.2", + "@typescript-eslint/types": "5.10.2", + "@typescript-eslint/typescript-estree": "5.10.2", "debug": "^4.3.2" } }, "@typescript-eslint/scope-manager": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.10.0.tgz", - "integrity": "sha512-tgNgUgb4MhqK6DoKn3RBhyZ9aJga7EQrw+2/OiDk5hKf3pTVZWyqBi7ukP+Z0iEEDMF5FDa64LqODzlfE4O/Dg==", + "version": "5.10.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.10.2.tgz", + "integrity": "sha512-39Tm6f4RoZoVUWBYr3ekS75TYgpr5Y+X0xLZxXqcZNDWZdJdYbKd3q2IR4V9y5NxxiPu/jxJ8XP7EgHiEQtFnw==", "dev": true, "requires": { - "@typescript-eslint/types": "5.10.0", - "@typescript-eslint/visitor-keys": "5.10.0" + "@typescript-eslint/types": "5.10.2", + "@typescript-eslint/visitor-keys": "5.10.2" } }, "@typescript-eslint/type-utils": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.10.0.tgz", - "integrity": "sha512-TzlyTmufJO5V886N+hTJBGIfnjQDQ32rJYxPaeiyWKdjsv2Ld5l8cbS7pxim4DeNs62fKzRSt8Q14Evs4JnZyQ==", + "version": "5.10.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.10.2.tgz", + "integrity": "sha512-uRKSvw/Ccs5FYEoXW04Z5VfzF2iiZcx8Fu7DGIB7RHozuP0VbKNzP1KfZkHBTM75pCpsWxIthEH1B33dmGBKHw==", "dev": true, "requires": { - "@typescript-eslint/utils": "5.10.0", + "@typescript-eslint/utils": "5.10.2", "debug": "^4.3.2", "tsutils": "^3.21.0" } }, "@typescript-eslint/types": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.10.0.tgz", - "integrity": "sha512-wUljCgkqHsMZbw60IbOqT/puLfyqqD5PquGiBo1u1IS3PLxdi3RDGlyf032IJyh+eQoGhz9kzhtZa+VC4eWTlQ==", + "version": "5.10.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.10.2.tgz", + "integrity": "sha512-Qfp0qk/5j2Rz3p3/WhWgu4S1JtMcPgFLnmAKAW061uXxKSa7VWKZsDXVaMXh2N60CX9h6YLaBoy9PJAfCOjk3w==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.10.0.tgz", - "integrity": "sha512-x+7e5IqfwLwsxTdliHRtlIYkgdtYXzE0CkFeV6ytAqq431ZyxCFzNMNR5sr3WOlIG/ihVZr9K/y71VHTF/DUQA==", + "version": "5.10.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.10.2.tgz", + "integrity": "sha512-WHHw6a9vvZls6JkTgGljwCsMkv8wu8XU8WaYKeYhxhWXH/atZeiMW6uDFPLZOvzNOGmuSMvHtZKd6AuC8PrwKQ==", "dev": true, "requires": { - "@typescript-eslint/types": "5.10.0", - "@typescript-eslint/visitor-keys": "5.10.0", + "@typescript-eslint/types": "5.10.2", + "@typescript-eslint/visitor-keys": "5.10.2", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", @@ -10421,33 +10578,33 @@ } }, "@typescript-eslint/utils": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.10.0.tgz", - "integrity": "sha512-IGYwlt1CVcFoE2ueW4/ioEwybR60RAdGeiJX/iDAw0t5w0wK3S7QncDwpmsM70nKgGTuVchEWB8lwZwHqPAWRg==", + "version": "5.10.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.10.2.tgz", + "integrity": "sha512-vuJaBeig1NnBRkf7q9tgMLREiYD7zsMrsN1DA3wcoMDvr3BTFiIpKjGiYZoKPllfEwN7spUjv7ZqD+JhbVjEPg==", "dev": true, "requires": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.10.0", - "@typescript-eslint/types": "5.10.0", - "@typescript-eslint/typescript-estree": "5.10.0", + "@typescript-eslint/scope-manager": "5.10.2", + "@typescript-eslint/types": "5.10.2", + "@typescript-eslint/typescript-estree": "5.10.2", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" } }, "@typescript-eslint/visitor-keys": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.10.0.tgz", - "integrity": "sha512-GMxj0K1uyrFLPKASLmZzCuSddmjZVbVj3Ouy5QVuIGKZopxvOr24JsS7gruz6C3GExE01mublZ3mIBOaon9zuQ==", + "version": "5.10.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.10.2.tgz", + "integrity": "sha512-zHIhYGGGrFJvvyfwHk5M08C5B5K4bewkm+rrvNTKk1/S15YHR+SA/QUF8ZWscXSfEaB8Nn2puZj+iHcoxVOD/Q==", "dev": true, "requires": { - "@typescript-eslint/types": "5.10.0", + "@typescript-eslint/types": "5.10.2", "eslint-visitor-keys": "^3.0.0" } }, "@verdaccio/types": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/@verdaccio/types/-/types-10.1.0.tgz", - "integrity": "sha512-lKycP0Bp5ZURIxsau/jZSNPQy7mLIKilK5pBk1fUPu9recpfw5G9/DF5oOzwUvBkgfmLFzOV896dHSAiasKYbA==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@verdaccio/types/-/types-10.2.2.tgz", + "integrity": "sha512-9V+MujwCsreox27d30Ny3dq/2sLJKhNGOVU38i+CWFQIl2VOCRFQPGTkJNHLjEUC5XHEpeXPI5mtHHAO+ePiEw==", "dev": true }, "abab": { @@ -10457,13 +10614,13 @@ "dev": true }, "accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", "dev": true, "requires": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" + "mime-types": "~2.1.34", + "negotiator": "0.6.3" } }, "acorn": { @@ -10824,9 +10981,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001300", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001300.tgz", - "integrity": "sha512-cVjiJHWGcNlJi8TZVKNMnvMid3Z3TTdDHmLDzlOdIiZq138Exvo0G+G0wTdVYolxKb4AYwC+38pxodiInVtJSA==", + "version": "1.0.30001306", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001306.tgz", + "integrity": "sha512-Wd1OuggRzg1rbnM5hv1wXs2VkxJH/AA+LuudlIqvZiCvivF+wJJe2mgBZC8gPMgI7D76PP5CTx8Luvaqc1V6OQ==", "dev": true }, "chalk": { @@ -11484,6 +11641,16 @@ "yaml": "^1.10.0" } }, + "cosmiconfig-typescript-loader": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-1.0.4.tgz", + "integrity": "sha512-ulv2dvwurP/MZAIthXm69bO7EzzIUThZ6RJ1qXhdlXM6to3F+IKBL/17EnhYSG52A5N1KcAUu66vSG/3/77KrA==", + "dev": true, + "requires": { + "cosmiconfig": "^7", + "ts-node": "^10.4.0" + } + }, "create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", @@ -11831,9 +11998,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.4.48", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.48.tgz", - "integrity": "sha512-RT3SEmpv7XUA+tKXrZGudAWLDpa7f8qmhjcLaM6OD/ERxjQ/zAojT8/Vvo0BSzbArkElFZ1WyZ9FuwAYbkdBNA==", + "version": "1.4.63", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.63.tgz", + "integrity": "sha512-e0PX/LRJPFRU4kzJKLvTobxyFdnANCvcoDCe8XcyTqP58nTWIwdsHvXLIl1RkB39X5yaosLaroMASWB0oIsgCA==", "dev": true }, "emittery": { @@ -11951,9 +12118,9 @@ } }, "eslint": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.7.0.tgz", - "integrity": "sha512-ifHYzkBGrzS2iDU7KjhCAVMGCvF6M3Xfs8X8b37cgrUlDt6bWRTpRh6T/gtSXv1HJ/BUGgmjvNvOEGu85Iif7w==", + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.8.0.tgz", + "integrity": "sha512-H3KXAzQGBH1plhYS3okDix2ZthuYJlQQEGE5k0IKuEqUSiyu4AmxxlJ2MtTYeJ3xB4jDhcYCwGOg2TXYdnDXlQ==", "dev": true, "requires": { "@eslint/eslintrc": "^1.0.5", @@ -12427,9 +12594,9 @@ } }, "flatted": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.4.tgz", - "integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", + "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", "dev": true }, "form-data": { @@ -12728,9 +12895,9 @@ } }, "globals": { - "version": "13.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", - "integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==", + "version": "13.12.1", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.1.tgz", + "integrity": "sha512-317dFlgY2pdJZ9rspXDks7073GpDmXdfbM3vYYp0HAMKGDh1FfWPleI2ljVNLQX5M5lXcAslTcPTrOrMEFOjyw==", "dev": true, "requires": { "type-fest": "^0.20.2" @@ -13217,14 +13384,14 @@ } }, "jest": { - "version": "27.4.5", - "resolved": "https://registry.npmjs.org/jest/-/jest-27.4.5.tgz", - "integrity": "sha512-uT5MiVN3Jppt314kidCk47MYIRilJjA/l2mxwiuzzxGUeJIvA8/pDaJOAX5KWvjAo7SCydcW0/4WEtgbLMiJkg==", + "version": "27.4.7", + "resolved": "https://registry.npmjs.org/jest/-/jest-27.4.7.tgz", + "integrity": "sha512-8heYvsx7nV/m8m24Vk26Y87g73Ba6ueUd0MWed/NXMhSZIm62U/llVbS0PJe1SHunbyXjJ/BqG1z9bFjGUIvTg==", "dev": true, "requires": { - "@jest/core": "^27.4.5", + "@jest/core": "^27.4.7", "import-local": "^3.0.2", - "jest-cli": "^27.4.5" + "jest-cli": "^27.4.7" } }, "jest-changed-files": { @@ -13963,12 +14130,6 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, - "lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", - "dev": true - }, "lodash.ismatch": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz", @@ -14236,9 +14397,9 @@ "dev": true }, "negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "dev": true }, "neo-async": { @@ -14463,9 +14624,9 @@ "dev": true }, "pirates": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.4.tgz", - "integrity": "sha512-ZIrVPH+A52Dw84R0L3/VS9Op04PuQ2SEoJL6bkshmiTic/HldyW9Tf7oH5mhJZBK7NmDx27vSMrYEXPXclpDKw==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", + "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", "dev": true }, "pkg-dir": { @@ -14889,12 +15050,12 @@ "dev": true }, "resolve": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.21.0.tgz", - "integrity": "sha512-3wCbTpk5WJlyE4mSOtDLhqQmGFi0/TD9VPwmiolnk8U0wRgMEktqCXd3vy5buTO3tljvalNvKrjHEfrd2WpEKA==", + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", + "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", "dev": true, "requires": { - "is-core-module": "^2.8.0", + "is-core-module": "^2.8.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" } @@ -15003,14 +15164,6 @@ "dev": true, "requires": { "tslib": "^1.9.0" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } } }, "safe-buffer": { @@ -15156,6 +15309,12 @@ "source-map": "^0.6.0" } }, + "sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "dev": true + }, "spdx-correct": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", @@ -15637,9 +15796,9 @@ "dev": true }, "ts-jest": { - "version": "27.1.2", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.2.tgz", - "integrity": "sha512-eSOiJOWq6Hhs6Khzk5wKC5sgWIXgXqOCiIl1+3lfnearu58Hj4QpE5tUhQcA3xtZrELbcvAGCsd6HB8OsaVaTA==", + "version": "27.1.3", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.3.tgz", + "integrity": "sha512-6Nlura7s6uM9BVUAoqLH7JHyMXjz8gluryjpPXxr3IxZdAXnU6FhjvVLHFtfd1vsE1p8zD1OJfskkc0jhTSnkA==", "dev": true, "requires": { "bs-logger": "0.x", @@ -15653,23 +15812,37 @@ } }, "ts-node": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", - "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", - "dev": true, - "requires": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.4.0.tgz", + "integrity": "sha512-g0FlPvvCXSIO1JDF6S232P5jPYqBkRL9qly81ZgAOSU7rwI0stphCgd2kLiCrU9DjQCrJMWEqcNSjQL02s6d8A==", + "dev": true, + "requires": { + "@cspotcode/source-map-support": "0.7.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", "arg": "^4.1.0", "create-require": "^1.1.0", "diff": "^4.0.1", "make-error": "^1.1.1", - "source-map-support": "^0.5.17", "yn": "3.1.1" + }, + "dependencies": { + "acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true + } } }, "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true }, "tsutils": { @@ -15679,14 +15852,6 @@ "dev": true, "requires": { "tslib": "^1.8.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } } }, "type-check": { @@ -15736,15 +15901,15 @@ } }, "typescript": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.4.tgz", - "integrity": "sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg==", + "version": "4.5.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz", + "integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==", "dev": true }, "uglify-js": { - "version": "3.14.5", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.5.tgz", - "integrity": "sha512-qZukoSxOG0urUTvjc2ERMTcAy+BiFh3weWAkeurLwjrCba73poHmG3E36XEjd/JGukMzwTL7uCxZiAexj8ppvQ==", + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.15.0.tgz", + "integrity": "sha512-x+xdeDWq7FiORDvyIJ0q/waWd4PhjBNOm5dQUOq2AKC0IEjxOS66Ha9tctiVDGcRQuh69K7fgU5oRuTK4cysSg==", "dev": true, "optional": true }, diff --git a/package.json b/package.json index f3b1e52..8936780 100644 --- a/package.json +++ b/package.json @@ -44,29 +44,29 @@ "prom-client": "14.0.1" }, "devDependencies": { - "@commitlint/cli": "15.0.0", - "@commitlint/config-conventional": "15.0.0", + "@commitlint/cli": "16.1.0", + "@commitlint/config-conventional": "16.0.0", "@types/chance": "1.1.3", "@types/express": "4.17.13", - "@types/jest": "27.0.3", - "@types/node": "17.0.4", - "@typescript-eslint/eslint-plugin": "5.10.0", - "@verdaccio/types": "10.1.0", + "@types/jest": "27.4.0", + "@types/node": "17.0.14", + "@typescript-eslint/eslint-plugin": "5.10.2", + "@verdaccio/types": "10.2.2", "chance": "1.1.8", "commitizen": "4.2.4", "cz-conventional-changelog": "3.3.0", - "eslint": "8.7.0", + "eslint": "8.8.0", "eslint-config-prettier": "8.3.0", "eslint-plugin-prettier": "4.0.0", "express": "4.17.2", "husky": "7.0.4", - "jest": "27.4.5", + "jest": "27.4.7", "jest-junit": "13.0.0", "prettier": "2.5.1", "pretty-quick": "3.1.3", "standard-version": "9.3.2", - "ts-jest": "27.1.2", - "typescript": "4.5.4" + "ts-jest": "27.1.3", + "typescript": "4.5.5" }, "config": { "commitizen": { diff --git a/src/index.ts b/src/index.ts index 7780adf..f0c1470 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,5 @@ -import MetricsPlugin, { REQUEST_COUNTER_OPTIONS } from './metrics'; +import MetricsPlugin, { DEFAULT_METRICS_PATH } from './plugin'; +import { DEFAULT_METRIC_NAME_REQUESTS, DEFAULT_METRIC_NAME_PACKAGE_DOWNLOADS } from './metrics'; export default MetricsPlugin; -export { REQUEST_COUNTER_OPTIONS }; +export { DEFAULT_METRICS_PATH, DEFAULT_METRIC_NAME_REQUESTS, DEFAULT_METRIC_NAME_PACKAGE_DOWNLOADS }; diff --git a/src/metrics.ts b/src/metrics.ts index 43ead86..da937c4 100644 --- a/src/metrics.ts +++ b/src/metrics.ts @@ -1,110 +1,61 @@ import { collectDefaultMetrics, register, Counter } from 'prom-client'; -import { Logger, IPluginMiddleware, IBasicAuth, IStorageManager, PluginOptions } from '@verdaccio/types'; -import { Request, Response, NextFunction, Application } from 'express'; - -import { MetricsConfig, MetricsLabels } from './types'; -import { getUsername, getUserAgentData } from './utils'; - -export const REQUEST_COUNTER_OPTIONS = { - name: 'registry_requests', - help: 'HTTP requests made to the registry', - // Remember that every unique combination of key-value label pairs represents a new time series, which can - // dramatically increase the amount of data stored. Refer to: https://prometheus.io/docs/practices/naming/#labels - labelNames: ['username', 'userAgentName', 'statusCode', 'packageGroup'], -}; - -/** - * A Verdaccio middleware plugin implementation. If enabled the following functionality is added: - * 1. A single new metrics endpoint is exposed at a configurable path. - * 2. Metrics are collected related only to install/download of package tarballs. - * Refer to: https://verdaccio.org/docs/plugin-middleware/ - */ -export default class VerdaccioMiddlewarePlugin implements IPluginMiddleware { - public logger: Logger; - public metricsEnabled: boolean; - public collectDefaultMetrics: boolean; - public metricsPath: string; - public packageGroups: Record; - private requestsCounter = new Counter(REQUEST_COUNTER_OPTIONS); +import { PackageMetricsLabels, RequestMetricsLabels } from './types'; + +interface MetricOptions { + defaultMetricsEnabled: boolean; + requestMetricsEnabled: boolean; + requestMetricsName?: string; + packageMetricsEnabled: boolean; + packageMetricsName?: string; +} - public constructor(config: MetricsConfig, options: PluginOptions) { - this.metricsEnabled = [true, 'true'].includes(config.metricsEnabled); - this.collectDefaultMetrics = [true, 'true'].includes(config.collectDefaultMetrics); - this.metricsPath = config.metricsPath || '/-/metrics'; - this.packageGroups = config.packageGroups || {}; - this.logger = options.logger; +export const DEFAULT_METRIC_NAME_REQUESTS = 'registry_http_requests'; +export const DEFAULT_METRIC_NAME_PACKAGE_DOWNLOADS = 'registry_package_downloads'; +export const CONTENT_TYPE_METRICS = register.contentType; + +// Remember that every unique combination of key-value label pairs represents a new time series, which can +// dramatically increase the amount of data stored. Refer to: https://prometheus.io/docs/practices/naming/#labels +export class Metrics { + private readonly requestCounter: Counter | null = null; + private readonly packageCounter: Counter | null = null; + + constructor({ + defaultMetricsEnabled, + requestMetricsEnabled, + requestMetricsName, + packageMetricsEnabled, + packageMetricsName, + }: MetricOptions) { + defaultMetricsEnabled && collectDefaultMetrics(); + if (requestMetricsEnabled) { + this.requestCounter = new Counter({ + name: requestMetricsName || DEFAULT_METRIC_NAME_REQUESTS, + help: 'Count of HTTP requests made to the registry', + labelNames: ['username', 'userAgentName', 'statusCode', 'httpMethod'] as const, + }); + } + if (packageMetricsEnabled) { + this.packageCounter = new Counter({ + name: packageMetricsName || DEFAULT_METRIC_NAME_PACKAGE_DOWNLOADS, + help: 'Count of package downloads from the registry', + labelNames: ['username', 'userAgentName', 'statusCode', 'packageGroup'] as const, + }); + } } - /** - * This is the function that Verdaccio invokes when the appropriate configuration is present to use this plugin. - * @param {Application} app - The Express application object. - * @param {IBasicAuth} auth - The Verdaccio authentication service. - * @param {IStorageManager} storage -The Verdaccio storage manager service. - */ - public register_middlewares( - app: Application, - auth: IBasicAuth, - storage: IStorageManager, - ): void { - if (this.metricsEnabled) { - this.logger.info(`metrics: [register_middlewares] metrics are enabled and exposed at '${this.metricsPath}'`); - app.get(/.*[.]tgz$/i, this.collectMetrics.bind(this)); - app.get(this.metricsPath, this.getMetrics.bind(this)); - if (this.collectDefaultMetrics) { - collectDefaultMetrics(); - } - } else { - this.logger.warn('metrics: [register_middlewares] metrics are disabled'); - } + incrementRequestCounter(labels: RequestMetricsLabels) { + this.requestCounter && this.requestCounter.labels(labels).inc(1); } - /** - * Express callback function that responds to requests at the metrics path. - * @param {Request} req - The Express request object. - * @param {Response} res - The Express response object. - * @returns {Promise} - A promise that resolves to undefined since the function is async. - */ - public async getMetrics(req: Request, res: Response): Promise { - this.logger.debug(`metrics: [getMetrics] providing metrics response`); - res.setHeader('Content-Type', register.contentType); - res.status(200); - res.send(await register.metrics()); + incrementPackageDownloadCounter(labels: PackageMetricsLabels) { + this.packageCounter && this.packageCounter.labels(labels).inc(1); } /** - * Express middleware function responsible for collecting metrics on requests to install/download a package. - * @param {Request} req - The Express request object. - * @param {Response} res - The Express response object. - * @param {NextFunction} next - A function that invokes the next Express middleware function succeeding this one. - * @returns {Promise} - A promise that resolves to undefined since the function is async. + * Get the metrics response body. + * @return {Promise} - The metrics response body. */ - public collectMetrics(req: Request, res: Response, next: NextFunction): void { - const { path } = req; - - const authorization = req.header('authorization'); - const userAgentString = req.header('user-agent'); - const decodedPath = decodeURIComponent(path); - const { authType, username } = getUsername(this.logger, authorization); - const { userAgentName, userAgentVersion } = getUserAgentData(this.logger, userAgentString); - const [, packageGroup] = - Object.entries(this.packageGroups).find(([regex]: [string, string]) => new RegExp(regex).test(decodedPath)) || []; - - // We won't know the final status code until the response is sent to the client. Because of this we don't collect - // the metrics for this request until the response 'finish' event is emitted. - res.once('close', () => { - const { statusCode } = res; - const metricLabels: MetricsLabels = { username, userAgentName, statusCode }; - if (packageGroup) { - metricLabels.packageGroup = packageGroup; - } - this.logger.info( - { decodedPath, authType, userAgentString, userAgentVersion, ...metricLabels }, - 'metrics: [collectMetrics] response metrics collected', - ); - // @ts-ignore: The type definitions for `labels` are not great so ignore the TypeScript error. - this.requestsCounter.labels(metricLabels).inc(1); - }); - - next(); + getMetricsResponse() { + return register.metrics(); } } diff --git a/src/plugin.ts b/src/plugin.ts new file mode 100644 index 0000000..7969dd9 --- /dev/null +++ b/src/plugin.ts @@ -0,0 +1,180 @@ +import { Logger, IPluginMiddleware, IBasicAuth, IStorageManager, PluginOptions } from '@verdaccio/types'; +import { Request, Response, NextFunction, Application } from 'express'; + +import { MetricsConfig, PackageMetricsLabels, RequestMetricsLabels } from './types'; +import { getUsername, getUserAgentData } from './utils'; +import { CONTENT_TYPE_METRICS, Metrics } from './metrics'; + +export const DEFAULT_METRICS_PATH = '/-/metrics'; +export const DEFAULT_EXCLUDED_PATHS = [ + '^/$', // root path to web ui + '^/[-]/ping', // health endpoint + '^/[-]/(static|verdaccio|web)', // web ui related paths + '[.]ico$', // requests for icons (e.g. `favicon.ico`) +]; + +/** + * A Verdaccio middleware plugin implementation. If enabled the following functionality is added: + * 1. A single new metrics endpoint is exposed at a configurable path. + * 2. Metrics are collected related only to install/download of package tarballs. + * Refer to: https://verdaccio.org/docs/plugin-middleware/ + */ +export default class VerdaccioMiddlewarePlugin implements IPluginMiddleware { + private metrics: Metrics | null = null; + private readonly logger: Logger; + private readonly metricsConfig: MetricsConfig; + public readonly metricsPath: string; + public readonly defaultMetricsEnabled: boolean; + public readonly requestMetricsEnabled: boolean; + public readonly packageMetricsEnabled: boolean; + public readonly packageGroups: Record; + public readonly pathExclusions: RegExp[]; + + public constructor(config: MetricsConfig, options: PluginOptions) { + this.metricsConfig = config; + this.metricsPath = config.metricsPath || DEFAULT_METRICS_PATH; + this.defaultMetricsEnabled = [true, 'true'].includes(config.defaultMetrics?.enabled || false); + this.requestMetricsEnabled = [true, 'true'].includes(config.requestMetrics?.enabled || false); + this.packageMetricsEnabled = [true, 'true'].includes(config.packageMetrics?.enabled || false); + this.packageGroups = config.packageMetrics?.packageGroups || {}; + this.pathExclusions = (config.requestMetrics?.pathExclusions || DEFAULT_EXCLUDED_PATHS).map( + (path) => new RegExp(path, 'i'), + ); + this.pathExclusions.push(new RegExp(`^${this.metricsPath}$`)); + this.logger = options.logger; + } + + /** + * This is the function that Verdaccio invokes when the appropriate configuration is present to use this plugin. + * @param {Application} app - The Express application object. + * @param {IBasicAuth} auth - The Verdaccio authentication service. + * @param {IStorageManager} storage -The Verdaccio storage manager service. + */ + public register_middlewares( + app: Application, + auth: IBasicAuth, + storage: IStorageManager, + ): void { + const { defaultMetrics, requestMetrics, packageMetrics } = this.metricsConfig; + if (this.defaultMetricsEnabled || this.requestMetricsEnabled || this.packageMetricsEnabled) { + const { defaultMetricsEnabled, requestMetricsEnabled, packageMetricsEnabled } = this; + this.metrics = new Metrics({ + defaultMetricsEnabled, + requestMetricsEnabled, + requestMetricsName: this.metricsConfig?.requestMetrics?.metricName, + packageMetricsEnabled, + packageMetricsName: this.metricsConfig?.packageMetrics?.metricName, + }); + this.logger.info( + { defaultMetrics, requestMetrics, packageMetrics }, + `metrics: [register_middlewares] metrics are enabled and exposed at '${this.metricsPath}'`, + ); + requestMetricsEnabled && app.all(/.*/, this.collectRequestMetrics.bind(this)); + packageMetricsEnabled && app.get(/.*[.]tgz$/i, this.collectPackageMetrics.bind(this)); + app.get(this.metricsPath, this.getMetrics.bind(this)); + } else { + this.logger.warn( + { defaultMetrics, requestMetrics, packageMetrics }, + 'metrics: [register_middlewares] metrics are disabled', + ); + } + } + + /** + * Express callback function that responds to requests at the metrics path. + * @param {Request} req - The Express request object. + * @param {Response} res - The Express response object. + * @returns {Promise} - A promise that resolves to undefined since the function is async. + */ + public async getMetrics(req: Request, res: Response): Promise { + this.logger.debug(`metrics: [getMetrics] providing metrics response`); + const metricsResponse = this.metrics ? await this.metrics.getMetricsResponse() : ''; + res.setHeader('Content-Type', CONTENT_TYPE_METRICS); + res.status(200); + res.send(metricsResponse); + } + + /** + * Express middleware function responsible for collecting metrics on every incoming request. + * @param {Request} req - The Express request object. + * @param {Response} res - The Express response object. + * @param {NextFunction} next - A function that invokes the next Express middleware function succeeding this one. + */ + public collectRequestMetrics(req: Request, res: Response, next: NextFunction): void { + const { httpMethod, decodedPath, authType, username, userAgentString, userAgentName, userAgentVersion } = + this._getRequestMetadata(req); + + const pathIsExcluded = this.pathExclusions.some((pathRegex) => pathRegex.test(decodedPath)); + if (pathIsExcluded) { + this.logger.trace({ decodedPath }, `metrics: [collectRequestMetrics] path '${decodedPath}' is excluded`); + next(); + return; + } + + // We won't know the final status code until the response is sent to the client. Because of this we don't collect + // the metrics for this request until the response 'finish' event is emitted. + res.once('close', () => { + const { statusCode } = res; + const metricLabels: RequestMetricsLabels = { httpMethod, username, userAgentName, statusCode }; + this.logger.info( + { metricsType: 'request', decodedPath, authType, userAgentString, userAgentVersion, ...metricLabels }, + 'metrics: [collectRequestMetrics] request metrics collected', + ); + this.metrics?.incrementRequestCounter(metricLabels); + }); + + next(); + } + + /** + * Express middleware function responsible for collecting metrics on requests to install/download a package. + * @param {Request} req - The Express request object. + * @param {Response} res - The Express response object. + * @param {NextFunction} next - A function that invokes the next Express middleware function succeeding this one. + */ + public collectPackageMetrics(req: Request, res: Response, next: NextFunction): void { + // A `head` middleware handler needs to be defined first for the same path as the HTTP GET path or the `get` + // middleware handler will be called automatically for the HTTP HEAD requests. This isn't obvious and is only + // noted briefly in the Express API docs here: https://expressjs.com/en/api.html#routing-methods + if (req.method !== 'GET') { + return next(); + } + + const { decodedPath, authType, username, userAgentString, userAgentName, userAgentVersion } = + this._getRequestMetadata(req); + const [, packageGroup] = + Object.entries(this.packageGroups).find(([regex]: [string, string]) => new RegExp(regex).test(decodedPath)) || []; + + // We won't know the final status code until the response is sent to the client. Because of this we don't collect + // the metrics for this request until the response 'finish' event is emitted. + res.once('close', () => { + const { statusCode } = res; + const metricLabels: PackageMetricsLabels = { username, userAgentName, statusCode }; + if (packageGroup) { + metricLabels.packageGroup = packageGroup; + } + this.logger.info( + { metricsType: 'package', decodedPath, authType, userAgentString, userAgentVersion, ...metricLabels }, + 'metrics: [collectPackageMetrics] package metrics collected', + ); + this.metrics?.incrementPackageDownloadCounter(metricLabels); + }); + + next(); + } + + private _getRequestMetadata(req: Request) { + const { path, method: httpMethod } = req; + const authorization = req.header('authorization'); + const userAgentString = req.header('user-agent'); + const { authType, username } = getUsername(this.logger, authorization); + const { userAgentName, userAgentVersion } = getUserAgentData(this.logger, userAgentString); + + let decodedPath = path; + try { + decodedPath = decodeURIComponent(path); + } catch (error) {} // eslint-disable-line no-empty + + return { httpMethod, decodedPath, authType, username, userAgentString, userAgentName, userAgentVersion }; + } +} diff --git a/src/types/index.ts b/src/types/index.ts index 97cf0d7..9959e28 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,13 +1,35 @@ import { Config } from '@verdaccio/types'; export interface MetricsConfig extends Config { - metricsEnabled: boolean; - collectDefaultMetrics: boolean; - metricsPath: string; - packageGroups: Record; + metricsPath?: string; + requestMetrics?: RequestMetrics; + packageMetrics?: PackageMetrics; + defaultMetrics?: { + enabled?: boolean | 'true' | 'false'; + }; } -export interface MetricsLabels { +export interface MetricType { + enabled?: boolean | 'true' | 'false'; + metricName?: string; +} + +export interface RequestMetrics extends MetricType { + pathExclusions?: string[]; +} + +export interface PackageMetrics extends MetricType { + packageGroups?: Record; +} + +export interface RequestMetricsLabels extends Record { + username: string; + userAgentName: string; + httpMethod: string; + statusCode: string | number; +} + +export interface PackageMetricsLabels extends Record { username: string; userAgentName: string; statusCode: string | number; diff --git a/tests/pluginMetricsDefault.spec.ts b/tests/pluginMetricsDefault.spec.ts new file mode 100644 index 0000000..c143901 --- /dev/null +++ b/tests/pluginMetricsDefault.spec.ts @@ -0,0 +1,101 @@ +import { register } from 'prom-client'; +import { Application } from 'express'; +import { IBasicAuth, IStorageManager, PluginOptions } from '@verdaccio/types'; + +import MetricsPlugin, { DEFAULT_METRICS_PATH } from '../src'; +import { MetricsConfig } from '../src/types'; + +import { getExpressMocks, getLogger } from './testUtils'; + +describe('Default Metrics', () => { + describe('should register middleware (default metrics enabled)', () => { + const logger = getLogger(); + const app = { get: jest.fn() } as unknown as Application; + + beforeAll(() => { + register.clear(); + const metricsPlugin = new MetricsPlugin( + { defaultMetrics: { enabled: true } } as MetricsConfig, + { logger } as PluginOptions, + ); + metricsPlugin.register_middlewares(app, {} as IBasicAuth, {} as IStorageManager); + }); + + test('should invoke the correct express API calls', () => { + const mock = (app.get as jest.MockedFn).mock; + expect(app.get).toHaveBeenCalledTimes(1); + expect(mock.calls[0][0]).toEqual(DEFAULT_METRICS_PATH); + }); + }); + + describe('should register middleware (default metrics disabled)', () => { + const logger = getLogger(); + const app = { get: jest.fn() } as unknown as Application; + const metricsConfig = { defaultMetrics: { enabled: false } } as MetricsConfig; + + beforeAll(() => { + register.clear(); + const metricsPlugin = new MetricsPlugin(metricsConfig, { logger } as PluginOptions); + metricsPlugin.register_middlewares(app, {} as IBasicAuth, {} as IStorageManager); + }); + + test('should not invoke any express API', () => { + expect(app.get).toHaveBeenCalledTimes(0); + }); + + test('should log warn that metrics are disabled', () => { + expect(logger.warn).toHaveBeenCalledWith(metricsConfig, 'metrics: [register_middlewares] metrics are disabled'); + }); + }); + + describe('should collect default prometheus metrics when `defaultMetrics.enabled` is set to `true`', () => { + const { req, res, next } = getExpressMocks(); + const app = { get: jest.fn() } as unknown as Application; + + beforeAll(() => { + register.clear(); + const metricsPlugin = new MetricsPlugin( + { defaultMetrics: { enabled: true } } as MetricsConfig, + { logger: getLogger() } as PluginOptions, + ); + metricsPlugin.register_middlewares(app, {} as IBasicAuth, {} as IStorageManager); + metricsPlugin.collectPackageMetrics(req, res, next); + metricsPlugin.getMetrics(req, res); + }); + + test('should collect default prometheus metrics', async () => { + const metrics = await register.getMetricsAsJSON(); + expect(metrics.map(({ name }) => name).sort()).toEqual([ + 'nodejs_active_handles', + 'nodejs_active_handles_total', + 'nodejs_active_requests', + 'nodejs_active_requests_total', + 'nodejs_eventloop_lag_max_seconds', + 'nodejs_eventloop_lag_mean_seconds', + 'nodejs_eventloop_lag_min_seconds', + 'nodejs_eventloop_lag_p50_seconds', + 'nodejs_eventloop_lag_p90_seconds', + 'nodejs_eventloop_lag_p99_seconds', + 'nodejs_eventloop_lag_seconds', + 'nodejs_eventloop_lag_stddev_seconds', + 'nodejs_external_memory_bytes', + 'nodejs_gc_duration_seconds', + 'nodejs_heap_size_total_bytes', + 'nodejs_heap_size_used_bytes', + 'nodejs_heap_space_size_available_bytes', + 'nodejs_heap_space_size_total_bytes', + 'nodejs_heap_space_size_used_bytes', + 'nodejs_version_info', + 'process_cpu_seconds_total', + 'process_cpu_system_seconds_total', + 'process_cpu_user_seconds_total', + 'process_heap_bytes', + 'process_max_fds', + 'process_open_fds', + 'process_resident_memory_bytes', + 'process_start_time_seconds', + 'process_virtual_memory_bytes', + ]); + }); + }); +}); diff --git a/tests/metrics.spec.ts b/tests/pluginMetricsPackages.spec.ts similarity index 66% rename from tests/metrics.spec.ts rename to tests/pluginMetricsPackages.spec.ts index fc27de2..7e89279 100644 --- a/tests/metrics.spec.ts +++ b/tests/pluginMetricsPackages.spec.ts @@ -1,56 +1,52 @@ -import chanceJs from 'chance'; +import * as chanceJs from 'chance'; import { register } from 'prom-client'; import { Application } from 'express'; import { IBasicAuth, IStorageManager, PluginOptions } from '@verdaccio/types'; -import MetricsPlugin, { REQUEST_COUNTER_OPTIONS } from '../src'; +import MetricsPlugin, { DEFAULT_METRICS_PATH } from '../src'; import { MetricsConfig } from '../src/types'; import { AuthType, UNKNOWN } from '../src/utils'; -// @ts-ignore -import { getExpressMocks, getRequestOptions, getLogger } from './testUtils'; - +import { getExpressMocks, getRequestOptions, getLogger, getPackageMetricsJson } from './testUtils'; const chance = chanceJs(); -const getMetricsJson = (values) => [ - { - aggregator: 'sum', - help: REQUEST_COUNTER_OPTIONS.help, - name: REQUEST_COUNTER_OPTIONS.name, - type: 'counter', - values, - }, -]; - -describe('Metrics Plugin', () => { - describe('should register middleware (metrics enabled)', () => { +describe('Package Download Metrics', () => { + describe('should register middleware (package download metrics enabled)', () => { const logger = getLogger(); + const metricName = chance.word(); const app = { get: jest.fn() } as unknown as Application; beforeAll(() => { register.clear(); const metricsPlugin = new MetricsPlugin( - { metricsEnabled: true } as MetricsConfig, + { packageMetrics: { enabled: true, metricName } } as MetricsConfig, { logger } as PluginOptions, ); metricsPlugin.register_middlewares(app, {} as IBasicAuth, {} as IStorageManager); }); + test('should use the provided custom metric name', async () => { + const metrics = await register.getMetricsAsJSON(); + expect(metrics.length).toEqual(1); + expect(metrics[0].name).toEqual(metricName); + }); + test('should invoke the correct express API calls', () => { + const mock = (app.get as jest.MockedFn).mock; expect(app.get).toHaveBeenCalledTimes(2); + expect(mock.calls[0][0]).toEqual(/.*[.]tgz$/i); + expect(mock.calls[1][0]).toEqual(DEFAULT_METRICS_PATH); }); }); - describe('should register middleware (metrics disabled)', () => { + describe('should register middleware (package download metrics disabled)', () => { const logger = getLogger(); const app = { get: jest.fn() } as unknown as Application; + const metricsConfig = { packageMetrics: { enabled: false } } as MetricsConfig; beforeAll(() => { register.clear(); - const metricsPlugin = new MetricsPlugin( - { metricsEnabled: false } as MetricsConfig, - { logger } as PluginOptions, - ); + const metricsPlugin = new MetricsPlugin(metricsConfig, { logger } as PluginOptions); metricsPlugin.register_middlewares(app, {} as IBasicAuth, {} as IStorageManager); }); @@ -59,20 +55,46 @@ describe('Metrics Plugin', () => { }); test('should log warn that metrics are disabled', () => { - expect(logger.warn).toHaveBeenCalledWith('metrics: [register_middlewares] metrics are disabled'); + expect(logger.warn).toHaveBeenCalledWith(metricsConfig, 'metrics: [register_middlewares] metrics are disabled'); }); }); - describe('should use express APIs to provide a valid metrics response', () => { + describe('should not record metrics for HTTP HEAD requests', () => { + const app = { get: jest.fn() } as unknown as Application; + const { req, res, next } = getExpressMocks(getRequestOptions({ httpMethod: 'HEAD' })); + + beforeAll(() => { + register.clear(); + const metricsPlugin = new MetricsPlugin( + { packageMetrics: { enabled: true } } as MetricsConfig, + { logger: getLogger() } as PluginOptions, + ); + metricsPlugin.register_middlewares(app, {} as IBasicAuth, {} as IStorageManager); + metricsPlugin.collectPackageMetrics(req, res, next); + metricsPlugin.getMetrics(req, res); + }); + test('should invoke the correct express API calls', () => { + expect(next).toHaveBeenCalledTimes(1); + }); + + test('should not have collected any metrics', async () => { + const metrics = await register.getMetricsAsJSON(); + expect(metrics).toEqual(getPackageMetricsJson([])); + }); + }); + + describe('should provide a valid Prometheus text format response', () => { + const app = { get: jest.fn() } as unknown as Application; const { req, res, next } = getExpressMocks(); beforeAll(() => { register.clear(); const metricsPlugin = new MetricsPlugin( - { metricsEnabled: true } as MetricsConfig, + { packageMetrics: { enabled: true } } as MetricsConfig, { logger: getLogger() } as PluginOptions, ); - metricsPlugin.collectMetrics(req, res, next); + metricsPlugin.register_middlewares(app, {} as IBasicAuth, {} as IStorageManager); + metricsPlugin.collectPackageMetrics(req, res, next); metricsPlugin.getMetrics(req, res); }); @@ -84,9 +106,9 @@ describe('Metrics Plugin', () => { expect(res.send).toHaveBeenCalledTimes(1); expect(res.send).toHaveBeenCalledWith( [ - '# HELP registry_requests HTTP requests made to the registry', - '# TYPE registry_requests counter', - 'registry_requests{username="UNKNOWN",userAgentName="UNKNOWN",statusCode="200"} 1\n', + '# HELP registry_package_downloads Count of package downloads from the registry', + '# TYPE registry_package_downloads counter', + 'registry_package_downloads{username="UNKNOWN",userAgentName="UNKNOWN",statusCode="200"} 1\n', ].join('\n'), ); }); @@ -94,6 +116,7 @@ describe('Metrics Plugin', () => { describe('should collect metrics when `packageGroups` are NOT defined', () => { let metricsPlugin; + const app = { get: jest.fn() } as unknown as Application; const [username1, username2] = chance.n(chance.word, 2); const [userAgentArtifactory, userAgentNpm] = [ { userAgentName: 'Artifactory', userAgentVersion: '7.1.1' }, @@ -111,10 +134,11 @@ describe('Metrics Plugin', () => { beforeAll(() => { register.clear(); metricsPlugin = new MetricsPlugin( - { metricsEnabled: true } as MetricsConfig, + { packageMetrics: { enabled: true } } as MetricsConfig, { logger: getLogger() } as PluginOptions, ); - expressMocks.forEach(({ req, res, next }) => metricsPlugin.collectMetrics(req, res, next)); + metricsPlugin.register_middlewares(app, {} as IBasicAuth, {} as IStorageManager); + expressMocks.forEach(({ req, res, next }) => metricsPlugin.collectPackageMetrics(req, res, next)); }); test('should invoke the correct express API calls', () => { @@ -123,10 +147,10 @@ describe('Metrics Plugin', () => { }); }); - test('should not collect any metrics', async () => { + test('should collect metrics', async () => { const metrics = await register.getMetricsAsJSON(); expect(metrics).toEqual( - getMetricsJson([ + getPackageMetricsJson([ { value: 2, labels: { userAgentName: UNKNOWN, username: UNKNOWN, statusCode: 200 } }, { value: 2, labels: { userAgentName: UNKNOWN, username: username1, statusCode: 200 } }, { value: 1, labels: { userAgentName: 'Artifactory', username: username2, statusCode: 200 } }, @@ -138,14 +162,15 @@ describe('Metrics Plugin', () => { describe('should collect metrics when `packageGroups` are defined', () => { let metricsPlugin; + const app = { get: jest.fn() } as unknown as Application; const [username1, username2] = chance.n(chance.word, 2); const [userAgentArtifactory, userAgentNpm] = [ { userAgentName: 'Artifactory', userAgentVersion: '7.1.1' }, { userAgentName: 'npm', userAgentVersion: '8.2.3' }, ]; const packageGroups = { - '@scoped[/]test-package[^/]*9[.]1[.]x': 'test-package-9.1.x', - '@scoped[/]test-package': 'test-package', + '@scoped/test-package[^/]*9[.]1[.]x': 'test-package-9.1.x', + '@scoped/test-package': 'test-package', 'non-scoped': 'non-scoped', '.*': 'other', } as Record; @@ -163,10 +188,11 @@ describe('Metrics Plugin', () => { beforeAll(() => { register.clear(); metricsPlugin = new MetricsPlugin( - { metricsEnabled: true, packageGroups } as MetricsConfig, + { packageMetrics: { enabled: true, packageGroups } } as MetricsConfig, { logger: getLogger() } as PluginOptions, ); - expressMocks.forEach(({ req, res, next }) => metricsPlugin.collectMetrics(req, res, next)); + metricsPlugin.register_middlewares(app, {} as IBasicAuth, {} as IStorageManager); + expressMocks.forEach(({ req, res, next }) => metricsPlugin.collectPackageMetrics(req, res, next)); }); test('should invoke the correct express API calls', () => { @@ -175,10 +201,10 @@ describe('Metrics Plugin', () => { }); }); - test('should not collect any metrics', async () => { + test('should collect metrics', async () => { const metrics = await register.getMetricsAsJSON(); expect(metrics).toEqual( - getMetricsJson([ + getPackageMetricsJson([ { value: 2, labels: { username: UNKNOWN, userAgentName: UNKNOWN, packageGroup: 'test-package', statusCode: 200 }, @@ -208,56 +234,4 @@ describe('Metrics Plugin', () => { ); }); }); - - describe('should collect default prometheus metrics when `collectDefaultMetrics` is set to `true`', () => { - const { req, res, next } = getExpressMocks(); - const app = { get: jest.fn() } as unknown as Application; - - beforeAll(() => { - register.clear(); - const metricsPlugin = new MetricsPlugin( - { metricsEnabled: true, collectDefaultMetrics: true } as MetricsConfig, - { logger: getLogger() } as PluginOptions, - ); - metricsPlugin.register_middlewares(app, {} as IBasicAuth, {} as IStorageManager); - metricsPlugin.collectMetrics(req, res, next); - metricsPlugin.getMetrics(req, res); - }); - - test('should collect default prometheus metrics', async () => { - const metrics = await register.getMetricsAsJSON(); - expect(metrics.map(({ name }) => name).sort()).toEqual([ - 'nodejs_active_handles', - 'nodejs_active_handles_total', - 'nodejs_active_requests', - 'nodejs_active_requests_total', - 'nodejs_eventloop_lag_max_seconds', - 'nodejs_eventloop_lag_mean_seconds', - 'nodejs_eventloop_lag_min_seconds', - 'nodejs_eventloop_lag_p50_seconds', - 'nodejs_eventloop_lag_p90_seconds', - 'nodejs_eventloop_lag_p99_seconds', - 'nodejs_eventloop_lag_seconds', - 'nodejs_eventloop_lag_stddev_seconds', - 'nodejs_external_memory_bytes', - 'nodejs_gc_duration_seconds', - 'nodejs_heap_size_total_bytes', - 'nodejs_heap_size_used_bytes', - 'nodejs_heap_space_size_available_bytes', - 'nodejs_heap_space_size_total_bytes', - 'nodejs_heap_space_size_used_bytes', - 'nodejs_version_info', - 'process_cpu_seconds_total', - 'process_cpu_system_seconds_total', - 'process_cpu_user_seconds_total', - 'process_heap_bytes', - 'process_max_fds', - 'process_open_fds', - 'process_resident_memory_bytes', - 'process_start_time_seconds', - 'process_virtual_memory_bytes', - 'registry_requests', - ]); - }); - }); }); diff --git a/tests/pluginMetricsRequests.spec.ts b/tests/pluginMetricsRequests.spec.ts new file mode 100644 index 0000000..74c45aa --- /dev/null +++ b/tests/pluginMetricsRequests.spec.ts @@ -0,0 +1,188 @@ +import * as chanceJs from 'chance'; +import { register } from 'prom-client'; +import { Application } from 'express'; +import { IBasicAuth, IStorageManager, PluginOptions } from '@verdaccio/types'; + +import MetricsPlugin, { DEFAULT_METRICS_PATH } from '../src'; +import { MetricsConfig } from '../src/types'; +import { AuthType, UNKNOWN } from '../src/utils'; + +import { getExpressMocks, getRequestOptions, getLogger, getRequestMetricsJson } from './testUtils'; +const chance = chanceJs(); + +describe('HTTP Request Metrics', () => { + describe('should register middleware (http request metrics enabled)', () => { + const logger = getLogger(); + const metricName = chance.word(); + const app = { all: jest.fn(), get: jest.fn() } as unknown as Application; + + beforeAll(() => { + register.clear(); + const metricsPlugin = new MetricsPlugin( + { requestMetrics: { enabled: true, metricName } } as MetricsConfig, + { logger } as PluginOptions, + ); + metricsPlugin.register_middlewares(app, {} as IBasicAuth, {} as IStorageManager); + }); + + test('should use the provided custom metric name', async () => { + const metrics = await register.getMetricsAsJSON(); + expect(metrics.length).toEqual(1); + expect(metrics[0].name).toEqual(metricName); + }); + + test('should invoke the correct express API calls', () => { + const mockAll = (app.all as jest.MockedFn).mock; + const mockGet = (app.get as jest.MockedFn).mock; + expect(app.all).toHaveBeenCalledTimes(1); + expect(app.get).toHaveBeenCalledTimes(1); + expect(mockAll.calls[0][0]).toEqual(/.*/); + expect(mockGet.calls[0][0]).toEqual(DEFAULT_METRICS_PATH); + }); + }); + + describe('should register middleware (http request metrics disabled)', () => { + const logger = getLogger(); + const app = { all: jest.fn(), get: jest.fn() } as unknown as Application; + const metricsConfig = { requestMetrics: { enabled: false } } as MetricsConfig; + + beforeAll(() => { + register.clear(); + const metricsPlugin = new MetricsPlugin(metricsConfig, { logger } as PluginOptions); + metricsPlugin.register_middlewares(app, {} as IBasicAuth, {} as IStorageManager); + }); + + test('should not invoke any express API', () => { + expect(app.all).toHaveBeenCalledTimes(0); + }); + + test('should log warn that metrics are disabled', () => { + expect(logger.warn).toHaveBeenCalledWith(metricsConfig, 'metrics: [register_middlewares] metrics are disabled'); + }); + }); + + describe('should provide a valid Prometheus text format response', () => { + const app = { all: jest.fn(), get: jest.fn() } as unknown as Application; + const { req, res, next } = getExpressMocks(); + + beforeAll(() => { + register.clear(); + const metricsPlugin = new MetricsPlugin( + { requestMetrics: { enabled: true } } as MetricsConfig, + { logger: getLogger() } as PluginOptions, + ); + metricsPlugin.register_middlewares(app, {} as IBasicAuth, {} as IStorageManager); + metricsPlugin.collectRequestMetrics(req, res, next); + metricsPlugin.getMetrics(req, res); + }); + + test('should invoke the correct express API calls', () => { + expect(res.setHeader).toHaveBeenCalledTimes(1); + expect(res.setHeader).toHaveBeenCalledWith('Content-Type', register.contentType); + expect(res.status).toHaveBeenCalledTimes(1); + expect(res.status).toHaveBeenCalledWith(200); + expect(res.send).toHaveBeenCalledTimes(1); + expect(res.send).toHaveBeenCalledWith( + [ + '# HELP registry_http_requests Count of HTTP requests made to the registry', + '# TYPE registry_http_requests counter', + 'registry_http_requests{httpMethod="GET",username="UNKNOWN",userAgentName="UNKNOWN",statusCode="200"} 1\n', + ].join('\n'), + ); + }); + }); + + describe('should collect http metrics on every request', () => { + let metricsPlugin; + const app = { all: jest.fn(), get: jest.fn() } as unknown as Application; + const [username1, username2] = chance.n(chance.word, 2); + const [userAgentArtifactory, userAgentNpm] = [ + { userAgentName: 'Artifactory', userAgentVersion: '7.1.1' }, + { userAgentName: 'npm', userAgentVersion: '8.2.3' }, + ]; + const expressMocks = [ + getExpressMocks(), + getExpressMocks(getRequestOptions({ httpMethod: 'POST' })), + getExpressMocks(getRequestOptions({ authType: AuthType.jwt, username: username1 })), + getExpressMocks(getRequestOptions({ authType: AuthType.password, username: username1, httpMethod: 'PUT' })), + getExpressMocks(getRequestOptions({ authType: AuthType.jwt, username: username2, ...userAgentArtifactory })), + getExpressMocks(getRequestOptions({ authType: AuthType.password, username: username2, ...userAgentNpm })), + ]; + + beforeAll(() => { + register.clear(); + metricsPlugin = new MetricsPlugin( + { requestMetrics: { enabled: true } } as MetricsConfig, + { logger: getLogger() } as PluginOptions, + ); + metricsPlugin.register_middlewares(app, {} as IBasicAuth, {} as IStorageManager); + expressMocks.forEach(({ req, res, next }) => metricsPlugin.collectRequestMetrics(req, res, next)); + }); + + test('should invoke the correct express API calls', () => { + expressMocks.forEach(({ next }) => { + expect(next).toHaveBeenCalledTimes(1); + }); + }); + + test('should collect metrics', async () => { + const metrics = await register.getMetricsAsJSON(); + expect(metrics).toEqual( + getRequestMetricsJson([ + { value: 1, labels: { httpMethod: 'GET', userAgentName: UNKNOWN, username: UNKNOWN, statusCode: 200 } }, + { value: 1, labels: { httpMethod: 'POST', userAgentName: UNKNOWN, username: UNKNOWN, statusCode: 200 } }, + { value: 1, labels: { httpMethod: 'GET', userAgentName: UNKNOWN, username: username1, statusCode: 200 } }, + { value: 1, labels: { httpMethod: 'PUT', userAgentName: UNKNOWN, username: username1, statusCode: 200 } }, + { + value: 1, + labels: { httpMethod: 'GET', userAgentName: 'Artifactory', username: username2, statusCode: 200 }, + }, + { value: 1, labels: { httpMethod: 'GET', userAgentName: 'npm', username: username2, statusCode: 200 } }, + ]), + ); + }); + }); + + describe('should not collect http metrics on excluded paths', () => { + let metricsPlugin; + const app = { all: jest.fn(), get: jest.fn() } as unknown as Application; + const expressMocks = [ + // These 3 requests should count towards metrics + getExpressMocks(getRequestOptions({ path: '/@scoped/test-package' })), + getExpressMocks(getRequestOptions({ path: '/@scoped%2Ftest-package' })), + getExpressMocks(getRequestOptions({ path: `/non-scoped-${chance.word()}` })), + // These 6 requests should NOT count towards metrics + getExpressMocks(getRequestOptions({ path: '/' })), + getExpressMocks(getRequestOptions({ path: '/-/ping' })), + getExpressMocks(getRequestOptions({ path: `/-/static/${chance.word()}` })), + getExpressMocks(getRequestOptions({ path: `/-/verdaccio/${chance.word()}` })), + getExpressMocks(getRequestOptions({ path: `/-/web/${chance.word()}` })), + getExpressMocks(getRequestOptions({ path: '/-/favicon.ico' })), + ]; + + beforeAll(() => { + register.clear(); + metricsPlugin = new MetricsPlugin( + { requestMetrics: { enabled: true } } as MetricsConfig, + { logger: getLogger() } as PluginOptions, + ); + metricsPlugin.register_middlewares(app, {} as IBasicAuth, {} as IStorageManager); + expressMocks.forEach(({ req, res, next }) => metricsPlugin.collectRequestMetrics(req, res, next)); + }); + + test('should invoke the correct express API calls', () => { + expressMocks.forEach(({ next }) => { + expect(next).toHaveBeenCalledTimes(1); + }); + }); + + test('should collect metrics', async () => { + const metrics = await register.getMetricsAsJSON(); + expect(metrics).toEqual( + getRequestMetricsJson([ + { value: 3, labels: { httpMethod: 'GET', userAgentName: UNKNOWN, username: UNKNOWN, statusCode: 200 } }, + ]), + ); + }); + }); +}); diff --git a/tests/testUtils.ts b/tests/testUtils.ts index 3f357fe..490a3c6 100644 --- a/tests/testUtils.ts +++ b/tests/testUtils.ts @@ -1,9 +1,10 @@ /* eslint-disable @typescript-eslint/ban-types,@typescript-eslint/no-explicit-any,no-invalid-this */ -import chanceJs from 'chance'; +import * as chanceJs from 'chance'; import { Logger } from '@verdaccio/types'; import { Request, Response, NextFunction } from 'express'; import { AuthType } from '../src/utils'; +import { DEFAULT_METRIC_NAME_PACKAGE_DOWNLOADS, DEFAULT_METRIC_NAME_REQUESTS } from '../src'; const chance = chanceJs(); @@ -19,12 +20,32 @@ export const getLogger = (): Logger => ({ trace: jest.fn(), }); +export const getRequestMetricsJson = (values, name = DEFAULT_METRIC_NAME_REQUESTS) => [ + { + aggregator: 'sum', + help: 'Count of HTTP requests made to the registry', + name, + type: 'counter', + values, + }, +]; + +export const getPackageMetricsJson = (values, name = DEFAULT_METRIC_NAME_PACKAGE_DOWNLOADS) => [ + { + aggregator: 'sum', + help: 'Count of package downloads from the registry', + name, + type: 'counter', + values, + }, +]; + interface MockRequestOptions { authType?: AuthType; username?: string; userAgentName?: string; userAgentVersion?: string; - httpMethod?: string; + httpMethod?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'HEAD'; path?: string; } diff --git a/tests/utils.spec.ts b/tests/utils.spec.ts index 9fdc219..011a4dc 100644 --- a/tests/utils.spec.ts +++ b/tests/utils.spec.ts @@ -1,4 +1,4 @@ -import chanceJs from 'chance'; +import * as chanceJs from 'chance'; import { AuthType, getUserAgentData, getUsername, UNKNOWN } from '../src/utils'; @@ -50,7 +50,11 @@ const usernameTestCases = [ { username: UNKNOWN, authType: undefined, authHeader: null }, { username: UNKNOWN, authType: undefined, authHeader: chance.word() }, { username: UNKNOWN, authType: undefined, authHeader: `Bearer ${chance.word()}.${chance.word()}.${chance.word()}` }, - { username: UNKNOWN, authType: undefined, authHeader: `Basic ${chance.word()}` }, + { + username: UNKNOWN, + authType: undefined, + authHeader: `Basic ${chance.word({ length: chance.integer({ min: 10, max: 50 }) })}`, + }, { username: 'user_bearer', authType: AuthType.jwt, authHeader: generateMockBearerAuth('user_bearer') }, { username: 'user_basic', authType: AuthType.password, authHeader: generateMockBasicAuth('user_basic') }, ]; diff --git a/tsconfig.json b/tsconfig.json index 81793c2..b69ef79 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,10 +5,8 @@ "declaration": true, "noImplicitAny": false, "strict": true, - "rootDir": "src", "outDir": "lib", "allowSyntheticDefaultImports": true, - "esModuleInterop": true, "typeRoots": ["./node_modules/@verdaccio/types/lib/verdaccio", "./node_modules/@types"] }, "include": ["src/**/*.ts"]