From 97097bda3cab740a3804a083b5a310063ab292ae Mon Sep 17 00:00:00 2001 From: Cafe137 Date: Mon, 7 Nov 2022 14:08:10 +0100 Subject: [PATCH 1/3] feat: parse and deploy config.yaml --- package-lock.json | 204 ++++++++++-------- package.json | 2 + src/index.ts | 8 +- src/settings/parser/big-number-parser.ts | 32 +++ src/settings/parser/bool-parser.ts | 17 ++ src/settings/parser/enum-parser.ts | 11 + src/settings/parser/optional-string-parser.ts | 5 + src/settings/parser/parsing-error.ts | 11 + src/settings/parser/percentage-parser.ts | 17 ++ src/settings/parser/required-string-parser.ts | 11 + src/settings/parser/temporal-parser.ts | 36 ++++ src/settings/sample-settings.ts | 111 ++++++++++ src/settings/settings-factory.ts | 68 ++++++ src/settings/settings-service.ts | 31 +++ src/settings/settings.ts | 42 ++++ .../settings/parser/big-number-parser.spec.ts | 24 +++ test/settings/parser/bool-parser.spec.ts | 11 + test/settings/parser/enum-parser.spec.ts | 11 + .../parser/optional-string-parser.spec.ts | 6 + .../settings/parser/percentage-parser.spec.ts | 18 ++ .../parser/required-string-parser.spec.ts | 11 + test/settings/parser/temporal-parser.spec.ts | 19 ++ test/settings/settings-service.spec.ts | 15 ++ 23 files changed, 625 insertions(+), 96 deletions(-) create mode 100644 src/settings/parser/big-number-parser.ts create mode 100644 src/settings/parser/bool-parser.ts create mode 100644 src/settings/parser/enum-parser.ts create mode 100644 src/settings/parser/optional-string-parser.ts create mode 100644 src/settings/parser/parsing-error.ts create mode 100644 src/settings/parser/percentage-parser.ts create mode 100644 src/settings/parser/required-string-parser.ts create mode 100644 src/settings/parser/temporal-parser.ts create mode 100644 src/settings/sample-settings.ts create mode 100644 src/settings/settings-factory.ts create mode 100644 src/settings/settings-service.ts create mode 100644 src/settings/settings.ts create mode 100644 test/settings/parser/big-number-parser.spec.ts create mode 100644 test/settings/parser/bool-parser.spec.ts create mode 100644 test/settings/parser/enum-parser.spec.ts create mode 100644 test/settings/parser/optional-string-parser.spec.ts create mode 100644 test/settings/parser/percentage-parser.spec.ts create mode 100644 test/settings/parser/required-string-parser.spec.ts create mode 100644 test/settings/parser/temporal-parser.spec.ts create mode 100644 test/settings/settings-service.spec.ts diff --git a/package-lock.json b/package-lock.json index 4b32050a..d073dd0d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@ethersphere/swarm-cid": "^0.1.0", "express": "^4.18.1", "http-proxy-middleware": "^2.0.6", + "js-yaml": "^4.1.0", "prom-client": "^14.1.0", "request-stats": "^3.0.0", "winston": "^3.8.2" @@ -26,6 +27,7 @@ "@jest/types": "^29.0.3", "@types/express": "^4.17.14", "@types/jest": "^27.5.2", + "@types/js-yaml": "^4.0.5", "@types/node": "^18.7.23", "@types/request-stats": "^3.0.0", "@types/supertest": "^2.0.12", @@ -48,7 +50,7 @@ "typescript": "^4.8.3" }, "engines": { - "bee": "1.6.0-6ceadd35", + "bee": "1.7.0-bbf13011", "node": ">=12.0.0", "npm": ">=6.0.0" } @@ -988,12 +990,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@eslint/eslintrc/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, "node_modules/@eslint/eslintrc/node_modules/globals": { "version": "13.17.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", @@ -1009,18 +1005,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@eslint/eslintrc/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -1277,6 +1261,15 @@ "node": ">=8" } }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, "node_modules/@istanbuljs/load-nyc-config/node_modules/camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", @@ -1299,6 +1292,19 @@ "node": ">=8" } }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -2080,6 +2086,12 @@ "pretty-format": "^27.0.0" } }, + "node_modules/@types/js-yaml": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.5.tgz", + "integrity": "sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==", + "dev": true + }, "node_modules/@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", @@ -2634,13 +2646,9 @@ "dev": true }, "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, "node_modules/array-differ": { "version": "3.0.0", @@ -3711,6 +3719,28 @@ "node": ">=10" } }, + "node_modules/depcheck/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/depcheck/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/depcheck/node_modules/yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", @@ -4265,12 +4295,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/eslint/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, "node_modules/eslint/node_modules/eslint-scope": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", @@ -4308,18 +4332,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/eslint/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -6771,13 +6783,11 @@ "dev": true }, "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" @@ -10635,12 +10645,6 @@ "uri-js": "^4.2.2" } }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, "globals": { "version": "13.17.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", @@ -10650,15 +10654,6 @@ "type-fest": "^0.20.2" } }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -10851,6 +10846,15 @@ "resolve-from": "^5.0.0" }, "dependencies": { + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, "camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", @@ -10867,6 +10871,16 @@ "path-exists": "^4.0.0" } }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, "locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -11554,6 +11568,12 @@ "pretty-format": "^27.0.0" } }, + "@types/js-yaml": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.5.tgz", + "integrity": "sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==", + "dev": true + }, "@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", @@ -11973,13 +11993,9 @@ "dev": true }, "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, "array-differ": { "version": "3.0.0", @@ -12812,6 +12828,25 @@ "yargs": "^16.1.0" }, "dependencies": { + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, "yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", @@ -13141,12 +13176,6 @@ "uri-js": "^4.2.2" } }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, "eslint-scope": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", @@ -13172,15 +13201,6 @@ "type-fest": "^0.20.2" } }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -15215,13 +15235,11 @@ "dev": true }, "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "^2.0.1" } }, "jsdom": { diff --git a/package.json b/package.json index ef38c20f..b41387ec 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "@jest/types": "^29.0.3", "@types/express": "^4.17.14", "@types/jest": "^27.5.2", + "@types/js-yaml": "^4.0.5", "@types/node": "^18.7.23", "@types/request-stats": "^3.0.0", "@types/supertest": "^2.0.12", @@ -65,6 +66,7 @@ "@ethersphere/swarm-cid": "^0.1.0", "express": "^4.18.1", "http-proxy-middleware": "^2.0.6", + "js-yaml": "^4.1.0", "prom-client": "^14.1.0", "request-stats": "^3.0.0", "winston": "^3.8.2" diff --git a/src/index.ts b/src/index.ts index 02137b08..045b52f5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,13 +1,15 @@ #!/usr/bin/env node import { Application } from 'express' +import { EnvironmentVariables, getAppConfig, getContentConfig, getServerConfig, getStampsConfig } from './config' +import { ContentManager } from './content' +import { logger, subscribeLogServerRequests } from './logger' import { createApp } from './server' +import { createOrGetSettings } from './settings/settings-service' import { StampsManager } from './stamps' -import { getAppConfig, getServerConfig, getStampsConfig, EnvironmentVariables, getContentConfig } from './config' -import { logger, subscribeLogServerRequests } from './logger' -import { ContentManager } from './content' async function main() { + createOrGetSettings('config.yaml') // Configuration const stampsConfig = getStampsConfig(process.env as EnvironmentVariables) const contentConfig = getContentConfig(process.env as EnvironmentVariables) diff --git a/src/settings/parser/big-number-parser.ts b/src/settings/parser/big-number-parser.ts new file mode 100644 index 00000000..620e790d --- /dev/null +++ b/src/settings/parser/big-number-parser.ts @@ -0,0 +1,32 @@ +import { OutOfBoundsError, ParsingError } from './parsing-error' + +const multiplierMap: Record = { + identity: 1, + k: 1000, + m: 1000 * 1000, + b: 1000 * 1000 * 1000, + t: 1000 * 1000 * 1000 * 1000, +} + +export const bigNumberParser = { + parse(value: string, field: string): number { + const rawNumber = value.match(/\-?[0-9]+(\.[0-9]+)?/)?.[0] + const rawUnit = value.match(/[a-zA-Z]+/)?.[0] + const number = Number(rawNumber) + const unit = rawUnit ? rawUnit.trim().charAt(0).toLowerCase() : 'identity' + + if (isNaN(number)) { + throw new ParsingError(field, value, 'number', '20m') + } + + if (number < 0) { + throw new OutOfBoundsError(field, value, `Number must be positive.`) + } + + if (!multiplierMap[unit]) { + throw new ParsingError(field, value, 'number', '20m') + } + + return number * multiplierMap[unit] + }, +} diff --git a/src/settings/parser/bool-parser.ts b/src/settings/parser/bool-parser.ts new file mode 100644 index 00000000..078d37b9 --- /dev/null +++ b/src/settings/parser/bool-parser.ts @@ -0,0 +1,17 @@ +import { ParsingError } from './parsing-error' + +export const boolParser = { + parse(value: string, field: string): boolean { + const parsed = value.toLowerCase() + + if (parsed === 'true') { + return true + } + + if (parsed === 'false') { + return false + } + + throw new ParsingError(field, value, 'boolean', 'true or false') + }, +} diff --git a/src/settings/parser/enum-parser.ts b/src/settings/parser/enum-parser.ts new file mode 100644 index 00000000..5b5f83dd --- /dev/null +++ b/src/settings/parser/enum-parser.ts @@ -0,0 +1,11 @@ +import { ParsingError } from './parsing-error' + +export const enumParser = { + parse(value: string, field: string, options: T[] & string[]): T { + if (!options.includes(value.toLowerCase())) { + throw new ParsingError(field, value, 'enum', options.join(', ')) + } + + return value.toLowerCase() as T + }, +} diff --git a/src/settings/parser/optional-string-parser.ts b/src/settings/parser/optional-string-parser.ts new file mode 100644 index 00000000..4a333c7e --- /dev/null +++ b/src/settings/parser/optional-string-parser.ts @@ -0,0 +1,5 @@ +export const optionalStringParser = { + parse(value: string, _: string): string { + return value + }, +} diff --git a/src/settings/parser/parsing-error.ts b/src/settings/parser/parsing-error.ts new file mode 100644 index 00000000..ef48c8df --- /dev/null +++ b/src/settings/parser/parsing-error.ts @@ -0,0 +1,11 @@ +export class ParsingError extends Error { + constructor(field: string, badValue: string, label: string, example: string) { + super(`Could not parse ${field} as ${label}, got ${badValue || 'nothing'}. Example: ${example}`) + } +} + +export class OutOfBoundsError extends Error { + constructor(field: string, badValue: string, reason: string) { + super(`Value ${badValue || 'nothing'} for ${field} is not acceptable. ${reason}`) + } +} diff --git a/src/settings/parser/percentage-parser.ts b/src/settings/parser/percentage-parser.ts new file mode 100644 index 00000000..2bada0e2 --- /dev/null +++ b/src/settings/parser/percentage-parser.ts @@ -0,0 +1,17 @@ +import { OutOfBoundsError, ParsingError } from './parsing-error' + +export const percentageParser = { + parse(value: string, field: string): number { + const parsed = parseFloat(value) + + if (isNaN(parsed)) { + throw new ParsingError(field, value, 'percentage', '85%') + } + + if (parsed < 0 || parsed > 100) { + throw new OutOfBoundsError(field, value, `Percentage must be between 0 and 100.`) + } + + return parsed * 0.01 + }, +} diff --git a/src/settings/parser/required-string-parser.ts b/src/settings/parser/required-string-parser.ts new file mode 100644 index 00000000..9b974f87 --- /dev/null +++ b/src/settings/parser/required-string-parser.ts @@ -0,0 +1,11 @@ +import { ParsingError } from './parsing-error' + +export const requiredStringParser = { + parse(value: string, field: string): string { + if (!value) { + throw new ParsingError(field, value, 'string', 'test') + } + + return value + }, +} diff --git a/src/settings/parser/temporal-parser.ts b/src/settings/parser/temporal-parser.ts new file mode 100644 index 00000000..4484dd19 --- /dev/null +++ b/src/settings/parser/temporal-parser.ts @@ -0,0 +1,36 @@ +import { OutOfBoundsError, ParsingError } from './parsing-error' + +const multiplierMap: Record = { + s: 1000, + m: 60 * 1000, + h: 60 * 60 * 1000, + d: 24 * 60 * 60 * 1000, +} + +export const temporalParser = { + parse(value: string, field: string): number { + const rawNumber = value.match(/[0-9]+/)?.[0] + const rawUnit = value.match(/[a-zA-Z]+/)?.[0] + + if (!rawUnit) { + throw new OutOfBoundsError(field, value, "Unit must be 's', 'm', 'h' or 'd'.") + } + + const number = Number(rawNumber) + const unit = rawUnit.trim().charAt(0).toLowerCase() + + if (isNaN(number)) { + throw new ParsingError(field, value, 'time', '20s') + } + + if (number < 0) { + throw new OutOfBoundsError(field, value, `Time must be positive.`) + } + + if (!multiplierMap[unit]) { + throw new ParsingError(field, value, 'time', '20s') + } + + return number * multiplierMap[unit] + }, +} diff --git a/src/settings/sample-settings.ts b/src/settings/sample-settings.ts new file mode 100644 index 00000000..b154095f --- /dev/null +++ b/src/settings/sample-settings.ts @@ -0,0 +1,111 @@ +export const SAMPLE_SETTINGS_YAML = `# [Connection to the Bee node] + +bee: + api: http://localhost:1633 + debugApi: http://localhost:1635 + +# [Server settings] + +server: + port: 3000 + # Required for the bzz.link feature. + # TODO: #1 Explain bzz.link feature + # TODO: #2 Do we really need it? Can't we just rely on subdomains? + hostname: localhost + # Require authentication header for all requests. + # This effectively makes the gateway private. + # + # May be left empty to disable authentication. + authSecret: '' + # Possible values: debug | info | warn | error + logLevel: info + +# [Stamp management] + +# Periodically checks for valid stamps. +# +# A stamp is considered valid, when it has enough TTL, capacity +# and its amount and depth parameters match with this config. + +stamp: + # Run periodical automatic postage stamp management job this often. + checkFrequency: 60s + + # Possible values: hardcoded | autobuy | autoextend + # May be left empty to disable postage stamp management. + mode: '' + + # [Hardcoded mode settings] + + hardcoded: + # Always use this batch ID + batchId: '' + + # [Autobuy mode settings] + + autobuy: + # If there are no valid stamps, purchase one with the following amount and depth + amount: 20k + depth: 22 + # Trigger purchase when TTL is below this. + ttlThreshold: 15m + # Trigger purchase when usage is above this. + usageThreshold: 70% + + # [Autoextend mode settings] + + autoextend: + # If there are no stamps to extend, purchase one with the following amount and depth + defaultAmount: 20k + defaultDepth: 22 + # Enable diluting the postage stamp to increase its depth and therefor capacity + extendCapacity: true + # Enable topping up the postage stamp to increase its amount and therefor TTL + extendTtl: true + # Extend stamp by this amount + extendAmount: 20k + # Trigger topup when TTL is below this. + ttlThreshold: 15m + # Trigger dilution when usage is above this. + usageThreshold: 70% + +# [Content reupload] + +# Periodically checks reachability of locally pinned content. +# +# Reuploads when cannot be reached over the network +# Uses the Bee stewardship API. +# +# May be used to ensure self-hosted content stays online. +# +# TODO: "self-hosted" is probably a wrong term here +contentReupload: + enabled: false + reuploadFrequency: 6h + +# [Miscellaneous features] + +# Enables resolving CIDs in place of Swarm hashes. +# +# e.g. http://bah5acgzaxtfhomziqdigaikb3cfnotehagiuzyprlr6w7uo7lhl3f6ngyq7a.localhost:3000 +# will convert the CID to Swarm hash +cid: true + +# Enables resolving ENS domains in place of Swarm hashes. +# +# e.g. http://swarm.localhost:3000 will find bzz:// content at swarm.eth +ens: true + +# Adds 'x-bee-node' HTTP header to all responses, which +# exposes the hash of the Bee node overlay address. +# +# Useful for identifying individual Bee nodes in a cluster +# without exposing identity. +exposeHashedIdentity: false + +# Removes swarm-pin HTTP header from requests, in order +# to avoid filling up disk space by public requests. +# +# Useful when running as a public gateway. +removePinHeader: true +` diff --git a/src/settings/settings-factory.ts b/src/settings/settings-factory.ts new file mode 100644 index 00000000..c9106ebe --- /dev/null +++ b/src/settings/settings-factory.ts @@ -0,0 +1,68 @@ +import { bigNumberParser } from './parser/big-number-parser' +import { boolParser } from './parser/bool-parser' +import { enumParser } from './parser/enum-parser' +import { optionalStringParser } from './parser/optional-string-parser' +import { percentageParser } from './parser/percentage-parser' +import { requiredStringParser } from './parser/required-string-parser' +import { temporalParser } from './parser/temporal-parser' +import { Settings } from './settings' + +export function makeSettings(settings: Record): Settings { + return { + bee: { + api: requiredStringParser.parse(settings?.bee?.api, 'bee.api'), + debugApi: requiredStringParser.parse(settings?.bee?.debugApi, 'bee.debugApi'), + }, + server: { + port: bigNumberParser.parse(settings?.server?.port, 'server.port'), + hostname: optionalStringParser.parse(settings?.server?.hostname, 'server.hostname'), + authSecret: optionalStringParser.parse(settings?.server?.authSecret, 'server.authSecret'), + logLevel: enumParser.parse(settings?.server?.logLevel, 'server.logLevel', ['debug', 'info', 'warn', 'error']), + }, + stamp: { + checkFrequency: temporalParser.parse(settings?.stamp?.checkFrequency, 'stamp.checkFrequency'), + mode: enumParser.parse(settings?.stamp?.mode, 'stamp.mode', ['', 'hardcoded', 'autobuy', 'autoextend']), + hardcoded: { + batchId: optionalStringParser.parse(settings?.stamp?.hardcoded?.batchId, 'stamp.hardcoded.batchId'), + }, + autobuy: { + amount: bigNumberParser.parse(settings?.stamp?.autobuy?.amount, 'stamp.autobuy.amount'), + depth: bigNumberParser.parse(settings?.stamp?.autobuy?.depth, 'stamp.autobuy.depth'), + ttlThreshold: temporalParser.parse(settings?.stamp?.autobuy?.ttlThreshold, 'stamp.autobuy.ttlThreshold'), + usageThreshold: percentageParser.parse( + settings?.stamp?.autobuy?.usageThreshold, + 'stamp.autobuy.usageThreshold', + ), + }, + autoextend: { + defaultAmount: bigNumberParser.parse( + settings?.stamp?.autoextend?.defaultAmount, + 'stamp.autoextend.defaultAmount', + ), + defaultDepth: bigNumberParser.parse(settings?.stamp?.autoextend?.defaultDepth, 'stamp.autoextend.defaultDepth'), + extendCapacity: boolParser.parse( + settings?.stamp?.autoextend?.extendCapacity, + 'stamp.autoextend.extendCapacity', + ), + extendTtl: boolParser.parse(settings?.stamp?.autoextend?.extendTtl, 'stamp.autoextend.extendTtl'), + extendAmount: bigNumberParser.parse(settings?.stamp?.autoextend?.extendAmount, 'stamp.autoextend.extendAmount'), + ttlThreshold: temporalParser.parse(settings?.stamp?.autoextend?.ttlThreshold, 'stamp.autoextend.ttlThreshold'), + usageThreshold: percentageParser.parse( + settings?.stamp?.autoextend?.usageThreshold, + 'stamp.autoextend.usageThreshold', + ), + }, + }, + contentReupload: { + enabled: boolParser.parse(settings?.contentReupload?.enabled, 'contentReupload.enabled'), + reuploadFrequency: temporalParser.parse( + settings?.contentReupload?.reuploadFrequency, + 'contentReupload.reuploadFrequency', + ), + }, + cid: boolParser.parse(settings?.cid, 'cid'), + ens: boolParser.parse(settings?.ens, 'ens'), + exposeHashedIdentity: boolParser.parse(settings?.exposeHashedIdentity, 'exposeHashedIdentity'), + removePinHeader: boolParser.parse(settings?.removePinHeader, 'removePinHeader'), + } +} diff --git a/src/settings/settings-service.ts b/src/settings/settings-service.ts new file mode 100644 index 00000000..e9085ea6 --- /dev/null +++ b/src/settings/settings-service.ts @@ -0,0 +1,31 @@ +import fs from 'fs' +import { FAILSAFE_SCHEMA, load } from 'js-yaml' +import { SAMPLE_SETTINGS_YAML } from './sample-settings' +import { Settings } from './settings' +import { makeSettings } from './settings-factory' + +export function createOrGetSettings(path: string): Settings { + if (!checkYamlPath(path)) { + deploySampleSettings(path) + } + + return makeSettings(loadYamlFromFile(path)) +} + +export function deploySampleSettings(path: string) { + fs.writeFileSync(path, SAMPLE_SETTINGS_YAML) +} + +export function checkYamlPath(path: string): boolean { + return fs.existsSync(path) +} + +export function loadYamlFromFile(path: string): Record { + return parseYaml(fs.readFileSync(path, 'utf8')) +} + +export function parseYaml(yaml: string): Record { + return load(yaml, { + schema: FAILSAFE_SCHEMA, + }) as Record +} diff --git a/src/settings/settings.ts b/src/settings/settings.ts new file mode 100644 index 00000000..e46718b2 --- /dev/null +++ b/src/settings/settings.ts @@ -0,0 +1,42 @@ +export interface Settings { + bee: { + api: string + debugApi: string + } + server: { + port: number + hostname: string + authSecret: string + logLevel: 'debug' | 'info' | 'warn' | 'error' + } + stamp: { + checkFrequency: number + mode: '' | 'hardcoded' | 'autobuy' | 'autoextend' + hardcoded: { + batchId: string + } + autobuy: { + amount: number + depth: number + ttlThreshold: number + usageThreshold: number + } + autoextend: { + defaultAmount: number + defaultDepth: number + extendCapacity: boolean + extendTtl: boolean + extendAmount: number + ttlThreshold: number + usageThreshold: number + } + } + contentReupload: { + enabled: boolean + reuploadFrequency: number + } + cid: boolean + ens: boolean + exposeHashedIdentity: boolean + removePinHeader: boolean +} diff --git a/test/settings/parser/big-number-parser.spec.ts b/test/settings/parser/big-number-parser.spec.ts new file mode 100644 index 00000000..c8ed8246 --- /dev/null +++ b/test/settings/parser/big-number-parser.spec.ts @@ -0,0 +1,24 @@ +import { bigNumberParser } from '../../../src/settings/parser/big-number-parser' + +test('bigNumberParser', () => { + expect(bigNumberParser.parse('20', 'test')).toEqual(20) + expect(bigNumberParser.parse('20k', 'test')).toEqual(20000) + expect(bigNumberParser.parse('20.5k', 'test')).toEqual(20500) + expect(bigNumberParser.parse('20K', 'test')).toEqual(20000) + expect(bigNumberParser.parse('20m', 'test')).toEqual(20000000) + expect(bigNumberParser.parse('20b', 'test')).toEqual(20000000000) + expect(bigNumberParser.parse('20t', 'test')).toEqual(20000000000000) +}) + +test('bigNumberParser error', () => { + expect(() => bigNumberParser.parse('', 'test')).toThrowError( + 'Could not parse test as number, got nothing. Example: 20', + ) + expect(() => bigNumberParser.parse('az', 'test')).toThrowError('Could not parse test as number, got az. Example: 20') + expect(() => bigNumberParser.parse('20x', 'test')).toThrowError( + 'Could not parse test as number, got 20x. Example: 20', + ) + expect(() => bigNumberParser.parse('-15k', 'test')).toThrowError( + 'Value -15k for test is not acceptable. Number must be positive.', + ) +}) diff --git a/test/settings/parser/bool-parser.spec.ts b/test/settings/parser/bool-parser.spec.ts new file mode 100644 index 00000000..1db9a2f5 --- /dev/null +++ b/test/settings/parser/bool-parser.spec.ts @@ -0,0 +1,11 @@ +import { boolParser } from '../../../src/settings/parser/bool-parser' + +test('boolParser', () => { + expect(boolParser.parse('true', 'test')).toEqual(true) + expect(boolParser.parse('false', 'test')).toEqual(false) +}) + +test('boolParser error', () => { + expect(() => boolParser.parse('', 'test')).toThrowError('Could not parse test as boolean, got nothing. Example: true') + expect(() => boolParser.parse('az', 'test')).toThrowError('Could not parse test as boolean, got az. Example: true') +}) diff --git a/test/settings/parser/enum-parser.spec.ts b/test/settings/parser/enum-parser.spec.ts new file mode 100644 index 00000000..7028d6c6 --- /dev/null +++ b/test/settings/parser/enum-parser.spec.ts @@ -0,0 +1,11 @@ +import { enumParser } from '../../../src/settings/parser/enum-parser' + +test('enumParser', () => { + expect(enumParser.parse('test', 'test', ['test'])).toEqual('test') +}) + +test('enumParser error', () => { + expect(() => enumParser.parse('az', 'test', ['test1', 'test2'])).toThrowError( + 'Could not parse test as enum, got az. Example: test1, test2', + ) +}) diff --git a/test/settings/parser/optional-string-parser.spec.ts b/test/settings/parser/optional-string-parser.spec.ts new file mode 100644 index 00000000..371acb64 --- /dev/null +++ b/test/settings/parser/optional-string-parser.spec.ts @@ -0,0 +1,6 @@ +import { optionalStringParser } from '../../../src/settings/parser/optional-string-parser' + +test('optionalStringParser', () => { + expect(optionalStringParser.parse('', 'test')).toEqual('') + expect(optionalStringParser.parse('test', 'test')).toEqual('test') +}) diff --git a/test/settings/parser/percentage-parser.spec.ts b/test/settings/parser/percentage-parser.spec.ts new file mode 100644 index 00000000..93a10e8a --- /dev/null +++ b/test/settings/parser/percentage-parser.spec.ts @@ -0,0 +1,18 @@ +import { percentageParser } from '../../../src/settings/parser/percentage-parser' + +test('percentageParser', () => { + expect(percentageParser.parse('20%', 'test')).toEqual(0.2) + expect(percentageParser.parse('20.5%', 'test')).toBeCloseTo(0.205, 5) +}) + +test('percentageParser error', () => { + expect(() => percentageParser.parse('', 'test')).toThrowError( + 'Could not parse test as percentage, got nothing. Example: 85%', + ) + expect(() => percentageParser.parse('az', 'test')).toThrowError( + 'Could not parse test as percentage, got az. Example: 85%', + ) + expect(() => percentageParser.parse('-15', 'test')).toThrowError( + 'Value -15 for test is not acceptable. Percentage must be between 0 and 100.', + ) +}) diff --git a/test/settings/parser/required-string-parser.spec.ts b/test/settings/parser/required-string-parser.spec.ts new file mode 100644 index 00000000..f64ede1f --- /dev/null +++ b/test/settings/parser/required-string-parser.spec.ts @@ -0,0 +1,11 @@ +import { requiredStringParser } from '../../../src/settings/parser/required-string-parser' + +test('requiredStringParser', () => { + expect(requiredStringParser.parse('test', 'test')).toEqual('test') +}) + +test('requiredStringParser error', () => { + expect(() => requiredStringParser.parse('', 'test')).toThrowError( + 'Could not parse test as string, got nothing. Example: test', + ) +}) diff --git a/test/settings/parser/temporal-parser.spec.ts b/test/settings/parser/temporal-parser.spec.ts new file mode 100644 index 00000000..6cd68a5d --- /dev/null +++ b/test/settings/parser/temporal-parser.spec.ts @@ -0,0 +1,19 @@ +import { temporalParser } from '../../../src/settings/parser/temporal-parser' + +test('temporalParser', () => { + expect(temporalParser.parse('1s', 'test')).toEqual(1000) + expect(temporalParser.parse('1m', 'test')).toEqual(60000) + expect(temporalParser.parse('1h', 'test')).toEqual(3600000) + expect(temporalParser.parse('1d', 'test')).toEqual(86400000) +}) + +test('temporalParser error', () => { + expect(() => temporalParser.parse('', 'test')).toThrowError( + "Value nothing for test is not acceptable. Unit must be 's', 'm', 'h' or 'd'.", + ) + expect(() => temporalParser.parse('az', 'test')).toThrowError('Could not parse test as time, got az. Example: 20s') + expect(() => temporalParser.parse('20x', 'test')).toThrowError('Could not parse test as time, got 20x. Example: 20s') + expect(() => temporalParser.parse('-15k', 'test')).toThrowError( + 'Could not parse test as time, got -15k. Example: 20s', + ) +}) diff --git a/test/settings/settings-service.spec.ts b/test/settings/settings-service.spec.ts new file mode 100644 index 00000000..d3fb4f67 --- /dev/null +++ b/test/settings/settings-service.spec.ts @@ -0,0 +1,15 @@ +import { SAMPLE_SETTINGS_YAML } from '../../src/settings/sample-settings' +import { makeSettings } from '../../src/settings/settings-factory' +import { parseYaml } from '../../src/settings/settings-service' + +test('settings-service', () => { + const settings = parseYaml(SAMPLE_SETTINGS_YAML) + const settingsObject = makeSettings(settings) + expect(settingsObject).toHaveProperty('bee.api', 'http://localhost:1633') + expect(settingsObject).toHaveProperty('server.logLevel', 'info') + expect(settingsObject).toHaveProperty('cid', true) +}) + +test('settings-service error', () => { + expect(() => makeSettings({})).toThrowError('Could not parse bee.api as string, got nothing. Example: test') +}) From aa012227e7ea5f4094be43cda6faa1143a72bc44 Mon Sep 17 00:00:00 2001 From: Cafe137 Date: Tue, 8 Nov 2022 18:11:17 +0100 Subject: [PATCH 2/3] feat: partition settings and create depth-parser --- src/index.ts | 2 - src/settings/parser/depth-parser.ts | 17 +++++ src/settings/settings-factory.ts | 5 +- src/settings/settings-singleton.ts | 3 + src/settings/settings.ts | 86 +++++++++++++---------- test/settings/parser/depth-parser.spec.ts | 13 ++++ 6 files changed, 86 insertions(+), 40 deletions(-) create mode 100644 src/settings/parser/depth-parser.ts create mode 100644 src/settings/settings-singleton.ts create mode 100644 test/settings/parser/depth-parser.spec.ts diff --git a/src/index.ts b/src/index.ts index 045b52f5..56812279 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,11 +5,9 @@ import { EnvironmentVariables, getAppConfig, getContentConfig, getServerConfig, import { ContentManager } from './content' import { logger, subscribeLogServerRequests } from './logger' import { createApp } from './server' -import { createOrGetSettings } from './settings/settings-service' import { StampsManager } from './stamps' async function main() { - createOrGetSettings('config.yaml') // Configuration const stampsConfig = getStampsConfig(process.env as EnvironmentVariables) const contentConfig = getContentConfig(process.env as EnvironmentVariables) diff --git a/src/settings/parser/depth-parser.ts b/src/settings/parser/depth-parser.ts new file mode 100644 index 00000000..16a15b32 --- /dev/null +++ b/src/settings/parser/depth-parser.ts @@ -0,0 +1,17 @@ +import { OutOfBoundsError, ParsingError } from './parsing-error' + +export const depthParser = { + parse(value: string, field: string): number { + const parsed = Number(value) + + if (!parsed) { + throw new ParsingError(field, value, 'depth', '22') + } + + if (parsed < 20 || parsed > 64) { + throw new OutOfBoundsError(field, value, `Depth must be between 20 and 64.`) + } + + return parsed + }, +} diff --git a/src/settings/settings-factory.ts b/src/settings/settings-factory.ts index c9106ebe..30eb3d46 100644 --- a/src/settings/settings-factory.ts +++ b/src/settings/settings-factory.ts @@ -1,5 +1,6 @@ import { bigNumberParser } from './parser/big-number-parser' import { boolParser } from './parser/bool-parser' +import { depthParser } from './parser/depth-parser' import { enumParser } from './parser/enum-parser' import { optionalStringParser } from './parser/optional-string-parser' import { percentageParser } from './parser/percentage-parser' @@ -27,7 +28,7 @@ export function makeSettings(settings: Record): Settings { }, autobuy: { amount: bigNumberParser.parse(settings?.stamp?.autobuy?.amount, 'stamp.autobuy.amount'), - depth: bigNumberParser.parse(settings?.stamp?.autobuy?.depth, 'stamp.autobuy.depth'), + depth: depthParser.parse(settings?.stamp?.autobuy?.depth, 'stamp.autobuy.depth'), ttlThreshold: temporalParser.parse(settings?.stamp?.autobuy?.ttlThreshold, 'stamp.autobuy.ttlThreshold'), usageThreshold: percentageParser.parse( settings?.stamp?.autobuy?.usageThreshold, @@ -39,7 +40,7 @@ export function makeSettings(settings: Record): Settings { settings?.stamp?.autoextend?.defaultAmount, 'stamp.autoextend.defaultAmount', ), - defaultDepth: bigNumberParser.parse(settings?.stamp?.autoextend?.defaultDepth, 'stamp.autoextend.defaultDepth'), + defaultDepth: depthParser.parse(settings?.stamp?.autoextend?.defaultDepth, 'stamp.autoextend.defaultDepth'), extendCapacity: boolParser.parse( settings?.stamp?.autoextend?.extendCapacity, 'stamp.autoextend.extendCapacity', diff --git a/src/settings/settings-singleton.ts b/src/settings/settings-singleton.ts new file mode 100644 index 00000000..c0ef1cd6 --- /dev/null +++ b/src/settings/settings-singleton.ts @@ -0,0 +1,3 @@ +import { createOrGetSettings } from './settings-service' + +export const settings = createOrGetSettings('config.yaml') diff --git a/src/settings/settings.ts b/src/settings/settings.ts index e46718b2..bfc8439d 100644 --- a/src/settings/settings.ts +++ b/src/settings/settings.ts @@ -1,40 +1,54 @@ +export interface BeeSettings { + api: string + debugApi: string +} + +export interface ServerSettings { + port: number + hostname: string + authSecret: string + logLevel: 'debug' | 'info' | 'warn' | 'error' +} + +export interface ContentReuploadSettings { + enabled: boolean + reuploadFrequency: number +} + +export interface StampHardcodedSettings { + batchId: string +} + +export interface StampAutobuySettings { + amount: number + depth: number + ttlThreshold: number + usageThreshold: number +} + +export interface StampAutoextendSettings { + defaultAmount: number + defaultDepth: number + extendCapacity: boolean + extendTtl: boolean + extendAmount: number + ttlThreshold: number + usageThreshold: number +} + +export interface StampSettings { + checkFrequency: number + mode: '' | 'hardcoded' | 'autobuy' | 'autoextend' + hardcoded: StampHardcodedSettings + autobuy: StampAutobuySettings + autoextend: StampAutoextendSettings +} + export interface Settings { - bee: { - api: string - debugApi: string - } - server: { - port: number - hostname: string - authSecret: string - logLevel: 'debug' | 'info' | 'warn' | 'error' - } - stamp: { - checkFrequency: number - mode: '' | 'hardcoded' | 'autobuy' | 'autoextend' - hardcoded: { - batchId: string - } - autobuy: { - amount: number - depth: number - ttlThreshold: number - usageThreshold: number - } - autoextend: { - defaultAmount: number - defaultDepth: number - extendCapacity: boolean - extendTtl: boolean - extendAmount: number - ttlThreshold: number - usageThreshold: number - } - } - contentReupload: { - enabled: boolean - reuploadFrequency: number - } + bee: BeeSettings + server: ServerSettings + stamp: StampSettings + contentReupload: ContentReuploadSettings cid: boolean ens: boolean exposeHashedIdentity: boolean diff --git a/test/settings/parser/depth-parser.spec.ts b/test/settings/parser/depth-parser.spec.ts new file mode 100644 index 00000000..7638c598 --- /dev/null +++ b/test/settings/parser/depth-parser.spec.ts @@ -0,0 +1,13 @@ +import { depthParser } from '../../../src/settings/parser/depth-parser' + +test('depthParser', () => { + expect(depthParser.parse('20', 'test')).toEqual(20) +}) + +test('depthParser error', () => { + expect(() => depthParser.parse('', 'test')).toThrowError('Could not parse test as depth, got nothing. Example: 22') + expect(() => depthParser.parse('az', 'test')).toThrowError('Could not parse test as depth, got az. Example: 22') + expect(() => depthParser.parse('-15', 'test')).toThrowError( + 'Value -15 for test is not acceptable. Depth must be between 20 and 64.', + ) +}) From 69f899ad51f84e3976767dfbc445442801c5e0f7 Mon Sep 17 00:00:00 2001 From: Cafe137 Date: Tue, 9 May 2023 11:53:38 +0200 Subject: [PATCH 3/3] refactor: simplify code --- Dockerfile | 23 +- package-lock.json | 1273 +++------------------- package.json | 6 +- src/config.ts | 196 ---- src/content.ts | 16 +- src/index.ts | 33 +- src/logger.ts | 25 +- src/readiness.ts | 7 +- src/server.ts | 65 +- src/settings/parser/big-number-parser.ts | 12 +- src/settings/settings-factory.ts | 7 +- src/settings/settings-singleton.ts | 2 +- src/settings/settings.ts | 6 +- src/stamp/StampManipulation.ts | 56 + src/stamp/StampRetrieval.ts | 23 + src/stamps.ts | 365 ++----- src/utils.ts | 20 - 17 files changed, 355 insertions(+), 1780 deletions(-) delete mode 100644 src/config.ts create mode 100644 src/stamp/StampManipulation.ts create mode 100644 src/stamp/StampRetrieval.ts diff --git a/Dockerfile b/Dockerfile index f689412d..79520dbe 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,20 +1,17 @@ -FROM node:lts-bullseye-slim as base +FROM node:alpine -WORKDIR /app +WORKDIR /usr/src/app -COPY . . +COPY package*.json . +COPY tsconfig.json . +COPY src src +COPY public public -RUN npm ci +RUN npm install -RUN npm run build +EXPOSE 4000 -FROM node:lts-bullseye-slim +RUN mkdir /usr/src/config -WORKDIR /app -COPY --from=base --chown=nobody:nogroup /app/dist dist -COPY --from=base --chown=nobody:nogroup /app/public public -COPY --from=base --chown=nobody:nogroup /app/node_modules node_modules -USER nobody -EXPOSE 3000 +CMD [ "node", "dist/index.js", "/usr/src/config/config.yaml" ] -ENTRYPOINT [ "node", "dist/index.js"] diff --git a/package-lock.json b/package-lock.json index d073dd0d..8323ab90 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,8 +9,9 @@ "version": "0.6.0", "license": "BSD-3-Clause", "dependencies": { - "@ethersphere/bee-js": "^5.0.0", + "@ethersphere/bee-js": "6.0.0-pre.9", "@ethersphere/swarm-cid": "^0.1.0", + "bignumber.js": "^9.1.1", "express": "^4.18.1", "http-proxy-middleware": "^2.0.6", "js-yaml": "^4.1.0", @@ -23,7 +24,6 @@ }, "devDependencies": { "@commitlint/cli": "^17.1.2", - "@ethersphere/bee-factory": "^0.4.1", "@jest/types": "^29.0.3", "@types/express": "^4.17.14", "@types/jest": "^27.5.2", @@ -665,12 +665,6 @@ "node": ">=6.9.0" } }, - "node_modules/@balena/dockerignore": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@balena/dockerignore/-/dockerignore-1.0.2.tgz", - "integrity": "sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q==", - "dev": true - }, "node_modules/@bcoe/v8-coverage": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", @@ -1023,161 +1017,28 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@ethersphere/bee-factory": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@ethersphere/bee-factory/-/bee-factory-0.4.1.tgz", - "integrity": "sha512-GXqQmDpNW5aOgX4SnlRFqgnxU8g/pMONbMUmnCjceDakuM+boIymnutPPug9BFgndNYbjpP7grBT9Ybd5/poZQ==", - "dev": true, - "dependencies": { - "@ethersphere/bee-js": "^4.0.0", - "chalk": "^2.4.2", - "dockerode": "^3.3.1", - "furious-commander": "^1.7.0", - "node-fetch": "3.0.0-beta.9", - "ora": "^5.3.0" - }, - "bin": { - "bee-factory": "dist/src/index.js" - }, - "engines": { - "bee": "1.6.0", - "node": ">=12.0.0", - "npm": ">=6.0.0" - } - }, - "node_modules/@ethersphere/bee-factory/node_modules/@ethersphere/bee-js": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@ethersphere/bee-js/-/bee-js-4.1.1.tgz", - "integrity": "sha512-4hbgi7rHnku+2pv352z7txV6IhP+2iipCKaQS4sOFhjieui44LE0Lox8gy5tud9tY2wRN2wY6LTqvo2EaYrVRQ==", - "dev": true, - "dependencies": { - "@ethersphere/swarm-cid": "^0.1.0", - "@types/readable-stream": "^2.3.13", - "bufferutil": "^4.0.6", - "elliptic": "^6.5.4", - "fetch-blob": "2.1.2", - "isomorphic-ws": "^4.0.1", - "js-sha3": "^0.8.0", - "ky": "^0.25.1", - "ky-universal": "^0.8.2", - "semver": "^7.3.5", - "tar-js": "^0.3.0", - "utf-8-validate": "^5.0.9", - "web-streams-polyfill": "^4.0.0-beta.1", - "ws": "^8.6.0" - }, - "engines": { - "bee": "1.6.0-6ceadd35", - "beeApiVersion": "3.0.1", - "beeDebugApiVersion": "2.0.1", - "node": ">=12.0.0", - "npm": ">=6.0.0" - } - }, - "node_modules/@ethersphere/bee-factory/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@ethersphere/bee-factory/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@ethersphere/bee-factory/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@ethersphere/bee-factory/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/@ethersphere/bee-factory/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@ethersphere/bee-factory/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@ethersphere/bee-factory/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@ethersphere/bee-factory/node_modules/web-streams-polyfill": { - "version": "4.0.0-beta.3", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", - "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", - "dev": true, - "engines": { - "node": ">= 14" - } - }, "node_modules/@ethersphere/bee-js": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@ethersphere/bee-js/-/bee-js-5.0.0.tgz", - "integrity": "sha512-Dr5Xon0UZvi37fvbyGj46kw3/0D8fydwfDtVtFHKi2p7mNEizG0uok2mXvwLZrMvUMOS8uXkFhbQjTFBjB+pWA==", + "version": "6.0.0-pre.9", + "resolved": "https://registry.npmjs.org/@ethersphere/bee-js/-/bee-js-6.0.0-pre.9.tgz", + "integrity": "sha512-8MEtWX7tI8fFaKROnWdDiFJs/6g41Zp+fuO89Qib8mBQCtjYHeMEaRdxmayoGzUKng1vhff9j0vqu7W2UymhnQ==", "dependencies": { "@ethersphere/swarm-cid": "^0.1.0", "@types/readable-stream": "^2.3.13", - "bufferutil": "^4.0.6", + "axios": "^1.3.4", + "cafe-utility": "^9.0.1", "elliptic": "^6.5.4", "fetch-blob": "2.1.2", "isomorphic-ws": "^4.0.1", "js-sha3": "^0.8.0", - "ky": "^0.25.1", - "ky-universal": "^0.8.2", "semver": "^7.3.5", "tar-js": "^0.3.0", - "utf-8-validate": "^5.0.9", - "web-streams-polyfill": "^4.0.0-beta.1", + "web-streams-polyfill": "^4.0.0-beta.3", "ws": "^8.7.0" }, "engines": { - "bee": "1.7.0-bbf13011", - "beeApiVersion": "3.0.2", - "beeDebugApiVersion": "3.0.2", + "bee": "1.13.0-f1067884", + "beeApiVersion": "4.0.0", + "beeDebugApiVersion": "4.0.0", "node": ">=14.0.0", "npm": ">=6.0.0" } @@ -2484,17 +2345,6 @@ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true }, - "node_modules/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "dependencies": { - "event-target-shim": "^5.0.0" - }, - "engines": { - "node": ">=6.5" - } - }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -2694,15 +2544,6 @@ "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", "dev": true }, - "node_modules/asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "dev": true, - "dependencies": { - "safer-buffer": "~2.1.0" - } - }, "node_modules/async": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", @@ -2711,8 +2552,30 @@ "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", + "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==", + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axios/node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } }, "node_modules/babel-jest": { "version": "27.5.1", @@ -2837,33 +2700,12 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", - "dev": true, - "dependencies": { - "tweetnacl": "^0.14.3" + "node_modules/bignumber.js": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.1.tgz", + "integrity": "sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig==", + "engines": { + "node": "*" } }, "node_modules/binary-extensions": { @@ -2880,17 +2722,6 @@ "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.2.tgz", "integrity": "sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw==" }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dev": true, - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, "node_modules/bn.js": { "version": "4.12.0", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", @@ -3013,30 +2844,6 @@ "node-int64": "^0.4.0" } }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -3048,6 +2855,8 @@ "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.6.tgz", "integrity": "sha512-jduaYOYtnio4aIAyc6UbvPCVcgq7nYpVnucyxr6eCYg/Woad9Hf/oxxBRDnGGjPfjUm6j5O/uBWhIu4iLebFaw==", "hasInstallScript": true, + "optional": true, + "peer": true, "dependencies": { "node-gyp-build": "^4.3.0" }, @@ -3055,16 +2864,6 @@ "node": ">=6.14.2" } }, - "node_modules/buildcheck": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/buildcheck/-/buildcheck-0.0.3.tgz", - "integrity": "sha512-pziaA+p/wdVImfcbsZLNF32EiWyujlQLwolMqUQE8xpKNOH7KmZQaY8sXN7DGOEzPAElo9QTaeNRfGnf3iOJbA==", - "dev": true, - "optional": true, - "engines": { - "node": ">=10.0.0" - } - }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -3073,6 +2872,11 @@ "node": ">= 0.8" } }, + "node_modules/cafe-utility": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cafe-utility/-/cafe-utility-9.0.1.tgz", + "integrity": "sha512-QiTaoRPkK8UyBBsGwv4zJRM88JibhcQ2GRHsLYTq+JaKIK6UyW1xSOlerJWAFHS/r/SpXffCQxlNIeiOA7MgCQ==" + }, "node_modules/call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", @@ -3212,12 +3016,6 @@ "node": ">= 6" } }, - "node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "dev": true - }, "node_modules/ci-info": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.4.0.tgz", @@ -3230,30 +3028,6 @@ "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", "dev": true }, - "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-spinners": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz", - "integrity": "sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==", - "dev": true, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -3265,15 +3039,6 @@ "wrap-ansi": "^7.0.0" } }, - "node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "dev": true, - "engines": { - "node": ">=0.8" - } - }, "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -3351,7 +3116,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, "dependencies": { "delayed-stream": "~1.0.0" }, @@ -3499,21 +3263,6 @@ "typescript": ">=3" } }, - "node_modules/cpu-features": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.4.tgz", - "integrity": "sha512-fKiZ/zp1mUwQbnzb9IghXtHtDoTMtNeb8oYGx6kX2SYfhnG0HNdBEBIzB9b5KlXu5DQPhfy3mInbBxFcgwAr3A==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "dependencies": { - "buildcheck": "0.0.3", - "nan": "^2.15.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, "node_modules/create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", @@ -3567,14 +3316,6 @@ "node": ">=8" } }, - "node_modules/data-uri-to-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz", - "integrity": "sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==", - "engines": { - "node": ">= 6" - } - }, "node_modules/data-urls": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", @@ -3664,20 +3405,10 @@ "node": ">=0.10.0" } }, - "node_modules/defaults": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", - "integrity": "sha512-s82itHOnYrN0Ib8r+z7laQz3sdE+4FP3d9Q7VLO7U+KRT+CR0GsWuyHxzdAY82I7cXv0G/twrqomTJLOssO5HA==", - "dev": true, - "dependencies": { - "clone": "^1.0.2" - } - }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, "engines": { "node": ">=0.4.0" } @@ -3831,35 +3562,6 @@ "node": ">=8" } }, - "node_modules/docker-modem": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-3.0.6.tgz", - "integrity": "sha512-h0Ow21gclbYsZ3mkHDfsYNDqtRhXS8fXr51bU0qr1dxgTMJj0XufbzX+jhNOvA8KuEEzn6JbvLVhXyv+fny9Uw==", - "dev": true, - "dependencies": { - "debug": "^4.1.1", - "readable-stream": "^3.5.0", - "split-ca": "^1.0.1", - "ssh2": "^1.11.0" - }, - "engines": { - "node": ">= 8.0" - } - }, - "node_modules/dockerode": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/dockerode/-/dockerode-3.3.4.tgz", - "integrity": "sha512-3EUwuXnCU+RUlQEheDjmBE0B7q66PV9Rw5NiH1sXwINq0M9c5ERP9fxgkw36ZHOtzf4AGEEYySnkx/sACC9EgQ==", - "dev": true, - "dependencies": { - "@balena/dockerignore": "^1.0.2", - "docker-modem": "^3.0.0", - "tar-fs": "~2.0.1" - }, - "engines": { - "node": ">= 8.0" - } - }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -3961,15 +3663,6 @@ "node": ">= 0.8" } }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "dependencies": { - "once": "^1.4.0" - } - }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -4454,14 +4147,6 @@ "node": ">= 0.6" } }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "engines": { - "node": ">=6" - } - }, "node_modules/eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", @@ -4856,12 +4541,6 @@ "node": ">= 0.6" } }, - "node_modules/fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "dev": true - }, "node_modules/fs-extra": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", @@ -4901,20 +4580,6 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, - "node_modules/furious-commander": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/furious-commander/-/furious-commander-1.7.1.tgz", - "integrity": "sha512-EwgVMVU1y1JNpqMdBPezuJRFufGlTumTKKau1eEvOJ6AxYgNkdDiQxl+GD3PBZo6fJo5dpBf+0TohoSdA7oeug==", - "dev": true, - "dependencies": { - "madlad": "^1.2.1", - "reflect-metadata": "^0.1.13" - }, - "engines": { - "node": ">=10.0.0", - "npm": ">=6.0.0" - } - }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -5290,26 +4955,6 @@ "node": ">=0.10.0" } }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, "node_modules/ignore": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", @@ -5498,15 +5143,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -5570,18 +5206,6 @@ "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", "dev": true }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -6962,41 +6586,6 @@ "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" }, - "node_modules/ky": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/ky/-/ky-0.25.1.tgz", - "integrity": "sha512-PjpCEWlIU7VpiMVrTwssahkYXX1by6NCT0fhTUX34F3DTinARlgMpriuroolugFPcMgpPWrOW4mTb984Qm1RXA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/ky?sponsor=1" - } - }, - "node_modules/ky-universal": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/ky-universal/-/ky-universal-0.8.2.tgz", - "integrity": "sha512-xe0JaOH9QeYxdyGLnzUOVGK4Z6FGvDVzcXFTdrYA1f33MZdEa45sUDaMBy98xQMcsd2XIBrTXRrRYnegcSdgVQ==", - "dependencies": { - "abort-controller": "^3.0.0", - "node-fetch": "3.0.0-beta.9" - }, - "engines": { - "node": ">=10.17" - }, - "funding": { - "url": "https://github.com/sindresorhus/ky-universal?sponsor=1" - }, - "peerDependencies": { - "ky": ">=0.17.0", - "web-streams-polyfill": ">=2.0.0" - }, - "peerDependenciesMeta": { - "web-streams-polyfill": { - "optional": true - } - } - }, "node_modules/leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -7058,22 +6647,6 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/logform": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/logform/-/logform-2.4.2.tgz", @@ -7109,12 +6682,6 @@ "node": ">=10" } }, - "node_modules/madlad": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/madlad/-/madlad-1.3.0.tgz", - "integrity": "sha512-ioUsF0t9s72dsf5fcwb2jUWM13aVkVqSUJr88TTIEMoBQ+TKS4GzEDkrFx4XjsGwPpCgIvuZMwx1RDAyqpJVmA==", - "dev": true - }, "node_modules/magic-string": { "version": "0.25.9", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", @@ -7359,12 +6926,6 @@ "node": ">=0.10.0" } }, - "node_modules/mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "dev": true - }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -7403,13 +6964,6 @@ "node": ">=8" } }, - "node_modules/nan": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.16.0.tgz", - "integrity": "sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA==", - "dev": true, - "optional": true - }, "node_modules/nanoid": { "version": "3.3.4", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", @@ -7441,26 +6995,12 @@ "resolved": "https://registry.npmjs.org/next-line/-/next-line-1.1.0.tgz", "integrity": "sha512-+I10J3wKNoKddNxn0CNpoZ3eTZuqxjNM3b1GImVx22+ePI+Y15P8g/j3WsbP0fhzzrFzrtjOAoq5NCCucswXOQ==" }, - "node_modules/node-fetch": { - "version": "3.0.0-beta.9", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.0.0-beta.9.tgz", - "integrity": "sha512-RdbZCEynH2tH46+tj0ua9caUHVWrd/RHnRfvly2EVdqGmI3ndS1Vn/xjm5KuGejDt2RNDQsVRLPNd2QPwcewVg==", - "dependencies": { - "data-uri-to-buffer": "^3.0.1", - "fetch-blob": "^2.1.1" - }, - "engines": { - "node": "^10.17 || >=12.3" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/node-fetch" - } - }, "node_modules/node-gyp-build": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.5.0.tgz", "integrity": "sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg==", + "optional": true, + "peer": true, "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", @@ -7670,29 +7210,6 @@ "node": ">= 0.8.0" } }, - "node_modules/ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "dev": true, - "dependencies": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -8044,6 +7561,11 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", @@ -8056,16 +7578,6 @@ "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", "dev": true }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, "node_modules/punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -8338,12 +7850,6 @@ "node": ">=8" } }, - "node_modules/reflect-metadata": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", - "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", - "dev": true - }, "node_modules/regexpp": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", @@ -8456,19 +7962,6 @@ "node": ">=10" } }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -8825,12 +8318,6 @@ "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==", "dev": true }, - "node_modules/split-ca": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/split-ca/-/split-ca-1.0.1.tgz", - "integrity": "sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ==", - "dev": true - }, "node_modules/split2": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", @@ -8846,24 +8333,6 @@ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, - "node_modules/ssh2": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.11.0.tgz", - "integrity": "sha512-nfg0wZWGSsfUe/IBJkXVll3PEZ//YH2guww+mP88gTpuSU4FtZN7zu9JoeTGOyCNx2dTDtT9fOpWwlzyj4uOOw==", - "dev": true, - "hasInstallScript": true, - "dependencies": { - "asn1": "^0.2.4", - "bcrypt-pbkdf": "^1.0.2" - }, - "engines": { - "node": ">=10.16.0" - }, - "optionalDependencies": { - "cpu-features": "~0.0.4", - "nan": "^2.16.0" - } - }, "node_modules/stack-trace": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", @@ -9094,18 +8563,6 @@ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "dev": true }, - "node_modules/tar-fs": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.1.tgz", - "integrity": "sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA==", - "dev": true, - "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.0.0" - } - }, "node_modules/tar-js": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/tar-js/-/tar-js-0.3.0.tgz", @@ -9114,22 +8571,6 @@ "node": "*" } }, - "node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "dev": true, - "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/tdigest": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/tdigest/-/tdigest-0.1.2.tgz", @@ -9421,12 +8862,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/tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", - "dev": true - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -9567,6 +9002,8 @@ "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.9.tgz", "integrity": "sha512-Yek7dAy0v3Kl0orwMlvi7TPtiCNrdfHNd7Gcc/pLq4BLXqfAmd0J7OWMizUQnTTJsyjKn02mU7anqwfmUP4J8Q==", "hasInstallScript": true, + "optional": true, + "peer": true, "dependencies": { "node-gyp-build": "^4.3.0" }, @@ -9664,25 +9101,6 @@ "makeerror": "1.0.12" } }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "dev": true, - "dependencies": { - "defaults": "^1.0.3" - } - }, - "node_modules/web-streams-polyfill": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", - "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", - "optional": true, - "peer": true, - "engines": { - "node": ">= 8" - } - }, "node_modules/webidl-conversions": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", @@ -10383,12 +9801,6 @@ "to-fast-properties": "^2.0.0" } }, - "@balena/dockerignore": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@balena/dockerignore/-/dockerignore-1.0.2.tgz", - "integrity": "sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q==", - "dev": true - }, "@bcoe/v8-coverage": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", @@ -10622,170 +10034,68 @@ "integrity": "sha512-AXYd23w1S/bv3fTs3Lz0vjiYemS08jWkI3hYyS9I1ry+0f+Yjs1wm+sU0BS8qDOPrBIkp4qHYC16I8uVtpLajQ==", "dev": true, "requires": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.4.0", - "globals": "^13.15.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "dependencies": { - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "globals": { - "version": "13.17.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", - "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - } - } - }, - "@ethersphere/bee-factory": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@ethersphere/bee-factory/-/bee-factory-0.4.1.tgz", - "integrity": "sha512-GXqQmDpNW5aOgX4SnlRFqgnxU8g/pMONbMUmnCjceDakuM+boIymnutPPug9BFgndNYbjpP7grBT9Ybd5/poZQ==", - "dev": true, - "requires": { - "@ethersphere/bee-js": "^4.0.0", - "chalk": "^2.4.2", - "dockerode": "^3.3.1", - "furious-commander": "^1.7.0", - "node-fetch": "3.0.0-beta.9", - "ora": "^5.3.0" - }, - "dependencies": { - "@ethersphere/bee-js": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@ethersphere/bee-js/-/bee-js-4.1.1.tgz", - "integrity": "sha512-4hbgi7rHnku+2pv352z7txV6IhP+2iipCKaQS4sOFhjieui44LE0Lox8gy5tud9tY2wRN2wY6LTqvo2EaYrVRQ==", - "dev": true, - "requires": { - "@ethersphere/swarm-cid": "^0.1.0", - "@types/readable-stream": "^2.3.13", - "bufferutil": "^4.0.6", - "elliptic": "^6.5.4", - "fetch-blob": "2.1.2", - "isomorphic-ws": "^4.0.1", - "js-sha3": "^0.8.0", - "ky": "^0.25.1", - "ky-universal": "^0.8.2", - "semver": "^7.3.5", - "tar-js": "^0.3.0", - "utf-8-validate": "^5.0.9", - "web-streams-polyfill": "^4.0.0-beta.1", - "ws": "^8.6.0" - } - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.4.0", + "globals": "^13.15.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" } }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "globals": { + "version": "13.17.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", + "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", "dev": true, "requires": { - "color-name": "1.1.3" + "type-fest": "^0.20.2" } }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "web-streams-polyfill": { - "version": "4.0.0-beta.3", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", - "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true } } }, "@ethersphere/bee-js": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@ethersphere/bee-js/-/bee-js-5.0.0.tgz", - "integrity": "sha512-Dr5Xon0UZvi37fvbyGj46kw3/0D8fydwfDtVtFHKi2p7mNEizG0uok2mXvwLZrMvUMOS8uXkFhbQjTFBjB+pWA==", + "version": "6.0.0-pre.9", + "resolved": "https://registry.npmjs.org/@ethersphere/bee-js/-/bee-js-6.0.0-pre.9.tgz", + "integrity": "sha512-8MEtWX7tI8fFaKROnWdDiFJs/6g41Zp+fuO89Qib8mBQCtjYHeMEaRdxmayoGzUKng1vhff9j0vqu7W2UymhnQ==", "requires": { "@ethersphere/swarm-cid": "^0.1.0", "@types/readable-stream": "^2.3.13", - "bufferutil": "^4.0.6", + "axios": "^1.3.4", + "cafe-utility": "^9.0.1", "elliptic": "^6.5.4", "fetch-blob": "2.1.2", "isomorphic-ws": "^4.0.1", "js-sha3": "^0.8.0", - "ky": "^0.25.1", - "ky-universal": "^0.8.2", "semver": "^7.3.5", "tar-js": "^0.3.0", - "utf-8-validate": "^5.0.9", - "web-streams-polyfill": "^4.0.0-beta.1", + "web-streams-polyfill": "^4.0.0-beta.3", "ws": "^8.7.0" }, "dependencies": { @@ -11877,14 +11187,6 @@ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true }, - "abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "requires": { - "event-target-shim": "^5.0.0" - } - }, "accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -12032,15 +11334,6 @@ "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", "dev": true }, - "asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "dev": true, - "requires": { - "safer-buffer": "~2.1.0" - } - }, "async": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", @@ -12049,8 +11342,29 @@ "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "axios": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", + "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==", + "requires": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + }, + "dependencies": { + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + } + } }, "babel-jest": { "version": "27.5.1", @@ -12153,20 +11467,10 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, - "base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true - }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", - "dev": true, - "requires": { - "tweetnacl": "^0.14.3" - } + "bignumber.js": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.1.tgz", + "integrity": "sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig==" }, "binary-extensions": { "version": "2.2.0", @@ -12179,17 +11483,6 @@ "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.2.tgz", "integrity": "sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw==" }, - "bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dev": true, - "requires": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, "bn.js": { "version": "4.12.0", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", @@ -12288,16 +11581,6 @@ "node-int64": "^0.4.0" } }, - "buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, - "requires": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, "buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -12308,22 +11591,22 @@ "version": "4.0.6", "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.6.tgz", "integrity": "sha512-jduaYOYtnio4aIAyc6UbvPCVcgq7nYpVnucyxr6eCYg/Woad9Hf/oxxBRDnGGjPfjUm6j5O/uBWhIu4iLebFaw==", + "optional": true, + "peer": true, "requires": { "node-gyp-build": "^4.3.0" } }, - "buildcheck": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/buildcheck/-/buildcheck-0.0.3.tgz", - "integrity": "sha512-pziaA+p/wdVImfcbsZLNF32EiWyujlQLwolMqUQE8xpKNOH7KmZQaY8sXN7DGOEzPAElo9QTaeNRfGnf3iOJbA==", - "dev": true, - "optional": true - }, "bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" }, + "cafe-utility": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cafe-utility/-/cafe-utility-9.0.1.tgz", + "integrity": "sha512-QiTaoRPkK8UyBBsGwv4zJRM88JibhcQ2GRHsLYTq+JaKIK6UyW1xSOlerJWAFHS/r/SpXffCQxlNIeiOA7MgCQ==" + }, "call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", @@ -12413,12 +11696,6 @@ } } }, - "chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "dev": true - }, "ci-info": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.4.0.tgz", @@ -12431,21 +11708,6 @@ "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", "dev": true }, - "cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "requires": { - "restore-cursor": "^3.1.0" - } - }, - "cli-spinners": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz", - "integrity": "sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==", - "dev": true - }, "cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -12457,12 +11719,6 @@ "wrap-ansi": "^7.0.0" } }, - "clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "dev": true - }, "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -12535,7 +11791,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, "requires": { "delayed-stream": "~1.0.0" } @@ -12652,17 +11907,6 @@ "dev": true, "requires": {} }, - "cpu-features": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.4.tgz", - "integrity": "sha512-fKiZ/zp1mUwQbnzb9IghXtHtDoTMtNeb8oYGx6kX2SYfhnG0HNdBEBIzB9b5KlXu5DQPhfy3mInbBxFcgwAr3A==", - "dev": true, - "optional": true, - "requires": { - "buildcheck": "0.0.3", - "nan": "^2.15.0" - } - }, "create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", @@ -12709,11 +11953,6 @@ "integrity": "sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==", "dev": true }, - "data-uri-to-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz", - "integrity": "sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==" - }, "data-urls": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", @@ -12782,20 +12021,10 @@ "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", "dev": true }, - "defaults": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", - "integrity": "sha512-s82itHOnYrN0Ib8r+z7laQz3sdE+4FP3d9Q7VLO7U+KRT+CR0GsWuyHxzdAY82I7cXv0G/twrqomTJLOssO5HA==", - "dev": true, - "requires": { - "clone": "^1.0.2" - } - }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" }, "depcheck": { "version": "1.4.3", @@ -12917,29 +12146,6 @@ "path-type": "^4.0.0" } }, - "docker-modem": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-3.0.6.tgz", - "integrity": "sha512-h0Ow21gclbYsZ3mkHDfsYNDqtRhXS8fXr51bU0qr1dxgTMJj0XufbzX+jhNOvA8KuEEzn6JbvLVhXyv+fny9Uw==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "readable-stream": "^3.5.0", - "split-ca": "^1.0.1", - "ssh2": "^1.11.0" - } - }, - "dockerode": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/dockerode/-/dockerode-3.3.4.tgz", - "integrity": "sha512-3EUwuXnCU+RUlQEheDjmBE0B7q66PV9Rw5NiH1sXwINq0M9c5ERP9fxgkw36ZHOtzf4AGEEYySnkx/sACC9EgQ==", - "dev": true, - "requires": { - "@balena/dockerignore": "^1.0.2", - "docker-modem": "^3.0.0", - "tar-fs": "~2.0.1" - } - }, "doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -13022,15 +12228,6 @@ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "requires": { - "once": "^1.4.0" - } - }, "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -13362,11 +12559,6 @@ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" }, - "event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" - }, "eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", @@ -13689,12 +12881,6 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" }, - "fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "dev": true - }, "fs-extra": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", @@ -13724,16 +12910,6 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, - "furious-commander": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/furious-commander/-/furious-commander-1.7.1.tgz", - "integrity": "sha512-EwgVMVU1y1JNpqMdBPezuJRFufGlTumTKKau1eEvOJ6AxYgNkdDiQxl+GD3PBZo6fJo5dpBf+0TohoSdA7oeug==", - "dev": true, - "requires": { - "madlad": "^1.2.1", - "reflect-metadata": "^0.1.13" - } - }, "gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -14002,12 +13178,6 @@ "safer-buffer": ">= 2.1.2 < 3" } }, - "ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true - }, "ignore": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", @@ -14150,12 +13320,6 @@ "is-extglob": "^2.1.1" } }, - "is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "dev": true - }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -14198,12 +13362,6 @@ "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", "dev": true }, - "is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true - }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -15359,20 +14517,6 @@ "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" }, - "ky": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/ky/-/ky-0.25.1.tgz", - "integrity": "sha512-PjpCEWlIU7VpiMVrTwssahkYXX1by6NCT0fhTUX34F3DTinARlgMpriuroolugFPcMgpPWrOW4mTb984Qm1RXA==" - }, - "ky-universal": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/ky-universal/-/ky-universal-0.8.2.tgz", - "integrity": "sha512-xe0JaOH9QeYxdyGLnzUOVGK4Z6FGvDVzcXFTdrYA1f33MZdEa45sUDaMBy98xQMcsd2XIBrTXRrRYnegcSdgVQ==", - "requires": { - "abort-controller": "^3.0.0", - "node-fetch": "3.0.0-beta.9" - } - }, "leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -15422,16 +14566,6 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, - "log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - } - }, "logform": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/logform/-/logform-2.4.2.tgz", @@ -15461,12 +14595,6 @@ "yallist": "^4.0.0" } }, - "madlad": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/madlad/-/madlad-1.3.0.tgz", - "integrity": "sha512-ioUsF0t9s72dsf5fcwb2jUWM13aVkVqSUJr88TTIEMoBQ+TKS4GzEDkrFx4XjsGwPpCgIvuZMwx1RDAyqpJVmA==", - "dev": true - }, "magic-string": { "version": "0.25.9", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", @@ -15651,12 +14779,6 @@ } } }, - "mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "dev": true - }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -15688,13 +14810,6 @@ } } }, - "nan": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.16.0.tgz", - "integrity": "sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA==", - "dev": true, - "optional": true - }, "nanoid": { "version": "3.3.4", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", @@ -15717,19 +14832,12 @@ "resolved": "https://registry.npmjs.org/next-line/-/next-line-1.1.0.tgz", "integrity": "sha512-+I10J3wKNoKddNxn0CNpoZ3eTZuqxjNM3b1GImVx22+ePI+Y15P8g/j3WsbP0fhzzrFzrtjOAoq5NCCucswXOQ==" }, - "node-fetch": { - "version": "3.0.0-beta.9", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.0.0-beta.9.tgz", - "integrity": "sha512-RdbZCEynH2tH46+tj0ua9caUHVWrd/RHnRfvly2EVdqGmI3ndS1Vn/xjm5KuGejDt2RNDQsVRLPNd2QPwcewVg==", - "requires": { - "data-uri-to-buffer": "^3.0.1", - "fetch-blob": "^2.1.1" - } - }, "node-gyp-build": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.5.0.tgz", - "integrity": "sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg==" + "integrity": "sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg==", + "optional": true, + "peer": true }, "node-int64": { "version": "0.4.0", @@ -15887,23 +14995,6 @@ "word-wrap": "^1.2.3" } }, - "ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "dev": true, - "requires": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - } - }, "p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -16147,6 +15238,11 @@ "ipaddr.js": "1.9.1" } }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", @@ -16159,16 +15255,6 @@ "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", "dev": true }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -16367,12 +15453,6 @@ "strip-indent": "^3.0.0" } }, - "reflect-metadata": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", - "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", - "dev": true - }, "regexpp": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", @@ -16452,16 +15532,6 @@ "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==", "dev": true }, - "restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, - "requires": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - } - }, "reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -16737,12 +15807,6 @@ "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==", "dev": true }, - "split-ca": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/split-ca/-/split-ca-1.0.1.tgz", - "integrity": "sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ==", - "dev": true - }, "split2": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", @@ -16758,18 +15822,6 @@ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, - "ssh2": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.11.0.tgz", - "integrity": "sha512-nfg0wZWGSsfUe/IBJkXVll3PEZ//YH2guww+mP88gTpuSU4FtZN7zu9JoeTGOyCNx2dTDtT9fOpWwlzyj4uOOw==", - "dev": true, - "requires": { - "asn1": "^0.2.4", - "bcrypt-pbkdf": "^1.0.2", - "cpu-features": "~0.0.4", - "nan": "^2.16.0" - } - }, "stack-trace": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", @@ -16941,36 +15993,11 @@ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "dev": true }, - "tar-fs": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.1.tgz", - "integrity": "sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA==", - "dev": true, - "requires": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.0.0" - } - }, "tar-js": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/tar-js/-/tar-js-0.3.0.tgz", "integrity": "sha512-9uqP2hJUZNKRkwPDe5nXxXdzo6w+BFBPq9x/tyi5/U/DneuSesO/HMb0y5TeWpfcv49YDJTs7SrrZeeu8ZHWDA==" }, - "tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "dev": true, - "requires": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - } - }, "tdigest": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/tdigest/-/tdigest-0.1.2.tgz", @@ -17172,12 +16199,6 @@ "tslib": "^1.8.1" } }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", - "dev": true - }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -17273,6 +16294,8 @@ "version": "5.0.9", "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.9.tgz", "integrity": "sha512-Yek7dAy0v3Kl0orwMlvi7TPtiCNrdfHNd7Gcc/pLq4BLXqfAmd0J7OWMizUQnTTJsyjKn02mU7anqwfmUP4J8Q==", + "optional": true, + "peer": true, "requires": { "node-gyp-build": "^4.3.0" } @@ -17354,22 +16377,6 @@ "makeerror": "1.0.12" } }, - "wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "dev": true, - "requires": { - "defaults": "^1.0.3" - } - }, - "web-streams-polyfill": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", - "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", - "optional": true, - "peer": true - }, "webidl-conversions": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", diff --git a/package.json b/package.json index b41387ec..fa408230 100644 --- a/package.json +++ b/package.json @@ -30,12 +30,11 @@ "depcheck": "depcheck .", "check:types": "tsc --project tsconfig.test.json", "test": "jest --verbose", - "bee": "bee-factory start" + "bee": "npx bee-factory start" }, "license": "BSD-3-Clause", "devDependencies": { "@commitlint/cli": "^17.1.2", - "@ethersphere/bee-factory": "^0.4.1", "@jest/types": "^29.0.3", "@types/express": "^4.17.14", "@types/jest": "^27.5.2", @@ -62,8 +61,9 @@ "typescript": "^4.8.3" }, "dependencies": { - "@ethersphere/bee-js": "^5.0.0", + "@ethersphere/bee-js": "6.0.0-pre.9", "@ethersphere/swarm-cid": "^0.1.0", + "bignumber.js": "^9.1.1", "express": "^4.18.1", "http-proxy-middleware": "^2.0.6", "js-yaml": "^4.1.0", diff --git a/src/config.ts b/src/config.ts deleted file mode 100644 index 6230b410..00000000 --- a/src/config.ts +++ /dev/null @@ -1,196 +0,0 @@ -export interface AppConfig { - beeApiUrl: string - beeDebugApiUrl: string - authorization?: string - hostname?: string - cidSubdomains?: boolean - ensSubdomains?: boolean - removePinHeader?: boolean - exposeHashedIdentity?: boolean -} - -export interface ServerConfig { - hostname: string - port: number -} - -interface StampsConfigHardcoded { - mode: 'hardcoded' - stamp: string -} -export interface StampsConfigExtends { - mode: 'extendsTTL' - ttlMin: number - depth: number - amount: string - refreshPeriod: number - beeDebugApiUrl: string -} - -export interface ContentConfigReupload { - beeApiUrl: string - refreshPeriod: number -} - -export interface StampsConfigAutobuy { - mode: 'autobuy' - depth: number - amount: string - beeDebugApiUrl: string - usageThreshold: number - usageMax: number - ttlMin: number - refreshPeriod: number -} - -export type StampsConfig = StampsConfigHardcoded | StampsConfigAutobuy | StampsConfigExtends - -export type ContentConfig = ContentConfigReupload - -export type EnvironmentVariables = Partial<{ - // Logging - LOG_LEVEL: string - - // Proxy - BEE_API_URL: string - AUTH_SECRET: string - - // Server - PORT: string - HOSTNAME: string - - // Identity - EXPOSE_HASHED_IDENTITY: string - - // CID subdomain support - CID_SUBDOMAINS: string - ENS_SUBDOMAINS: string - - // Headers manipulation - REMOVE_PIN_HEADER: string - - // Stamps - BEE_DEBUG_API_URL: string - POSTAGE_STAMP: string - POSTAGE_DEPTH: string - POSTAGE_AMOUNT: string - POSTAGE_USAGE_THRESHOLD: string - POSTAGE_USAGE_MAX: string - POSTAGE_TTL_MIN: string - POSTAGE_REFRESH_PERIOD: string - POSTAGE_EXTENDSTTL: string - REUPLOAD_PERIOD: string -}> - -export const SUPPORTED_LEVELS = ['critical', 'error', 'warn', 'info', 'verbose', 'debug'] as const -export type SupportedLevels = typeof SUPPORTED_LEVELS[number] - -export const DEFAULT_BEE_API_URL = 'http://localhost:1633' -export const DEFAULT_BEE_DEBUG_API_URL = 'http://localhost:1635' -export const DEFAULT_HOSTNAME = 'localhost' -export const DEFAULT_PORT = 3000 -export const DEFAULT_POSTAGE_USAGE_THRESHOLD = 0.7 -export const DEFAULT_POSTAGE_USAGE_MAX = 0.9 -export const DEFAULT_POSTAGE_REFRESH_PERIOD = 60_000 -export const DEFAULT_LOG_LEVEL = 'info' -export const MINIMAL_EXTENDS_TTL_VALUE = 60 -export const READINESS_TIMEOUT_MS = 3000 -export const ERROR_NO_STAMP = 'No postage stamp' - -export const logLevel = - process.env.LOG_LEVEL && SUPPORTED_LEVELS.includes(process.env.LOG_LEVEL as SupportedLevels) - ? process.env.LOG_LEVEL - : DEFAULT_LOG_LEVEL - -export function getAppConfig({ - BEE_API_URL, - BEE_DEBUG_API_URL, - AUTH_SECRET, - CID_SUBDOMAINS, - ENS_SUBDOMAINS, - HOSTNAME, - REMOVE_PIN_HEADER, - EXPOSE_HASHED_IDENTITY, -}: EnvironmentVariables = {}): AppConfig { - return { - hostname: HOSTNAME || DEFAULT_HOSTNAME, - beeApiUrl: BEE_API_URL || DEFAULT_BEE_API_URL, - beeDebugApiUrl: BEE_DEBUG_API_URL || DEFAULT_BEE_DEBUG_API_URL, - authorization: AUTH_SECRET, - cidSubdomains: CID_SUBDOMAINS === 'true', - ensSubdomains: ENS_SUBDOMAINS === 'true', - removePinHeader: REMOVE_PIN_HEADER ? REMOVE_PIN_HEADER === 'true' : true, - exposeHashedIdentity: EXPOSE_HASHED_IDENTITY === 'true', - } -} - -export function getServerConfig({ PORT, HOSTNAME }: EnvironmentVariables = {}): ServerConfig { - return { hostname: HOSTNAME || DEFAULT_HOSTNAME, port: Number(PORT || DEFAULT_PORT) } -} - -export function getStampsConfig({ - BEE_DEBUG_API_URL, - POSTAGE_STAMP, - POSTAGE_DEPTH, - POSTAGE_AMOUNT, - POSTAGE_USAGE_THRESHOLD, - POSTAGE_USAGE_MAX, - POSTAGE_TTL_MIN, - POSTAGE_REFRESH_PERIOD, - POSTAGE_EXTENDSTTL, -}: EnvironmentVariables = {}): StampsConfig | undefined { - const refreshPeriod = Number(POSTAGE_REFRESH_PERIOD || DEFAULT_POSTAGE_REFRESH_PERIOD) - const beeDebugApiUrl = BEE_DEBUG_API_URL || DEFAULT_BEE_DEBUG_API_URL - - // Start in hardcoded mode - if (POSTAGE_STAMP) return { mode: 'hardcoded', stamp: POSTAGE_STAMP } - // Start autobuy - else if (!POSTAGE_EXTENDSTTL && POSTAGE_DEPTH && POSTAGE_AMOUNT && BEE_DEBUG_API_URL) { - return { - mode: 'autobuy', - depth: Number(POSTAGE_DEPTH), - amount: POSTAGE_AMOUNT, - usageThreshold: Number(POSTAGE_USAGE_THRESHOLD || DEFAULT_POSTAGE_USAGE_THRESHOLD), - usageMax: Number(POSTAGE_USAGE_MAX || DEFAULT_POSTAGE_USAGE_MAX), - ttlMin: Number(POSTAGE_TTL_MIN || (refreshPeriod / 1000) * 5), - refreshPeriod, - beeDebugApiUrl, - } - } else if ( - POSTAGE_EXTENDSTTL === 'true' && - POSTAGE_AMOUNT && - POSTAGE_DEPTH && - Number(POSTAGE_TTL_MIN) >= MINIMAL_EXTENDS_TTL_VALUE - ) { - return { - mode: 'extendsTTL', - depth: Number(POSTAGE_DEPTH), - ttlMin: Number(POSTAGE_TTL_MIN), - amount: POSTAGE_AMOUNT, - refreshPeriod, - beeDebugApiUrl, - } - } - // Missing one of the variables needed for the autobuy or extends TTL - else if (POSTAGE_DEPTH || POSTAGE_AMOUNT || POSTAGE_TTL_MIN || BEE_DEBUG_API_URL) { - throw new Error( - `config: please provide POSTAGE_DEPTH=${POSTAGE_DEPTH}, POSTAGE_AMOUNT=${POSTAGE_AMOUNT}, POSTAGE_TTL_MIN=${POSTAGE_TTL_MIN} ${ - POSTAGE_EXTENDSTTL === 'true' ? 'at least 60 seconds ' : '' - }or BEE_DEBUG_API_URL=${BEE_DEBUG_API_URL} for the feature to work`, - ) - } - - // Stamps rewrite is disabled - return undefined -} - -export function getContentConfig({ BEE_API_URL, REUPLOAD_PERIOD }: EnvironmentVariables = {}): ContentConfig | false { - if (!REUPLOAD_PERIOD) { - return false - } - - return { - beeApiUrl: BEE_API_URL || DEFAULT_BEE_API_URL, - refreshPeriod: Number(REUPLOAD_PERIOD), - } -} diff --git a/src/content.ts b/src/content.ts index b0c80058..6d817313 100644 --- a/src/content.ts +++ b/src/content.ts @@ -1,8 +1,8 @@ import { Bee } from '@ethersphere/bee-js' import client from 'prom-client' -import type { ContentConfig } from './config' import { logger } from './logger' import { register } from './metrics' +import { settings } from './settings/settings-singleton' const contentReuploadCounter = new client.Counter({ name: 'content_reupload_counter', @@ -26,20 +26,15 @@ export class ContentManager { const pins = await beeApi.getAllPins() if (!pins.length) { - logger.info(`no pins found`) - return } - logger.info(`checking pinned content (${pins.length} pins)`) for (const pin of pins) { const isRetrievable = await beeApi.isReferenceRetrievable(pin) - logger.debug(`pin ${pin} is ${isRetrievable ? 'retrievable' : 'not retrievable'}`) - if (!isRetrievable && !this.isReuploading) { this.isReuploading = true try { - logger.debug(`reuploading pinned content: ${pin}`) + logger.info(`reuploading pinned content: ${pin}`) await beeApi.reuploadPinnedData(pin) contentReuploadCounter.inc() logger.info(`pinned content reuploaded: ${pin}`) @@ -54,12 +49,13 @@ export class ContentManager { /** * Start the manager that checks for pinned content availability and reuploads the data if needed. */ - start(config: ContentConfig): void { - const refreshContent = async () => this.attemptRefreshContentReupload(new Bee(config.beeApiUrl)) + start(): void { + logger.info('Starting content manager') + const refreshContent = async () => this.attemptRefreshContentReupload(new Bee(settings.bee.api)) this.stop() refreshContent() - this.interval = setInterval(refreshContent, config.refreshPeriod) + this.interval = setInterval(refreshContent, settings.contentReupload.reuploadFrequency) } stop(): void { diff --git a/src/index.ts b/src/index.ts index 56812279..734c31b8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,46 +1,31 @@ #!/usr/bin/env node import { Application } from 'express' -import { EnvironmentVariables, getAppConfig, getContentConfig, getServerConfig, getStampsConfig } from './config' import { ContentManager } from './content' import { logger, subscribeLogServerRequests } from './logger' import { createApp } from './server' +import { settings } from './settings/settings-singleton' import { StampsManager } from './stamps' async function main() { - // Configuration - const stampsConfig = getStampsConfig(process.env as EnvironmentVariables) - const contentConfig = getContentConfig(process.env as EnvironmentVariables) - const appConfig = getAppConfig(process.env as EnvironmentVariables) - const { hostname, port } = getServerConfig(process.env as EnvironmentVariables) - - logger.debug('proxy config', appConfig) - logger.debug('server config', { hostname: hostname, port }) - let app: Application - if (contentConfig) { - logger.debug('content config', contentConfig) + if (settings.contentReupload.enabled) { const contentManager = new ContentManager() - logger.info('starting content manager') - contentManager.start(contentConfig) + contentManager.start() } - if (stampsConfig) { - logger.debug('stamps config', stampsConfig) + if (settings.stamp.mode) { const stampManager = new StampsManager() - logger.info('starting postage stamp manager') - stampManager.start(stampsConfig) - logger.info('starting the proxy') - app = createApp(appConfig, stampManager) + stampManager.start() + app = createApp(stampManager) } else { - logger.info('starting the app without postage stamps management') - app = createApp(appConfig) + app = createApp() } // Start the Proxy - const server = app.listen(port, () => { - logger.info(`starting gateway-proxy at ${hostname}:${port}`) + const server = app.listen(settings.server.port, () => { + logger.info(`starting gateway-proxy at ${settings.server.hostname}:${settings.server.port}`) }) subscribeLogServerRequests(server) diff --git a/src/logger.ts b/src/logger.ts index b9a859d8..444dc28c 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -1,17 +1,9 @@ -import { createLogger, format, transports, Logger, Logform } from 'winston' -import requestStats from 'request-stats' import type { Server } from 'http' - -import { SupportedLevels, logLevel, SUPPORTED_LEVELS } from './config' - -const supportedLevels: Record = SUPPORTED_LEVELS.reduce( - (acc, cur, idx) => ({ ...acc, [cur]: idx }), - {} as Record, -) +import requestStats from 'request-stats' +import { Logform, Logger, createLogger, format, transports } from 'winston' export const logger: Logger = createLogger({ // To see more detailed errors, change this to 'debug' - levels: supportedLevels, format: format.combine( format.errors({ stack: true }), format.metadata(), @@ -19,23 +11,12 @@ export const logger: Logger = createLogger({ format.printf(formatLogMessage), format.colorize({ all: true }), ), - transports: [new transports.Console({ level: logLevel })], + transports: [new transports.Console()], }) -logger.info(`using max log level=${logLevel}`) - -function processMetadata(metadata: Record): string { - // Create array of "=" strings from an object - const serializedMetadata = Object.entries(metadata).map(([key, value]) => `${key}=${JSON.stringify(value)}`) - - return serializedMetadata.join(' ') -} - export function formatLogMessage(info: Logform.TransformableInfo): string { let message = `time="${info.timestamp}" level="${info.level}" msg="${info.message}"` - if (Object.keys(info.metadata).length > 0) message = `${message} ${processMetadata(info.metadata)}` - return message.replace(/\n/g, '\\n') } diff --git a/src/readiness.ts b/src/readiness.ts index 17248e59..c996dc9c 100644 --- a/src/readiness.ts +++ b/src/readiness.ts @@ -1,5 +1,4 @@ import { Bee, BeeDebug, Utils } from '@ethersphere/bee-js' -import { ERROR_NO_STAMP, READINESS_TIMEOUT_MS } from './config' import { logger } from './logger' import { StampsManager } from './stamps' import { getErrorMessage } from './utils' @@ -24,7 +23,7 @@ export async function checkReadiness( return ready } else { try { - const health = await beeDebug.getHealth({ timeout: READINESS_TIMEOUT_MS }) + const health = await beeDebug.getHealth({ timeout: 3000 }) const ready = health.status === 'ok' return ready ? ReadinessStatus.OK : ReadinessStatus.HEALTH_CHECK_FAILED @@ -37,14 +36,14 @@ export async function checkReadiness( async function tryUploadingSingleChunk(bee: Bee, stampsManager: StampsManager): Promise { const chunk = makeChunk() try { - await bee.uploadChunk(stampsManager.postageStamp, chunk, { timeout: READINESS_TIMEOUT_MS, deferred: true }) + await bee.uploadChunk(stampsManager.postageStamp, chunk, { deferred: true }, { timeout: 3000 }) return ReadinessStatus.OK } catch (error) { const errorMessage = getErrorMessage(error) logger.error('unable to upload readiness-check chunk to bee', errorMessage) - if (errorMessage === ERROR_NO_STAMP) { + if (errorMessage === 'No postage stamp') { return ReadinessStatus.NO_STAMP } diff --git a/src/server.ts b/src/server.ts index 918cfa86..ed981541 100644 --- a/src/server.ts +++ b/src/server.ts @@ -1,12 +1,12 @@ import { Bee, BeeDebug } from '@ethersphere/bee-js' import express, { Application } from 'express' -import { createProxyMiddleware, Options } from 'http-proxy-middleware' +import { Options, createProxyMiddleware } from 'http-proxy-middleware' import * as bzzLink from './bzz-link' -import { AppConfig, DEFAULT_HOSTNAME, ERROR_NO_STAMP } from './config' -import { fetchBeeIdentity, getHashedIdentity, HASHED_IDENTITY_HEADER } from './identity' +import { HASHED_IDENTITY_HEADER, fetchBeeIdentity, getHashedIdentity } from './identity' import { logger } from './logger' import { register } from './metrics' -import { checkReadiness, ReadinessStatus } from './readiness' +import { ReadinessStatus, checkReadiness } from './readiness' +import { settings } from './settings/settings-singleton' import type { StampsManager } from './stamps' import { getErrorMessage } from './utils' @@ -21,37 +21,22 @@ export const GET_PROXY_ENDPOINTS = [ ] export const POST_PROXY_ENDPOINTS = ['/bzz', '/bytes', '/chunks', '/feeds/:owner/:topic', '/soc/:owner/:id'] -export const createApp = ( - { - hostname, - beeApiUrl, - beeDebugApiUrl, - authorization, - cidSubdomains, - ensSubdomains, - removePinHeader, - exposeHashedIdentity, - }: AppConfig, - stampManager?: StampsManager, -): Application => { +export const createApp = (stampManager?: StampsManager): Application => { const commonOptions: Options = { - target: beeApiUrl, + target: settings.bee.api, changeOrigin: true, logProvider: () => logger, } - const bee = new Bee(beeApiUrl) - const beeDebug = new BeeDebug(beeDebugApiUrl) + const bee = new Bee(settings.bee.api) + const beeDebug = new BeeDebug(settings.bee.debugApi) // Create Express Server const app = express() // Register hashed identity - if (exposeHashedIdentity) { - if (!beeDebugApiUrl) { - throw Error('BEE_DEBUG_API_URL is not set, but EXPOSE_HASHED_IDENTITY is set to true') - } - const beeDebug = new BeeDebug(beeDebugApiUrl) + if (settings.exposeHashedIdentity) { + const beeDebug = new BeeDebug(settings.bee.debugApi) fetchBeeIdentity(beeDebug) app.use((_, res, next) => { res.set(HASHED_IDENTITY_HEADER, getHashedIdentity()) @@ -59,15 +44,15 @@ export const createApp = ( }) } - if (hostname) { - const subdomainOffset = hostname.split('.').length + if (settings.server.hostname) { + const subdomainOffset = settings.server.hostname.split('.').length app.set('subdomain offset', subdomainOffset) } // Authorization - if (authorization) { + if (settings.server.authSecret) { app.use('', (req, res, next) => { - if (req.headers.authorization === authorization) { + if (req.headers.authorization === settings.server.authSecret) { next() } else { res.sendStatus(403) @@ -75,25 +60,21 @@ export const createApp = ( }) } - if (cidSubdomains || ensSubdomains) { - if (!hostname) { + if (settings.cid || settings.ens) { + if (!settings.server.hostname) { throw new Error('For Bzz.link support you have to configure HOSTNAME env!') } - if (hostname === DEFAULT_HOSTNAME) { - logger.warn(`bzz.link support is enabled but HOSTNAME is set to the default ${DEFAULT_HOSTNAME}`) - } - - if (cidSubdomains) logger.info(`enabling CID subdomain support with hostname ${hostname}`) + if (settings.cid) logger.info(`enabling CID subdomain support with hostname ${settings.server.hostname}`) - if (ensSubdomains) logger.info(`enabling ENS subdomain support with hostname ${hostname}`) + if (settings.ens) logger.info(`enabling ENS subdomain support with hostname ${settings.server.hostname}`) app.get( '*', createProxyMiddleware(bzzLink.requestFilter, { ...commonOptions, - cookieDomainRewrite: hostname, - router: bzzLink.routerClosure(beeApiUrl, Boolean(cidSubdomains), Boolean(ensSubdomains)), + cookieDomainRewrite: settings.server.hostname, + router: bzzLink.routerClosure(settings.bee.api, settings.cid, settings.ens), }), ) @@ -129,7 +110,7 @@ export const createApp = ( const options: Options = { ...commonOptions } options.onProxyReq = (proxyReq, _req, res) => { - if (removePinHeader) { + if (settings.removePinHeader) { proxyReq.removeHeader('swarm-pin') } @@ -140,8 +121,8 @@ export const createApp = ( } catch (error) { logger.error('proxy failure', error) - if (getErrorMessage(error) === ERROR_NO_STAMP) { - res.writeHead(503).end(ERROR_NO_STAMP) + if (getErrorMessage(error) === 'No postage stamp') { + res.writeHead(503).end('No postage stamp') } else { res.writeHead(503).end('Service Unavailable') } diff --git a/src/settings/parser/big-number-parser.ts b/src/settings/parser/big-number-parser.ts index 620e790d..a8d9f626 100644 --- a/src/settings/parser/big-number-parser.ts +++ b/src/settings/parser/big-number-parser.ts @@ -1,3 +1,4 @@ +import BigNumber from 'bignumber.js' import { OutOfBoundsError, ParsingError } from './parsing-error' const multiplierMap: Record = { @@ -9,17 +10,18 @@ const multiplierMap: Record = { } export const bigNumberParser = { - parse(value: string, field: string): number { + parse(value: string, field: string): string { const rawNumber = value.match(/\-?[0-9]+(\.[0-9]+)?/)?.[0] const rawUnit = value.match(/[a-zA-Z]+/)?.[0] - const number = Number(rawNumber) const unit = rawUnit ? rawUnit.trim().charAt(0).toLowerCase() : 'identity' - if (isNaN(number)) { + if (!rawNumber) { throw new ParsingError(field, value, 'number', '20m') } - if (number < 0) { + const number = new BigNumber(rawNumber) + + if (number.lte(0)) { throw new OutOfBoundsError(field, value, `Number must be positive.`) } @@ -27,6 +29,6 @@ export const bigNumberParser = { throw new ParsingError(field, value, 'number', '20m') } - return number * multiplierMap[unit] + return number.multipliedBy(multiplierMap[unit]).toString() }, } diff --git a/src/settings/settings-factory.ts b/src/settings/settings-factory.ts index 30eb3d46..0df79080 100644 --- a/src/settings/settings-factory.ts +++ b/src/settings/settings-factory.ts @@ -15,7 +15,7 @@ export function makeSettings(settings: Record): Settings { debugApi: requiredStringParser.parse(settings?.bee?.debugApi, 'bee.debugApi'), }, server: { - port: bigNumberParser.parse(settings?.server?.port, 'server.port'), + port: parseInt(bigNumberParser.parse(settings?.server?.port, 'server.port'), 10), hostname: optionalStringParser.parse(settings?.server?.hostname, 'server.hostname'), authSecret: optionalStringParser.parse(settings?.server?.authSecret, 'server.authSecret'), logLevel: enumParser.parse(settings?.server?.logLevel, 'server.logLevel', ['debug', 'info', 'warn', 'error']), @@ -29,7 +29,7 @@ export function makeSettings(settings: Record): Settings { autobuy: { amount: bigNumberParser.parse(settings?.stamp?.autobuy?.amount, 'stamp.autobuy.amount'), depth: depthParser.parse(settings?.stamp?.autobuy?.depth, 'stamp.autobuy.depth'), - ttlThreshold: temporalParser.parse(settings?.stamp?.autobuy?.ttlThreshold, 'stamp.autobuy.ttlThreshold'), + ttlThreshold: temporalParser.parse(settings?.stamp?.autobuy?.ttlThreshold, 'stamp.autobuy.ttlThreshold') / 1000, usageThreshold: percentageParser.parse( settings?.stamp?.autobuy?.usageThreshold, 'stamp.autobuy.usageThreshold', @@ -47,7 +47,8 @@ export function makeSettings(settings: Record): Settings { ), extendTtl: boolParser.parse(settings?.stamp?.autoextend?.extendTtl, 'stamp.autoextend.extendTtl'), extendAmount: bigNumberParser.parse(settings?.stamp?.autoextend?.extendAmount, 'stamp.autoextend.extendAmount'), - ttlThreshold: temporalParser.parse(settings?.stamp?.autoextend?.ttlThreshold, 'stamp.autoextend.ttlThreshold'), + ttlThreshold: + temporalParser.parse(settings?.stamp?.autoextend?.ttlThreshold, 'stamp.autoextend.ttlThreshold') / 1000, usageThreshold: percentageParser.parse( settings?.stamp?.autoextend?.usageThreshold, 'stamp.autoextend.usageThreshold', diff --git a/src/settings/settings-singleton.ts b/src/settings/settings-singleton.ts index c0ef1cd6..e17b04f4 100644 --- a/src/settings/settings-singleton.ts +++ b/src/settings/settings-singleton.ts @@ -1,3 +1,3 @@ import { createOrGetSettings } from './settings-service' -export const settings = createOrGetSettings('config.yaml') +export const settings = createOrGetSettings(process.argv[2] || 'config.yaml') diff --git a/src/settings/settings.ts b/src/settings/settings.ts index bfc8439d..6cf4285f 100644 --- a/src/settings/settings.ts +++ b/src/settings/settings.ts @@ -20,18 +20,18 @@ export interface StampHardcodedSettings { } export interface StampAutobuySettings { - amount: number + amount: string depth: number ttlThreshold: number usageThreshold: number } export interface StampAutoextendSettings { - defaultAmount: number + defaultAmount: string defaultDepth: number extendCapacity: boolean extendTtl: boolean - extendAmount: number + extendAmount: string ttlThreshold: number usageThreshold: number } diff --git a/src/stamp/StampManipulation.ts b/src/stamp/StampManipulation.ts new file mode 100644 index 00000000..526a2247 --- /dev/null +++ b/src/stamp/StampManipulation.ts @@ -0,0 +1,56 @@ +import { BeeDebug, PostageBatch } from '@ethersphere/bee-js' +import client from 'prom-client' +import { logger } from '../logger' +import { register } from '../metrics' +import { settings } from '../settings/settings-singleton' + +export async function buyStamp(depth: number, amount: string): Promise { + logger.info(`Buying new postage stamp with depth ${depth} and amount ${amount}`) + const beeDebug = new BeeDebug(settings.bee.debugApi) + const batchId = await beeDebug.createPostageBatch(amount, depth, { waitForUsable: true }) + const stamp = await beeDebug.getPostageBatch(batchId) + purchaseCounter.inc() + logger.info(`Bought new postage stamp with depth ${depth} and amount ${amount}`) + + return stamp +} + +export async function diluteStamp(id: string, depth: number): Promise { + logger.info(`Diluting postage stamp with id ${id} and depth ${depth}`) + const beeDebug = new BeeDebug(settings.bee.debugApi) + await beeDebug.diluteBatch(id, depth) + const stamp = await beeDebug.getPostageBatch(id) + diluteCounter.inc() + logger.info(`Diluted postage stamp with id ${id} and depth ${depth}`) + + return stamp +} + +export async function topupStamp(id: string, amount: string): Promise { + logger.info(`Topping up postage stamp with id ${id} and amount ${amount}`) + const beeDebug = new BeeDebug(settings.bee.debugApi) + await beeDebug.topUpBatch(id, amount) + const stamp = await beeDebug.getPostageBatch(id) + topupCounter.inc() + logger.info(`Topped up postage stamp with id ${id} and amount ${amount}`) + + return stamp +} + +const purchaseCounter = new client.Counter({ + name: 'stamp_purchase_counter', + help: 'How many stamps were purchased', +}) +register.registerMetric(purchaseCounter) + +const topupCounter = new client.Counter({ + name: 'stamp_topup_counter', + help: 'How many topup operations were performed', +}) +register.registerMetric(topupCounter) + +const diluteCounter = new client.Counter({ + name: 'stamp_dilute_counter', + help: 'How many dilute operations were performed', +}) +register.registerMetric(diluteCounter) diff --git a/src/stamp/StampRetrieval.ts b/src/stamp/StampRetrieval.ts new file mode 100644 index 00000000..f9e1eb62 --- /dev/null +++ b/src/stamp/StampRetrieval.ts @@ -0,0 +1,23 @@ +import { BeeDebug, PostageBatch } from '@ethersphere/bee-js' +import { settings } from '../settings/settings-singleton' + +export async function getUsableStamps(): Promise { + const beeDebug = new BeeDebug(settings.bee.debugApi) + const stamps = await beeDebug.getAllPostageBatch() + + return stamps.filter(stamp => stamp.usable).sort((a, b) => (a.batchTTL > b.batchTTL ? 1 : -1)) +} + +export async function getExpiringStamps(): Promise { + const stamps = await getUsableStamps() + return stamps.filter(x => x.batchTTL <= settings.stamp.autoextend.ttlThreshold) +} + +export async function getNearlyFullStamps(): Promise { + const stamps = await getUsableStamps() + return stamps.filter(x => getUsage(x) >= settings.stamp.autoextend.usageThreshold) +} + +export function getUsage({ utilization, depth, bucketDepth }: PostageBatch): number { + return utilization / Math.pow(2, depth - bucketDepth) +} diff --git a/src/stamps.ts b/src/stamps.ts index f519ec9c..a05ff7ae 100644 --- a/src/stamps.ts +++ b/src/stamps.ts @@ -1,326 +1,89 @@ -import { BeeDebug, PostageBatch, BatchId } from '@ethersphere/bee-js' import client from 'prom-client' -import { ERROR_NO_STAMP, StampsConfig, StampsConfigAutobuy, StampsConfigExtends } from './config' import { logger } from './logger' import { register } from './metrics' -import { waitForStampUsable } from './utils' - -interface Options { - timeout?: number -} - -const stampPurchaseCounter = new client.Counter({ - name: 'stamp_purchase_counter', - help: 'How many stamps were purchased', -}) -register.registerMetric(stampPurchaseCounter) - -const stampPurchaseFailedCounter = new client.Counter({ - name: 'stamp_purchase_failed_counter', - help: 'How many stamps failed to be purchased', -}) -register.registerMetric(stampPurchaseFailedCounter) - -const stampCheckCounter = new client.Counter({ - name: 'stamp_check_counter', - help: 'How many times were stamps retrieved from server', -}) -register.registerMetric(stampCheckCounter) - -const stampGetCounter = new client.Counter({ - name: 'stamp_get_counter', - help: 'How many times was get postageStamp called', -}) -register.registerMetric(stampGetCounter) - -const stampGetErrorCounter = new client.Counter({ - name: 'stamp_get_error_counter', - help: 'How many times was get postageStamp called and there was no valid postage stamp', -}) -register.registerMetric(stampGetErrorCounter) - -const stampTtlGauge = new client.Gauge({ - name: 'stamp_ttl_gauge', - help: 'TTL on the selected automanaged stamp', -}) -register.registerMetric(stampTtlGauge) - -const stampUsageGauge = new client.Gauge({ - name: 'stamp_usage_gauge', - help: 'Usage on the selected automanaged stamp', -}) -register.registerMetric(stampUsageGauge) - -const stampUsableCountGauge = new client.Gauge({ - name: 'stamp_usable_count_gauge', - help: 'How many stamps exist on the bee node that can be used', -}) -register.registerMetric(stampUsableCountGauge) - -/** - * Calculate usage of a given postage stamp - * - * @param stamp Postage stamp which usage should be determined - */ -export function getUsage({ utilization, depth, bucketDepth }: PostageBatch): number { - return utilization / Math.pow(2, depth - bucketDepth) -} - -/** - * Filter the stamps and only return those that are usable, have correct amount, depth, are not close to beying maxUsage or close to expire - * - * @param stamps Postage stamps to be filtered - * @param depth Postage stamps depth - * @param amount Postage stamps amount - * @param maxUsage Maximal usage of the stamp to be usable by the proxy - * @param minTTL Minimal TTL of the stamp to be usable by the proxy - * - * @returns Filtered stamps soltered by usage - */ -export function filterUsableStampsAutobuy( - stamps: PostageBatch[], - depth: number, - amount: string, - maxUsage: number, - minTTL: number, -): PostageBatch[] { - const usableStamps = stamps - // filter to get stamps that have the right depth, amount and are not fully used or expired - .filter(s => s.usable && s.depth === depth && s.amount === amount && getUsage(s) < maxUsage && s.batchTTL > minTTL) - // sort the stamps by usage - .sort((a, b) => (getUsage(a) < getUsage(b) ? 1 : -1)) - - // return the all usable stamp sorted by usage - return usableStamps -} - -/** - * Filter the stamps and only return those that are usable and sort by from closer to farer expire TTL - * - * @param stamps Postage stamps to be filtered - * - * @returns Filtered stamps soltered by usage - */ -export function filterUsableStampsExtends(stamps: PostageBatch[]): PostageBatch[] { - const usableStamps = stamps - // filter to get stamps that have the right depth, amount and are not fully used or expired - .filter(s => s.usable) - // sort the stamps by usage - .sort((a, b) => (a.batchTTL > b.batchTTL ? 1 : -1)) - - // return the all usable stamp sorted by usage - return usableStamps -} - -/** - * Buy new postage stamp and wait until it is usable - * - * @param depth Postage stamps depth - * @param amount Postage stamps amount - * @param beeDebug Connection to debug endpoint for checking/buying stamps - * @param options - * timeout (optional) How long should the system wait for the stamp to be usable in ms, default to 10000 - * - * @returns Newly bought postage stamp - */ -export async function buyNewStamp( - depth: number, - amount: string, - beeDebug: BeeDebug, -): Promise<{ batchId: BatchId; stamp: PostageBatch }> { - logger.info('buying new stamp') - const batchId = await beeDebug.createPostageBatch(amount, depth, { waitForUsable: false }) - await waitForStampUsable(beeDebug, batchId) - stampPurchaseCounter.inc() - - const stamp = await beeDebug.getPostageBatch(batchId) - logger.info('successfully bought new stamp', { stamp }) - - return { batchId, stamp } -} - -export async function topUpStamp(beeDebug: BeeDebug, postageBatchId: string, amount: string): Promise { - await beeDebug.topUpBatch(postageBatchId, amount) - const stamp = await beeDebug.getPostageBatch(postageBatchId) - - return stamp -} +import { settings } from './settings/settings-singleton' +import { buyStamp, diluteStamp, topupStamp } from './stamp/StampManipulation' +import { getExpiringStamps, getNearlyFullStamps, getUsableStamps, getUsage } from './stamp/StampRetrieval' +import { sleep } from './utils' export class StampsManager { private stamp?: string - private usableStamps?: PostageBatch[] - private interval?: ReturnType - private isBuyingStamp?: boolean = false - private extendingStamps: string[] = [] - - /** - * Get postage stamp that should be replaced in a the proxy request header - * - * @return Postage stamp that should be used by the proxy - * - * @throws Error if there is no postage stamp - */ - get postageStamp(): string { - stampGetCounter.inc() - - if (this.stamp) { - const stamp = this.stamp - logger.info('using hardcoded stamp', { stamp }) - - return stamp - } - - if (this.usableStamps && this.usableStamps[0]) { - const stamp = this.usableStamps[0] - logger.info('using autobought stamp', { stamp }) - return stamp.batchID - } - - stampGetErrorCounter.inc() - throw new Error(ERROR_NO_STAMP) - } - - /** - * Refresh stamps from the bee node and if needed buy new stamp - * - * @param config Stamps config - * @param beeDebug Connection to debug endpoint for checking/buying stamps - */ - public async refreshStampsAutobuy(config: StampsConfigAutobuy, beeDebug: BeeDebug): Promise { - try { - stampCheckCounter.inc() - logger.info('checking postage stamps') - const stamps = await beeDebug.getAllPostageBatch() - logger.debug('retrieved stamps', stamps) - - const { depth, amount, usageMax, usageThreshold, ttlMin } = config - - // Get all usable stamps sorted by usage from most used to least - this.usableStamps = filterUsableStampsAutobuy(stamps, depth, amount, usageMax, ttlMin) - const leastUsed = this.usableStamps[this.usableStamps.length - 1] - const mostUsed = this.usableStamps[0] - - stampTtlGauge.set(mostUsed ? mostUsed.batchTTL : 0) - stampUsageGauge.set(mostUsed ? getUsage(mostUsed) : 0) - stampUsableCountGauge.set(this.usableStamps.length) - - // Check if the least used stamps is starting to get full and if so purchase new stamp - if (!this.isBuyingStamp && (!leastUsed || getUsage(leastUsed) > usageThreshold)) { - this.isBuyingStamp = true + async start(): Promise { + if (settings.stamp.mode === 'hardcoded') { + this.stamp = settings.stamp.hardcoded.batchId + } else if (settings.stamp.mode === 'autobuy') { + while (true) { try { - const { stamp } = await buyNewStamp(depth, amount, beeDebug) - - // Add the bought postage stamp - this.usableStamps.push(stamp) - stampUsableCountGauge.set(this.usableStamps.length) - } catch (e) { - logger.error('failed to buy postage stamp', e) - stampPurchaseFailedCounter.inc() - } finally { - this.isBuyingStamp = false - } - } - } catch (e) { - logger.error('failed to refresh postage stamp', e) - } - } - - public async refreshStampsExtends(config: StampsConfigExtends, beeDebug: BeeDebug): Promise { - stampCheckCounter.inc() - logger.info('checking postage stamps') - - try { - const stamps = await beeDebug.getAllPostageBatch() - logger.debug('retrieved stamps', stamps) - - const { amount, ttlMin, depth } = config - - // Get all usable stamps sorted by usage from most used to least - this.usableStamps = filterUsableStampsExtends(stamps) - - if (!this.isBuyingStamp) { - if (this.usableStamps.length === 0) { - this.isBuyingStamp = true - try { - const { stamp: newStamp } = await buyNewStamp(depth, amount, beeDebug) - - // Add the bought postage stamp - this.usableStamps.push(newStamp) - } finally { - this.isBuyingStamp = false - } - } else { - await this.verifyUsableStamps(beeDebug, ttlMin, config, amount) + await this.refreshStampsAutobuy() + } catch (e: any) { + logger.error(e) + e.stack && logger.error(e.stack) } + await sleep(settings.stamp.checkFrequency) } - } catch (e) { - logger.error('failed to refresh on extends postage stamps', e) - } - } - - async verifyUsableStamps( - beeDebug: BeeDebug, - ttlMin: number, - config: StampsConfigAutobuy | StampsConfigExtends, - amount: string, - ) { - for (let i = 0; i < this.usableStamps!.length; i++) { - const stamp = this.usableStamps![i] - - const minTimeThreshold = ttlMin + config.refreshPeriod / 1000 - - if (stamp.batchTTL < minTimeThreshold && !this.extendingStamps.includes(stamp.batchID)) { - this.extendingStamps.push(stamp.batchID) - logger.info(`extending postage stamp ${stamp.batchID}`) - + } else if (settings.stamp.mode === 'autoextend') { + while (true) { try { - const stampRes = await topUpStamp(beeDebug, stamp.batchID, amount) - - setTimeout(() => this.completeTopUp(stampRes), 60000) + await this.refreshStampsExtends() } catch (e: any) { - // error that indicate that 2 stamps are trying to be extended at the same time. Comes out as a warning - const errorStampIndex = this.extendingStamps.indexOf(stamp.batchID) - this.extendingStamps.splice(errorStampIndex, 1) - logger.error('failed to topup postage stamp', e) + logger.error(e) + e.stack && logger.error(e.stack) } + await sleep(settings.stamp.checkFrequency) } } } - completeTopUp(stamp: PostageBatch) { - logger.info('successfully extended postage stamp', { stamp }) - // remove stamps from extending stamps array - const stampIndex = this.extendingStamps.findIndex(id => stamp.batchID === id) - this.extendingStamps.splice(stampIndex, 1) - } - - /** - * Start the manager in either hardcoded or autobuy mode - */ - async start(config: StampsConfig): Promise { - // Hardcoded stamp mode - if (config.mode === 'hardcoded') this.stamp = config.stamp - // Autobuy or ExtendsTTL mode - else { - let refreshStamps: () => Promise + get postageStamp(): string { + if (this.stamp) { + stampGetCounter.inc() + return this.stamp + } - if (config.mode === 'autobuy') { - refreshStamps = async () => this.refreshStampsAutobuy(config, new BeeDebug(config.beeDebugApiUrl)) - } else { - refreshStamps = async () => this.refreshStampsExtends(config, new BeeDebug(config.beeDebugApiUrl)) - } - this.stop() - await refreshStamps() + stampNoneCounter.inc() + throw new Error('No postage stamp') + } - this.interval = setInterval(refreshStamps, config.refreshPeriod) + public async refreshStampsAutobuy(): Promise { + const stamps = await getUsableStamps() + if (!stamps.length) { + const stamp = await buyStamp(settings.stamp.autobuy.depth, settings.stamp.autobuy.amount) + this.stamp = stamp.batchID } + this.stamp = stamps[0].batchID } - stop(): void { - if (this.interval) { - clearInterval(this.interval) - this.interval = undefined + public async refreshStampsExtends(): Promise { + const stamps = await getUsableStamps() + if (!stamps.length) { + const stamp = await buyStamp(settings.stamp.autoextend.defaultDepth, settings.stamp.autoextend.defaultAmount) + this.stamp = stamp.batchID + return + } + this.stamp = stamps[0].batchID + const expiringStamps = await getExpiringStamps() + for (const stamp of expiringStamps) { + logger.info(`Stamp TTL is ${stamp.batchTTL} which is less than ${settings.stamp.autoextend.ttlThreshold}`) + await topupStamp(stamp.batchID, settings.stamp.autoextend.extendAmount) + } + const nearlyFullStamps = await getNearlyFullStamps() + for (const stamp of nearlyFullStamps) { + logger.info(`Stamp usage is ${getUsage(stamp)} which is more than ${settings.stamp.autoextend.usageThreshold}`) + await topupStamp(stamp.batchID, stamp.amount) + await diluteStamp(stamp.batchID, stamp.depth + 1) } } } + +const stampGetCounter = new client.Counter({ + name: 'stamp_get_counter', + help: 'Amount of times a stamp was retrieved to be used', +}) +register.registerMetric(stampGetCounter) + +const stampNoneCounter = new client.Counter({ + name: 'stamp_none_counter', + help: 'Amount of times no stamp was available', +}) +register.registerMetric(stampNoneCounter) diff --git a/src/utils.ts b/src/utils.ts index 9f9c8f4e..1d96f420 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,5 +1,3 @@ -import { BeeDebug } from '@ethersphere/bee-js' - /** * Sleep for N miliseconds * @@ -20,21 +18,3 @@ export function getErrorMessage(error: unknown): string | undefined { return String(error) } - -// TODO: https://github.com/ethersphere/gateway-proxy/issues/378 (Revert when Bee 1.9.0 is released) -export async function waitForStampUsable(beeDebug: BeeDebug, batchId: string): Promise { - for (let tries = 0; tries < 60; tries++) { - try { - const batch = await beeDebug.getPostageBatch(batchId) - - if (batch.usable) { - return - } else { - await sleep(3000) - } - } catch { - await sleep(3000) - } - } - throw Error(`Stamp not found/usable: ${batchId}`) -}