diff --git a/docker-compose.yml b/docker-compose.yml index 37f5cbf..ac4a14b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,7 +13,7 @@ services: server: build: . image: aragon/protocol-backend - command: npm run start:server:dev + command: yarn start:server:dev depends_on: - postgres - loki @@ -23,6 +23,7 @@ services: volumes: - ./packages/server/src:/app/packages/server/src - ./packages/server/test:/app/packages/server/test + - ./packages/shared/build:/app/packages/shared/build env_file: - .env logging: &loki @@ -35,14 +36,14 @@ services: services: build: . image: aragon/protocol-backend - command: npm run start:services:dev + command: yarn start:services:dev depends_on: - server - loki volumes: - - ./packages/services/bin:/app/packages/services/bin - ./packages/services/src:/app/packages/services/src - ./packages/services/test:/app/packages/services/test + - ./packages/shared/build:/app/packages/shared/build env_file: - .env logging: *loki @@ -50,7 +51,7 @@ services: backoffice: build: . image: aragon/protocol-backend - command: npm run start:app + command: yarn start:app depends_on: - server - loki @@ -75,7 +76,6 @@ services: # speed up development by mounting some local directories with hot reload - ./packages/server/src:/app/packages/server/src - ./packages/server/test:/app/packages/server/test - - ./packages/services/bin:/app/packages/services/bin - ./packages/services/src:/app/packages/services/src - ./packages/services/test:/app/packages/services/test - ./packages/shared/src:/app/packages/shared/src diff --git a/packages/app/.gitignore b/packages/app/.gitignore deleted file mode 100644 index f2c3ae6..0000000 --- a/packages/app/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -# See https://help.github.com/ignore-files/ for more about ignoring files. - -# Production files -build diff --git a/packages/server/.babelrc b/packages/server/.babelrc deleted file mode 100644 index 2225215..0000000 --- a/packages/server/.babelrc +++ /dev/null @@ -1,14 +0,0 @@ -{ - "presets": [ - [ - "@babel/preset-env", - { - "targets": { - "node": true - }, - "useBuiltIns": "entry", - "corejs": 3 - } - ] - ] -} diff --git a/packages/server/.eslintrc.json b/packages/server/.eslintrc.json new file mode 100644 index 0000000..97c581c --- /dev/null +++ b/packages/server/.eslintrc.json @@ -0,0 +1,15 @@ +{ + "root": true, + "plugins": ["@typescript-eslint"], + "parser": "@typescript-eslint/parser", + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended" + ], + "rules": { + // allow using any and non null assertion for Objection models + "@typescript-eslint/explicit-module-boundary-types": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-non-null-assertion": "off" + } +} diff --git a/packages/server/.gitignore b/packages/server/.gitignore deleted file mode 100644 index 378eac2..0000000 --- a/packages/server/.gitignore +++ /dev/null @@ -1 +0,0 @@ -build diff --git a/packages/server/babel.config.json b/packages/server/babel.config.json new file mode 100644 index 0000000..0474fb8 --- /dev/null +++ b/packages/server/babel.config.json @@ -0,0 +1,18 @@ +{ + "presets": [ + "@babel/preset-typescript", + [ + "@babel/preset-env", + { + "targets": { + "node": "current" + }, + "useBuiltIns": "usage", + "corejs": 3 + } + ] + ], + "plugins": [ + "@babel/plugin-proposal-export-namespace-from" + ] +} diff --git a/packages/server/package.json b/packages/server/package.json index 09ff487..d3dca48 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -4,22 +4,21 @@ "private": true, "author": "Aragon One", "license": "(GPL-3.0-or-later OR AGPL-3.0-or-later)", - "engines": { - "node": ">=9.0.0" - }, "scripts": { - "build": "babel ./src --out-dir ./build --source-maps --copy-files", - "build:shared": "yarn workspace @aragon/protocol-backend-shared build", - "start": "yarn build && yarn db:setup && node ./build", - "start:dev": "yarn db:setup && nodemon --ignore ./build --exec babel-node ./src/index.js", - "knex": "npx babel-node ./node_modules/.bin/knex", - "test": "npx mocha test --recursive --exit --require @babel/register", + "lint": "eslint src/**/*.ts --ext .ts", + "test": "npx mocha test --recursive --exit --require ./test/helpers/babel-ts", + "build": "yarn lint && yarn check-types && yarn build:js", + "build:js": "babel ./src --out-dir ./build --extensions .ts,.js", + "build:shared": "lerna run build --scope '*/*-shared' --stream", + "build:clean": "yarn clean && yarn build", + "clean": "rm -rf ./build", + "check-types": "tsc", + "start": "yarn db:setup && node ./build/index.js", + "start:dev": "yarn db:setup && nodemon --exec 'babel-node --extensions .ts,.js' ./src/index.js", "db:setup": "lerna run db:setup --scope '*/*-shared' --stream" }, "dependencies": { "@aragon/protocol-backend-shared": "^0.2.21", - "@promster/express": "^4.0.0", - "@promster/server": "^4.0.0", "bcryptjs": "^2.4.3", "body-parser": "^1.19.0", "cookie-parser": "^1.4.5", @@ -33,7 +32,6 @@ "http-status-codes": "^1.4.0", "morgan": "^1.9.1", "postmark": "^2.5.3", - "prom-client": "^11.5.3", "regenerator-runtime": "^0.13.3", "validator": "^13.0.0", "web3-utils": "^1.2.4" @@ -43,9 +41,15 @@ "@babel/core": "^7.7.7", "@babel/node": "^7.7.7", "@babel/preset-env": "^7.7.7", + "@babel/preset-typescript": "^7.12.1", + "@babel/register": "^7.12.1", + "@typescript-eslint/eslint-plugin": "^4.7.0", + "@typescript-eslint/parser": "^4.7.0", "chai": "^4.2.0", "chai-http": "^4.3.0", + "eslint": "^7.13.0", "mocha": "^7.1.1", - "nodemon": "^2.0.2" + "nodemon": "^2.0.2", + "typescript": "^4.0.5" } } diff --git a/packages/server/src/app.js b/packages/server/src/app.js index df38d60..3168483 100644 --- a/packages/server/src/app.js +++ b/packages/server/src/app.js @@ -6,7 +6,7 @@ import helmet from 'helmet' import morgan from 'morgan' import express from 'express' import bodyParser from 'body-parser' -import { createMiddleware } from '@promster/express' +import metrics from './helpers/metrics-reporter' import routes from './routes' import errorHandler from './errors/error-handler' @@ -20,7 +20,7 @@ dotenv.config() // Set up express layers const app = express() app.set('trust proxy', 1) // required for secure sessions -app.use(createMiddleware({ app })) +app.use(metrics.createExpressMiddleware()) app.use(helmet()) app.use(morgan('dev')) app.use(corsMiddleware) diff --git a/packages/server/src/errors/error-handler.js b/packages/server/src/errors/error-handler.js index fe96b2e..be41fa8 100644 --- a/packages/server/src/errors/error-handler.js +++ b/packages/server/src/errors/error-handler.js @@ -3,14 +3,13 @@ import HttpStatus from 'http-status-codes' import { Errors as PostmarkErrors } from 'postmark' import HttpError from './http-error' -import MetricsReporter from '../helpers/metrics-reporter' +import metrics from '../helpers/metrics-reporter' export default app => (err, req, res, next) => { if (res.headersSent) { return next(err) } - const reporter = MetricsReporter(app) let code, body if (err instanceof HttpError) { @@ -25,7 +24,7 @@ export default app => (err, req, res, next) => { code = HttpStatus.INTERNAL_SERVER_ERROR body = { errors: [{ email: 'Could not send email.' }] } console.error(err.stack) - reporter.emailError() + metrics.emailError() } else if (err.message.includes('CORS')) { code = HttpStatus.BAD_REQUEST @@ -37,7 +36,7 @@ export default app => (err, req, res, next) => { body = 'Something went wrong :(' if (err instanceof DBError) { - reporter.dbError() + metrics.dbError() } } diff --git a/packages/server/src/helpers/metrics-reporter.js b/packages/server/src/helpers/metrics-reporter.js deleted file mode 100644 index 14b0e24..0000000 --- a/packages/server/src/helpers/metrics-reporter.js +++ /dev/null @@ -1,42 +0,0 @@ -const COUNTER_METRICS = { - db: [ - { name: 'queries', help: 'DB queries per table' }, - { name: 'errors', help: 'DB errors' }, - ], - email: [ - { name: 'errors', help: 'Total email errors' }, - ] -} - -class MetricsReporter { - constructor(app) { - this._initializeCounterMetrics(app) - } - - dbQuery() { - this.db.queries.inc() - } - - dbError() { - this.db.errors.inc() - } - - emailError() { - this.email.errors.inc() - } - - _initializeCounterMetrics(app) { - const { locals: { Prometheus: { Counter, Registry: { globalRegistry: registry } } } } = app - - Object.keys(COUNTER_METRICS).forEach(type => { - this[type] = {} - COUNTER_METRICS[type].forEach(({ name, help, labelNames }) => { - const metricName = `${type}_${name}` - const metric = registry.getSingleMetric(metricName) - this[type][name] = metric || new Counter({ name: metricName, help, labelNames }) - }) - }) - } -} - -export default app => new MetricsReporter(app) diff --git a/packages/server/src/helpers/metrics-reporter.ts b/packages/server/src/helpers/metrics-reporter.ts new file mode 100644 index 0000000..549dfc4 --- /dev/null +++ b/packages/server/src/helpers/metrics-reporter.ts @@ -0,0 +1,26 @@ +import { Reporter, Metrics } from '@aragon/protocol-backend-shared/build/helpers/metrics-reporter' + +const COUNTER_METRICS: Metrics = { + db: [ + { name: 'queries', help: 'DB queries per table' }, + { name: 'errors', help: 'DB errors' }, + ], + email: [ + { name: 'errors', help: 'Total email errors' }, + ] +} + +class MetricsReporter extends Reporter { + + dbQuery(): void { + this.counters.db.queries.inc() + } + dbError(): void { + this.counters.db.errors.inc() + } + emailError(): void { + this.counters.email.errors.inc() + } +} + +export default new MetricsReporter(COUNTER_METRICS) diff --git a/packages/server/src/index.js b/packages/server/src/index.js index 7798ea8..dccba89 100644 --- a/packages/server/src/index.js +++ b/packages/server/src/index.js @@ -1,16 +1,13 @@ import app from './app' -import { createServer } from '@promster/server' -import { signalIsUp } from '@promster/express' +import metrics from './helpers/metrics-reporter' const serverPort = process.env.SERVER_PORT || 8000 app.listen(serverPort, error => { if (error) return console.error(error) - signalIsUp() + metrics.setExpressMiddlewareUp() console.log(`Server listening on port ${serverPort}`) }) // Start Prometheus metrics -const metricsPort = process.env.SERVER_METRICS_PORT || 9091 -createServer({ port: metricsPort }).then(() => - console.log(`Metrics server started on port ${metricsPort}`) -) +const metricsPort = Number(process.env.SERVER_METRICS_PORT) || 9091 +metrics.createServer(metricsPort).then() diff --git a/packages/server/test/helpers/babel-ts.js b/packages/server/test/helpers/babel-ts.js new file mode 100644 index 0000000..3c81210 --- /dev/null +++ b/packages/server/test/helpers/babel-ts.js @@ -0,0 +1,4 @@ +// This is added to mocha --require ./test/helpers/babel-ts to allow importing .ts files in tests +require('@babel/register')({ + extensions: ['.js','.ts'], +}) diff --git a/packages/server/tsconfig.json b/packages/server/tsconfig.json new file mode 100644 index 0000000..cf89835 --- /dev/null +++ b/packages/server/tsconfig.json @@ -0,0 +1,16 @@ +{ + "include": [ + "src/**/*" + ], + "compilerOptions": { + "strict": true, + "noEmit": true, + "moduleResolution": "Node", + // allow advanced js features, proper targeting is done using Babel + "target": "ESNext", + // allow non-ES modules with 'export =' syntax as default imports + "esModuleInterop": true, + // allow json import + "resolveJsonModule": true + } +} diff --git a/packages/services/.babelrc b/packages/services/.babelrc deleted file mode 100644 index 2225215..0000000 --- a/packages/services/.babelrc +++ /dev/null @@ -1,14 +0,0 @@ -{ - "presets": [ - [ - "@babel/preset-env", - { - "targets": { - "node": true - }, - "useBuiltIns": "entry", - "corejs": 3 - } - ] - ] -} diff --git a/packages/services/.eslintrc.json b/packages/services/.eslintrc.json new file mode 100644 index 0000000..97c581c --- /dev/null +++ b/packages/services/.eslintrc.json @@ -0,0 +1,15 @@ +{ + "root": true, + "plugins": ["@typescript-eslint"], + "parser": "@typescript-eslint/parser", + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended" + ], + "rules": { + // allow using any and non null assertion for Objection models + "@typescript-eslint/explicit-module-boundary-types": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-non-null-assertion": "off" + } +} diff --git a/packages/services/README.md b/packages/services/README.md index 7e28a73..05b4448 100644 --- a/packages/services/README.md +++ b/packages/services/README.md @@ -2,15 +2,6 @@ This repo provides a set of background services in charge of those parts of Aragon Protocol that can be done automatically to ensure a good user experience for the users of the protocol. -### Setup - -To work locally, simply go to the root directory, and make sure you have set up a propoer `.env` file following the `.env.sample` file. -Once you have done that, spin up a docker container with: -```bash -docker-compose build -docker-compose up -d -``` - ### Workers It provides the following list of worker services: @@ -22,7 +13,7 @@ It provides the following list of worker services: - [`Settlements`](./src/workers/settlements.js): It will try to execute and settle penalties, rewards, and appeals for all the rounds of a dispute if possible. Configured initially to run one time per five minutes indefinitely. - [`Contract Monitor`](./src/workers/contract-monitor.js): It periodically queries protocol address for any failing transactions in the past 24 hours and renders Prometheus gauge metrics that can be used for alerting. -All the background services are configured through the `config.js` file using the following variables: +All the background services are configured through the [`src/bin/config.js`](src/bin/config.js) file using the following variables: - `name`: Name of the worker used for logging. It will assume `unknown` if undefined. - `path`: Name of the root file that will be executed every time a new job for that worker is executed. This parameter is mandatory, and it must be a file exporting an async function accepting the following list of parameters: `worker`, `job`, `logger`. - `processes`: Number of workers run in parallel. It will assume `1` if undefined. @@ -33,4 +24,4 @@ All the background services are configured through the `config.js` file using th ### Keys -This repo needs the private key to be defined as a envrionment variable `PRIVATE_KEY`. +This repo needs the private key to be defined as a environment variable `PRIVATE_KEY`. diff --git a/packages/services/babel.config.json b/packages/services/babel.config.json new file mode 100644 index 0000000..0474fb8 --- /dev/null +++ b/packages/services/babel.config.json @@ -0,0 +1,18 @@ +{ + "presets": [ + "@babel/preset-typescript", + [ + "@babel/preset-env", + { + "targets": { + "node": "current" + }, + "useBuiltIns": "usage", + "corejs": 3 + } + ] + ], + "plugins": [ + "@babel/plugin-proposal-export-namespace-from" + ] +} diff --git a/packages/services/bin/main.js b/packages/services/bin/main.js deleted file mode 100644 index c54e3b2..0000000 --- a/packages/services/bin/main.js +++ /dev/null @@ -1,18 +0,0 @@ -#! /usr/bin/env node - -import { fork } from 'child_process' -import { workers } from '../config' -import Logger from '@aragon/protocol-backend-shared/helpers/logger' - -require('dotenv').config() - -Logger.setDefaults(false, true) -const logger = Logger('services') - -for (const { name, path, processes, times, repeat, color = 'white', metricsPort } of workers) { - for (let process = 1; process <= processes; process++) { - logger.info(`Creating worker ${name} #${process}`) - const child = fork('./bin/worker', [path, name, times, repeat, color, metricsPort]) - logger.success(`Created worker ${name} #${process} with pid #${child.pid}`) - } -} diff --git a/packages/services/package.json b/packages/services/package.json index d699296..9539ee9 100644 --- a/packages/services/package.json +++ b/packages/services/package.json @@ -4,20 +4,22 @@ "private": true, "author": "Aragon One", "license": "(GPL-3.0-or-later OR AGPL-3.0-or-later)", - "main": "index.js", "scripts": { - "build:shared": "yarn workspace @aragon/protocol-backend-shared build", - "build:server": "yarn workspace @aragon/protocol-backend-server build", - "start": "npm run build:server && babel-node ./bin/main.js", - "start:dev": "npm run build:server && nodemon --ignore ./build --exec babel-node ./bin/main.js", - "test": "npx mocha test --recursive --exit --require @babel/register", + "lint": "eslint src/**/*.ts --ext .ts", + "test": "npx mocha test --recursive --exit --require ./test/helpers/babel-ts", + "build": "yarn lint && yarn check-types && yarn build:js", + "build:js": "babel ./src --out-dir ./build --extensions .ts,.js", + "build:shared": "lerna run build --scope '*/*-shared' --stream", + "build:clean": "yarn clean && yarn build", + "clean": "rm -rf ./build", + "check-types": "tsc", + "start": "yarn db:setup && node ./build/bin/main.js", + "start:dev": "yarn db:setup && nodemon --exec 'babel-node --extensions .ts,.js' ./src/bin/main.js", "db:setup": "lerna run db:setup --scope '*/*-shared' --stream" }, "dependencies": { "@aragon/protocol-backend-server": "^0.2.21", "@aragon/protocol-backend-shared": "^0.2.21", - "@promster/metrics": "^4.1.10", - "@promster/server": "^4.2.12", "axios": "^0.19.0", "dateformat": "^3.0.3", "dotenv": "^8.2.0", @@ -31,10 +33,16 @@ "@babel/core": "^7.7.7", "@babel/node": "^7.7.7", "@babel/preset-env": "^7.7.7", + "@babel/preset-typescript": "^7.12.1", + "@babel/register": "^7.12.1", + "@typescript-eslint/eslint-plugin": "^4.7.0", + "@typescript-eslint/parser": "^4.7.0", "chai": "^4.2.0", + "eslint": "^7.13.0", "mocha": "^7.1.2", "nodemon": "^2.0.2", "sinon": "^9.0.2", - "sinon-chai": "^3.5.0" + "sinon-chai": "^3.5.0", + "typescript": "^4.0.5" } } diff --git a/packages/services/config.js b/packages/services/src/bin/config.js similarity index 81% rename from packages/services/config.js rename to packages/services/src/bin/config.js index 3fcb272..39271bf 100644 --- a/packages/services/config.js +++ b/packages/services/src/bin/config.js @@ -4,7 +4,7 @@ const workers = [ { name: 'heartbeat', color: 'yellow', - path: './src/workers/heartbeat', + path: '../workers/heartbeat', processes: 1, times: 0, repeat: ONE_MINUTE * 10, @@ -13,7 +13,7 @@ const workers = [ { name: 'reveal', color: 'pink', - path: './src/workers/reveal', + path: '../workers/reveal', processes: 1, times: 0, repeat: ONE_MINUTE, @@ -22,7 +22,7 @@ const workers = [ { name: 'settlements', color: 'cyan', - path: './src/workers/settlements', + path: '../workers/settlements', processes: 1, times: 0, repeat: ONE_MINUTE * 5, @@ -31,7 +31,7 @@ const workers = [ { name: 'monitor-keeper', color: 'green', - path: './src/workers/monitor-keeper', + path: '../workers/monitor-keeper', processes: 1, times: 0, repeat: ONE_MINUTE * 30, @@ -40,7 +40,7 @@ const workers = [ { name: 'notification-scanner', color: 'blue', - path: './src/workers/notification-scanner', + path: '../workers/notification-scanner', processes: 1, times: 0, repeat: ONE_MINUTE, @@ -49,7 +49,7 @@ const workers = [ { name: 'notification-sender', color: 'magenta', - path: './src/workers/notification-sender', + path: '../workers/notification-sender', processes: 1, times: 0, repeat: ONE_MINUTE * 5, @@ -57,7 +57,7 @@ const workers = [ }, { name: 'contract-monitor', - path: './src/workers/contract-monitor', + path: '../workers/contract-monitor', processes: 1, times: 0, repeat: ONE_MINUTE * 5, diff --git a/packages/services/src/bin/main.js b/packages/services/src/bin/main.js new file mode 100644 index 0000000..37cd135 --- /dev/null +++ b/packages/services/src/bin/main.js @@ -0,0 +1,21 @@ +#! /usr/bin/env node + +import path from 'path' +import { fork } from 'child_process' +import { workers } from './config' +import Logger from '@aragon/protocol-backend-shared/helpers/logger' + +require('dotenv').config() + +Logger.setDefaults(false, true) +const logger = Logger('services') + +for (const { name, path: workerPath, processes, times, repeat, color = 'white', metricsPort } of workers) { + for (let p = 1; p <= processes; p++) { + logger.info(`Creating worker ${name} #${p}`) + let execArgv = process.execArgv + if (execArgv[0] && execArgv[0].includes('babel')) execArgv.push('--extensions', '.ts,.js') + const child = fork(path.resolve(__dirname, './worker'), [workerPath, name, times, repeat, color, metricsPort], { execArgv }) + logger.success(`Created worker ${name} #${p} with pid #${child.pid}`) + } +} diff --git a/packages/services/bin/worker.js b/packages/services/src/bin/worker.js similarity index 83% rename from packages/services/bin/worker.js rename to packages/services/src/bin/worker.js index fbe5481..c0e774f 100644 --- a/packages/services/bin/worker.js +++ b/packages/services/src/bin/worker.js @@ -1,9 +1,7 @@ -#! /usr/bin/env node - import path from 'path' -import Logger from '../src/helpers/worker-logger' -import MetricsReporter from '../src/helpers/metrics-reporter' -import errorHandler from '../src/helpers/error-handler' +import Logger from '../helpers/worker-logger' +import MetricsReporter from '../helpers/metrics-reporter' +import errorHandler from '../helpers/error-handler' import sleep from '@aragon/protocol-backend-shared/build/helpers/sleep' let [workerPath, name, times, repeat, color, metricsPort] = process.argv.slice(2) @@ -19,7 +17,7 @@ Logger.setDefaults(false, true) const logger = Logger(name, color) const metrics = new MetricsReporter(name, metricsPort) const ctx = { logger, metrics } -const worker = require(path.resolve(process.cwd(), workerPath)).default +const worker = require(path.resolve(__dirname, workerPath)).default async function run() { logger.info(`Starting worker, will run ${times == 0 ? 'infinite' : times} jobs`) diff --git a/packages/services/src/helpers/metrics-reporter.js b/packages/services/src/helpers/metrics-reporter.js deleted file mode 100644 index 7abfcd7..0000000 --- a/packages/services/src/helpers/metrics-reporter.js +++ /dev/null @@ -1,102 +0,0 @@ -import { Prometheus } from '@promster/metrics' -import { createServer } from '@promster/server' - -const generalMetrics = { - worker: [ - { name: 'runs', help: 'Total worker runs' }, - { name: 'success', help: 'Total successful worker runs' }, - { name: 'errors', help: 'Total worker run errors' }, - ] -} -const workerMetrics = { - heartbeat: { ... generalMetrics }, - reveal: { ... generalMetrics }, - settlements: { ... generalMetrics }, - 'monitor-keeper': { ... generalMetrics }, - 'notification-scanner': { - ... generalMetrics, - notifications: [ - { name: 'scanned', help: 'Total notifications scanned', labelNames: ['scannerName'] }, - ] - }, - 'notification-sender': { - ... generalMetrics, - notifications: [ - { name: 'sent', help: 'Total notifications sent', labelNames: ['scannerName'] }, - ] - }, - 'contract-monitor': { - ... generalMetrics, - transaction: [ - { name: 'errors', help: 'Total recent transaction errors', labelNames: ['type'], metricType: 'gauge' }, - ] - } -} - -class MetricsReporter { - constructor(workerName, port) { - this._defaultLabels = { workerName } - this._initializeCounterMetrics(workerMetrics[workerName], port) - this._startServer(port) - } - - workerRun() { - this.worker.runs.inc(this._defaultLabels) - } - - workerSuccess() { - this.worker.success.inc(this._defaultLabels) - } - - workerError() { - this.worker.errors.inc(this._defaultLabels) - } - - notificationScanned(scannerName) { - this.notifications.scanned.inc({ - ... this._defaultLabels, - scannerName - }) - } - - notificationSent(scannerName) { - this.notifications.sent.inc({ - ... this._defaultLabels, - scannerName - }) - } - - transactionErrors(type, count) { - this.transaction.errors.set({ type }, count) - } - - _initializeCounterMetrics(metrics) { - const { Counter, Gauge, Registry: { globalRegistry: registry } } = Prometheus - Object.keys(metrics).forEach(type => { - this[type] = {} - metrics[type].forEach(({ name, help, labelNames, metricType }) => { - if (!labelNames) labelNames = [] - labelNames.push('workerName') - const metricName = `${type}_${name}` - const metric = registry.getSingleMetric(metricName) - if (metric) { - this[type][name] = metric - } - else if (metricType == 'gauge') { - this[type][name] = new Gauge({ name: metricName, help, labelNames }) - } - else { - this[type][name] = new Counter({ name: metricName, help, labelNames }) - } - }) - }) - } - - _startServer(port) { - createServer({ port }).then(() => - console.log(`Metrics server started on port ${port}`) - ) - } -} - -export default MetricsReporter diff --git a/packages/services/src/helpers/metrics-reporter.ts b/packages/services/src/helpers/metrics-reporter.ts new file mode 100644 index 0000000..27b0fea --- /dev/null +++ b/packages/services/src/helpers/metrics-reporter.ts @@ -0,0 +1,83 @@ +import { Reporter, Metrics } from '@aragon/protocol-backend-shared/build/helpers/metrics-reporter' + +const generalMetrics: Metrics = { + worker: [ + { name: 'runs', help: 'Total worker runs' }, + { name: 'success', help: 'Total successful worker runs' }, + { name: 'errors', help: 'Total worker run errors' }, + ] +} + +interface WorkerMetrics { + [workerName: string]: Metrics +} + +const workerMetrics: WorkerMetrics = { + heartbeat: { ... generalMetrics }, + reveal: { ... generalMetrics }, + settlements: { ... generalMetrics }, + 'monitor-keeper': { ... generalMetrics }, + 'notification-scanner': { + ... generalMetrics, + notifications: [ + { name: 'scanned', help: 'Total notifications scanned', labelNames: ['scannerName'] }, + ] + }, + 'notification-sender': { + ... generalMetrics, + notifications: [ + { name: 'sent', help: 'Total notifications sent', labelNames: ['scannerName'] }, + ] + }, + 'contract-monitor': { + ... generalMetrics, + transaction: [ + { name: 'errors', help: 'Total recent transaction errors', labelNames: ['type'], type: 'gauge' }, + ] + } +} + +class MetricsReporter extends Reporter { + + private _defaultLabels: { + [labelName: string]: string + } + + constructor(workerName: string, port: number) { + super(workerMetrics[workerName], ['workerName']) + this._defaultLabels = { workerName } + this.createServer(Number(port)) + } + + workerRun(): void { + this.counters.worker.runs.inc(this._defaultLabels) + } + + workerSuccess(): void { + this.counters.worker.success.inc(this._defaultLabels) + } + + workerError(): void { + this.counters.worker.errors.inc(this._defaultLabels) + } + + notificationScanned(scannerName: string): void { + this.counters.notifications.scanned.inc({ + ... this._defaultLabels, + scannerName + }) + } + + notificationSent(scannerName: string): void { + this.counters.notifications.sent.inc({ + ... this._defaultLabels, + scannerName + }) + } + + transactionErrors(type: string, count: number): void { + this.gauges.transaction.errors.set({ type }, count) + } +} + +export default MetricsReporter diff --git a/packages/services/test/helpers/babel-ts.js b/packages/services/test/helpers/babel-ts.js new file mode 100644 index 0000000..3c81210 --- /dev/null +++ b/packages/services/test/helpers/babel-ts.js @@ -0,0 +1,4 @@ +// This is added to mocha --require ./test/helpers/babel-ts to allow importing .ts files in tests +require('@babel/register')({ + extensions: ['.js','.ts'], +}) diff --git a/packages/services/tsconfig.json b/packages/services/tsconfig.json new file mode 100644 index 0000000..cf89835 --- /dev/null +++ b/packages/services/tsconfig.json @@ -0,0 +1,16 @@ +{ + "include": [ + "src/**/*" + ], + "compilerOptions": { + "strict": true, + "noEmit": true, + "moduleResolution": "Node", + // allow advanced js features, proper targeting is done using Babel + "target": "ESNext", + // allow non-ES modules with 'export =' syntax as default imports + "esModuleInterop": true, + // allow json import + "resolveJsonModule": true + } +} diff --git a/packages/shared/.gitignore b/packages/shared/.gitignore deleted file mode 100644 index 999deaf..0000000 --- a/packages/shared/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -# See https://help.github.com/ignore-files/ for more about ignoring files. - -# Dependencies -node_modules -package-lock.json diff --git a/packages/shared/package.json b/packages/shared/package.json index b2a696a..e62592c 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -18,6 +18,9 @@ "@aragon/protocol-evm": "1.0.0-beta.2", "@aragon/truffle-config-v5": "^1.0.1", "@aragonone/erc20-faucet": "^1.0.0", + "@promster/express": "^4.1.15", + "@promster/metrics": "^4.1.13", + "@promster/server": "^4.2.16", "@truffle/config": "1.2.7", "axios": "^0.19.0", "chalk": "^2.4.2", @@ -29,6 +32,7 @@ "objection": "^2.2.3", "pg": "^8.4.2", "postmark": "^2.5.3", + "prom-client": "^12.0.0", "web3-utils": "^1.2.4" }, "devDependencies": { @@ -36,7 +40,9 @@ "@babel/core": "^7.12.3", "@babel/preset-env": "^7.12.1", "@babel/preset-typescript": "^7.12.1", + "@promster/types": "^1.0.8", "@types/bcryptjs": "^2.4.2", + "@types/express": "^4.17.8", "@types/jsonwebtoken": "^8.5.0", "@typescript-eslint/eslint-plugin": "^4.6.1", "@typescript-eslint/parser": "^4.6.1", diff --git a/packages/shared/src/helpers/metrics-reporter.ts b/packages/shared/src/helpers/metrics-reporter.ts new file mode 100644 index 0000000..0a03ed8 --- /dev/null +++ b/packages/shared/src/helpers/metrics-reporter.ts @@ -0,0 +1,61 @@ +import { Prometheus } from '@promster/metrics' +import { createServer } from '@promster/server' +import { signalIsUp, createMiddleware } from '@promster/express' +import { Server } from 'http' + +export interface Metrics { + [group: string]: { + name: string + help: string + labelNames?: string[] + type?: 'gauge' | 'counter' + }[] +} + +export class Reporter { + + server: Server | null = null + + counters: { + [group: string]: { + [name: string]: Prometheus.Counter + } + } + + gauges: { + [group: string]: { + [name: string]: Prometheus.Gauge + } + } + + constructor(metrics: Metrics, globalLabelNames?: string[]) { + this.counters = {} + this.gauges = {} + this._initializeMetrics(metrics, globalLabelNames) + } + + private _initializeMetrics(metrics: Metrics, globalLabelNames?: string[]) { + const { Counter, Gauge } = Prometheus + Object.keys(metrics).forEach((group: string) => { + metrics[group].forEach(({ name, help, labelNames=[], type }) => { + if (globalLabelNames) labelNames.push(...globalLabelNames) + if (type == 'gauge') { + if (!this.gauges[group]) this.gauges[group] = {} + this.gauges[group][name] = new Gauge({ name: `${group}_${name}`, help, labelNames }) + } + else { + if (!this.counters[group]) this.counters[group] = {} + this.counters[group][name] = new Counter({ name: `${group}_${name}`, help, labelNames }) + } + }) + }) + } + + async createServer(port: number): Promise { + this.server = await createServer({port}) + console.log(`Prometheus metrics server started on port ${port}`) + } + + createExpressMiddleware: typeof createMiddleware = (options) => createMiddleware(options) + setExpressMiddlewareUp(): void { signalIsUp() } +} diff --git a/yarn.lock b/yarn.lock index 7336d34..b7a6740 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1184,7 +1184,7 @@ "@babel/register@^7.12.1": version "7.12.1" - resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.12.1.tgz#cdb087bdfc4f7241c03231f22e15d211acf21438" + resolved "https://testnet.thegraph.com/npm-registry/@babel%2fregister/-/register-7.12.1.tgz#cdb087bdfc4f7241c03231f22e15d211acf21438" integrity sha512-XWcmseMIncOjoydKZnWvWi0/5CUCD+ZYKhRwgYlWOrA8fGZ/FjuLRpqtIhLOVD/fvR1b9DQHtZPn68VvhpYf+Q== dependencies: find-cache-dir "^2.0.0" @@ -2563,16 +2563,16 @@ dependencies: "@types/node" ">= 8" -"@promster/express@^4.0.0": - version "4.1.14" - resolved "https://registry.yarnpkg.com/@promster/express/-/express-4.1.14.tgz#12e290bae55b96fc7e124510b0572f64c6831327" - integrity sha512-drjKQpYUEEmWWjIY9nlrdsCmsDGDzoJS6k4g6D8IOf3TlKoCLRdz+RGXwHsXdc/BPzUrferxAhw13kY9rnwClQ== +"@promster/express@^4.1.15": + version "4.1.15" + resolved "https://testnet.thegraph.com/npm-registry/@promster%2fexpress/-/express-4.1.15.tgz#80c66301a855c95cd3edcd573411e66d9046dfb2" + integrity sha512-4+pO7vmAcVFs8unfidMtc4GZ/1FZs+GtCXE/q6yzd046E5Y5ok+QF7cYu35mlQjti9loBTk/4LvkoNay/V8PpQ== dependencies: "@promster/metrics" "^4.1.13" merge-options "3.0.3" tslib "2.0.3" -"@promster/metrics@^4.1.10", "@promster/metrics@^4.1.13": +"@promster/metrics@^4.1.13": version "4.1.13" resolved "https://registry.yarnpkg.com/@promster/metrics/-/metrics-4.1.13.tgz#cc6b6533bc7d09fdac2b856fd0c77e440cb1151c" integrity sha512-qOUKD4ygMef5q1Mx9kvOIGak1hgTnZB2FlR+wW0K5Mgp+MDVh4aaUBIEAsNuPaFY8/MqM6pRrHgszmV0V6qKwg== @@ -2588,7 +2588,7 @@ optionalDependencies: gc-stats "1.4.0" -"@promster/server@^4.0.0", "@promster/server@^4.2.12": +"@promster/server@^4.2.16": version "4.2.16" resolved "https://registry.yarnpkg.com/@promster/server/-/server-4.2.16.tgz#cddad63f4d84e74b8c32bf113a6eb4d0d8c26857" integrity sha512-dzuo3mvyKlHz79eXzbRsZ94aSEmEpn9WAeDP2Z988UbE+D7Q0kTIEbw2VCmEhNwFRh3nJ3kuX9A4d3GQjKAUHw== @@ -2596,6 +2596,11 @@ "@promster/metrics" "^4.1.13" tslib "2.0.3" +"@promster/types@^1.0.8": + version "1.0.8" + resolved "https://testnet.thegraph.com/npm-registry/@promster%2ftypes/-/types-1.0.8.tgz#5742851799b0f52161ad33486411635e9770b130" + integrity sha512-LUzbNGSbi+Pm+qVv/47x9qGzrge7VxkzymZLdqHmo/g0U+bQ2qttJKpZqiSASbychdWGKgwe3KXbXE4Ii8OeeA== + "@sindresorhus/is@^0.14.0": version "0.14.0" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" @@ -2930,11 +2935,26 @@ dependencies: "@types/node" "*" +"@types/body-parser@*": + version "1.19.0" + resolved "https://testnet.thegraph.com/npm-registry/@types%2fbody-parser/-/body-parser-1.19.0.tgz#0685b3c47eb3006ffed117cdd55164b61f80538f" + integrity sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ== + dependencies: + "@types/connect" "*" + "@types/node" "*" + "@types/chai@4": version "4.2.14" resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.2.14.tgz#44d2dd0b5de6185089375d976b4ec5caf6861193" integrity sha512-G+ITQPXkwTrslfG5L/BksmbLUA0M1iybEsmCWPqzSxsRRhJZimBKJkoMi8fr/CPygPTj4zO5pJH7I2/cm9M7SQ== +"@types/connect@*": + version "3.4.33" + resolved "https://testnet.thegraph.com/npm-registry/@types%2fconnect/-/connect-3.4.33.tgz#31610c901eca573b8713c3330abc6e6b9f588546" + integrity sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A== + dependencies: + "@types/node" "*" + "@types/cookiejar@*": version "2.1.2" resolved "https://registry.yarnpkg.com/@types/cookiejar/-/cookiejar-2.1.2.tgz#66ad9331f63fe8a3d3d9d8c6e3906dd10f6446e8" @@ -2945,6 +2965,25 @@ resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d" integrity sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag== +"@types/express-serve-static-core@*": + version "4.17.13" + resolved "https://testnet.thegraph.com/npm-registry/@types%2fexpress-serve-static-core/-/express-serve-static-core-4.17.13.tgz#d9af025e925fc8b089be37423b8d1eac781be084" + integrity sha512-RgDi5a4nuzam073lRGKTUIaL3eF2+H7LJvJ8eUnCI0wA6SNjXc44DCmWNiTLs/AZ7QlsFWZiw/gTG3nSQGL0fA== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + +"@types/express@^4.17.8": + version "4.17.8" + resolved "https://testnet.thegraph.com/npm-registry/@types%2fexpress/-/express-4.17.8.tgz#3df4293293317e61c60137d273a2e96cd8d5f27a" + integrity sha512-wLhcKh3PMlyA2cNAB9sjM1BntnhPMiM0JOBwPBqttjHev2428MLEB4AYVN+d8s2iyCVZac+o41Pflm/ZH5vLXQ== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "*" + "@types/qs" "*" + "@types/serve-static" "*" + "@types/glob@^7.1.1": version "7.1.3" resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.3.tgz#e6ba80f36b7daad2c685acd9266382e68985c183" @@ -2990,6 +3029,11 @@ resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.162.tgz#65d78c397e0d883f44afbf1f7ba9867022411470" integrity sha512-alvcho1kRUnnD1Gcl4J+hK0eencvzq9rmzvFPRmP5rPHx9VVsJj6bKLTATPVf9ktgv4ujzh7T+XWKp+jhuODig== +"@types/mime@*": + version "2.0.3" + resolved "https://testnet.thegraph.com/npm-registry/@types%2fmime/-/mime-2.0.3.tgz#c893b73721db73699943bfc3653b1deb7faa4a3a" + integrity sha512-Jus9s4CDbqwocc5pOAnh8ShfrnMcPHuJYzVcSUU7lrh8Ni5HuIqX3oilL86p3dlTrk0LzHRCgA/GQ7uNCw6l2Q== + "@types/minimatch@*": version "3.0.3" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" @@ -3037,6 +3081,16 @@ resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.4.tgz#15925414e0ad2cd765bfef58842f7e26a7accb24" integrity sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug== +"@types/qs@*": + version "6.9.5" + resolved "https://testnet.thegraph.com/npm-registry/@types%2fqs/-/qs-6.9.5.tgz#434711bdd49eb5ee69d90c1d67c354a9a8ecb18b" + integrity sha512-/JHkVHtx/REVG0VVToGRGH2+23hsYLHdyG+GrvoUGlGAd0ErauXDyvHtRI/7H7mzLm+tBCKA7pfcpkQ1lf58iQ== + +"@types/range-parser@*": + version "1.2.3" + resolved "https://testnet.thegraph.com/npm-registry/@types%2frange-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c" + integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA== + "@types/secp256k1@^4.0.1": version "4.0.1" resolved "https://registry.yarnpkg.com/@types/secp256k1/-/secp256k1-4.0.1.tgz#fb3aa61a1848ad97d7425ff9dcba784549fca5a4" @@ -3044,6 +3098,14 @@ dependencies: "@types/node" "*" +"@types/serve-static@*": + version "1.13.6" + resolved "https://testnet.thegraph.com/npm-registry/@types%2fserve-static/-/serve-static-1.13.6.tgz#866b1b8dec41c36e28c7be40ac725b88be43c5c1" + integrity sha512-nuRJmv7jW7VmCVTn+IgYDkkbbDGyIINOeu/G0d74X3lm6E5KfMeQPJhxIt1ayQeQB3cSxvYs1RA/wipYoFB4EA== + dependencies: + "@types/mime" "*" + "@types/node" "*" + "@types/stack-utils@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e" @@ -3104,6 +3166,19 @@ semver "^7.3.2" tsutils "^3.17.1" +"@typescript-eslint/eslint-plugin@^4.7.0": + version "4.7.0" + resolved "https://testnet.thegraph.com/npm-registry/@typescript-eslint%2feslint-plugin/-/eslint-plugin-4.7.0.tgz#85c9bbda00c0cb604d3c241f7bc7fb171a2d3479" + integrity sha512-li9aiSVBBd7kU5VlQlT1AqP0uWGDK6JYKUQ9cVDnOg34VNnd9t4jr0Yqc/bKxJr/tDCPDaB4KzoSFN9fgVxe/Q== + dependencies: + "@typescript-eslint/experimental-utils" "4.7.0" + "@typescript-eslint/scope-manager" "4.7.0" + debug "^4.1.1" + functional-red-black-tree "^1.0.1" + regexpp "^3.0.0" + semver "^7.3.2" + tsutils "^3.17.1" + "@typescript-eslint/experimental-utils@2.34.0": version "2.34.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.34.0.tgz#d3524b644cdb40eebceca67f8cf3e4cc9c8f980f" @@ -3126,6 +3201,18 @@ eslint-scope "^5.0.0" eslint-utils "^2.0.0" +"@typescript-eslint/experimental-utils@4.7.0": + version "4.7.0" + resolved "https://testnet.thegraph.com/npm-registry/@typescript-eslint%2fexperimental-utils/-/experimental-utils-4.7.0.tgz#8d1058c38bec3d3bbd9c898a1c32318d80faf3c5" + integrity sha512-cymzovXAiD4EF+YoHAB5Oh02MpnXjvyaOb+v+BdpY7lsJXZQN34oIETeUwVT2XfV9rSNpXaIcknDLfupO/tUoA== + dependencies: + "@types/json-schema" "^7.0.3" + "@typescript-eslint/scope-manager" "4.7.0" + "@typescript-eslint/types" "4.7.0" + "@typescript-eslint/typescript-estree" "4.7.0" + eslint-scope "^5.0.0" + eslint-utils "^2.0.0" + "@typescript-eslint/parser@^2.10.0": version "2.34.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.34.0.tgz#50252630ca319685420e9a39ca05fe185a256bc8" @@ -3146,6 +3233,16 @@ "@typescript-eslint/typescript-estree" "4.6.1" debug "^4.1.1" +"@typescript-eslint/parser@^4.7.0": + version "4.7.0" + resolved "https://testnet.thegraph.com/npm-registry/@typescript-eslint%2fparser/-/parser-4.7.0.tgz#44bdab0f788b478178368baa65d3365fdc63da1c" + integrity sha512-+meGV8bMP1sJHBI2AFq1GeTwofcGiur8LoIr6v+rEmD9knyCqDlrQcFHR0KDDfldHIFDU/enZ53fla6ReF4wRw== + dependencies: + "@typescript-eslint/scope-manager" "4.7.0" + "@typescript-eslint/types" "4.7.0" + "@typescript-eslint/typescript-estree" "4.7.0" + debug "^4.1.1" + "@typescript-eslint/scope-manager@4.6.1": version "4.6.1" resolved "https://testnet.thegraph.com/npm-registry/@typescript-eslint%2fscope-manager/-/scope-manager-4.6.1.tgz#21872b91cbf7adfc7083f17b8041149148baf992" @@ -3154,11 +3251,24 @@ "@typescript-eslint/types" "4.6.1" "@typescript-eslint/visitor-keys" "4.6.1" +"@typescript-eslint/scope-manager@4.7.0": + version "4.7.0" + resolved "https://testnet.thegraph.com/npm-registry/@typescript-eslint%2fscope-manager/-/scope-manager-4.7.0.tgz#2115526085fb72723ccdc1eeae75dec7126220ed" + integrity sha512-ILITvqwDJYbcDCROj6+Ob0oCKNg3SH46iWcNcTIT9B5aiVssoTYkhKjxOMNzR1F7WSJkik4zmuqve5MdnA0DyA== + dependencies: + "@typescript-eslint/types" "4.7.0" + "@typescript-eslint/visitor-keys" "4.7.0" + "@typescript-eslint/types@4.6.1": version "4.6.1" resolved "https://testnet.thegraph.com/npm-registry/@typescript-eslint%2ftypes/-/types-4.6.1.tgz#d3ad7478f53f22e7339dc006ab61aac131231552" integrity sha512-k2ZCHhJ96YZyPIsykickez+OMHkz06xppVLfJ+DY90i532/Cx2Z+HiRMH8YZQo7a4zVd/TwNBuRCdXlGK4yo8w== +"@typescript-eslint/types@4.7.0": + version "4.7.0" + resolved "https://testnet.thegraph.com/npm-registry/@typescript-eslint%2ftypes/-/types-4.7.0.tgz#5e95ef5c740f43d942542b35811f87b62fccca69" + integrity sha512-uLszFe0wExJc+I7q0Z/+BnP7wao/kzX0hB5vJn4LIgrfrMLgnB2UXoReV19lkJQS1a1mHWGGODSxnBx6JQC3Sg== + "@typescript-eslint/typescript-estree@2.34.0": version "2.34.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.34.0.tgz#14aeb6353b39ef0732cc7f1b8285294937cf37d5" @@ -3186,6 +3296,20 @@ semver "^7.3.2" tsutils "^3.17.1" +"@typescript-eslint/typescript-estree@4.7.0": + version "4.7.0" + resolved "https://testnet.thegraph.com/npm-registry/@typescript-eslint%2ftypescript-estree/-/typescript-estree-4.7.0.tgz#539531167f05ba20eb0b6785567076679e29d393" + integrity sha512-5XZRQznD1MfUmxu1t8/j2Af4OxbA7EFU2rbo0No7meb46eHgGkSieFdfV6omiC/DGIBhH9H9gXn7okBbVOm8jw== + dependencies: + "@typescript-eslint/types" "4.7.0" + "@typescript-eslint/visitor-keys" "4.7.0" + debug "^4.1.1" + globby "^11.0.1" + is-glob "^4.0.1" + lodash "^4.17.15" + semver "^7.3.2" + tsutils "^3.17.1" + "@typescript-eslint/visitor-keys@4.6.1": version "4.6.1" resolved "https://testnet.thegraph.com/npm-registry/@typescript-eslint%2fvisitor-keys/-/visitor-keys-4.6.1.tgz#6b125883402d8939df7b54528d879e88f7ba3614" @@ -3194,6 +3318,14 @@ "@typescript-eslint/types" "4.6.1" eslint-visitor-keys "^2.0.0" +"@typescript-eslint/visitor-keys@4.7.0": + version "4.7.0" + resolved "https://testnet.thegraph.com/npm-registry/@typescript-eslint%2fvisitor-keys/-/visitor-keys-4.7.0.tgz#6783824f22acfc49e754970ed21b88ac03b80e6f" + integrity sha512-aDJDWuCRsf1lXOtignlfiPODkzSxxop7D0rZ91L6ZuMlcMCSh0YyK+gAfo5zN/ih6WxMwhoXgJWC3cWQdaKC+A== + dependencies: + "@typescript-eslint/types" "4.7.0" + eslint-visitor-keys "^2.0.0" + "@webassemblyjs/ast@1.8.5": version "1.8.5" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.8.5.tgz#51b1c5fe6576a34953bf4b253df9f0d490d9e359" @@ -7007,6 +7139,49 @@ eslint@^7.12.1: text-table "^0.2.0" v8-compile-cache "^2.0.3" +eslint@^7.13.0: + version "7.13.0" + resolved "https://testnet.thegraph.com/npm-registry/eslint/-/eslint-7.13.0.tgz#7f180126c0dcdef327bfb54b211d7802decc08da" + integrity sha512-uCORMuOO8tUzJmsdRtrvcGq5qposf7Rw0LwkTJkoDbOycVQtQjmnhZSuLQnozLE4TmAzlMVV45eCHmQ1OpDKUQ== + dependencies: + "@babel/code-frame" "^7.0.0" + "@eslint/eslintrc" "^0.2.1" + ajv "^6.10.0" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.0.1" + doctrine "^3.0.0" + enquirer "^2.3.5" + eslint-scope "^5.1.1" + eslint-utils "^2.1.0" + eslint-visitor-keys "^2.0.0" + espree "^7.3.0" + esquery "^1.2.0" + esutils "^2.0.2" + file-entry-cache "^5.0.1" + functional-red-black-tree "^1.0.1" + glob-parent "^5.0.0" + globals "^12.1.0" + ignore "^4.0.6" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + js-yaml "^3.13.1" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash "^4.17.19" + minimatch "^3.0.4" + natural-compare "^1.4.0" + optionator "^0.9.1" + progress "^2.0.0" + regexpp "^3.1.0" + semver "^7.2.1" + strip-ansi "^6.0.0" + strip-json-comments "^3.1.0" + table "^5.2.3" + text-table "^0.2.0" + v8-compile-cache "^2.0.3" + esm@^3.2.25: version "3.2.25" resolved "https://testnet.thegraph.com/npm-registry/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10" @@ -13933,16 +14108,9 @@ progress@^2.0.0: resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== -prom-client@^11.5.3: - version "11.5.3" - resolved "https://registry.yarnpkg.com/prom-client/-/prom-client-11.5.3.tgz#5fedfce1083bac6c2b223738e966d0e1643756f8" - integrity sha512-iz22FmTbtkyL2vt0MdDFY+kWof+S9UB/NACxSn2aJcewtw+EERsen0urSkZ2WrHseNdydsvcxCTAnPcSMZZv4Q== - dependencies: - tdigest "^0.1.1" - prom-client@^12.0.0: version "12.0.0" - resolved "https://registry.yarnpkg.com/prom-client/-/prom-client-12.0.0.tgz#9689379b19bd3f6ab88a9866124db9da3d76c6ed" + resolved "https://testnet.thegraph.com/npm-registry/prom-client/-/prom-client-12.0.0.tgz#9689379b19bd3f6ab88a9866124db9da3d76c6ed" integrity sha512-JbzzHnw0VDwCvoqf8y1WDtq4wSBAbthMB1pcVI/0lzdqHGJI3KBJDXle70XK+c7Iv93Gihqo0a5LlOn+g8+DrQ== dependencies: tdigest "^0.1.1"