From ca1c63bd3bbac06f5a348b9b88a13e7338975d62 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Sat, 26 Sep 2020 10:47:22 +0200 Subject: [PATCH 01/10] initial code porting --- .gitignore | 1 + env.js | 104 +++ index.js | 27 + logger.js | 14 + package-lock.json | 2261 +++++++++++++++++++++++++++++++++++++++++++++ package.json | 12 +- sync/client.js | 32 + 7 files changed, 2449 insertions(+), 2 deletions(-) create mode 100644 .gitignore create mode 100644 env.js create mode 100644 index.js create mode 100644 logger.js create mode 100644 package-lock.json create mode 100644 sync/client.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/env.js b/env.js new file mode 100644 index 0000000..b1ca45a --- /dev/null +++ b/env.js @@ -0,0 +1,104 @@ +const ip = require('ip') + +const ENV_TEST_BRANCH = 'TEST_BRANCH' +const ENV_TEST_CASE = 'TEST_CASE' +const ENV_TEST_GROUP_ID = 'TEST_GROUP_ID' +const ENV_TEST_GROUP_INSTANCE_COUNT = 'TEST_GROUP_INSTANCE_COUNT' +const ENV_TEST_INSTANCE_COUNT = 'TEST_INSTANCE_COUNT' +const ENV_TEST_INSTANCE_PARAMS = 'TEST_INSTANCE_PARAMS' +const ENV_TEST_INSTANCE_ROLE = 'TEST_INSTANCE_ROLE' +const ENV_TEST_OUTPUTS_PATH = 'TEST_OUTPUTS_PATH' +const ENV_TEST_PLAN = 'TEST_PLAN' +const ENV_TEST_REPO = 'TEST_REPO' +const ENV_TEST_RUN = 'TEST_RUN' +const ENV_TEST_SIDECAR = 'TEST_SIDECAR' +const ENV_TEST_START_TIME = 'TEST_START_TIME' +const ENV_TEST_SUBNET = 'TEST_SUBNET' +const ENV_TEST_TAG = 'TEST_TAG' + +function parseKeyValues (env) { + const res = {} + + for (const d of env) { + const splt = d.split('=') + if (splt.length < 2) { + throw new Error(`invalid key-value: ${d}`) + } + res[splt[0]] = splt.slice(1) + } + + return res +} + +function parseRunParams (env) { + const m = parseKeyValues(env) + return { + TestBranch: m[ENV_TEST_BRANCH], + TestCase: m[ENV_TEST_CASE], + TestGroupID: m[ENV_TEST_GROUP_ID], + TestGroupInstanceCount: Number.parseInt(m[ENV_TEST_GROUP_INSTANCE_COUNT]), + TestInstanceCount: Number.parseInt(m[ENV_TEST_INSTANCE_COUNT]), + TestInstanceParams: unpackParams(m[ENV_TEST_INSTANCE_PARAMS]), + TestInstanceRole: m[ENV_TEST_INSTANCE_ROLE], + TestOutputsPath: m[ENV_TEST_OUTPUTS_PATH], + TestPlan: m[ENV_TEST_PLAN], + TestRepo: m[ENV_TEST_REPO], + TestRun: m[ENV_TEST_RUN], + TestSidecar: m[ENV_TEST_SIDECAR] === 'true', + TestStartTime: Date.parse(ENV_TEST_START_TIME), + TestSubnet: ip.cidrSubnet(m[ENV_TEST_SUBNET]), + TestTag: m[ENV_TEST_TAG] + } +} + +function unpackParams (packed) { + const spltparams = packed.split('|') + const params = {} + + for (const s of spltparams) { + const v = s.split('=') + if (v.length !== 2) { + continue + } + params[v[0]] = v[1] + } + + return params +} + +class RunEnv { + constructor (params) { + this.runParams = params + } + + static currentRunEnv () { + return RunEnv.parseRunEnv(process.env) + } + + static parseRunEnv (env) { + const p = parseRunParams(env) + return new RunEnv(p) + } + + recordMessage () { + + } + + recordStart () { + + } + + recordSuccess () { + + } + + recordFailure () { + + } + + recordCrash () { + + } +} + +module.exports = RunEnv diff --git a/index.js b/index.js new file mode 100644 index 0000000..250e9f1 --- /dev/null +++ b/index.js @@ -0,0 +1,27 @@ +const HTTP_PORT = 6060 +const HTTP_PORT_FALLBACK = 0 + +async function invokeMap (cases) { + const runenv = {} // TODO: get current environemnt + + if (cases[runenv.testCase]) { + await invokeHelper(runenv, cases[runenv.testCase]) + } else { + throw new Error(`unrecognized test case: ${runenv.testCase}`) + } +} + +async function invoke (fn) { + const runenv = {} // TODO: get current environemnt + await invokeHelper(runenv, fn) +} + +async function invokeHelper (runenv, fn) { + // TODO: the rest of the checks + await fn(runenv) +} + +module.exports = { + invoke, + invokeMap +} diff --git a/logger.js b/logger.js new file mode 100644 index 0000000..c55adab --- /dev/null +++ b/logger.js @@ -0,0 +1,14 @@ +const { __esModule } = require('ioredis/built/redis') +const winston = require('winston') + +const logger = winston.createLogger({ + level: 'debug', + format: winston.format.json(), + transports: [ + new winston.transports.Console({ + format: winston.format.simple() + }) + ] +}) + +module.exports = logger diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..66fa35e --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2261 @@ +{ + "name": "sdk-js", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", + "dev": true + }, + "@babel/highlight": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@dabh/diagnostics": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz", + "integrity": "sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q==", + "requires": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, + "@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", + "dev": true + }, + "acorn": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.0.tgz", + "integrity": "sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w==", + "dev": true + }, + "acorn-jsx": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", + "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", + "dev": true + }, + "ajv": { + "version": "6.12.5", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.5.tgz", + "integrity": "sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag==", + "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" + } + }, + "ansi-escapes": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", + "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "dev": true, + "requires": { + "type-fest": "^0.11.0" + }, + "dependencies": { + "type-fest": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "dev": true + } + } + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "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" + } + }, + "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" + } + }, + "array-includes": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.1.tgz", + "integrity": "sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0", + "is-string": "^1.0.5" + } + }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "dev": true + }, + "async": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", + "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "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, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "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-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "dev": true + }, + "cluster-key-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz", + "integrity": "sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw==" + }, + "color": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", + "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", + "requires": { + "color-convert": "^1.9.1", + "color-string": "^1.5.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==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "color-string": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", + "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" + }, + "colorspace": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz", + "integrity": "sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ==", + "requires": { + "color": "3.0.x", + "text-hex": "1.0.x" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "contains-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", + "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "requires": { + "ms": "2.1.2" + } + }, + "debug-log": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/debug-log/-/debug-log-1.0.1.tgz", + "integrity": "sha1-IwdjLUwEOCuN+KMvcLiVBG1SdF8=", + "dev": true + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "deglob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/deglob/-/deglob-4.0.1.tgz", + "integrity": "sha512-/g+RDZ7yf2HvoW+E5Cy+K94YhgcFgr6C8LuHZD1O5HoNPkf3KY6RfXJ0DBGlB/NkLi5gml+G9zqRzk9S0mHZCg==", + "dev": true, + "requires": { + "find-root": "^1.0.0", + "glob": "^7.0.5", + "ignore": "^5.0.0", + "pkg-config": "^1.1.0", + "run-parallel": "^1.1.2", + "uniq": "^1.0.1" + }, + "dependencies": { + "ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "dev": true + } + } + }, + "denque": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/denque/-/denque-1.4.1.tgz", + "integrity": "sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ==" + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.17.6", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", + "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.0", + "is-regex": "^1.1.0", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "eslint": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.8.0.tgz", + "integrity": "sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "ajv": "^6.10.0", + "chalk": "^2.1.0", + "cross-spawn": "^6.0.5", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "eslint-scope": "^5.0.0", + "eslint-utils": "^1.4.3", + "eslint-visitor-keys": "^1.1.0", + "espree": "^6.1.2", + "esquery": "^1.0.1", + "esutils": "^2.0.2", + "file-entry-cache": "^5.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.0.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "inquirer": "^7.0.0", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.14", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.3", + "progress": "^2.0.0", + "regexpp": "^2.0.1", + "semver": "^6.1.2", + "strip-ansi": "^5.2.0", + "strip-json-comments": "^3.0.1", + "table": "^5.2.3", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + } + }, + "eslint-config-standard": { + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-14.1.1.tgz", + "integrity": "sha512-Z9B+VR+JIXRxz21udPTL9HpFMyoMUEeX1G251EQ6e05WD9aPVtVBn09XUmZ259wCMlCDmYDSZG62Hhm+ZTJcUg==", + "dev": true + }, + "eslint-config-standard-jsx": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard-jsx/-/eslint-config-standard-jsx-8.1.0.tgz", + "integrity": "sha512-ULVC8qH8qCqbU792ZOO6DaiaZyHNS/5CZt3hKqHkEhVlhPEPN3nfBqqxJCyp59XrjIBZPu1chMYe9T2DXZ7TMw==", + "dev": true + }, + "eslint-import-resolver-node": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz", + "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "resolve": "^1.13.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "eslint-module-utils": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz", + "integrity": "sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "pkg-dir": "^2.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "eslint-plugin-es": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-2.0.0.tgz", + "integrity": "sha512-f6fceVtg27BR02EYnBhgWLFQfK6bN4Ll0nQFrBHOlCsAyxeZkn0NHns5O0YZOPrV1B3ramd6cgFwaoFLcSkwEQ==", + "dev": true, + "requires": { + "eslint-utils": "^1.4.2", + "regexpp": "^3.0.0" + }, + "dependencies": { + "regexpp": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", + "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", + "dev": true + } + } + }, + "eslint-plugin-import": { + "version": "2.18.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.18.2.tgz", + "integrity": "sha512-5ohpsHAiUBRNaBWAF08izwUGlbrJoJJ+W9/TBwsGoR1MnlgfwMIKrFeSjWbt6moabiXW9xNvtFz+97KHRfI4HQ==", + "dev": true, + "requires": { + "array-includes": "^3.0.3", + "contains-path": "^0.1.0", + "debug": "^2.6.9", + "doctrine": "1.5.0", + "eslint-import-resolver-node": "^0.3.2", + "eslint-module-utils": "^2.4.0", + "has": "^1.0.3", + "minimatch": "^3.0.4", + "object.values": "^1.1.0", + "read-pkg-up": "^2.0.0", + "resolve": "^1.11.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "doctrine": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "isarray": "^1.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "eslint-plugin-node": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-10.0.0.tgz", + "integrity": "sha512-1CSyM/QCjs6PXaT18+zuAXsjXGIGo5Rw630rSKwokSs2jrYURQc4R5JZpoanNCqwNmepg+0eZ9L7YiRUJb8jiQ==", + "dev": true, + "requires": { + "eslint-plugin-es": "^2.0.0", + "eslint-utils": "^1.4.2", + "ignore": "^5.1.1", + "minimatch": "^3.0.4", + "resolve": "^1.10.1", + "semver": "^6.1.0" + }, + "dependencies": { + "ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "dev": true + } + } + }, + "eslint-plugin-promise": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-4.2.1.tgz", + "integrity": "sha512-VoM09vT7bfA7D+upt+FjeBO5eHIJQBUWki1aPvB+vbNiHS3+oGIJGIeyBtKQTME6UPXXy3vV07OL1tHd3ANuDw==", + "dev": true + }, + "eslint-plugin-react": { + "version": "7.14.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.14.3.tgz", + "integrity": "sha512-EzdyyBWC4Uz2hPYBiEJrKCUi2Fn+BJ9B/pJQcjw5X+x/H2Nm59S4MJIvL4O5NEE0+WbnQwEBxWY03oUk+Bc3FA==", + "dev": true, + "requires": { + "array-includes": "^3.0.3", + "doctrine": "^2.1.0", + "has": "^1.0.3", + "jsx-ast-utils": "^2.1.0", + "object.entries": "^1.1.0", + "object.fromentries": "^2.0.0", + "object.values": "^1.1.0", + "prop-types": "^15.7.2", + "resolve": "^1.10.1" + }, + "dependencies": { + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + } + } + }, + "eslint-plugin-standard": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-4.0.1.tgz", + "integrity": "sha512-v/KBnfyaOMPmZc/dmc6ozOdWqekGp7bBGq4jLAecEfPGmfKiWS4sA8sC0LqiV9w5qmXAtXVn4M3p1jSyhY85SQ==", + "dev": true + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", + "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + }, + "espree": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz", + "integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==", + "dev": true, + "requires": { + "acorn": "^7.1.1", + "acorn-jsx": "^5.2.0", + "eslint-visitor-keys": "^1.1.0" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esquery": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", + "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "fast-safe-stringify": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", + "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==" + }, + "fecha": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.0.tgz", + "integrity": "sha512-aN3pcx/DSmtyoovUudctc8+6Hl4T+hI9GBBHLjA76jdZl7+b1sgh5g4k+u/GL3dTy1/pnYzKp69FpJ0OicE3Wg==" + }, + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-entry-cache": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", + "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "dev": true, + "requires": { + "flat-cache": "^2.0.1" + } + }, + "find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "dev": true + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "flat-cache": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "dev": true, + "requires": { + "flatted": "^2.0.0", + "rimraf": "2.6.3", + "write": "1.0.3" + } + }, + "flatted": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", + "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", + "dev": true + }, + "fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "get-stdin": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-7.0.0.tgz", + "integrity": "sha512-zRKcywvrXlXsA0v0i9Io4KDRaAw7+a1ZpjRwl9Wox8PFlVCCHra7E9c4kqXCoCM9nR5tBkaTTZRBoCm60bFqTQ==", + "dev": true + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "globals": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "dev": true, + "requires": { + "type-fest": "^0.8.1" + } + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, + "hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "dev": true + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "import-fresh": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", + "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "inquirer": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz", + "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.19", + "mute-stream": "0.0.8", + "run-async": "^2.4.0", + "rxjs": "^6.6.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "ioredis": { + "version": "4.17.3", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-4.17.3.tgz", + "integrity": "sha512-iRvq4BOYzNFkDnSyhx7cmJNOi1x/HWYe+A4VXHBu4qpwJaGT1Mp+D2bVGJntH9K/Z/GeOM/Nprb8gB3bmitz1Q==", + "requires": { + "cluster-key-slot": "^1.1.0", + "debug": "^4.1.1", + "denque": "^1.1.0", + "lodash.defaults": "^4.2.0", + "lodash.flatten": "^4.4.0", + "redis-commands": "1.5.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0", + "standard-as-callback": "^2.0.1" + }, + "dependencies": { + "redis-commands": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.5.0.tgz", + "integrity": "sha512-6KxamqpZ468MeQC3bkWmCB1fp56XL64D4Kf0zJSwDZbVLLm7KFkoIcHrgRvQ+sk8dnhySs7+yBg94yIkAK7aJg==" + } + } + }, + "ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-callable": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==", + "dev": true + }, + "is-date-object": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-negative-zero": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz", + "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=", + "dev": true + }, + "is-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } + }, + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" + }, + "is-string": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", + "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", + "dev": true + }, + "is-symbol": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-yaml": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", + "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "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 + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "jsx-ast-utils": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.4.1.tgz", + "integrity": "sha512-z1xSldJ6imESSzOjd3NNkieVJKRlKYSOtMG8SFyCj2FIrvSaSuli/WjpBkEzCBoR9bYYYFgqJw61Xhu7Lcgk+w==", + "dev": true, + "requires": { + "array-includes": "^3.1.1", + "object.assign": "^4.1.0" + } + }, + "kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", + "dev": true + }, + "lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" + }, + "lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" + }, + "logform": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.2.0.tgz", + "integrity": "sha512-N0qPlqfypFx7UHNn4B3lzS/b0uLqt2hmuoa+PpuXNYgozdJYAyauF5Ky0BWVjrxDlMWiT3qN4zPq3vVAfZy7Yg==", + "requires": { + "colors": "^1.2.1", + "fast-safe-stringify": "^2.0.4", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "triple-beam": "^1.3.0" + } + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object-inspect": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", + "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object.assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz", + "integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.0", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + }, + "dependencies": { + "es-abstract": { + "version": "1.18.0-next.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.0.tgz", + "integrity": "sha512-elZXTZXKn51hUBdJjSZGYRujuzilgXo8vSPQzjGYXLvSlGiCo8VO8ZGV3kjo9a0WNJJ57hENagwbtlRuHuzkcQ==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.0", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + } + } + }, + "object.entries": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.2.tgz", + "integrity": "sha512-BQdB9qKmb/HyNdMNWVr7O3+z5MUIx3aiegEIJqjMBbBf0YT9RRxTJSim4mzFqtyr7PDAHigq0N9dO0m0tRakQA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5", + "has": "^1.0.3" + } + }, + "object.fromentries": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.2.tgz", + "integrity": "sha512-r3ZiBH7MQppDJVLx6fhD618GKNG40CZYH9wgwdhKxBDDbQgjeWGGd4AtkZad84d291YxvWe7bJGuE65Anh0dxQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, + "object.values": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz", + "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "requires": { + "fn.name": "1.x.x" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "dev": true, + "requires": { + "pify": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "pkg-conf": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-3.1.0.tgz", + "integrity": "sha512-m0OTbR/5VPNPqO1ph6Fqbj7Hv6QU7gR/tQW40ZqrL1rjgCU85W6C1bJn0BItuJqnR98PWzw7Z8hHeChD1WrgdQ==", + "dev": true, + "requires": { + "find-up": "^3.0.0", + "load-json-file": "^5.2.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "load-json-file": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-5.3.0.tgz", + "integrity": "sha512-cJGP40Jc/VXUsp8/OrnyKyTZ1y6v/dphm3bioS+RrKXjK2BB6wHUd6JptZEFDGgGahMT+InnZO5i1Ei9mpC8Bw==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.15", + "parse-json": "^4.0.0", + "pify": "^4.0.1", + "strip-bom": "^3.0.0", + "type-fest": "^0.3.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "type-fest": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz", + "integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==", + "dev": true + } + } + }, + "pkg-config": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pkg-config/-/pkg-config-1.1.1.tgz", + "integrity": "sha1-VX7yLXPaPIg3EHdmxS6tq94pj+Q=", + "dev": true, + "requires": { + "debug-log": "^1.0.0", + "find-root": "^1.0.0", + "xtend": "^4.0.1" + } + }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "dev": true, + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true + }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "dev": true, + "requires": { + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" + } + }, + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=" + }, + "redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=", + "requires": { + "redis-errors": "^1.0.0" + } + }, + "regexpp": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "dev": true + }, + "resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "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" + } + }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true + }, + "run-parallel": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz", + "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==", + "dev": true + }, + "rxjs": { + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz", + "integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "dev": true + }, + "simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "requires": { + "is-arrayish": "^0.3.1" + }, + "dependencies": { + "is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + } + } + }, + "slice-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + } + } + }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.6.tgz", + "integrity": "sha512-+orQK83kyMva3WyPf59k1+Y525csj5JejicWut55zeTWANuN17qSiSLUXWtzHeNWORSvT7GLDJ/E/XiIWoXBTw==", + "dev": true + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" + }, + "standard": { + "version": "14.3.4", + "resolved": "https://registry.npmjs.org/standard/-/standard-14.3.4.tgz", + "integrity": "sha512-+lpOkFssMkljJ6eaILmqxHQ2n4csuEABmcubLTb9almFi1ElDzXb1819fjf/5ygSyePCq4kU2wMdb2fBfb9P9Q==", + "dev": true, + "requires": { + "eslint": "~6.8.0", + "eslint-config-standard": "14.1.1", + "eslint-config-standard-jsx": "8.1.0", + "eslint-plugin-import": "~2.18.0", + "eslint-plugin-node": "~10.0.0", + "eslint-plugin-promise": "~4.2.1", + "eslint-plugin-react": "~7.14.2", + "eslint-plugin-standard": "~4.0.0", + "standard-engine": "^12.0.0" + } + }, + "standard-as-callback": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.0.1.tgz", + "integrity": "sha512-NQOxSeB8gOI5WjSaxjBgog2QFw55FV8TkS6Y07BiB3VJ8xNTvUYm0wl0s8ObgQ5NhdpnNfigMIKjgPESzgr4tg==" + }, + "standard-engine": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/standard-engine/-/standard-engine-12.1.0.tgz", + "integrity": "sha512-DVJnWM1CGkag4ucFLGdiYWa5/kJURPONmMmk17p8FT5NE4UnPZB1vxWnXnRo2sPSL78pWJG8xEM+1Tu19z0deg==", + "dev": true, + "requires": { + "deglob": "^4.0.1", + "get-stdin": "^7.0.0", + "minimist": "^1.2.5", + "pkg-conf": "^3.1.0" + } + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + } + } + }, + "string.prototype.trimend": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", + "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "string.prototype.trimstart": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", + "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + } + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "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" + } + }, + "table": { + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", + "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "dev": true, + "requires": { + "ajv": "^6.10.2", + "lodash": "^4.17.14", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + }, + "dependencies": { + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + } + } + }, + "text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "triple-beam": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", + "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" + }, + "tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "dev": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + }, + "uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", + "dev": true + }, + "uri-js": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", + "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "v8-compile-cache": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz", + "integrity": "sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ==", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "winston": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.3.3.tgz", + "integrity": "sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw==", + "requires": { + "@dabh/diagnostics": "^2.0.2", + "async": "^3.1.0", + "is-stream": "^2.0.0", + "logform": "^2.2.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.4.0" + } + }, + "winston-transport": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.4.0.tgz", + "integrity": "sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw==", + "requires": { + "readable-stream": "^2.3.7", + "triple-beam": "^1.2.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "write": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", + "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", + "dev": true, + "requires": { + "mkdirp": "^0.5.1" + } + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + } + } +} diff --git a/package.json b/package.json index 7d0b29b..0eeea52 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "js-sdk", + "name": "sdk-js", "version": "1.0.0", "description": "", "main": "index.js", @@ -17,5 +17,13 @@ "bugs": { "url": "https://github.com/testground/sdk-js/issues" }, - "homepage": "https://github.com/testground/sdk-js#readme" + "homepage": "https://github.com/testground/sdk-js#readme", + "devDependencies": { + "standard": "^14.3.4" + }, + "dependencies": { + "ioredis": "^4.17.3", + "ip": "^1.1.5", + "winston": "^3.3.3" + } } diff --git a/sync/client.js b/sync/client.js new file mode 100644 index 0000000..be374e2 --- /dev/null +++ b/sync/client.js @@ -0,0 +1,32 @@ +const Redis = require('ioredis') +const logger = require('../logger') + +const ENV_REDIS_HOST = 'REDIS_HOST' +const ENV_REDIS_PORT = 'REDIS_PORT' + +async function redisClient () { + let port = 6379 + const host = process.env[ENV_REDIS_HOST] + + if (process.env[ENV_REDIS_PORT]) { + port = Number.parseInt(process.env[ENV_REDIS_PORT]) + } + + logger.debug('trying redis host', { host, port }) + + const opts = { + port, + host, + maxRetriesPerRequest: 30 + } + + const client = new Redis(opts) + + // Check if connection works + if (await client.ping() !== 'PONG') { + throw new Error('failed to ping redis instance') + } + + logger.debug('redis ping OK', opts) + return client +} From 68ba5ab4ca511a13b3c9efd778bba194238d6b1c Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Fri, 2 Oct 2020 11:16:27 +0200 Subject: [PATCH 02/10] cleanup --- consts.js | 0 network.js | 19 +++++++++++++++++++ index.js => run.js | 4 ++-- env.js => runtime/index.js | 34 +++++++++++++++++++--------------- 4 files changed, 40 insertions(+), 17 deletions(-) create mode 100644 consts.js create mode 100644 network.js rename index.js => run.js (89%) rename env.js => runtime/index.js (88%) diff --git a/consts.js b/consts.js new file mode 100644 index 0000000..e69de29 diff --git a/network.js b/network.js new file mode 100644 index 0000000..185e88a --- /dev/null +++ b/network.js @@ -0,0 +1,19 @@ +class NetworkClient { + constructor () { + // TODO + } + + async waitNetworkInitialized () { + // TODO + } + + async configureNetwork () { + // TODO + } + + async getDataNetworkIP () { + // TODO + } +} + +module.exports = NetworkClient diff --git a/index.js b/run.js similarity index 89% rename from index.js rename to run.js index 250e9f1..0e66903 100644 --- a/index.js +++ b/run.js @@ -1,5 +1,5 @@ -const HTTP_PORT = 6060 -const HTTP_PORT_FALLBACK = 0 +/* const HTTP_PORT = 6060 +const HTTP_PORT_FALLBACK = 0 */ async function invokeMap (cases) { const runenv = {} // TODO: get current environemnt diff --git a/env.js b/runtime/index.js similarity index 88% rename from env.js rename to runtime/index.js index b1ca45a..d256e86 100644 --- a/env.js +++ b/runtime/index.js @@ -66,39 +66,43 @@ function unpackParams (packed) { return params } +function currentRunEnv () { + return parseRunEnv(process.env) +} + +function parseRunEnv (env) { + const p = parseRunParams(env) + return new RunEnv(p) +} + class RunEnv { constructor (params) { this.runParams = params } - static currentRunEnv () { - return RunEnv.parseRunEnv(process.env) - } - - static parseRunEnv (env) { - const p = parseRunParams(env) - return new RunEnv(p) - } - recordMessage () { - + // TODO } recordStart () { - + // TODO } recordSuccess () { - + // TODO } recordFailure () { - + // TODO } recordCrash () { - + // TODO } } -module.exports = RunEnv +module.exports = { + RunEnv, + currentRunEnv, + parseRunEnv +} From fff081911f798170a0e788b99968db16ee298962 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Fri, 2 Oct 2020 12:20:06 +0200 Subject: [PATCH 03/10] updates and fixes --- example-plan/env | 15 +++ example-plan/index.js | 216 ++++++++++++++++++++++++++++++++++++++++++ example-plan/teste.js | 31 ++++++ run.js => index.js | 5 +- runtime/index.js | 49 ++++------ 5 files changed, 283 insertions(+), 33 deletions(-) create mode 100644 example-plan/env create mode 100644 example-plan/index.js create mode 100644 example-plan/teste.js rename run.js => index.js (80%) diff --git a/example-plan/env b/example-plan/env new file mode 100644 index 0000000..9f67797 --- /dev/null +++ b/example-plan/env @@ -0,0 +1,15 @@ +export TEST_BRANCH= +export TEST_CASE=pingpong +export TEST_GROUP_ID= +export TEST_GROUP_INSTANCE_COUNT= +export TEST_INSTANCE_COUNT= +export TEST_INSTANCE_PARAMS= +export TEST_INSTANCE_ROLE= +export TEST_OUTPUTS_PATH= +export TEST_PLAN= +export TEST_REPO= +export TEST_RUN= +export TEST_SIDECAR= +export TEST_START_TIME= +export TEST_SUBNET=127.0.0.1/24 +export TEST_TAG= diff --git a/example-plan/index.js b/example-plan/index.js new file mode 100644 index 0000000..46a4ab5 --- /dev/null +++ b/example-plan/index.js @@ -0,0 +1,216 @@ +// This is an example plan. This plan is being used to test out the +// public API of this package. It may not be the best example as it +// will change over time will the package is produced. In the end, +// this will be deleted. + +const os = require('os') +const { invokeMap } = require('../') + +const testcases = { + pingpong: pingpong +} + +;(async () => { + // This is the plan entry point. + await invokeMap(testcases) +})() + +// TODO +const sync = {} +const network = {} + +async function pingpong (runenv) { + runenv.recordMessage('before sync.MustBoundClient') + const client = await sync.mustBoundClient(runenv) + + if (!runenv.testSidecar) { + return + } + + const netclient = await network.newClient(client, runenv) + runenv.recordMessage('before netclient.MustWaitNetworkInitialized') + await netclient.waitNetworkInitialized() + + const oldAddrs = os.networkInterfaces() + + const config = { + network: 'default', + enable: true, + default: { + latency: 100, // in milliseconds + badwidth: 1 << 20 // 1 Mib + }, + callbackState: 'network-configured', + routingPolicy: 'deny-all' // TODO: make constants + } + + runenv.recordMessage('before netclient.MustConfigureNetwork') + await netclient.configureNetwork(config) + + const seq = await client.signalAndWait('ip-allocation', runenv.testInstanceCount) + + // Make sure that the IP addresses don't change unless we request it. + const newAddrs = os.networkInterfaces() + if (!sameAddrs(oldAddrs, newAddrs)) { + throw new Error('interfaces changed') + } + + runenv.recordMessage(`I am ${seq}`) + + const ipC = (seq >> 8) + 1 + const ipD = seq + + /* + + config.IPv4 = &runenv.TestSubnet.IPNet + config.IPv4.IP = append(config.IPv4.IP[0:2:2], ipC, ipD) + config.CallbackState = "ip-changed" + + var ( + listener *net.TCPListener + conn *net.TCPConn + ) + + if seq == 1 { + listener, err = net.ListenTCP("tcp4", &net.TCPAddr{Port: 1234}) + if err != nil { + return err + } + defer listener.Close() + } + + runenv.RecordMessage("before reconfiguring network") + netclient.MustConfigureNetwork(ctx, config) + + switch seq { + case 1: + conn, err = listener.AcceptTCP() + case 2: + conn, err = net.DialTCP("tcp4", nil, &net.TCPAddr{ + IP: append(config.IPv4.IP[:3:3], 1), + Port: 1234, + }) + default: + return fmt.Errorf("expected at most two test instances") + } + if err != nil { + return err + } + + defer conn.Close() + + // trying to measure latency here. + err = conn.SetNoDelay(true) + if err != nil { + return err + } + + pingPong := func(test string, rttMin, rttMax time.Duration) error { + buf := make([]byte, 1) + + runenv.RecordMessage("waiting until ready") + + // wait till both sides are ready + _, err = conn.Write([]byte{0}) + if err != nil { + return err + } + + _, err = conn.Read(buf) + if err != nil { + return err + } + + start := time.Now() + + // write sequence number. + runenv.RecordMessage("writing my id") + _, err = conn.Write([]byte{byte(seq)}) + if err != nil { + return err + } + + // pong other sequence number + runenv.RecordMessage("reading their id") + _, err = conn.Read(buf) + if err != nil { + return err + } + + runenv.RecordMessage("returning their id") + _, err = conn.Write(buf) + if err != nil { + return err + } + + runenv.RecordMessage("reading my id") + // read our sequence number + _, err = conn.Read(buf) + if err != nil { + return err + } + + runenv.RecordMessage("done") + + // stop + end := time.Now() + + // check the sequence number. + if buf[0] != byte(seq) { + return fmt.Errorf("read unexpected value") + } + + // check the RTT + rtt := end.Sub(start) + if rtt < rttMin || rtt > rttMax { + return fmt.Errorf("expected an RTT between %s and %s, got %s", rttMin, rttMax, rtt) + } + runenv.RecordMessage("ping RTT was %s [%s, %s]", rtt, rttMin, rttMax) + + // Don't reconfigure the network until we're done with the first test. + state := sync.State("ping-pong-" + test) + client.MustSignalAndWait(ctx, state, runenv.TestInstanceCount) + + return nil + } + err = pingPong("200", 200*time.Millisecond, 215*time.Millisecond) + if err != nil { + return err + } + + config.Default.Latency = 10 * time.Millisecond + config.CallbackState = "latency-reduced" + netclient.MustConfigureNetwork(ctx, config) + + runenv.RecordMessage("ping pong") + err = pingPong("10", 20*time.Millisecond, 35*time.Millisecond) + if err != nil { + return err + } + */ +} + +function sameAddrs (a, b) { + if (a.length !== b.length) { + return false + } + + a = Object.values(a) + .flat() + .reduce((acc, curr) => { + if (!acc.includes(curr.cidr)) { + acc.push(curr.cidr) + } + return acc + }, []) + + b = Object.values(b).flat() + + for (const { cidr } of b) { + if (!a.includes(cidr)) { + return false + } + } + + return true +} diff --git a/example-plan/teste.js b/example-plan/teste.js new file mode 100644 index 0000000..2a2fd23 --- /dev/null +++ b/example-plan/teste.js @@ -0,0 +1,31 @@ + +const os = require('os') + +console.log( + sameAddrs(os.networkInterfaces(), os.networkInterfaces()) +) + +function sameAddrs (a, b) { + if (a.length !== b.length) { + return false + } + + a = Object.values(a) + .flat() + .reduce((acc, curr) => { + if (!acc.includes(curr.cidr)) { + acc.push(curr.cidr) + } + return acc + }, []) + + b = Object.values(b).flat() + + for (const { cidr } of b) { + if (!a.includes(cidr)) { + return false + } + } + + return true +} diff --git a/run.js b/index.js similarity index 80% rename from run.js rename to index.js index 0e66903..5135395 100644 --- a/run.js +++ b/index.js @@ -1,8 +1,7 @@ -/* const HTTP_PORT = 6060 -const HTTP_PORT_FALLBACK = 0 */ +const runtime = require('./runtime') async function invokeMap (cases) { - const runenv = {} // TODO: get current environemnt + const runenv = runtime.currentRunEnv() if (cases[runenv.testCase]) { await invokeHelper(runenv, cases[runenv.testCase]) diff --git a/runtime/index.js b/runtime/index.js index d256e86..c9e2c19 100644 --- a/runtime/index.js +++ b/runtime/index.js @@ -16,38 +16,23 @@ const ENV_TEST_START_TIME = 'TEST_START_TIME' const ENV_TEST_SUBNET = 'TEST_SUBNET' const ENV_TEST_TAG = 'TEST_TAG' -function parseKeyValues (env) { - const res = {} - - for (const d of env) { - const splt = d.split('=') - if (splt.length < 2) { - throw new Error(`invalid key-value: ${d}`) - } - res[splt[0]] = splt.slice(1) - } - - return res -} - function parseRunParams (env) { - const m = parseKeyValues(env) return { - TestBranch: m[ENV_TEST_BRANCH], - TestCase: m[ENV_TEST_CASE], - TestGroupID: m[ENV_TEST_GROUP_ID], - TestGroupInstanceCount: Number.parseInt(m[ENV_TEST_GROUP_INSTANCE_COUNT]), - TestInstanceCount: Number.parseInt(m[ENV_TEST_INSTANCE_COUNT]), - TestInstanceParams: unpackParams(m[ENV_TEST_INSTANCE_PARAMS]), - TestInstanceRole: m[ENV_TEST_INSTANCE_ROLE], - TestOutputsPath: m[ENV_TEST_OUTPUTS_PATH], - TestPlan: m[ENV_TEST_PLAN], - TestRepo: m[ENV_TEST_REPO], - TestRun: m[ENV_TEST_RUN], - TestSidecar: m[ENV_TEST_SIDECAR] === 'true', - TestStartTime: Date.parse(ENV_TEST_START_TIME), - TestSubnet: ip.cidrSubnet(m[ENV_TEST_SUBNET]), - TestTag: m[ENV_TEST_TAG] + testBranch: env[ENV_TEST_BRANCH], + testCase: env[ENV_TEST_CASE], + testGroupID: env[ENV_TEST_GROUP_ID], + testGroupInstanceCount: Number.parseInt(env[ENV_TEST_GROUP_INSTANCE_COUNT]), + testInstanceCount: Number.parseInt(env[ENV_TEST_INSTANCE_COUNT]), + testInstanceParams: unpackParams(env[ENV_TEST_INSTANCE_PARAMS]), + testInstanceRole: env[ENV_TEST_INSTANCE_ROLE], + testOutputsPath: env[ENV_TEST_OUTPUTS_PATH], + testPlan: env[ENV_TEST_PLAN], + testRepo: env[ENV_TEST_REPO], + testRun: env[ENV_TEST_RUN], + testSidecar: env[ENV_TEST_SIDECAR] === 'true', + testStartTime: Date.parse(ENV_TEST_START_TIME), + testSubnet: ip.cidrSubnet(env[ENV_TEST_SUBNET]), + testTag: env[ENV_TEST_TAG] } } @@ -78,6 +63,10 @@ function parseRunEnv (env) { class RunEnv { constructor (params) { this.runParams = params + + for (const param in params) { + this[param] = params[param] + } } recordMessage () { From 0788d25e10b4cc5215003062fc576bdc95d056bc Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Fri, 2 Oct 2020 14:14:44 +0200 Subject: [PATCH 04/10] defined externnal structure --- example-plan/index.js | 9 +++------ example-plan/teste.js | 31 ---------------------------- index.js | 10 +++++++-- network.js | 19 ----------------- network/index.js | 17 ++++++++++++++++ runtime/index.js | 47 +++++++++++++++++-------------------------- sync/client.js | 4 ++++ sync/index.js | 40 ++++++++++++++++++++++++++++++++++++ 8 files changed, 91 insertions(+), 86 deletions(-) delete mode 100644 example-plan/teste.js delete mode 100644 network.js create mode 100644 network/index.js create mode 100644 sync/index.js diff --git a/example-plan/index.js b/example-plan/index.js index 46a4ab5..6445bea 100644 --- a/example-plan/index.js +++ b/example-plan/index.js @@ -4,7 +4,7 @@ // this will be deleted. const os = require('os') -const { invokeMap } = require('../') +const { invokeMap, sync, network } = require('../') const testcases = { pingpong: pingpong @@ -16,18 +16,15 @@ const testcases = { })() // TODO -const sync = {} -const network = {} - async function pingpong (runenv) { runenv.recordMessage('before sync.MustBoundClient') - const client = await sync.mustBoundClient(runenv) + const client = sync.newBoundClient(runenv) if (!runenv.testSidecar) { return } - const netclient = await network.newClient(client, runenv) + const netclient = network.newClient(client, runenv) runenv.recordMessage('before netclient.MustWaitNetworkInitialized') await netclient.waitNetworkInitialized() diff --git a/example-plan/teste.js b/example-plan/teste.js deleted file mode 100644 index 2a2fd23..0000000 --- a/example-plan/teste.js +++ /dev/null @@ -1,31 +0,0 @@ - -const os = require('os') - -console.log( - sameAddrs(os.networkInterfaces(), os.networkInterfaces()) -) - -function sameAddrs (a, b) { - if (a.length !== b.length) { - return false - } - - a = Object.values(a) - .flat() - .reduce((acc, curr) => { - if (!acc.includes(curr.cidr)) { - acc.push(curr.cidr) - } - return acc - }, []) - - b = Object.values(b).flat() - - for (const { cidr } of b) { - if (!a.includes(cidr)) { - return false - } - } - - return true -} diff --git a/index.js b/index.js index 5135395..af0b089 100644 --- a/index.js +++ b/index.js @@ -1,4 +1,6 @@ const runtime = require('./runtime') +const sync = require('./sync') +const network = require('./network') async function invokeMap (cases) { const runenv = runtime.currentRunEnv() @@ -11,7 +13,7 @@ async function invokeMap (cases) { } async function invoke (fn) { - const runenv = {} // TODO: get current environemnt + const runenv = runtime.currentRunEnv() await invokeHelper(runenv, fn) } @@ -22,5 +24,9 @@ async function invokeHelper (runenv, fn) { module.exports = { invoke, - invokeMap + invokeMap, + + network, + runtime, + sync } diff --git a/network.js b/network.js deleted file mode 100644 index 185e88a..0000000 --- a/network.js +++ /dev/null @@ -1,19 +0,0 @@ -class NetworkClient { - constructor () { - // TODO - } - - async waitNetworkInitialized () { - // TODO - } - - async configureNetwork () { - // TODO - } - - async getDataNetworkIP () { - // TODO - } -} - -module.exports = NetworkClient diff --git a/network/index.js b/network/index.js new file mode 100644 index 0000000..deabe3a --- /dev/null +++ b/network/index.js @@ -0,0 +1,17 @@ +function newClient (client, runenv) { + return { + configureNetwork: () => { + // TODO + }, + getDataNetworkIP: () => { + // TODO + }, + waitNetworkInitialized: () => { + // TODO + } + } +} + +module.exports = { + newClient +} diff --git a/runtime/index.js b/runtime/index.js index c9e2c19..daf58c8 100644 --- a/runtime/index.js +++ b/runtime/index.js @@ -57,41 +57,32 @@ function currentRunEnv () { function parseRunEnv (env) { const p = parseRunParams(env) - return new RunEnv(p) + return newRunEnv(p) } -class RunEnv { - constructor (params) { - this.runParams = params - - for (const param in params) { - this[param] = params[param] +function newRunEnv (params) { + return { + ...params, + recordMessage: () => { + // TODO + }, + recordStart: () => { + // TODO + }, + recordSuccess: () => { + // TODO + }, + recordFailure: () => { + // TODO + }, + recordCrash: () => { + // TODO } } - - recordMessage () { - // TODO - } - - recordStart () { - // TODO - } - - recordSuccess () { - // TODO - } - - recordFailure () { - // TODO - } - - recordCrash () { - // TODO - } } module.exports = { - RunEnv, + newRunEnv, currentRunEnv, parseRunEnv } diff --git a/sync/client.js b/sync/client.js index be374e2..7bb6ab1 100644 --- a/sync/client.js +++ b/sync/client.js @@ -30,3 +30,7 @@ async function redisClient () { logger.debug('redis ping OK', opts) return client } + +module.exports = { + redisClient +} diff --git a/sync/index.js b/sync/index.js new file mode 100644 index 0000000..5e1e994 --- /dev/null +++ b/sync/index.js @@ -0,0 +1,40 @@ +function newBoundClient (runenv) { + // TODO + return newClient() +} + +function newGenericClient (logger) { + // TODO + return newClient() +} + +function newClient () { + return { + barrier: (state, target) => { + // TODO + }, + publish: (topic, payload) => { + // TODO + }, + publishAndWait: (topic, payload, state, target) => { + // TODO + }, + publishSubscribe: (topic, payload) => { + // TODO + }, + signalAndWait: (state, target) => { + // TODO + }, + signalEntry: (state) => { + // TODO + }, + subscribe: (topic) => { + // TODO + } + } +} + +module.exports = { + newBoundClient, + newGenericClient +} From 9a383b2ca529635a0d08e96d731f456152031eaa Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Fri, 2 Oct 2020 16:11:18 +0200 Subject: [PATCH 05/10] cleaning and defining --- consts.js | 0 example-plan/index.js | 2 +- logger.js | 1 - runtime/events.js | 63 ++++++++++++++++++++++++ runtime/index.js | 84 +++++++------------------------- runtime/params.js | 93 ++++++++++++++++++++++++++++++++++++ sync/index.js | 50 +++++++++++++++++-- sync/{client.js => redis.js} | 3 +- 8 files changed, 223 insertions(+), 73 deletions(-) delete mode 100644 consts.js create mode 100644 runtime/events.js create mode 100644 runtime/params.js rename sync/{client.js => redis.js} (90%) diff --git a/consts.js b/consts.js deleted file mode 100644 index e69de29..0000000 diff --git a/example-plan/index.js b/example-plan/index.js index 6445bea..38c6f28 100644 --- a/example-plan/index.js +++ b/example-plan/index.js @@ -18,7 +18,7 @@ const testcases = { // TODO async function pingpong (runenv) { runenv.recordMessage('before sync.MustBoundClient') - const client = sync.newBoundClient(runenv) + const client = await sync.newBoundClient(runenv) if (!runenv.testSidecar) { return diff --git a/logger.js b/logger.js index c55adab..8cd2d02 100644 --- a/logger.js +++ b/logger.js @@ -1,4 +1,3 @@ -const { __esModule } = require('ioredis/built/redis') const winston = require('winston') const logger = winston.createLogger({ diff --git a/runtime/events.js b/runtime/events.js new file mode 100644 index 0000000..8b4e9ef --- /dev/null +++ b/runtime/events.js @@ -0,0 +1,63 @@ +const EVENT_TYPE_START = 'start' +const EVENT_TYPE_MESSAGE = 'message' +const EVENT_TYPE_FINISH = 'finish' + +const EVENT_OUTCOME_OK = 'OK' +const EVENT_OUTCOME_FAILED = 'failed' +const EVENT_OUTCOME_CRASHED = 'crashed' + +function newEvents ({ logger, runParams }) { + return { + recordMessage: (msg) => { + const event = { + type: EVENT_TYPE_MESSAGE, + message: msg + } + + logger.info('', { event }) + }, + recordStart: () => { + const event = { + type: EVENT_TYPE_START, + runenv: runParams + } + + logger.info('', { event }) + // TODO: re.metrics.recordEvent(&evt) + }, + recordSuccess: () => { + const event = { + type: EVENT_TYPE_FINISH, + outcome: EVENT_OUTCOME_OK + } + + logger.info('', { event }) + // TODO: re.metrics.recordEvent(&evt) + }, + recordFailure: (err) => { + const event = { + type: EVENT_TYPE_FINISH, + outcome: EVENT_OUTCOME_FAILED, + error: err.toString() + } + + logger.info('', { event }) + // TODO: re.metrics.recordEvent(&evt) + }, + recordCrash: (err) => { + const event = { + type: EVENT_TYPE_FINISH, + outcome: EVENT_OUTCOME_CRASHED, + error: err.toString(), + stacktrace: err.stack + } + + logger.info('', { event }) + // TODO: re.metrics.recordEvent(&evt) + } + } +} + +module.exports = { + newEvents +} diff --git a/runtime/index.js b/runtime/index.js index daf58c8..4159e50 100644 --- a/runtime/index.js +++ b/runtime/index.js @@ -1,55 +1,7 @@ -const ip = require('ip') +const winston = require('winston') -const ENV_TEST_BRANCH = 'TEST_BRANCH' -const ENV_TEST_CASE = 'TEST_CASE' -const ENV_TEST_GROUP_ID = 'TEST_GROUP_ID' -const ENV_TEST_GROUP_INSTANCE_COUNT = 'TEST_GROUP_INSTANCE_COUNT' -const ENV_TEST_INSTANCE_COUNT = 'TEST_INSTANCE_COUNT' -const ENV_TEST_INSTANCE_PARAMS = 'TEST_INSTANCE_PARAMS' -const ENV_TEST_INSTANCE_ROLE = 'TEST_INSTANCE_ROLE' -const ENV_TEST_OUTPUTS_PATH = 'TEST_OUTPUTS_PATH' -const ENV_TEST_PLAN = 'TEST_PLAN' -const ENV_TEST_REPO = 'TEST_REPO' -const ENV_TEST_RUN = 'TEST_RUN' -const ENV_TEST_SIDECAR = 'TEST_SIDECAR' -const ENV_TEST_START_TIME = 'TEST_START_TIME' -const ENV_TEST_SUBNET = 'TEST_SUBNET' -const ENV_TEST_TAG = 'TEST_TAG' - -function parseRunParams (env) { - return { - testBranch: env[ENV_TEST_BRANCH], - testCase: env[ENV_TEST_CASE], - testGroupID: env[ENV_TEST_GROUP_ID], - testGroupInstanceCount: Number.parseInt(env[ENV_TEST_GROUP_INSTANCE_COUNT]), - testInstanceCount: Number.parseInt(env[ENV_TEST_INSTANCE_COUNT]), - testInstanceParams: unpackParams(env[ENV_TEST_INSTANCE_PARAMS]), - testInstanceRole: env[ENV_TEST_INSTANCE_ROLE], - testOutputsPath: env[ENV_TEST_OUTPUTS_PATH], - testPlan: env[ENV_TEST_PLAN], - testRepo: env[ENV_TEST_REPO], - testRun: env[ENV_TEST_RUN], - testSidecar: env[ENV_TEST_SIDECAR] === 'true', - testStartTime: Date.parse(ENV_TEST_START_TIME), - testSubnet: ip.cidrSubnet(env[ENV_TEST_SUBNET]), - testTag: env[ENV_TEST_TAG] - } -} - -function unpackParams (packed) { - const spltparams = packed.split('|') - const params = {} - - for (const s of spltparams) { - const v = s.split('=') - if (v.length !== 2) { - continue - } - params[v[0]] = v[1] - } - - return params -} +const { newEvents } = require('./events') +const { parseRunParams } = require('./params') function currentRunEnv () { return parseRunEnv(process.env) @@ -61,23 +13,23 @@ function parseRunEnv (env) { } function newRunEnv (params) { + const options = { + runParams: params, + logger: winston.createLogger({ + level: 'debug', + format: winston.format.json(), + transports: [ + new winston.transports.Console({ + format: winston.format.simple() + }) + ] + }) + } + return { ...params, - recordMessage: () => { - // TODO - }, - recordStart: () => { - // TODO - }, - recordSuccess: () => { - // TODO - }, - recordFailure: () => { - // TODO - }, - recordCrash: () => { - // TODO - } + ...options, + ...newEvents(options) } } diff --git a/runtime/params.js b/runtime/params.js new file mode 100644 index 0000000..0122c4e --- /dev/null +++ b/runtime/params.js @@ -0,0 +1,93 @@ +const ip = require('ip') + +const ENV_TEST_BRANCH = 'TEST_BRANCH' +const ENV_TEST_CASE = 'TEST_CASE' +const ENV_TEST_GROUP_ID = 'TEST_GROUP_ID' +const ENV_TEST_GROUP_INSTANCE_COUNT = 'TEST_GROUP_INSTANCE_COUNT' +const ENV_TEST_INSTANCE_COUNT = 'TEST_INSTANCE_COUNT' +const ENV_TEST_INSTANCE_PARAMS = 'TEST_INSTANCE_PARAMS' +const ENV_TEST_INSTANCE_ROLE = 'TEST_INSTANCE_ROLE' +const ENV_TEST_OUTPUTS_PATH = 'TEST_OUTPUTS_PATH' +const ENV_TEST_PLAN = 'TEST_PLAN' +const ENV_TEST_REPO = 'TEST_REPO' +const ENV_TEST_RUN = 'TEST_RUN' +const ENV_TEST_SIDECAR = 'TEST_SIDECAR' +const ENV_TEST_START_TIME = 'TEST_START_TIME' +const ENV_TEST_SUBNET = 'TEST_SUBNET' +const ENV_TEST_TAG = 'TEST_TAG' + +function parseRunParams (env) { + const params = { + testBranch: env[ENV_TEST_BRANCH], + testCase: env[ENV_TEST_CASE], + testGroupID: env[ENV_TEST_GROUP_ID], + testGroupInstanceCount: Number.parseInt(env[ENV_TEST_GROUP_INSTANCE_COUNT]), + testInstanceCount: Number.parseInt(env[ENV_TEST_INSTANCE_COUNT]), + testInstanceParams: unpackParams(env[ENV_TEST_INSTANCE_PARAMS]), + testInstanceRole: env[ENV_TEST_INSTANCE_ROLE], + testOutputsPath: env[ENV_TEST_OUTPUTS_PATH], + testPlan: env[ENV_TEST_PLAN], + testRepo: env[ENV_TEST_REPO], + testRun: env[ENV_TEST_RUN], + testSidecar: env[ENV_TEST_SIDECAR] === 'true', + testStartTime: Date.parse(ENV_TEST_START_TIME), + testSubnet: ip.cidrSubnet(env[ENV_TEST_SUBNET]), + testTag: env[ENV_TEST_TAG] + } + + params.toJSON = () => { + const json = { + plan: params.testPlan, + case: params.testCase, + run: params.testRun, + instances: params.testInstanceCount, + outputs_path: params.testOutputsPath, + network: params.testSubnet.toString(), // TODO: check for correct method + group: params.testGroupID, + group_instances: params.testGroupInstanceCount + } + + if (params.testRepo) { + json.repo = params.testRepo + } + + if (params.testCommit) { + json.commit = params.testCommit + } + + if (params.testBranch) { + json.branch = params.testBranch + } + + if (params.testTag) { + json.tag = params.testTag + } + + return json + } + + params.toEnvVars = () => { + // TODO + } + + return params +} + +function unpackParams (packed) { + const spltparams = packed.split('|') + const params = {} + + for (const s of spltparams) { + const v = s.split('=') + if (v.length !== 2) { + continue + } + params[v[0]] = v[1] + } + + return params +} + +module.exports = { + parseRunParams +} diff --git a/sync/index.js b/sync/index.js index 5e1e994..9041f4f 100644 --- a/sync/index.js +++ b/sync/index.js @@ -1,6 +1,7 @@ +const { redisClient } = require('./redis') + function newBoundClient (runenv) { - // TODO - return newClient() + return newClient(runenv.logger, () => runenv.runParams) } function newGenericClient (logger) { @@ -8,10 +9,53 @@ function newGenericClient (logger) { return newClient() } -function newClient () { +async function newClient (logger, extractor) { + const redis = await redisClient(logger) + return { barrier: (state, target) => { + // a barrier with target zero is satisfied immediately; log a warning as + // this is probably programmer error. + if (target === 0) { + logger.warn('requested a barrier with target zero; satisfying immediately', { state }) + const b = {} + /* + b := &Barrier{C: make(chan error, 1)} + b.C <- nil + close(b.C) + return b, nil + */ + return + } + + const params = extractor() + if (!params) { + throw new Error('no run parameters provided') + } + // TODO + const b = { + state, + target + } + + /* + + b := &Barrier{ + C: make(chan error, 1), + state: state, + key: state.Key(rp), + target: int64(target), + ctx: ctx, + } + + resultCh := make(chan error) + c.barrierCh <- &newBarrier{b, resultCh} + err := <-resultCh + return b, err + */ + + return b }, publish: (topic, payload) => { // TODO diff --git a/sync/client.js b/sync/redis.js similarity index 90% rename from sync/client.js rename to sync/redis.js index 7bb6ab1..3120cb8 100644 --- a/sync/client.js +++ b/sync/redis.js @@ -1,10 +1,9 @@ const Redis = require('ioredis') -const logger = require('../logger') const ENV_REDIS_HOST = 'REDIS_HOST' const ENV_REDIS_PORT = 'REDIS_PORT' -async function redisClient () { +async function redisClient (logger) { let port = 6379 const host = process.env[ENV_REDIS_HOST] From acf2587c2057c3db6733d7831de64b3b3a7a9f19 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Sat, 3 Oct 2020 16:23:18 +0200 Subject: [PATCH 06/10] mega sync implementation start --- logger.js | 13 -------- sync/index.js | 84 +++++++++++---------------------------------------- sync/redis.js | 5 ++- sync/state.js | 81 +++++++++++++++++++++++++++++++++++++++++++++++++ sync/sugar.js | 25 +++++++++++++++ sync/topic.js | 76 ++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 204 insertions(+), 80 deletions(-) delete mode 100644 logger.js create mode 100644 sync/state.js create mode 100644 sync/sugar.js create mode 100644 sync/topic.js diff --git a/logger.js b/logger.js deleted file mode 100644 index 8cd2d02..0000000 --- a/logger.js +++ /dev/null @@ -1,13 +0,0 @@ -const winston = require('winston') - -const logger = winston.createLogger({ - level: 'debug', - format: winston.format.json(), - transports: [ - new winston.transports.Console({ - format: winston.format.simple() - }) - ] -}) - -module.exports = logger diff --git a/sync/index.js b/sync/index.js index 9041f4f..193da44 100644 --- a/sync/index.js +++ b/sync/index.js @@ -1,84 +1,36 @@ const { redisClient } = require('./redis') +const { createState } = require('./state') +const { createTopic } = require('./topic') +const { createSugar } = require('./sugar') function newBoundClient (runenv) { return newClient(runenv.logger, () => runenv.runParams) } -function newGenericClient (logger) { - // TODO - return newClient() -} - async function newClient (logger, extractor) { const redis = await redisClient(logger) - return { - barrier: (state, target) => { - // a barrier with target zero is satisfied immediately; log a warning as - // this is probably programmer error. - if (target === 0) { - logger.warn('requested a barrier with target zero; satisfying immediately', { state }) - const b = {} - /* - b := &Barrier{C: make(chan error, 1)} - b.C <- nil - close(b.C) - return b, nil - */ - return - } - - const params = extractor() - if (!params) { - throw new Error('no run parameters provided') - } - - // TODO - const b = { - state, - target - } - - /* + const options = { + logger, extractor, redis + } - b := &Barrier{ - C: make(chan error, 1), - state: state, - key: state.Key(rp), - target: int64(target), - ctx: ctx, - } + // TODO: we need something to simulate contexts (or just deadlines) + // on most of the fuctions. - resultCh := make(chan error) - c.barrierCh <- &newBarrier{b, resultCh} - err := <-resultCh - return b, err - */ + const base = { + ...createState(options), + ...createTopic(options) + } - return b - }, - publish: (topic, payload) => { - // TODO - }, - publishAndWait: (topic, payload, state, target) => { - // TODO - }, - publishSubscribe: (topic, payload) => { - // TODO - }, - signalAndWait: (state, target) => { - // TODO - }, - signalEntry: (state) => { - // TODO - }, - subscribe: (topic) => { - // TODO + return { + ...base, + ...createSugar(base), + close: () => { + return redis.disconnect() } } } module.exports = { - newBoundClient, - newGenericClient + newBoundClient } diff --git a/sync/redis.js b/sync/redis.js index 3120cb8..f2839b2 100644 --- a/sync/redis.js +++ b/sync/redis.js @@ -1,5 +1,7 @@ const Redis = require('ioredis') +const REDIS_PAYLOAD_KEY = 'p' + const ENV_REDIS_HOST = 'REDIS_HOST' const ENV_REDIS_PORT = 'REDIS_PORT' @@ -31,5 +33,6 @@ async function redisClient (logger) { } module.exports = { - redisClient + redisClient, + REDIS_PAYLOAD_KEY } diff --git a/sync/state.js b/sync/state.js new file mode 100644 index 0000000..1e317ec --- /dev/null +++ b/sync/state.js @@ -0,0 +1,81 @@ +function sleep (ms) { + return new Promise((resolve) => { + setTimeout(resolve, ms) + }) +} + +function stateKey (state, params) { + return `run:${params.testRun}:plan:${params.testPlan}:case:${params.testCase}:states:${state}` +} + +function barrier ({ logger, extractor, redis }) { + return async (state, target) => { + // a barrier with target zero is satisfied immediately; log a warning as + // this is probably programmer error. + if (target === 0) { + logger.warn('requested a barrier with target zero; satisfying immediately', { state }) + return { + state, + wait: Promise.resolve() + } + } + + const params = extractor() + if (!params) { + throw new Error('no run parameters provided') + } + + const key = stateKey(state, params) + + return { + state, + key, + target, + wait: (async () => { + // TODO: cancel on redis disconnect (manual) and cancel method maybe? + // NOTE: o sdk-go, we have a barrierWorker that fetches all barriers at once. + // For simplicity, for now, I decided to create a async function here to wait for + // the result, so we'll be executing a READ for each ongoing barrier. I'm not + // completely sure if this is a good idea or if there is any specific reason why + // the Go implementation decided to go for a global barrier worker. (Ask @nonsense) + + while (true) { + const curr = await redis.get(key) + + if (curr >= target) { + logger.debug('barrier was hit; informing waiters', { key, target, curr }) + return + } else { + logger.debug('barrier still unsatisfied', { key, target, curr }) + } + + await sleep(1000) // 1s + } + })() + } + } +} + +function signalEntry ({ logger, extractor, redis }) { + return async (state) => { + const params = await extractor() + if (!params) { + throw new Error('no run parameters provided') + } + + const key = stateKey(state, params) + logger.debug('signalling entry to state', { key }) + + const seq = await redis.incr(key) + logger.debug('new value of state', { key, value: seq }) + + return seq + } +} + +module.exports = { + createState: (options) => ({ + barrier: barrier(options), + signalEntry: signalEntry(options) + }) +} diff --git a/sync/sugar.js b/sync/sugar.js new file mode 100644 index 0000000..b54e994 --- /dev/null +++ b/sync/sugar.js @@ -0,0 +1,25 @@ +function createSugar ({ publish, subscribe, barrier, signalEntry }) { + return { + publishAndWait: async (topic, payload, state, target) => { + const seq = await publish(topic, payload) + const b = await barrier(state, target) + await b.wait + return seq + }, + publishSubscribe: async (topic, payload) => { + const seq = await publish(topic, payload) + const sub = await subscribe(topic) + return { seq, sub } + }, + signalAndWait: async (state, target) => { + const seq = await signalEntry(state) + const b = await barrier(state, target) + await b.wait + return seq + } + } +} + +module.exports = { + createSugar +} diff --git a/sync/topic.js b/sync/topic.js new file mode 100644 index 0000000..db15b5a --- /dev/null +++ b/sync/topic.js @@ -0,0 +1,76 @@ +const { REDIS_PAYLOAD_KEY } = require('./redis') + +function topicKey (topic, params) { + return `run:${params.testRun}:plan:${params.testPlan}:case:${params.testCase}:topics:${topic}` +} + +function publish ({ logger, extractor, redis }) { + return async (topic, payload) => { + const params = await extractor() + if (!params) { + throw new Error('no run parameters provided') + } + + logger.debug('publishig item on topic', { topic, payload }) + + const json = JSON.stringify(payload) + logger.debug('serialized json payload', { topic, json }) + + const key = topicKey(topic, params) + logger.debug('resolved key for publish', { topic, key }) + + const results = await redis + .multi() + .xadd(key, '*', REDIS_PAYLOAD_KEY, json) + .xlen(key) + .exec() + + const seq = results[1][1] + logger.debug('successfully published item; sequence number obtained', { topic, seq }) + + return seq + } +} + +function subscribe ({ logger, extractor, redis }) { + return async (topic) => { + const params = await extractor() + if (!params) { + throw new Error('no run parameters provided') + } + + const key = topicKey(topic, params) + let lastid = '0' + + return (async function * () { + while (true) { + // TODO: figure out way to cancel this. + const result = await redis.xread('COUNT', 10, 'BLOCK', 0, 'STREAMS', key, lastid) + + for (const [stream, values] of result) { + if (stream !== key) { + logger.debug("XREAD response: rcvd messages for a stream we're not subscribed to", { stream }) + break + } + + for (const [id, [key, jsonPayload]] of values) { + if (key !== REDIS_PAYLOAD_KEY) { + logger.debug('XREAD response: invalid payload key', { stream, key }) + break + } + const payload = JSON.parse(jsonPayload) + yield payload + lastid = id + } + } + } + })() + } +} + +module.exports = { + createTopic: (options) => ({ + publish: publish(options), + subscribe: subscribe(options) + }) +} From 5690a03faf28e7b4d2ac9b8321713e1a0f4328ff Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Sat, 3 Oct 2020 16:55:17 +0200 Subject: [PATCH 07/10] implement network --- network/index.js | 83 +++++++++++++++++++++++++++++++++++++++++------ package-lock.json | 10 +++--- package.json | 4 +-- runtime/params.js | 6 ++-- sync/state.js | 2 +- 5 files changed, 84 insertions(+), 21 deletions(-) diff --git a/network/index.js b/network/index.js index deabe3a..0da5829 100644 --- a/network/index.js +++ b/network/index.js @@ -1,14 +1,77 @@ -function newClient (client, runenv) { - return { - configureNetwork: () => { - // TODO - }, - getDataNetworkIP: () => { - // TODO - }, - waitNetworkInitialized: () => { - // TODO +const os = require('os') +const ipaddr = require('ipaddr.js') + +function waitNetworkInitialized ({ client, runenv }) { + return async () => { + if (runenv.testSidecar) { + try { + const barrier = await client.barrier('network-initialized', runenv.testInstanceCount) + await barrier.wait + } catch (err) { + runenv.recordMessage('network initialisation failed') + throw err + } + } + + runenv.recordMessage('network initialisation successful') + } +} + +function configureNetwork ({ client, runenv }) { + return async (config) => { + if (!runenv.testSidecar) { + runenv.logger.warn('ignoring network change request; running in a sidecar-less environment') + return + } + + if (!config.callbackState) { + throw new Error('failed to configure network; no callback state provided') + } + + const hostname = os.hostname() + const topic = `network:${hostname}` + const target = config.callbackTarget === 0 + ? config.testInstanceCount // Fall back to instance count on zero value. + : config.callbackTarget + + await client.publishAndWait(topic, config, config.callbackState, target) + } +} + +function getDataNetworkIP ({ client, runenv }) { + return async () => { + if (!runenv.testSidecar) { + // this must be a local:exec runner and we currently don't support + // traffic shaping on it for now, just return the loopback address + return '127.0.0.1' + } + + const ifaces = os.networkInterfaces().flat() + + for (const { address, family } of ifaces) { + if (family !== 'IPv4') { + runenv.recordMessage(`ignoring non ip4 addr ${address}`) + continue + } + + const addr = ipaddr.parse(address) + if (addr.match(runenv.testSubnet)) { + runenv.recordMessage(`detected data network IP: ${address}`) + return address + } else { + runenv.recordMessage(`${address} not in data subnet ${runenv.testSubnet.toString()}`) + } } + + throw new Error(`unable to determine data network IP. no interface found with IP in ${runenv.testSubnet.toString()}`) + } +} + +function newClient (options) { + return { + waitNetworkInitialized: waitNetworkInitialized(options), + configureNetwork: configureNetwork(options), + getDataNetworkIP: getDataNetworkIP(options) } } diff --git a/package-lock.json b/package-lock.json index 66fa35e..d3bdc0d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { - "name": "sdk-js", + "name": "testground-sdk", "version": "1.0.0", "lockfileVersion": 1, "requires": true, @@ -1055,10 +1055,10 @@ } } }, - "ip": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", - "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" + "ipaddr.js": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.0.tgz", + "integrity": "sha512-S54H9mIj0rbxRIyrDMEuuER86LdlgUg9FSeZ8duQb6CUG2iRrA36MYVQBSprTF/ZeAwvyQ5mDGuNvIPM0BIl3w==" }, "is-arrayish": { "version": "0.2.1", diff --git a/package.json b/package.json index 0eeea52..0ef936a 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "sdk-js", + "name": "testground-sdk", "version": "1.0.0", "description": "", "main": "index.js", @@ -23,7 +23,7 @@ }, "dependencies": { "ioredis": "^4.17.3", - "ip": "^1.1.5", + "ipaddr.js": "^2.0.0", "winston": "^3.3.3" } } diff --git a/runtime/params.js b/runtime/params.js index 0122c4e..2c89f26 100644 --- a/runtime/params.js +++ b/runtime/params.js @@ -1,4 +1,4 @@ -const ip = require('ip') +const ipaddr = require('ipaddr.js') const ENV_TEST_BRANCH = 'TEST_BRANCH' const ENV_TEST_CASE = 'TEST_CASE' @@ -31,7 +31,7 @@ function parseRunParams (env) { testRun: env[ENV_TEST_RUN], testSidecar: env[ENV_TEST_SIDECAR] === 'true', testStartTime: Date.parse(ENV_TEST_START_TIME), - testSubnet: ip.cidrSubnet(env[ENV_TEST_SUBNET]), + testSubnet: ipaddr.parseCIDR(env[ENV_TEST_SUBNET]), testTag: env[ENV_TEST_TAG] } @@ -42,7 +42,7 @@ function parseRunParams (env) { run: params.testRun, instances: params.testInstanceCount, outputs_path: params.testOutputsPath, - network: params.testSubnet.toString(), // TODO: check for correct method + network: params.testSubnet.toString(), group: params.testGroupID, group_instances: params.testGroupInstanceCount } diff --git a/sync/state.js b/sync/state.js index 1e317ec..ab10f36 100644 --- a/sync/state.js +++ b/sync/state.js @@ -20,7 +20,7 @@ function barrier ({ logger, extractor, redis }) { } } - const params = extractor() + const params = await extractor() if (!params) { throw new Error('no run parameters provided') } From ec32f3b86bb60be550f8b35a3c73af5c99f1a940 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Sat, 3 Oct 2020 16:59:09 +0200 Subject: [PATCH 08/10] streamline --- sync/sugar.js | 48 ++++++++++++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/sync/sugar.js b/sync/sugar.js index b54e994..b5ff782 100644 --- a/sync/sugar.js +++ b/sync/sugar.js @@ -1,25 +1,33 @@ -function createSugar ({ publish, subscribe, barrier, signalEntry }) { - return { - publishAndWait: async (topic, payload, state, target) => { - const seq = await publish(topic, payload) - const b = await barrier(state, target) - await b.wait - return seq - }, - publishSubscribe: async (topic, payload) => { - const seq = await publish(topic, payload) - const sub = await subscribe(topic) - return { seq, sub } - }, - signalAndWait: async (state, target) => { - const seq = await signalEntry(state) - const b = await barrier(state, target) - await b.wait - return seq - } +function publishAndWait ({ publish, barrier }) { + return async (topic, payload, state, target) => { + const seq = await publish(topic, payload) + const b = await barrier(state, target) + await b.wait + return seq + } +} + +function publishSubscribe ({ publish, subscribe }) { + return async (topic, payload) => { + const seq = await publish(topic, payload) + const sub = await subscribe(topic) + return { seq, sub } + } +} + +function signalAndWait ({ signalEntry, barrier }) { + return async (state, target) => { + const seq = await signalEntry(state) + const b = await barrier(state, target) + await b.wait + return seq } } module.exports = { - createSugar + createSugar: (options) => ({ + publishAndWait: publishAndWait(options), + publishSubscribe: publishSubscribe(options), + signalAndWait: signalAndWait(options) + }) } From c679a8e09d4bc4ddd1f77c191285b6e92dd4f6ce Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Sat, 3 Oct 2020 18:13:16 +0200 Subject: [PATCH 09/10] cleanup and fix-ups --- runtime/events.js | 2 +- runtime/index.js | 14 ++++++++++++-- runtime/params.js | 4 ++-- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/runtime/events.js b/runtime/events.js index 8b4e9ef..d0d2666 100644 --- a/runtime/events.js +++ b/runtime/events.js @@ -2,7 +2,7 @@ const EVENT_TYPE_START = 'start' const EVENT_TYPE_MESSAGE = 'message' const EVENT_TYPE_FINISH = 'finish' -const EVENT_OUTCOME_OK = 'OK' +const EVENT_OUTCOME_OK = 'ok' const EVENT_OUTCOME_FAILED = 'failed' const EVENT_OUTCOME_CRASHED = 'crashed' diff --git a/runtime/index.js b/runtime/index.js index 4159e50..7e9a28d 100644 --- a/runtime/index.js +++ b/runtime/index.js @@ -13,14 +13,24 @@ function parseRunEnv (env) { } function newRunEnv (params) { + const format = winston.format.combine( + winston.format((info, opts = {}) => { + info.ts = Date.now() * 1000000 // timestamp with nanoseconds, doesn't have precision, + info.group_id = params.testGroupId + info.run_id = params.testRun + return info + })(), + winston.format.json() + ) + const options = { runParams: params, logger: winston.createLogger({ level: 'debug', - format: winston.format.json(), + format, transports: [ new winston.transports.Console({ - format: winston.format.simple() + format }) ] }) diff --git a/runtime/params.js b/runtime/params.js index 2c89f26..07d2637 100644 --- a/runtime/params.js +++ b/runtime/params.js @@ -20,7 +20,7 @@ function parseRunParams (env) { const params = { testBranch: env[ENV_TEST_BRANCH], testCase: env[ENV_TEST_CASE], - testGroupID: env[ENV_TEST_GROUP_ID], + testGroupId: env[ENV_TEST_GROUP_ID], testGroupInstanceCount: Number.parseInt(env[ENV_TEST_GROUP_INSTANCE_COUNT]), testInstanceCount: Number.parseInt(env[ENV_TEST_INSTANCE_COUNT]), testInstanceParams: unpackParams(env[ENV_TEST_INSTANCE_PARAMS]), @@ -43,7 +43,7 @@ function parseRunParams (env) { instances: params.testInstanceCount, outputs_path: params.testOutputsPath, network: params.testSubnet.toString(), - group: params.testGroupID, + group: params.testGroupId, group_instances: params.testGroupInstanceCount } From aea0568dfef3c873cd2505d3c749db7faa856e8b Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Sat, 3 Oct 2020 18:15:19 +0200 Subject: [PATCH 10/10] remove example --- example-plan/env | 15 --- example-plan/index.js | 213 ------------------------------------------ 2 files changed, 228 deletions(-) delete mode 100644 example-plan/env delete mode 100644 example-plan/index.js diff --git a/example-plan/env b/example-plan/env deleted file mode 100644 index 9f67797..0000000 --- a/example-plan/env +++ /dev/null @@ -1,15 +0,0 @@ -export TEST_BRANCH= -export TEST_CASE=pingpong -export TEST_GROUP_ID= -export TEST_GROUP_INSTANCE_COUNT= -export TEST_INSTANCE_COUNT= -export TEST_INSTANCE_PARAMS= -export TEST_INSTANCE_ROLE= -export TEST_OUTPUTS_PATH= -export TEST_PLAN= -export TEST_REPO= -export TEST_RUN= -export TEST_SIDECAR= -export TEST_START_TIME= -export TEST_SUBNET=127.0.0.1/24 -export TEST_TAG= diff --git a/example-plan/index.js b/example-plan/index.js deleted file mode 100644 index 38c6f28..0000000 --- a/example-plan/index.js +++ /dev/null @@ -1,213 +0,0 @@ -// This is an example plan. This plan is being used to test out the -// public API of this package. It may not be the best example as it -// will change over time will the package is produced. In the end, -// this will be deleted. - -const os = require('os') -const { invokeMap, sync, network } = require('../') - -const testcases = { - pingpong: pingpong -} - -;(async () => { - // This is the plan entry point. - await invokeMap(testcases) -})() - -// TODO -async function pingpong (runenv) { - runenv.recordMessage('before sync.MustBoundClient') - const client = await sync.newBoundClient(runenv) - - if (!runenv.testSidecar) { - return - } - - const netclient = network.newClient(client, runenv) - runenv.recordMessage('before netclient.MustWaitNetworkInitialized') - await netclient.waitNetworkInitialized() - - const oldAddrs = os.networkInterfaces() - - const config = { - network: 'default', - enable: true, - default: { - latency: 100, // in milliseconds - badwidth: 1 << 20 // 1 Mib - }, - callbackState: 'network-configured', - routingPolicy: 'deny-all' // TODO: make constants - } - - runenv.recordMessage('before netclient.MustConfigureNetwork') - await netclient.configureNetwork(config) - - const seq = await client.signalAndWait('ip-allocation', runenv.testInstanceCount) - - // Make sure that the IP addresses don't change unless we request it. - const newAddrs = os.networkInterfaces() - if (!sameAddrs(oldAddrs, newAddrs)) { - throw new Error('interfaces changed') - } - - runenv.recordMessage(`I am ${seq}`) - - const ipC = (seq >> 8) + 1 - const ipD = seq - - /* - - config.IPv4 = &runenv.TestSubnet.IPNet - config.IPv4.IP = append(config.IPv4.IP[0:2:2], ipC, ipD) - config.CallbackState = "ip-changed" - - var ( - listener *net.TCPListener - conn *net.TCPConn - ) - - if seq == 1 { - listener, err = net.ListenTCP("tcp4", &net.TCPAddr{Port: 1234}) - if err != nil { - return err - } - defer listener.Close() - } - - runenv.RecordMessage("before reconfiguring network") - netclient.MustConfigureNetwork(ctx, config) - - switch seq { - case 1: - conn, err = listener.AcceptTCP() - case 2: - conn, err = net.DialTCP("tcp4", nil, &net.TCPAddr{ - IP: append(config.IPv4.IP[:3:3], 1), - Port: 1234, - }) - default: - return fmt.Errorf("expected at most two test instances") - } - if err != nil { - return err - } - - defer conn.Close() - - // trying to measure latency here. - err = conn.SetNoDelay(true) - if err != nil { - return err - } - - pingPong := func(test string, rttMin, rttMax time.Duration) error { - buf := make([]byte, 1) - - runenv.RecordMessage("waiting until ready") - - // wait till both sides are ready - _, err = conn.Write([]byte{0}) - if err != nil { - return err - } - - _, err = conn.Read(buf) - if err != nil { - return err - } - - start := time.Now() - - // write sequence number. - runenv.RecordMessage("writing my id") - _, err = conn.Write([]byte{byte(seq)}) - if err != nil { - return err - } - - // pong other sequence number - runenv.RecordMessage("reading their id") - _, err = conn.Read(buf) - if err != nil { - return err - } - - runenv.RecordMessage("returning their id") - _, err = conn.Write(buf) - if err != nil { - return err - } - - runenv.RecordMessage("reading my id") - // read our sequence number - _, err = conn.Read(buf) - if err != nil { - return err - } - - runenv.RecordMessage("done") - - // stop - end := time.Now() - - // check the sequence number. - if buf[0] != byte(seq) { - return fmt.Errorf("read unexpected value") - } - - // check the RTT - rtt := end.Sub(start) - if rtt < rttMin || rtt > rttMax { - return fmt.Errorf("expected an RTT between %s and %s, got %s", rttMin, rttMax, rtt) - } - runenv.RecordMessage("ping RTT was %s [%s, %s]", rtt, rttMin, rttMax) - - // Don't reconfigure the network until we're done with the first test. - state := sync.State("ping-pong-" + test) - client.MustSignalAndWait(ctx, state, runenv.TestInstanceCount) - - return nil - } - err = pingPong("200", 200*time.Millisecond, 215*time.Millisecond) - if err != nil { - return err - } - - config.Default.Latency = 10 * time.Millisecond - config.CallbackState = "latency-reduced" - netclient.MustConfigureNetwork(ctx, config) - - runenv.RecordMessage("ping pong") - err = pingPong("10", 20*time.Millisecond, 35*time.Millisecond) - if err != nil { - return err - } - */ -} - -function sameAddrs (a, b) { - if (a.length !== b.length) { - return false - } - - a = Object.values(a) - .flat() - .reduce((acc, curr) => { - if (!acc.includes(curr.cidr)) { - acc.push(curr.cidr) - } - return acc - }, []) - - b = Object.values(b).flat() - - for (const { cidr } of b) { - if (!a.includes(cidr)) { - return false - } - } - - return true -}