diff --git a/.eslintrc.js b/.eslintrc.js index 6e63aa01..9b063c7b 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -6,13 +6,7 @@ module.exports = { project: 'tsconfig.eslint.json', sourceType: 'module', }, - plugins: [ - '@typescript-eslint', - 'eslint-plugin-import', - 'eslint-plugin-jsdoc', - 'eslint-plugin-prefer-arrow', - 'sort-imports-es6-autofix', - ], + plugins: ['@typescript-eslint', 'eslint-plugin-import', 'eslint-plugin-jsdoc', 'eslint-plugin-prefer-arrow'], rules: { '@typescript-eslint/adjacent-overload-signatures': 'error', '@typescript-eslint/array-type': [ @@ -192,7 +186,6 @@ module.exports = { radix: 'error', semi: 'off', // use @typescript-eslint/semi instead 'sort-imports': ['error', { allowSeparatedGroups: true, ignoreDeclarationSort: true }], - 'sort-imports-es6-autofix/sort-imports-es6': 'error', 'space-before-function-paren': 'off', 'space-in-parens': ['error', 'never'], 'spaced-comment': [ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 752eda16..39b1155a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -53,4 +53,3 @@ jobs: node-version: ${{ matrix.node }} - run: npm install - run: npm run test - - run: npm run test:integration diff --git a/README.md b/README.md index 4596163d..9d97d0cc 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,8 @@ If the parameter is omitted, all jobs are started/stopped/cancelled/removed. | removeJob | `string` | Stops and removes the job with the provided name (if on the schedule) from both the schedule and the database. | | count | `boolean` (optional) | Returns the number of jobs on the schedule. Only started jobs are counted if parameter is set to true. | | list | | Returns descriptions of all jobs on the schedule. | +| check | `string` | Returns execution information of the job with the provided name from the database. This also works if the job is not on the schedule. | +| clear | | Removes all jobs from the database. This also removes jobs that are not on this schedule, but were defined by other schedules. However, does NOT stop job executions - this will cause currently running jobs to fail. Consider using stop/cancel/remove methods instead! | | get | `string` | Returns a description of the job. Returns undefined if no job with the provided name is defined. | | run | `string` | Runs the job with the provided name once, immediately. Note that `maxRunning` is respected, ie. the execution is skipped if the job is already running `maxRunning` times. | | on | `'debug'` or `'error'`, `function` | Define a callback for debug or error events. | @@ -103,18 +105,6 @@ mongoSchedule.on('debug', ({ data, message }: MomoEvent) => { | error | type | `MomoErrorType` | `'defining job failed'` or `'scheduling job failed'` or `'executing job failed'` or `'stopping job failed'` | | error | error (optional) | `Error` | The root cause of the error. | -### Other functions - -momo-scheduler also includes some utility functions to retrieve information on momo jobs from its database: - -| function | parameter | description | -|---------------|-----------|-------------| -| `connect` | `{ url: string }` | Establishes a connection with MongoDB. If you want to schedule jobs, you should use `MongoSchedule.connect` instead to create a connected schedule. | -| `isConnected` | | Returns true if a connection to MongoDB was established and false otherwise. | -| `check` | `string` | Retrieves information on the last job execution. Returns undefined if job cannot be found or was never executed. | -| `list` | | Lists all jobs. | -| `clear` | | Removes all jobs from the database. Do not use this if MongoSchedule has already been started. Subsequent execution of all jobs will fail! | - ## License This project is open source and licensed under [Apache 2.0](LICENSE). diff --git a/package-lock.json b/package-lock.json index bac5a71b..f2a02d28 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,26 +14,26 @@ } }, "@babel/compat-data": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.15.0.tgz", - "integrity": "sha512-0NqAC1IJE0S0+lL1SWFMxMkz1pKCNCjI4tr2Zx4LJSXxCLAdr6KyArnY+sno5m3yH9g737ygOyPABDsnXkpxiA==", + "version": "7.14.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.14.7.tgz", + "integrity": "sha512-nS6dZaISCXJ3+518CWiBfEr//gHyMO02uDxBkXTKZDN5POruCnOZ1N4YBRZDCabwF8nZMWBpRxIicmXtBs+fvw==", "dev": true }, "@babel/core": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.15.0.tgz", - "integrity": "sha512-tXtmTminrze5HEUPn/a0JtOzzfp0nk+UEXQ/tqIJo3WDGypl/2OFQEMll/zSFU8f/lfmfLXvTaORHF3cfXIQMw==", + "version": "7.14.6", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.14.6.tgz", + "integrity": "sha512-gJnOEWSqTk96qG5BoIrl5bVtc23DCycmIePPYnamY9RboYdI4nFy5vAQMSl81O5K/W0sLDWfGysnOECC+KUUCA==", "dev": true, "requires": { "@babel/code-frame": "^7.14.5", - "@babel/generator": "^7.15.0", - "@babel/helper-compilation-targets": "^7.15.0", - "@babel/helper-module-transforms": "^7.15.0", - "@babel/helpers": "^7.14.8", - "@babel/parser": "^7.15.0", + "@babel/generator": "^7.14.5", + "@babel/helper-compilation-targets": "^7.14.5", + "@babel/helper-module-transforms": "^7.14.5", + "@babel/helpers": "^7.14.6", + "@babel/parser": "^7.14.6", "@babel/template": "^7.14.5", - "@babel/traverse": "^7.15.0", - "@babel/types": "^7.15.0", + "@babel/traverse": "^7.14.5", + "@babel/types": "^7.14.5", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -51,15 +51,6 @@ "@babel/highlight": "^7.14.5" } }, - "json5": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", - "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", @@ -75,12 +66,12 @@ } }, "@babel/generator": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.15.0.tgz", - "integrity": "sha512-eKl4XdMrbpYvuB505KTta4AV9g+wWzmVBW69tX0H2NwKVKd2YJbKgyK6M8j/rgLbmHOYJn6rUklV677nOyJrEQ==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.14.5.tgz", + "integrity": "sha512-y3rlP+/G25OIX3mYKKIOlQRcqj7YgrvHxOLbVmyLJ9bPmi5ttvUmpydVjcFjZphOktWuA7ovbx91ECloWTfjIA==", "dev": true, "requires": { - "@babel/types": "^7.15.0", + "@babel/types": "^7.14.5", "jsesc": "^2.5.1", "source-map": "^0.5.0" }, @@ -94,12 +85,12 @@ } }, "@babel/helper-compilation-targets": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.15.0.tgz", - "integrity": "sha512-h+/9t0ncd4jfZ8wsdAsoIxSa61qhBYlycXiHWqJaQBCXAhDCMbPRSMTGnZIkkmt1u4ag+UQmuqcILwqKzZ4N2A==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.14.5.tgz", + "integrity": "sha512-v+QtZqXEiOnpO6EYvlImB6zCD2Lel06RzOPzmkz/D/XgQiUu3C/Jb1LOqSt/AIA34TYi/Q+KlT8vTQrgdxkbLw==", "dev": true, "requires": { - "@babel/compat-data": "^7.15.0", + "@babel/compat-data": "^7.14.5", "@babel/helper-validator-option": "^7.14.5", "browserslist": "^4.16.6", "semver": "^6.3.0" @@ -143,12 +134,12 @@ } }, "@babel/helper-member-expression-to-functions": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.15.0.tgz", - "integrity": "sha512-Jq8H8U2kYiafuj2xMTPQwkTBnEEdGKpT35lJEQsRRjnG0LW3neucsaMWLgKcwu3OHKNeYugfw+Z20BXBSEs2Lg==", + "version": "7.14.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.14.7.tgz", + "integrity": "sha512-TMUt4xKxJn6ccjcOW7c4hlwyJArizskAhoSTOCkA0uZ+KghIaci0Qg9R043kUMWI9mtQfgny+NQ5QATnZ+paaA==", "dev": true, "requires": { - "@babel/types": "^7.15.0" + "@babel/types": "^7.14.5" } }, "@babel/helper-module-imports": { @@ -161,19 +152,19 @@ } }, "@babel/helper-module-transforms": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.15.0.tgz", - "integrity": "sha512-RkGiW5Rer7fpXv9m1B3iHIFDZdItnO2/BLfWVW/9q7+KqQSDY5kUfQEbzdXM1MVhJGcugKV7kRrNVzNxmk7NBg==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.14.5.tgz", + "integrity": "sha512-iXpX4KW8LVODuAieD7MzhNjmM6dzYY5tfRqT+R9HDXWl0jPn/djKmA+G9s/2C2T9zggw5tK1QNqZ70USfedOwA==", "dev": true, "requires": { "@babel/helper-module-imports": "^7.14.5", - "@babel/helper-replace-supers": "^7.15.0", - "@babel/helper-simple-access": "^7.14.8", + "@babel/helper-replace-supers": "^7.14.5", + "@babel/helper-simple-access": "^7.14.5", "@babel/helper-split-export-declaration": "^7.14.5", - "@babel/helper-validator-identifier": "^7.14.9", + "@babel/helper-validator-identifier": "^7.14.5", "@babel/template": "^7.14.5", - "@babel/traverse": "^7.15.0", - "@babel/types": "^7.15.0" + "@babel/traverse": "^7.14.5", + "@babel/types": "^7.14.5" } }, "@babel/helper-optimise-call-expression": { @@ -192,24 +183,24 @@ "dev": true }, "@babel/helper-replace-supers": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.15.0.tgz", - "integrity": "sha512-6O+eWrhx+HEra/uJnifCwhwMd6Bp5+ZfZeJwbqUTuqkhIT6YcRhiZCOOFChRypOIe0cV46kFrRBlm+t5vHCEaA==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.14.5.tgz", + "integrity": "sha512-3i1Qe9/8x/hCHINujn+iuHy+mMRLoc77b2nI9TB0zjH1hvn9qGlXjWlggdwUcju36PkPCy/lpM7LLUdcTyH4Ow==", "dev": true, "requires": { - "@babel/helper-member-expression-to-functions": "^7.15.0", + "@babel/helper-member-expression-to-functions": "^7.14.5", "@babel/helper-optimise-call-expression": "^7.14.5", - "@babel/traverse": "^7.15.0", - "@babel/types": "^7.15.0" + "@babel/traverse": "^7.14.5", + "@babel/types": "^7.14.5" } }, "@babel/helper-simple-access": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.14.8.tgz", - "integrity": "sha512-TrFN4RHh9gnWEU+s7JloIho2T76GPwRHhdzOWLqTrMnlas8T9O7ec+oEDNsRXndOmru9ymH9DFrEOxpzPoSbdg==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.14.5.tgz", + "integrity": "sha512-nfBN9xvmCt6nrMZjfhkl7i0oTV3yxR4/FztsbOASyTvVcoYd0TRHh7eMLdlEcCqobydC0LAF3LtC92Iwxo0wyw==", "dev": true, "requires": { - "@babel/types": "^7.14.8" + "@babel/types": "^7.14.5" } }, "@babel/helper-split-export-declaration": { @@ -222,9 +213,9 @@ } }, "@babel/helper-validator-identifier": { - "version": "7.14.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.9.tgz", - "integrity": "sha512-pQYxPY0UP6IHISRitNe8bsijHex4TWZXi2HwKVsjPiltzlhse2znVcm9Ace510VT1kxIHjGJCZZQBX2gJDbo0g==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz", + "integrity": "sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg==", "dev": true }, "@babel/helper-validator-option": { @@ -234,14 +225,14 @@ "dev": true }, "@babel/helpers": { - "version": "7.15.3", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.15.3.tgz", - "integrity": "sha512-HwJiz52XaS96lX+28Tnbu31VeFSQJGOeKHJeaEPQlTl7PnlhFElWPj8tUXtqFIzeN86XxXoBr+WFAyK2PPVz6g==", + "version": "7.14.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.14.6.tgz", + "integrity": "sha512-yesp1ENQBiLI+iYHSJdoZKUtRpfTlL1grDIX9NRlAVppljLw/4tTyYupIB7uIYmC3stW/imAv8EqaKaS/ibmeA==", "dev": true, "requires": { "@babel/template": "^7.14.5", - "@babel/traverse": "^7.15.0", - "@babel/types": "^7.15.0" + "@babel/traverse": "^7.14.5", + "@babel/types": "^7.14.5" } }, "@babel/highlight": { @@ -308,9 +299,9 @@ } }, "@babel/parser": { - "version": "7.15.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.3.tgz", - "integrity": "sha512-O0L6v/HvqbdJawj0iBEfVQMc3/6WP+AeOsovsIgBFyJaG+W2w7eqvZB7puddATmWuARlm1SX7DwxJ/JJUnDpEA==", + "version": "7.14.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.7.tgz", + "integrity": "sha512-X67Z5y+VBJuHB/RjwECp8kSl5uYi0BvRbNeWqkaJCVh+LiTPl19WBUfG627psSgp9rSf6ojuXghQM3ha6qHHdA==", "dev": true }, "@babel/plugin-syntax-async-generators": { @@ -453,18 +444,18 @@ } }, "@babel/traverse": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.15.0.tgz", - "integrity": "sha512-392d8BN0C9eVxVWd8H6x9WfipgVH5IaIoLp23334Sc1vbKKWINnvwRpb4us0xtPaCumlwbTtIYNA0Dv/32sVFw==", + "version": "7.14.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.14.7.tgz", + "integrity": "sha512-9vDr5NzHu27wgwejuKL7kIOm4bwEtaPQ4Z6cpCmjSuaRqpH/7xc4qcGEscwMqlkwgcXl6MvqoAjZkQ24uSdIZQ==", "dev": true, "requires": { "@babel/code-frame": "^7.14.5", - "@babel/generator": "^7.15.0", + "@babel/generator": "^7.14.5", "@babel/helper-function-name": "^7.14.5", "@babel/helper-hoist-variables": "^7.14.5", "@babel/helper-split-export-declaration": "^7.14.5", - "@babel/parser": "^7.15.0", - "@babel/types": "^7.15.0", + "@babel/parser": "^7.14.7", + "@babel/types": "^7.14.5", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -487,12 +478,12 @@ } }, "@babel/types": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.0.tgz", - "integrity": "sha512-OBvfqnllOIdX4ojTHpwZbpvz4j3EWyjkZEdmjH0/cgsd6QOdSgU8rLSk6ard/pcW7rlmjdVSX/AWOaORR1uNOQ==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.14.5.tgz", + "integrity": "sha512-M/NzBpEL95I5Hh4dwhin5JlE7EzO5PHMAuzjxss3tiOBD46KfQvVedN/3jEPZvdRvtsK2222XfdHogNIttFgcg==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.14.9", + "@babel/helper-validator-identifier": "^7.14.5", "to-fast-properties": "^2.0.0" } }, @@ -503,34 +494,20 @@ "dev": true }, "@es-joy/jsdoccomment": { - "version": "0.9.0-alpha.6", - "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.9.0-alpha.6.tgz", - "integrity": "sha512-/eTjMezCcNne3VvKGle3kgz14PeOb7sQAX/OsV6a4jCmjzWgrAlQuISLcqgqUlI97BFWCNz7E0U9YyRm3ucyOA==", + "version": "0.9.0-alpha.1", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.9.0-alpha.1.tgz", + "integrity": "sha512-Clxxc0PwpISoYYBibA+1L2qFJ7gvFVhI2Hos87S06K+Q0cXdOhZQJNKWuaQGPAeHjZEuUB/YoWOfwjuF2wirqA==", "dev": true, "requires": { - "comment-parser": "1.1.6-beta.3", + "comment-parser": "1.1.6-beta.0", "esquery": "^1.4.0", "jsdoc-type-pratt-parser": "1.0.4" - }, - "dependencies": { - "comment-parser": { - "version": "1.1.6-beta.3", - "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.1.6-beta.3.tgz", - "integrity": "sha512-LkPpVUx533rkxrkgphwWo0u6A3Vn9/fa8biHo2mrL6NUKPL6ubnF1P7NTm/M9i/rUvQ/mDxVqOVlmqy5uNUmVw==", - "dev": true - }, - "jsdoc-type-pratt-parser": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-1.0.4.tgz", - "integrity": "sha512-jzmW9gokeq9+bHPDR1nCeidMyFUikdZlbOhKzh9+/nJqB75XhpNKec1/UuxW5c4+O+Pi31Gc/dCboyfSm/pSpQ==", - "dev": true - } } }, "@eslint/eslintrc": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", - "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.2.tgz", + "integrity": "sha512-8nmGq/4ycLpIwzvhI4tNDmQztZ8sp+hI7cyG8i1nQDhkAbRzHpXPidRAHlNvCZQpJTKw5ItIpMw9RSToGF00mg==", "dev": true, "requires": { "ajv": "^6.12.4", @@ -1045,9 +1022,9 @@ "dev": true }, "@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.7.tgz", + "integrity": "sha512-BTIhocbPBSrRmHxOAJFtR18oLhxTtAFDAvL8hY1S3iU8k+E60W/YFs4jrixGzQjMpF4qPXxIQHcjVD9dz1C2QA==", "dev": true, "requires": { "@nodelib/fs.scandir": "2.1.5", @@ -1072,11 +1049,6 @@ "@sinonjs/commons": "^1.7.0" } }, - "@sqltools/formatter": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.2.tgz", - "integrity": "sha512-/5O7Fq6Vnv8L6ucmPjaWbVG1XkP4FO+w5glqfkIsq3Xw4oyNAdJddbnYodNDAfjVUvo/rrSCTom4kAND7T1o5Q==" - }, "@tootallnate/once": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", @@ -1102,9 +1074,9 @@ "dev": true }, "@tsconfig/node16": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", - "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.1.tgz", + "integrity": "sha512-FTgBI767POY/lKNDNbIzgAX6miIDBs6NTCbdlDb8TrWovHsSvaVIZDlTqym29C6UqhzwcJx4CYr+AlrMywA0cA==", "dev": true }, "@types/babel__core": { @@ -1148,15 +1120,6 @@ "@babel/types": "^7.3.0" } }, - "@types/bson": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.0.5.tgz", - "integrity": "sha512-vVLwMUqhYJSQ/WKcE60eFqcyuWse5fGH+NMAXHuKrUAPoryq3ATxk5o4bgYNtg5aOM4APVg7Hnb3ASqUYG0PKg==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, "@types/graceful-fs": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", @@ -1207,15 +1170,9 @@ } }, "@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", - "dev": true - }, - "@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", + "integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==", "dev": true }, "@types/lodash": { @@ -1231,29 +1188,18 @@ "dev": true }, "@types/mdast": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.10.tgz", - "integrity": "sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.6.tgz", + "integrity": "sha512-1a/QZGYe7cavCvv4OwByerPbCoIwSL3XHH+ajE4SlqWXqwSeGTMbML5we5PpYV2uLmpKPTGOacmcMt5R+w2l0w==", "dev": true, "requires": { "@types/unist": "*" } }, - "@types/mongodb": { - "version": "3.6.20", - "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.6.20.tgz", - "integrity": "sha512-WcdpPJCakFzcWWD9juKoZbRtQxKIMYF/JIAM4JrNHrMcnJL6/a2NWjXxW7fo9hxboxxkg+icff8d7+WIEvKgYQ==", - "dev": true, - "requires": { - "@types/bson": "*", - "@types/node": "*" - } - }, "@types/node": { "version": "16.3.2", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.3.2.tgz", - "integrity": "sha512-jJs9ErFLP403I+hMLGnqDRWT0RYKSvArxuBVh2veudHV7ifEC1WAmjJADacZ7mRbA2nWgHtn8xyECMAot0SkAw==", - "dev": true + "integrity": "sha512-jJs9ErFLP403I+hMLGnqDRWT0RYKSvArxuBVh2veudHV7ifEC1WAmjJADacZ7mRbA2nWgHtn8xyECMAot0SkAw==" }, "@types/pino": { "version": "6.3.9", @@ -1292,12 +1238,12 @@ "dev": true }, "@types/sonic-boom": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@types/sonic-boom/-/sonic-boom-2.1.1.tgz", - "integrity": "sha512-CiKn+8CDgtBspfAVPwC8PXCMPhqeL7pFS4aWuj+WJnHLZlu4OGPctdZ6Mob43jRe0kkd7Ztb2Hcu9kzB+b7ZFw==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@types/sonic-boom/-/sonic-boom-0.7.0.tgz", + "integrity": "sha512-AfqR0fZMoUXUNwusgXKxcE9DPlHNDHQp6nKYUd4PSRpLobF5CCevSpyTEBcVZreqaWKCnGBr9KI1fHMTttoB7A==", "dev": true, "requires": { - "sonic-boom": "*" + "@types/node": "*" } }, "@types/stack-utils": { @@ -1324,6 +1270,20 @@ "integrity": "sha512-Y2mHTRAbqfFkpjldbkHGY8JIzRN6XqYRliG8/24FcHm2D2PwW24fl5xMRTVGdrb7iMrwCaIEbLWerGIkXuFWVg==", "dev": true }, + "@types/webidl-conversions": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-6.1.1.tgz", + "integrity": "sha512-XAahCdThVuCFDQLT7R7Pk/vqeObFNL3YqRyFZg+AqAP/W1/w3xHaIxuW7WszQqTbIBOPRcItYJIou3i/mppu3Q==" + }, + "@types/whatwg-url": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.1.tgz", + "integrity": "sha512-2YubE1sjj5ifxievI5Ge1sckb9k/Er66HyR2c+3+I6VDUUg1TLPdYYTEbQ+DjRkS4nTxMJhgWfSfMRD2sl2EYQ==", + "requires": { + "@types/node": "*", + "@types/webidl-conversions": "*" + } + }, "@types/yargs": { "version": "15.0.14", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.14.tgz", @@ -1352,20 +1312,86 @@ "regexpp": "^3.1.0", "semver": "^7.3.5", "tsutils": "^3.21.0" + }, + "dependencies": { + "@typescript-eslint/experimental-utils": { + "version": "4.28.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.28.3.tgz", + "integrity": "sha512-zZYl9TnrxwEPi3FbyeX0ZnE8Hp7j3OCR+ELoUfbwGHGxWnHg9+OqSmkw2MoCVpZksPCZYpQzC559Ee9pJNHTQw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.7", + "@typescript-eslint/scope-manager": "4.28.3", + "@typescript-eslint/types": "4.28.3", + "@typescript-eslint/typescript-estree": "4.28.3", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + } + }, + "@typescript-eslint/scope-manager": { + "version": "4.28.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.28.3.tgz", + "integrity": "sha512-/8lMisZ5NGIzGtJB+QizQ5eX4Xd8uxedFfMBXOKuJGP0oaBBVEMbJVddQKDXyyB0bPlmt8i6bHV89KbwOelJiQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.28.3", + "@typescript-eslint/visitor-keys": "4.28.3" + } + }, + "@typescript-eslint/types": { + "version": "4.28.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.28.3.tgz", + "integrity": "sha512-kQFaEsQBQVtA9VGVyciyTbIg7S3WoKHNuOp/UF5RG40900KtGqfoiETWD/v0lzRXc+euVE9NXmfer9dLkUJrkA==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "4.28.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.3.tgz", + "integrity": "sha512-YAb1JED41kJsqCQt1NcnX5ZdTA93vKFCMP4lQYG6CFxd0VzDJcKttRlMrlG+1qiWAw8+zowmHU1H0OzjWJzR2w==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.28.3", + "@typescript-eslint/visitor-keys": "4.28.3", + "debug": "^4.3.1", + "globby": "^11.0.3", + "is-glob": "^4.0.1", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "4.28.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.3.tgz", + "integrity": "sha512-ri1OzcLnk1HH4gORmr1dllxDzzrN6goUIz/P4MHFV0YZJDCADPR3RvYNp0PW2SetKTThar6wlbFTL00hV2Q+fg==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.28.3", + "eslint-visitor-keys": "^2.0.0" + } + }, + "eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^2.0.0" + } + } } }, "@typescript-eslint/experimental-utils": { - "version": "4.28.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.28.3.tgz", - "integrity": "sha512-zZYl9TnrxwEPi3FbyeX0ZnE8Hp7j3OCR+ELoUfbwGHGxWnHg9+OqSmkw2MoCVpZksPCZYpQzC559Ee9pJNHTQw==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.14.2.tgz", + "integrity": "sha512-mV9pmET4C2y2WlyHmD+Iun8SAEqkLahHGBkGqDVslHkmoj3VnxnGP4ANlwuxxfq1BsKdl/MPieDbohCEQgKrwA==", "dev": true, "requires": { - "@types/json-schema": "^7.0.7", - "@typescript-eslint/scope-manager": "4.28.3", - "@typescript-eslint/types": "4.28.3", - "@typescript-eslint/typescript-estree": "4.28.3", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0" + "@types/json-schema": "^7.0.3", + "@typescript-eslint/scope-manager": "4.14.2", + "@typescript-eslint/types": "4.14.2", + "@typescript-eslint/typescript-estree": "4.14.2", + "eslint-scope": "^5.0.0", + "eslint-utils": "^2.0.0" } }, "@typescript-eslint/parser": { @@ -1378,46 +1404,90 @@ "@typescript-eslint/types": "4.28.3", "@typescript-eslint/typescript-estree": "4.28.3", "debug": "^4.3.1" + }, + "dependencies": { + "@typescript-eslint/scope-manager": { + "version": "4.28.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.28.3.tgz", + "integrity": "sha512-/8lMisZ5NGIzGtJB+QizQ5eX4Xd8uxedFfMBXOKuJGP0oaBBVEMbJVddQKDXyyB0bPlmt8i6bHV89KbwOelJiQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.28.3", + "@typescript-eslint/visitor-keys": "4.28.3" + } + }, + "@typescript-eslint/types": { + "version": "4.28.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.28.3.tgz", + "integrity": "sha512-kQFaEsQBQVtA9VGVyciyTbIg7S3WoKHNuOp/UF5RG40900KtGqfoiETWD/v0lzRXc+euVE9NXmfer9dLkUJrkA==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "4.28.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.3.tgz", + "integrity": "sha512-YAb1JED41kJsqCQt1NcnX5ZdTA93vKFCMP4lQYG6CFxd0VzDJcKttRlMrlG+1qiWAw8+zowmHU1H0OzjWJzR2w==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.28.3", + "@typescript-eslint/visitor-keys": "4.28.3", + "debug": "^4.3.1", + "globby": "^11.0.3", + "is-glob": "^4.0.1", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "4.28.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.3.tgz", + "integrity": "sha512-ri1OzcLnk1HH4gORmr1dllxDzzrN6goUIz/P4MHFV0YZJDCADPR3RvYNp0PW2SetKTThar6wlbFTL00hV2Q+fg==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.28.3", + "eslint-visitor-keys": "^2.0.0" + } + } } }, "@typescript-eslint/scope-manager": { - "version": "4.28.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.28.3.tgz", - "integrity": "sha512-/8lMisZ5NGIzGtJB+QizQ5eX4Xd8uxedFfMBXOKuJGP0oaBBVEMbJVddQKDXyyB0bPlmt8i6bHV89KbwOelJiQ==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.14.2.tgz", + "integrity": "sha512-cuV9wMrzKm6yIuV48aTPfIeqErt5xceTheAgk70N1V4/2Ecj+fhl34iro/vIssJlb7XtzcaD07hWk7Jk0nKghg==", "dev": true, "requires": { - "@typescript-eslint/types": "4.28.3", - "@typescript-eslint/visitor-keys": "4.28.3" + "@typescript-eslint/types": "4.14.2", + "@typescript-eslint/visitor-keys": "4.14.2" } }, "@typescript-eslint/types": { - "version": "4.28.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.28.3.tgz", - "integrity": "sha512-kQFaEsQBQVtA9VGVyciyTbIg7S3WoKHNuOp/UF5RG40900KtGqfoiETWD/v0lzRXc+euVE9NXmfer9dLkUJrkA==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.14.2.tgz", + "integrity": "sha512-LltxawRW6wXy4Gck6ZKlBD05tCHQUj4KLn4iR69IyRiDHX3d3NCAhO+ix5OR2Q+q9bjCrHE/HKt+riZkd1At8Q==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "4.28.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.3.tgz", - "integrity": "sha512-YAb1JED41kJsqCQt1NcnX5ZdTA93vKFCMP4lQYG6CFxd0VzDJcKttRlMrlG+1qiWAw8+zowmHU1H0OzjWJzR2w==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.14.2.tgz", + "integrity": "sha512-ESiFl8afXxt1dNj8ENEZT12p+jl9PqRur+Y19m0Z/SPikGL6rqq4e7Me60SU9a2M28uz48/8yct97VQYaGl0Vg==", "dev": true, "requires": { - "@typescript-eslint/types": "4.28.3", - "@typescript-eslint/visitor-keys": "4.28.3", - "debug": "^4.3.1", - "globby": "^11.0.3", + "@typescript-eslint/types": "4.14.2", + "@typescript-eslint/visitor-keys": "4.14.2", + "debug": "^4.1.1", + "globby": "^11.0.1", "is-glob": "^4.0.1", - "semver": "^7.3.5", - "tsutils": "^3.21.0" + "lodash": "^4.17.15", + "semver": "^7.3.2", + "tsutils": "^3.17.1" } }, "@typescript-eslint/visitor-keys": { - "version": "4.28.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.3.tgz", - "integrity": "sha512-ri1OzcLnk1HH4gORmr1dllxDzzrN6goUIz/P4MHFV0YZJDCADPR3RvYNp0PW2SetKTThar6wlbFTL00hV2Q+fg==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.14.2.tgz", + "integrity": "sha512-KBB+xLBxnBdTENs/rUgeUKO0UkPBRs2vD09oMRRIkj5BEN8PX1ToXV532desXfpQnZsYTyLLviS7JrPhdL154w==", "dev": true, "requires": { - "@typescript-eslint/types": "4.28.3", + "@typescript-eslint/types": "4.14.2", "eslint-visitor-keys": "^2.0.0" } }, @@ -1502,21 +1572,18 @@ "ansi-regex": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true }, "ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "requires": { "color-convert": "^2.0.1" } }, - "any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=" - }, "anymatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", @@ -1527,11 +1594,6 @@ "picomatch": "^2.0.4" } }, - "app-root-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-3.0.0.tgz", - "integrity": "sha512-qMcx+Gy2UZynHjOHOIXPNvpf+9cjvk3cWrBBK7zg4gH9+clobJRb9NGzcT7mQTcV/6Gm/1WelUtqxVXnNlrwcw==" - }, "arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", @@ -1542,6 +1604,7 @@ "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" } @@ -1592,9 +1655,9 @@ }, "dependencies": { "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", "dev": true } } @@ -1709,7 +1772,8 @@ "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true }, "base64-js": { "version": "1.5.1", @@ -1720,6 +1784,7 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz", "integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==", + "dev": true, "requires": { "readable-stream": "^2.3.5", "safe-buffer": "^5.1.1" @@ -1729,6 +1794,7 @@ "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" @@ -1750,16 +1816,16 @@ "dev": true }, "browserslist": { - "version": "4.16.8", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.8.tgz", - "integrity": "sha512-sc2m9ohR/49sWEbPj14ZSSZqp+kbi16aLao42Hmn3Z8FpjuMaq2xCA2l4zl9ITfyzvnvyE0hcg62YkIGKxgaNQ==", + "version": "4.16.6", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", + "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001251", - "colorette": "^1.3.0", - "electron-to-chromium": "^1.3.811", + "caniuse-lite": "^1.0.30001219", + "colorette": "^1.2.2", + "electron-to-chromium": "^1.3.723", "escalade": "^3.1.1", - "node-releases": "^1.1.75" + "node-releases": "^1.1.71" } }, "bs-logger": { @@ -1781,9 +1847,12 @@ } }, "bson": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.6.tgz", - "integrity": "sha512-EvVNVeGo4tHxwi8L6bPj3y3itEvStdwvvlojVxxbyYfoaxJ6keLgrTuKdyfEAszFK+H3olzBuafE0yoh0D1gdg==" + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/bson/-/bson-4.4.1.tgz", + "integrity": "sha512-Uu4OCZa0jouQJCKOk1EmmyqtdWAP5HVLru4lQxTwzJzxT+sJ13lVpEZU/MATDxtHiekWMAL84oQY3Xn1LpJVSg==", + "requires": { + "buffer": "^5.6.0" + } }, "buffer": { "version": "5.7.1", @@ -1801,9 +1870,9 @@ "dev": true }, "buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", "dev": true }, "call-bind": { @@ -1829,15 +1898,16 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001252", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001252.tgz", - "integrity": "sha512-I56jhWDGMtdILQORdusxBOH+Nl/KgQSdDmpJezYddnAkVOmnoU8zwjTV9xAjMIYxr0iPreEAVylCGcmHCjfaOw==", + "version": "1.0.30001245", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001245.tgz", + "integrity": "sha512-768fM9j1PKXpOCKws6eTo3RHmvTUsG9UrpT4WoREFeZgJBTi4/X9g565azS/rVUGtqb8nt7FjLeF5u4kukERnA==", "dev": true }, "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -1879,23 +1949,11 @@ "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", "dev": true }, - "cli-highlight": { - "version": "2.1.11", - "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.11.tgz", - "integrity": "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==", - "requires": { - "chalk": "^4.0.0", - "highlight.js": "^10.7.1", - "mz": "^2.4.0", - "parse5": "^5.1.1", - "parse5-htmlparser2-tree-adapter": "^6.0.0", - "yargs": "^16.0.0" - } - }, "cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, "requires": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", @@ -1918,6 +1976,7 @@ "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" } @@ -1925,12 +1984,13 @@ "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==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "colorette": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.3.0.tgz", - "integrity": "sha512-ecORCqbSFP7Wm8Y6lyqMJjexBQqXSF7SSeaTyGGphogUjBlFP9m9o08wy86HL2uB7fMTxtOUzLMk7ogKcxMg1w==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", + "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==", "dev": true }, "combined-stream": { @@ -1957,7 +2017,8 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true }, "convert-source-map": { "version": "1.8.0", @@ -1979,7 +2040,8 @@ "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=" + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true }, "create-require": { "version": "1.1.1", @@ -2033,9 +2095,10 @@ } }, "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, "requires": { "ms": "2.1.2" } @@ -2080,9 +2143,9 @@ "dev": true }, "denque": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz", - "integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==" + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.0.tgz", + "integrity": "sha512-CYiCSgIF1p6EUByQPlGkKnP1M9g0ZV3qMIrqMqZqdwazygIA/YP2vrbcyl1h/WppKJTdl1F85cXIle+394iDAQ==" }, "detect-newline": { "version": "3.1.0", @@ -2137,15 +2200,10 @@ } } }, - "dotenv": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", - "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==" - }, "electron-to-chromium": { - "version": "1.3.820", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.820.tgz", - "integrity": "sha512-5cFwDmo2yzEA9hn55KZ9+cX/b6DSFvpKz8Hb2fiDmriXWB+DBoXKXmncQwNRFBBTlUdsvPHCoy594OoMLAO0Tg==", + "version": "1.3.778", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.778.tgz", + "integrity": "sha512-Lw04qJaPtWdq0d7qKHJTgkam+FhFi3hm/scf1EyqJWdjO3ZIGUJhNmZJRXWb7yb/bRYXQyVGSpa9RqVpjjWMQw==", "dev": true }, "emittery": { @@ -2157,7 +2215,8 @@ "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==" + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true }, "end-of-stream": { "version": "1.4.4", @@ -2187,9 +2246,9 @@ } }, "es-abstract": { - "version": "1.18.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.5.tgz", - "integrity": "sha512-DDggyJLoS91CkJjgauM5c0yZMjiD1uK3KcaCeAmffGwZ+ODWzOkPN4QwRbsK5DOFf06fywmyLci3ZD8jLGhVYA==", + "version": "1.18.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.3.tgz", + "integrity": "sha512-nQIr12dxV7SSxE6r6f1l3DtAeEYdsGpps13dR0TwJg1S8gyp4ZPgy3FZcHBgbiQqnoqSTb+oC+kO4UQ0C/J8vw==", "dev": true, "requires": { "call-bind": "^1.0.2", @@ -2198,12 +2257,11 @@ "get-intrinsic": "^1.1.1", "has": "^1.0.3", "has-symbols": "^1.0.2", - "internal-slot": "^1.0.3", "is-callable": "^1.2.3", "is-negative-zero": "^2.0.1", "is-regex": "^1.1.3", "is-string": "^1.0.6", - "object-inspect": "^1.11.0", + "object-inspect": "^1.10.3", "object-keys": "^1.1.1", "object.assign": "^4.1.2", "string.prototype.trimend": "^1.0.4", @@ -2225,12 +2283,14 @@ "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true }, "escodegen": { "version": "2.0.0", @@ -2346,23 +2406,6 @@ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true }, - "eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^1.1.0" - }, - "dependencies": { - "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 - } - } - }, "ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", @@ -2378,30 +2421,36 @@ "dev": true }, "eslint-import-resolver-node": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", - "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", + "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": "^3.2.7", - "resolve": "^1.20.0" + "debug": "^2.6.9", + "resolve": "^1.13.1" }, "dependencies": { "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "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.1.1" + "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.2", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.2.tgz", - "integrity": "sha512-QG8pcgThYOuqxupd06oYTZoNOGaUdTY1PqK+oS6ElF6vs4pBdk/aYxFVQQXzcrAqp9m7cl7lb2ubazX+g16k2Q==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.1.tgz", + "integrity": "sha512-ZXI9B8cxAJIH4nfkhTwcRTEAnrVfobYqwjWy/QMCZ8rHkZHFjf9yO4BzpiF9kCSfNlMG54eKigISHpX0+AaT4A==", "dev": true, "requires": { "debug": "^3.2.7", @@ -2492,6 +2541,17 @@ "regextras": "^0.8.0", "semver": "^7.3.5", "spdx-expression-parse": "^3.0.1" + }, + "dependencies": { + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + } } }, "eslint-plugin-markdown": { @@ -2518,12 +2578,6 @@ "prettier-linter-helpers": "^1.0.0" } }, - "eslint-plugin-sort-imports-es6-autofix": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-sort-imports-es6-autofix/-/eslint-plugin-sort-imports-es6-autofix-0.6.0.tgz", - "integrity": "sha512-2NVaBGF9NN+727Fyq+jJYihdIeegjXeUUrZED9Q8FVB8MsV3YQEyXG96GVnXqWt0pmn7xfCZOZf3uKnIhBrfeQ==", - "dev": true - }, "eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -2535,12 +2589,20 @@ } }, "eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", "dev": true, "requires": { - "eslint-visitor-keys": "^2.0.0" + "eslint-visitor-keys": "^1.1.0" + }, + "dependencies": { + "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 + } } }, "eslint-visitor-keys": { @@ -2571,7 +2633,8 @@ "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true }, "esquery": { "version": "1.4.0", @@ -2705,16 +2768,17 @@ "dev": true }, "fast-glob": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", - "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", + "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==", "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", + "glob-parent": "^5.1.0", "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "micromatch": "^4.0.2", + "picomatch": "^2.2.1" } }, "fast-json-stable-stringify": { @@ -2742,9 +2806,9 @@ "dev": true }, "fastq": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.12.0.tgz", - "integrity": "sha512-VNX0QkHK3RsXVKr9KrlUv/FoTa0NdbYoHHl7uXHv2rzyHSlxjdNAKug2twd9luJxpcyNeAgf5iPPMutJO67Dfg==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz", + "integrity": "sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==", "dev": true, "requires": { "reusify": "^1.0.4" @@ -2768,11 +2832,6 @@ "pend": "~1.2.0" } }, - "figlet": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/figlet/-/figlet-1.5.2.tgz", - "integrity": "sha512-WOn21V8AhyE1QqVfPIVxe3tupJacq1xGkPTB4iagT6o+P2cAgEOOwIxMftr4+ZCTI6d551ij9j61DFr0nsP2uQ==" - }, "file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -2888,9 +2947,9 @@ "dev": true }, "flatted": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.2.tgz", - "integrity": "sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.1.tgz", + "integrity": "sha512-OMQjaErSFHmHqZe+PSidH5n8j3O0F2DdnVh8JB4j4eUQ2k6KvB0qGfrKIhapvez5JerBbmWkaLYUYWISaESoXg==", "dev": true }, "form-data": { @@ -2913,7 +2972,8 @@ "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true }, "fsevents": { "version": "2.3.2", @@ -2943,7 +3003,8 @@ "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true }, "get-intrinsic": { "version": "1.1.1", @@ -2978,6 +3039,7 @@ "version": "7.1.7", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -2997,18 +3059,18 @@ } }, "globals": { - "version": "13.11.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.11.0.tgz", - "integrity": "sha512-08/xrJ7wQjK9kkkRoI3OFUBbLx4f+6x3SGwcPvQ0QH6goFDrOU2oyAWrmh3dJezu65buo+HBMzAMQy6rovVC3g==", + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.10.0.tgz", + "integrity": "sha512-piHC3blgLGFjvOuMmWZX60f+na1lXFDhQXBf1UYp2fXPXqvEUbOhNwi6BsQ0bQishwedgnjkwv1d9zKf+MWw3g==", "dev": true, "requires": { "type-fest": "^0.20.2" } }, "globby": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", - "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.3.tgz", + "integrity": "sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg==", "dev": true, "requires": { "array-union": "^2.1.0", @@ -3020,9 +3082,9 @@ } }, "graceful-fs": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", - "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", "dev": true }, "has": { @@ -3034,21 +3096,6 @@ "function-bind": "^1.1.1" } }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "requires": { - "ansi-regex": "^2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - } - } - }, "has-bigints": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", @@ -3058,7 +3105,8 @@ "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true }, "has-symbols": { "version": "1.0.2", @@ -3066,20 +3114,6 @@ "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", "dev": true }, - "has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dev": true, - "requires": { - "has-symbols": "^1.0.2" - } - }, - "highlight.js": { - "version": "10.7.3", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", - "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==" - }, "hosted-git-info": { "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", @@ -3246,6 +3280,7 @@ "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" @@ -3254,18 +3289,8 @@ "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "internal-slot": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", - "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", - "dev": true, - "requires": { - "get-intrinsic": "^1.1.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" - } + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true }, "is-alphabetical": { "version": "1.0.4", @@ -3290,28 +3315,24 @@ "dev": true }, "is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, - "requires": { - "has-bigints": "^1.0.1" - } + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.2.tgz", + "integrity": "sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA==", + "dev": true }, "is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.1.tgz", + "integrity": "sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "call-bind": "^1.0.2" } }, "is-callable": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", - "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz", + "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==", "dev": true }, "is-ci": { @@ -3324,22 +3345,19 @@ } }, "is-core-module": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.6.0.tgz", - "integrity": "sha512-wShG8vs60jKfPWpF2KZRaAtvt3a20OAn7+IJ6hLPECpSABLcKtFKTTI4ZtH5QcBruBHlq+WsdHWyz0BCZW7svQ==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.5.0.tgz", + "integrity": "sha512-TXCMSDsEHMEEZ6eCA8rwRDbLu55MRGmrctljsBX/2v1d9/GzqHOxW5c5oPSgrUt2vBFXebu9rGqckXGPWOlYpg==", "dev": true, "requires": { "has": "^1.0.3" } }, "is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.4.tgz", + "integrity": "sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A==", + "dev": true }, "is-decimal": { "version": "1.0.4", @@ -3356,7 +3374,8 @@ "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==" + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true }, "is-generator-fn": { "version": "2.1.0", @@ -3392,13 +3411,10 @@ "dev": true }, "is-number-object": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", - "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.5.tgz", + "integrity": "sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw==", + "dev": true }, "is-potential-custom-element-name": { "version": "1.0.1", @@ -3407,29 +3423,26 @@ "dev": true }, "is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.3.tgz", + "integrity": "sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ==", "dev": true, "requires": { "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "has-symbols": "^1.0.2" } }, "is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "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==", "dev": true }, "is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.6.tgz", + "integrity": "sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w==", + "dev": true }, "is-symbol": { "version": "1.0.4", @@ -3449,7 +3462,8 @@ "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true }, "isexe": { "version": "2.0.0", @@ -4758,21 +4772,22 @@ "version": "3.14.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, "requires": { "argparse": "^1.0.7", "esprima": "^4.0.0" } }, "jsdoc-type-pratt-parser": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-1.1.1.tgz", - "integrity": "sha512-uelRmpghNwPBuZScwgBG/OzodaFk5RbO5xaivBdsAY70icWfShwZ7PCMO0x1zSkOa8T1FzHThmrdoyg/0AwV5g==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-1.0.4.tgz", + "integrity": "sha512-jzmW9gokeq9+bHPDR1nCeidMyFUikdZlbOhKzh9+/nJqB75XhpNKec1/UuxW5c4+O+Pi31Gc/dCboyfSm/pSpQ==", "dev": true }, "jsdom": { - "version": "16.7.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", - "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", + "version": "16.6.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.6.0.tgz", + "integrity": "sha512-Ty1vmF4NHJkolaEmdjtxTfSfkdb8Ywarwf63f+F8/mDD1uLSSWDxDuMiZxiPhwunLrn9LOSVItWj4bLYsLN3Dg==", "dev": true, "requires": { "abab": "^2.0.5", @@ -4800,7 +4815,7 @@ "whatwg-encoding": "^1.0.5", "whatwg-mimetype": "^2.3.0", "whatwg-url": "^8.5.0", - "ws": "^7.4.6", + "ws": "^7.4.5", "xml-name-validator": "^3.0.0" }, "dependencies": { @@ -4843,12 +4858,12 @@ "dev": true }, "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", + "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", "dev": true, "requires": { - "minimist": "^1.2.0" + "minimist": "^1.2.5" } }, "kleur": { @@ -5028,18 +5043,18 @@ } }, "mime-db": { - "version": "1.49.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.49.0.tgz", - "integrity": "sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA==", + "version": "1.48.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.48.0.tgz", + "integrity": "sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ==", "dev": true }, "mime-types": { - "version": "2.1.32", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.32.tgz", - "integrity": "sha512-hJGaVS4G4c9TSMYh2n6SQAGrC4RnfU+daP8G7cSCmaqNjiOoUY0VHCMS42pxnQmVF1GWwFhbHWn3RIxCqTmZ9A==", + "version": "2.1.31", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.31.tgz", + "integrity": "sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==", "dev": true, "requires": { - "mime-db": "1.49.0" + "mime-db": "1.48.0" } }, "mimic-fn": { @@ -5052,6 +5067,7 @@ "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" } @@ -5065,21 +5081,29 @@ "mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true }, "mongodb": { - "version": "3.6.10", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.10.tgz", - "integrity": "sha512-fvIBQBF7KwCJnDZUnFFy4WqEFP8ibdXeFANnylW19+vOwdjOAvqIzPdsNCEMT6VKTHnYu4K64AWRih0mkFms6Q==", - "requires": { - "bl": "^2.2.1", - "bson": "^1.1.4", - "denque": "^1.4.1", - "optional-require": "^1.0.3", - "safe-buffer": "^5.1.2", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.1.0.tgz", + "integrity": "sha512-Gx9U9MsFWgJ3E0v4oHAdWvYTGBznNYPCkhmD/3i/kPTY/URnPfHD5/6VoKUFrdgQTK3icFiM9976hVbqCRBO9Q==", + "requires": { + "bson": "^4.4.0", + "denque": "^1.5.0", + "mongodb-connection-string-url": "^1.0.1", "saslprep": "^1.0.0" } }, + "mongodb-connection-string-url": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-1.1.2.tgz", + "integrity": "sha512-mp5lv4guWuykOpkwNNqQ0tKKytuJUjL/aC/bu/DqoJVWL5NSh4j/u+gJ+EiOdweLujHyq6JZZqcTVipHhL5xRg==", + "requires": { + "@types/whatwg-url": "^8.0.0", + "whatwg-url": "^8.4.0" + } + }, "mongodb-memory-server": { "version": "7.2.1", "resolved": "https://registry.npmjs.org/mongodb-memory-server/-/mongodb-memory-server-7.2.1.tgz", @@ -5091,9 +5115,9 @@ }, "dependencies": { "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", "dev": true } } @@ -5123,16 +5147,36 @@ "yauzl": "^2.10.0" }, "dependencies": { + "bson": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.6.tgz", + "integrity": "sha512-EvVNVeGo4tHxwi8L6bPj3y3itEvStdwvvlojVxxbyYfoaxJ6keLgrTuKdyfEAszFK+H3olzBuafE0yoh0D1gdg==", + "dev": true + }, "camelcase": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", "dev": true }, + "mongodb": { + "version": "3.6.11", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.11.tgz", + "integrity": "sha512-4Y4lTFHDHZZdgMaHmojtNAlqkvddX2QQBEN0K//GzxhGwlI9tZ9R0vhbjr1Decw+TF7qK0ZLjQT292XgHRRQgw==", + "dev": true, + "requires": { + "bl": "^2.2.1", + "bson": "^1.1.4", + "denque": "^1.4.1", + "optional-require": "^1.0.3", + "safe-buffer": "^5.1.2", + "saslprep": "^1.0.0" + } + }, "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", "dev": true } } @@ -5140,17 +5184,8 @@ "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "requires": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" - } + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true }, "natural-compare": { "version": "1.4.0", @@ -5168,10 +5203,19 @@ "tslib": "^2.3.0" }, "dependencies": { + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", "dev": true } } @@ -5189,9 +5233,9 @@ "dev": true }, "node-releases": { - "version": "1.1.75", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.75.tgz", - "integrity": "sha512-Qe5OUajvqrqDSy6wrWFmMwfJ0jVgwiw4T3KqmbTcZ62qW0gQkheXYhcFM1+lOVcGUoRxcEcfyvFMAnDgaF1VWw==", + "version": "1.1.73", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.73.tgz", + "integrity": "sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg==", "dev": true }, "normalize-package-data": { @@ -5240,11 +5284,6 @@ "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==", "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=" - }, "object-inspect": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", @@ -5284,6 +5323,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, "requires": { "wrappy": "1" } @@ -5301,6 +5341,7 @@ "version": "1.1.7", "resolved": "https://registry.npmjs.org/optional-require/-/optional-require-1.1.7.tgz", "integrity": "sha512-cIeRZocXsZnZYn+SevbtSqNlLbeoS4mLzuNn4fvXRMDRNhTGg0sxuKXl0FnZCtnew85LorNxIbZp5OeliILhMw==", + "dev": true, "requires": { "require-at": "^1.0.6" } @@ -5358,11 +5399,6 @@ "callsites": "^3.0.0" } }, - "parent-require": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/parent-require/-/parent-require-1.0.0.tgz", - "integrity": "sha1-dGoWdjgIOoYLDu9nMssn7UbDKXc=" - }, "parse-entities": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", @@ -5387,26 +5423,6 @@ "json-parse-better-errors": "^1.0.1" } }, - "parse5": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", - "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==" - }, - "parse5-htmlparser2-tree-adapter": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", - "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", - "requires": { - "parse5": "^6.0.1" - }, - "dependencies": { - "parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" - } - } - }, "path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", @@ -5416,7 +5432,8 @@ "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=" + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true }, "path-key": { "version": "3.1.1", @@ -5466,18 +5483,6 @@ "pino-std-serializers": "^3.1.0", "quick-format-unescaped": "^4.0.3", "sonic-boom": "^1.0.2" - }, - "dependencies": { - "sonic-boom": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-1.4.1.tgz", - "integrity": "sha512-LRHh/A8tpW7ru89lrlkU4AszXt1dbwSjVWguGrmlxE7tawVmDBlI1PILMkXAxJTwqhgsEeTHzj36D5CmHgQmNg==", - "dev": true, - "requires": { - "atomic-sleep": "^1.0.0", - "flatstr": "^1.0.12" - } - } } }, "pino-std-serializers": { @@ -5549,7 +5554,8 @@ "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==" + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true }, "progress": { "version": "2.0.3", @@ -5576,8 +5582,7 @@ "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 + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" }, "queue-microtask": { "version": "1.2.3", @@ -5633,6 +5638,7 @@ "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -5646,15 +5652,11 @@ "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==" + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true } } }, - "reflect-metadata": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", - "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" - }, "regexpp": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", @@ -5670,12 +5672,14 @@ "require-at": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/require-at/-/require-at-1.0.6.tgz", - "integrity": "sha512-7i1auJbMUrXEAZCOQ0VNJgmcT2VOKPRl2YGJwgpHpC9CE91Mv4/4UYIUm4chGJaI381ZDq1JUicFii64Hapd8g==" + "integrity": "sha512-7i1auJbMUrXEAZCOQ0VNJgmcT2VOKPRl2YGJwgpHpC9CE91Mv4/4UYIUm4chGJaI381ZDq1JUicFii64Hapd8g==", + "dev": true }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true }, "require-from-string": { "version": "2.0.2", @@ -5743,7 +5747,8 @@ "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==" + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -5760,11 +5765,6 @@ "sparse-bitfield": "^3.0.3" } }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" - }, "saxes": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", @@ -5783,15 +5783,6 @@ "lru-cache": "^6.0.0" } }, - "sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -5807,17 +5798,6 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, - "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - } - }, "signal-exit": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", @@ -5848,12 +5828,13 @@ } }, "sonic-boom": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-2.1.0.tgz", - "integrity": "sha512-x2j9LXx27EDlyZEC32gBM+scNVMdPutU7FIKV2BOTKCnPrp7bY5BsplCMQ4shYYR3IhDSIrEXoqb6GlS+z7KyQ==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-1.4.1.tgz", + "integrity": "sha512-LRHh/A8tpW7ru89lrlkU4AszXt1dbwSjVWguGrmlxE7tawVmDBlI1PILMkXAxJTwqhgsEeTHzj36D5CmHgQmNg==", "dev": true, "requires": { - "atomic-sleep": "^1.0.0" + "atomic-sleep": "^1.0.0", + "flatstr": "^1.0.12" } }, "source-map": { @@ -5908,15 +5889,16 @@ } }, "spdx-license-ids": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.10.tgz", - "integrity": "sha512-oie3/+gKf7QtpitB0LYLETe+k8SifzsX4KixvpOsbI6S0kRiRQ5MKOio8eMSAKQ17N06+wdEOXRiId+zOxo0hA==", + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.9.tgz", + "integrity": "sha512-Ki212dKK4ogX+xDo4CtOZBVIwhsKBEfsEEcwmJfLQzirgc2jIWdzg40Unxz/HzEUqM1WFzVlQSMF9kZZ2HboLQ==", "dev": true }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true }, "stack-utils": { "version": "2.0.3", @@ -5949,6 +5931,7 @@ "version": "4.2.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -5979,6 +5962,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, "requires": { "safe-buffer": "~5.1.0" }, @@ -5986,7 +5970,8 @@ "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==" + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true } } }, @@ -5994,6 +5979,7 @@ "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" } @@ -6020,6 +6006,7 @@ "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" } @@ -6138,22 +6125,6 @@ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, - "thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "requires": { - "any-promise": "^1.0.0" - } - }, - "thenify-all": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=", - "requires": { - "thenify": ">= 3.1.0 < 4" - } - }, "throat": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz", @@ -6205,7 +6176,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", - "dev": true, "requires": { "punycode": "^2.1.1" } @@ -6226,17 +6196,6 @@ "mkdirp": "1.x", "semver": "7.x", "yargs-parser": "20.x" - }, - "dependencies": { - "json5": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", - "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - } } }, "ts-mockito": { @@ -6267,13 +6226,12 @@ } }, "tsconfig-paths": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.11.0.tgz", - "integrity": "sha512-7ecdYDnIdmv639mmDwslG6KQg1Z9STTz1j7Gcz0xa+nshh/gKDAHcPxRbWOsA3SPp0tXP2leTcY9Kw+NAkfZzA==", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.10.1.tgz", + "integrity": "sha512-rETidPDgCpltxF7MjBZlAFPUHv5aHH2MymyPvh+vEyWAED4Eb/WeMbsnD/JDr4OKPOA1TssDHgIcpTN5Kh0p6Q==", "dev": true, "requires": { - "@types/json5": "^0.0.29", - "json5": "^1.0.1", + "json5": "^2.2.0", "minimist": "^1.2.0", "strip-bom": "^3.0.0" } @@ -6281,7 +6239,8 @@ "tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true }, "tsutils": { "version": "3.21.0", @@ -6327,29 +6286,6 @@ "is-typedarray": "^1.0.0" } }, - "typeorm": { - "version": "0.2.28", - "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.2.28.tgz", - "integrity": "sha512-BTtUBGwsFzODvHY+AlWL9pvJ2uEj8qpHwmo03z43RvZkG8BAryQJQ3lZ7HlGvI9IQU8y1IYGWe97HsVr8kXB9g==", - "requires": { - "@sqltools/formatter": "1.2.2", - "app-root-path": "^3.0.0", - "buffer": "^5.5.0", - "chalk": "^4.1.0", - "cli-highlight": "^2.1.4", - "debug": "^4.1.1", - "dotenv": "^8.2.0", - "glob": "^7.1.6", - "js-yaml": "^3.14.0", - "mkdirp": "^1.0.4", - "reflect-metadata": "^0.1.13", - "sha.js": "^2.4.11", - "tslib": "^1.13.0", - "xml2js": "^0.4.23", - "yargonaut": "^1.1.2", - "yargs": "^16.0.3" - } - }, "typescript": { "version": "4.3.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", @@ -6395,7 +6331,8 @@ "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true }, "uuid": { "version": "8.3.2", @@ -6467,8 +6404,7 @@ "webidl-conversions": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", - "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", - "dev": true + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==" }, "whatwg-encoding": { "version": "1.0.5", @@ -6489,7 +6425,6 @@ "version": "8.7.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", - "dev": true, "requires": { "lodash": "^4.7.0", "tr46": "^2.1.0", @@ -6528,6 +6463,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, "requires": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -6537,7 +6473,8 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true }, "write-file-atomic": { "version": "3.0.3", @@ -6563,20 +6500,6 @@ "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", "dev": true }, - "xml2js": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", - "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", - "requires": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - } - }, - "xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" - }, "xmlchars": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", @@ -6586,7 +6509,8 @@ "y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true }, "yallist": { "version": "4.0.0", @@ -6594,57 +6518,11 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, - "yargonaut": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/yargonaut/-/yargonaut-1.1.4.tgz", - "integrity": "sha512-rHgFmbgXAAzl+1nngqOcwEljqHGG9uUZoPjsdZEs1w5JW9RXYzrSvH/u70C1JE5qFi0qjsdhnUX/dJRpWqitSA==", - "requires": { - "chalk": "^1.1.1", - "figlet": "^1.1.1", - "parent-require": "^1.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" - } - } - }, "yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, "requires": { "cliui": "^7.0.2", "escalade": "^3.1.1", @@ -6656,9 +6534,10 @@ } }, "yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==" + "version": "20.2.7", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.7.tgz", + "integrity": "sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==", + "dev": true }, "yauzl": { "version": "2.10.0", diff --git a/package.json b/package.json index d2236a9a..cf1667be 100644 --- a/package.json +++ b/package.json @@ -23,9 +23,8 @@ "human-interval": "2.0.1", "lodash": "4.17.21", "luxon": "2.0.1", - "mongodb": "3.6.10", + "mongodb": "4.1.0", "typed-emitter": "1.3.1", - "typeorm": "0.2.28", "uuid": "8.3.2" }, "devDependencies": { @@ -34,7 +33,6 @@ "@types/jest": "26.0.24", "@types/lodash": "4.14.171", "@types/luxon": "1.27.1", - "@types/mongodb": "3.6.20", "@types/node": "16.3.2", "@types/pino": "6.3.9", "@types/uuid": "8.3.1", @@ -48,7 +46,6 @@ "eslint-plugin-markdown": "2.2.0", "eslint-plugin-prefer-arrow": "1.2.3", "eslint-plugin-prettier": "3.4.0", - "eslint-plugin-sort-imports-es6-autofix": "^0.6.0", "jest": "27.0.6", "mongodb-memory-server": "7.2.1", "pino": "6.12.0", @@ -56,7 +53,6 @@ "ts-jest": "27.0.3", "ts-mockito": "2.6.1", "ts-node": "10.1.0", - "typescript": "4.3.5", - "uuid": "8.3.2" + "typescript": "4.3.5" } } diff --git a/src/Connection.ts b/src/Connection.ts new file mode 100644 index 00000000..c7f61cab --- /dev/null +++ b/src/Connection.ts @@ -0,0 +1,51 @@ +import { MongoClient } from 'mongodb'; + +import { ExecutionsRepository } from './repository/ExecutionsRepository'; +import { JOBS_COLLECTION_NAME, JobRepository } from './repository/JobRepository'; + +export interface MomoConnectionOptions { + url: string; +} + +export class Connection { + private executionsRepository?: ExecutionsRepository; + private jobRepository?: JobRepository; + + constructor(private readonly mongoClient: MongoClient) {} + + static async create(connectionOptions: MomoConnectionOptions): Promise { + const connection = new Connection(new MongoClient(connectionOptions.url)); + + await connection.connect(); + + return connection; + } + + getExecutionsRepository(): ExecutionsRepository { + if (this.executionsRepository === undefined) { + this.executionsRepository = new ExecutionsRepository(this.mongoClient); + } + return this.executionsRepository; + } + + getJobRepository(): JobRepository { + if (this.jobRepository === undefined) { + this.jobRepository = new JobRepository(this.mongoClient); + } + return this.jobRepository; + } + + async disconnect(): Promise { + await this.mongoClient.close(); + } + + async connect(): Promise { + await this.mongoClient.connect(); + + await this.mongoClient.db().collection(JOBS_COLLECTION_NAME).createIndex({ name: 1 }, { name: 'job_name_index' }); + await this.mongoClient + .db() + .collection(JOBS_COLLECTION_NAME) + .createIndex({ scheduleId: 1 }, { name: 'schedule_id_index' }); + } +} diff --git a/src/connect.ts b/src/connect.ts deleted file mode 100644 index 3b750861..00000000 --- a/src/connect.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { Connection, createConnection, getConnection } from 'typeorm'; - -import { ExecutionsEntity } from './repository/ExecutionsEntity'; -import { ExecutionsRepository } from './repository/ExecutionsRepository'; -import { JobEntity } from './repository/JobEntity'; -import { JobRepository } from './repository/JobRepository'; -import { isConnected } from './isConnected'; - -export const connectionName = 'momo'; - -export interface MomoConnectionOptions { - url: string; -} - -export async function connect(connectionOptions: MomoConnectionOptions): Promise { - if (isConnected()) { - return getConnection(connectionName); - } - - const connection = await createConnection({ - ...connectionOptions, - type: 'mongodb', - name: connectionName, - useUnifiedTopology: true, - entities: [ExecutionsEntity, JobEntity], - }); - - await connection.getCustomRepository(JobRepository).createCollectionIndex({ name: 1 }, { name: 'job_name_index' }); - await connection - .getCustomRepository(ExecutionsRepository) - .createCollectionIndex({ scheduleId: 1 }, { name: 'schedule_id_index' }); - - return connection; -} - -export async function disconnect(): Promise { - await getConnection(connectionName).close(); -} diff --git a/src/executor/JobExecutor.ts b/src/executor/JobExecutor.ts index 78cdc00d..08892fb0 100644 --- a/src/executor/JobExecutor.ts +++ b/src/executor/JobExecutor.ts @@ -1,11 +1,12 @@ import { DateTime } from 'luxon'; import { ExecutionStatus, JobResult } from '../job/ExecutionInfo'; +import { ExecutionsRepository } from '../repository/ExecutionsRepository'; import { Handler } from '../job/MomoJob'; import { JobEntity } from '../repository/JobEntity'; +import { JobRepository } from '../repository/JobRepository'; import { Logger } from '../logging/Logger'; import { MomoErrorType } from '../logging/error/MomoErrorType'; -import { getExecutionsRepository, getJobRepository } from '../repository/getRepository'; export class JobExecutor { private stopped = false; @@ -13,6 +14,8 @@ export class JobExecutor { constructor( private readonly handler: Handler, private readonly scheduleId: string, + private readonly executionsRepository: ExecutionsRepository, + private readonly jobRepository: JobRepository, private readonly logger: Logger ) {} @@ -21,9 +24,7 @@ export class JobExecutor { } async execute(jobEntity: JobEntity): Promise { - const executionsRepository = getExecutionsRepository(); - - const { added, running } = await executionsRepository.addExecution( + const { added, running } = await this.executionsRepository.addExecution( this.scheduleId, jobEntity.name, jobEntity.maxRunning @@ -40,7 +41,7 @@ export class JobExecutor { const { started, result } = await this.executeHandler(jobEntity); - await getJobRepository().updateJob(jobEntity.name, { + await this.jobRepository.updateJob(jobEntity.name, { executionInfo: { lastStarted: started.toISO(), lastFinished: DateTime.now().toISO(), @@ -55,7 +56,7 @@ export class JobExecutor { }); if (!this.stopped) { - await executionsRepository.removeExecution(this.scheduleId, jobEntity.name); + await this.executionsRepository.removeExecution(this.scheduleId, jobEntity.name); } return result; diff --git a/src/index.ts b/src/index.ts index 48a5f173..5592e802 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,5 @@ export { MongoSchedule } from './schedule/MongoSchedule'; -export { connect, MomoConnectionOptions } from './connect'; +export { MomoConnectionOptions } from './Connection'; export { momoError } from './logging/error/MomoError'; export { MomoErrorType } from './logging/error/MomoErrorType'; export { MomoEvent, MomoErrorEvent, MomoEventData } from './logging/MomoEvents'; @@ -7,7 +7,3 @@ export { MomoJob } from './job/MomoJob'; export { MomoJobDescription, JobSchedulerStatus } from './job/MomoJobDescription'; export { ExecutionStatus } from './job/ExecutionInfo'; export { ExecutionInfo } from './job/ExecutionInfo'; -export { isConnected } from './isConnected'; -export { check } from './job/check'; -export { clear } from './job/clear'; -export { list } from './job/list'; diff --git a/src/isConnected.ts b/src/isConnected.ts deleted file mode 100644 index 2961c774..00000000 --- a/src/isConnected.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { getConnection } from 'typeorm'; - -import { connectionName } from './connect'; - -/** - * Checks whether Momo is connected to a database. - */ -export function isConnected(): boolean { - try { - const connection = getConnection(connectionName); - return connection.isConnected; - } catch (e) { - return false; - } -} diff --git a/src/job/Job.ts b/src/job/Job.ts index cab44c53..517bdd03 100644 --- a/src/job/Job.ts +++ b/src/job/Job.ts @@ -1,7 +1,9 @@ -import { ExecutionInfo } from './ExecutionInfo'; -import { Handler } from './MomoJob'; +import { WithoutId } from 'mongodb'; -export type MomoJobStatus = JobDefinition & { executionInfo?: ExecutionInfo }; +import { Handler, MomoJob } from './MomoJob'; +import { JobEntity } from '../repository/JobEntity'; + +export type MomoJobStatus = WithoutId; export interface JobDefinition { name: string; @@ -14,3 +16,16 @@ export interface Job extends JobDefinition { immediate: boolean; handler: Handler; } + +export function toJob(job: MomoJob): Job { + return { immediate: false, concurrency: 1, maxRunning: 0, ...job }; +} + +export function toJobDefinition(job: Job): JobDefinition { + return { + name: job.name, + interval: job.interval, + maxRunning: job.maxRunning, + concurrency: job.concurrency, + }; +} diff --git a/src/job/check.ts b/src/job/check.ts deleted file mode 100644 index e5b8c778..00000000 --- a/src/job/check.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { ExecutionInfo } from './ExecutionInfo'; -import { getJobRepository } from '../repository/getRepository'; -import { isConnected } from '../isConnected'; - -/** - * Retrieves execution information about the job from the database. Returns undefined if the job cannot be found or was never executed. - * - * @param name the job to check - */ -export async function check(name: string): Promise { - if (!isConnected()) return; - const job = await getJobRepository().findOne({ name }); - return job?.executionInfo; -} diff --git a/src/job/clear.ts b/src/job/clear.ts deleted file mode 100644 index 17c87741..00000000 --- a/src/job/clear.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { getJobRepository } from '../repository/getRepository'; -import { isConnected } from '../index'; - -/** - * Removes all jobs from the database. - */ -export async function clear(): Promise { - if (!isConnected()) return; - await getJobRepository().deleteMany({}); -} diff --git a/src/job/define.ts b/src/job/define.ts deleted file mode 100644 index 7f0779da..00000000 --- a/src/job/define.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Job } from './Job'; -import { JobEntity } from '../repository/JobEntity'; -import { Logger } from '../logging/Logger'; -import { getJobRepository } from '../repository/getRepository'; -import { keepLatest } from './keepLatest'; - -export async function define(job: Job, logger?: Logger): Promise { - const { name, interval, concurrency, maxRunning } = job; - - logger?.debug('define job', { name, concurrency, interval, maxRunning }); - - const jobEntity = JobEntity.from(job); - const jobRepository = getJobRepository(); - const old = await keepLatest(name, logger); - - if (old) { - logger?.debug('update job in database', { name }); - await jobRepository.updateJob(name, jobEntity); - return; - } - - logger?.debug('save job to database', { name }); - await jobRepository.save(jobEntity); -} diff --git a/src/job/keepLatest.ts b/src/job/keepLatest.ts deleted file mode 100644 index a9e5a7e1..00000000 --- a/src/job/keepLatest.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { JobEntity } from '../repository/JobEntity'; -import { Logger } from '../logging/Logger'; -import { findLatest } from './findLatest'; -import { getJobRepository } from '../repository/getRepository'; - -export async function keepLatest(name: string, logger?: Logger): Promise { - const jobRepository = getJobRepository(); - const jobs = await jobRepository.find({ name }); - - if (jobs.length === 1) return jobs[0]; - - const latest = findLatest(jobs); - if (!latest) return undefined; - - logger?.debug('duplicate job, keep latest only', { name, count: jobs.length }); - - jobs.splice(jobs.indexOf(latest), 1); - await jobRepository.remove(jobs); - - return latest; -} diff --git a/src/job/list.ts b/src/job/list.ts deleted file mode 100644 index 80c91f66..00000000 --- a/src/job/list.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { MomoJobStatus } from './Job'; -import { getJobRepository } from '../repository/getRepository'; -import { isConnected } from '../isConnected'; - -/** - * Lists all jobs stored in the database. - */ -export async function list(): Promise { - if (!isConnected()) return []; - const jobs = await getJobRepository().find(); - - return jobs.map((job) => { - return { - name: job.name, - interval: job.interval, - concurrency: job.concurrency, - maxRunning: job.maxRunning, - executionInfo: job.executionInfo, - }; - }); -} diff --git a/src/job/withDefaults.ts b/src/job/withDefaults.ts deleted file mode 100644 index 483344b4..00000000 --- a/src/job/withDefaults.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { Job } from './Job'; -import { MomoJob } from './MomoJob'; - -export function withDefaults(job: MomoJob): Job { - return { immediate: false, concurrency: 1, maxRunning: 0, ...job }; -} diff --git a/src/repository/ExecutionsEntity.ts b/src/repository/ExecutionsEntity.ts index b0fd1dee..40fb496c 100644 --- a/src/repository/ExecutionsEntity.ts +++ b/src/repository/ExecutionsEntity.ts @@ -1,27 +1,12 @@ -import { Column, Entity, Index, ObjectID, ObjectIdColumn } from 'typeorm'; +import { ObjectId } from 'mongodb'; interface Executions { [name: string]: number; } -@Entity({ name: 'executions' }) -export class ExecutionsEntity { - @ObjectIdColumn() - public _id?: ObjectID; - - @Index() - @Column() - public scheduleId: string; - - @Column() - public timestamp: number; - - @Column() - public executions: Executions; - - constructor(scheduleId: string, timestamp: number, executions: Executions) { - this.scheduleId = scheduleId; - this.timestamp = timestamp; - this.executions = executions; - } +export interface ExecutionsEntity { + _id?: ObjectId; + scheduleId: string; + timestamp: number; + executions: Executions; } diff --git a/src/repository/ExecutionsRepository.ts b/src/repository/ExecutionsRepository.ts index 94c4ad34..e1eb6324 100644 --- a/src/repository/ExecutionsRepository.ts +++ b/src/repository/ExecutionsRepository.ts @@ -1,15 +1,21 @@ import { DateTime } from 'luxon'; -import { EntityRepository, MongoRepository } from 'typeorm'; +import { MongoClient } from 'mongodb'; import { ExecutionsEntity } from './ExecutionsEntity'; +import { Repository } from './Repository'; import { defaultInterval } from '../schedule/SchedulePing'; -@EntityRepository(ExecutionsEntity) -export class ExecutionsRepository extends MongoRepository { +export const EXECUTIONS_COLLECTION_NAME = 'executions'; + +export class ExecutionsRepository extends Repository { public static deadScheduleThreshold = 2 * defaultInterval; + constructor(mongoClient: MongoClient) { + super(mongoClient, EXECUTIONS_COLLECTION_NAME); + } + async addSchedule(scheduleId: string): Promise { - await this.save(new ExecutionsEntity(scheduleId, DateTime.now().toMillis(), {})); + await this.save({ scheduleId, timestamp: DateTime.now().toMillis(), executions: {} }); } async removeJob(scheduleId: string, name: string): Promise { @@ -20,7 +26,7 @@ export class ExecutionsRepository extends MongoRepository { const executions = executionsEntity.executions; delete executions[name]; - await this.update({ scheduleId }, { executions }); + await this.updateOne({ scheduleId }, { $set: { executions } }); } async addExecution( @@ -33,30 +39,29 @@ export class ExecutionsRepository extends MongoRepository { return { added: false, running }; } - await this.findOneAndUpdate({ scheduleId }, { $inc: { [`executions.${name}`]: 1 } }); + await this.updateOne({ scheduleId }, { $inc: { [`executions.${name}`]: 1 } }); return { added: true, running }; } async removeExecution(scheduleId: string, name: string): Promise { - await this.findOneAndUpdate({ scheduleId }, { $inc: { [`executions.${name}`]: -1 } }); + await this.updateOne({ scheduleId }, { $inc: { [`executions.${name}`]: -1 } }); } async countRunningExecutions(name: string): Promise { const timestamp = DateTime.now().toMillis() - ExecutionsRepository.deadScheduleThreshold; - const numbers = (await this.find({ where: { timestamp: { $gt: timestamp } } })).map((executionsEntity) => { + const numbers = (await this.find({ timestamp: { $gt: timestamp } })).map((executionsEntity) => { return executionsEntity.executions[name] ?? 0; }); return numbers.reduce((sum, current) => sum + current, 0); } async ping(scheduleId: string): Promise { - await this.findOneAndUpdate({ scheduleId }, { $set: { timestamp: DateTime.now().toMillis() } }); + await this.updateOne({ scheduleId }, { $set: { timestamp: DateTime.now().toMillis() } }); } async clean(): Promise { const timestamp = DateTime.now().toMillis() - ExecutionsRepository.deadScheduleThreshold; - const result = await this.deleteMany({ timestamp: { $lt: timestamp } }); - return result.deletedCount ?? 0; + return this.delete({ timestamp: { $lt: timestamp } }); } } diff --git a/src/repository/JobEntity.ts b/src/repository/JobEntity.ts index f7a022ad..fc5895d8 100644 --- a/src/repository/JobEntity.ts +++ b/src/repository/JobEntity.ts @@ -1,38 +1,9 @@ -import { Column, Entity, Index, ObjectID, ObjectIdColumn } from 'typeorm'; +import { ObjectId } from 'mongodb'; import { ExecutionInfo } from '../job/ExecutionInfo'; -import { Job } from '../job/Job'; +import { JobDefinition } from '../job/Job'; -@Entity({ name: 'jobs' }) -export class JobEntity { - @ObjectIdColumn() - public _id?: ObjectID; - - @Index() - @Column() - public name: string; - - @Column() - public interval: string; - - @Column() - public concurrency: number; - - @Column() - public maxRunning: number; - - @Column() - public executionInfo?: ExecutionInfo; - - constructor(name: string, interval: string, concurrency: number, maxRunning: number, executionInfo?: ExecutionInfo) { - this.name = name; - this.interval = interval; - this.concurrency = concurrency; - this.maxRunning = maxRunning; - this.executionInfo = executionInfo; - } - - static from({ name, interval, concurrency, maxRunning }: Job): JobEntity { - return new JobEntity(name, interval, concurrency, maxRunning); - } +export interface JobEntity extends JobDefinition { + _id?: ObjectId; + executionInfo?: ExecutionInfo; } diff --git a/src/repository/JobRepository.ts b/src/repository/JobRepository.ts index e542cfe7..80c41ef4 100644 --- a/src/repository/JobRepository.ts +++ b/src/repository/JobRepository.ts @@ -1,36 +1,83 @@ -import { EntityRepository, MongoRepository } from 'typeorm'; +import { MongoClient } from 'mongodb'; +import { ExecutionInfo } from '../job/ExecutionInfo'; +import { Job, MomoJobStatus, toJobDefinition } from '../job/Job'; import { JobEntity } from './JobEntity'; +import { Logger } from '../logging/Logger'; +import { Repository } from './Repository'; +import { findLatest } from '../job/findLatest'; -@EntityRepository(JobEntity) -export class JobRepository extends MongoRepository { - async updateJob(name: string, update: Partial): Promise { - const savedJobs = await this.find({ name }); +export const JOBS_COLLECTION_NAME = 'jobs'; + +export class JobRepository extends Repository { + private logger: Logger | undefined; + + constructor(mongoClient: MongoClient) { + super(mongoClient, JOBS_COLLECTION_NAME); + } + + setLogger(logger: Logger): void { + this.logger = logger; + } + + async check(name: string): Promise { + const job = await this.findOne({ name }); + return job?.executionInfo; + } + + async clear(): Promise { + await this.delete(); + } + + async define(job: Job): Promise { + const { name, interval, concurrency, maxRunning } = job; + const jobDefinition = toJobDefinition(job); + + this.logger?.debug('define job', { name, concurrency, interval, maxRunning }); - if (savedJobs[0] === undefined) { + const old = await this.keepLatest(name); + + if (old) { + this.logger?.debug('update job in database', { name }); + await this.updateJob(name, jobDefinition); return; } - const updatedJob = merge(savedJobs[0], update); - await this.save(updatedJob); + this.logger?.debug('save job to database', { name }); + await this.save(jobDefinition); } -} -function merge( - savedJob: JobEntity, - { interval, concurrency, maxRunning, executionInfo }: Partial -): JobEntity { - if (interval !== undefined) { - savedJob.interval = interval; - } - if (concurrency !== undefined) { - savedJob.concurrency = concurrency; + private async keepLatest(name: string): Promise { + const jobs = await this.find({ name }); + + if (jobs.length === 1) return jobs[0]; + + const latest = findLatest(jobs); + if (!latest) return undefined; + + this.logger?.debug('duplicate job, keep latest only', { name, count: jobs.length }); + + jobs.splice(jobs.indexOf(latest), 1); + await this.delete({ _id: { $in: jobs.map(({ _id }) => _id) } }); + + return latest; } - if (maxRunning !== undefined) { - savedJob.maxRunning = maxRunning; + + async list(): Promise { + const jobs = await this.find(); + + return jobs.map((job) => { + return { + name: job.name, + interval: job.interval, + concurrency: job.concurrency, + maxRunning: job.maxRunning, + executionInfo: job.executionInfo, + }; + }); } - if (executionInfo !== undefined) { - savedJob.executionInfo = executionInfo; + + async updateJob(name: string, update: Partial): Promise { + await this.updateOne({ name }, { $set: update }); } - return savedJob; } diff --git a/src/repository/Repository.ts b/src/repository/Repository.ts new file mode 100644 index 00000000..2529675c --- /dev/null +++ b/src/repository/Repository.ts @@ -0,0 +1,36 @@ +import { Collection, Filter, MongoClient, ObjectId, OptionalId, UpdateFilter } from 'mongodb'; +import { cloneDeep } from 'lodash'; + +export class Repository { + private readonly collection: Collection; + + constructor(mongoClient: MongoClient, collectionName: string) { + this.collection = mongoClient.db().collection(collectionName); + } + + async save(entity: OptionalId): Promise { + // insertOne mutates the entity and adds an _id, so we use cloneDeep + await this.collection.insertOne(cloneDeep(entity)); + } + + async updateOne(filter: Filter, update: UpdateFilter): Promise { + await this.collection.updateOne(filter, update); + } + + async find(filter: Filter = {}): Promise { + return this.collection.find(filter).toArray(); + } + + async findOne(filter: Filter = {}): Promise { + return this.collection.findOne(filter); + } + + async delete(filter: Filter = {}): Promise { + const result = await this.collection.deleteMany(filter); + return result.deletedCount; + } + + async deleteOne(filter: Filter): Promise { + await this.collection.deleteOne(filter); + } +} diff --git a/src/repository/getRepository.ts b/src/repository/getRepository.ts deleted file mode 100644 index 353f05ad..00000000 --- a/src/repository/getRepository.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { getConnection } from 'typeorm'; - -import { ExecutionsRepository } from './ExecutionsRepository'; -import { JobRepository } from './JobRepository'; -import { connectionName } from '../connect'; - -export function getJobRepository(): JobRepository { - return getConnection(connectionName).getCustomRepository(JobRepository); -} - -export function getExecutionsRepository(): ExecutionsRepository { - return getConnection(connectionName).getCustomRepository(ExecutionsRepository); -} diff --git a/src/schedule/MongoSchedule.ts b/src/schedule/MongoSchedule.ts index 7c733640..1d29ddaf 100644 --- a/src/schedule/MongoSchedule.ts +++ b/src/schedule/MongoSchedule.ts @@ -1,16 +1,23 @@ import { v4 as uuid } from 'uuid'; -import { MomoConnectionOptions, connect, disconnect } from '../connect'; +import { Connection, MomoConnectionOptions } from '../Connection'; import { Schedule } from './Schedule'; import { SchedulePing } from './SchedulePing'; -import { getExecutionsRepository } from '../repository/getRepository'; export class MongoSchedule extends Schedule { private readonly schedulePing: SchedulePing; + private readonly disconnectFct: () => Promise; - private constructor(scheduleId: string) { - super(scheduleId); - this.schedulePing = new SchedulePing(scheduleId, this.logger); + private constructor(scheduleId: string, connection: Connection) { + const executionsRepository = connection.getExecutionsRepository(); + const jobRepository = connection.getJobRepository(); + + super(scheduleId, executionsRepository, jobRepository); + + jobRepository.setLogger(this.logger); + + this.disconnectFct = connection.disconnect.bind(connection); + this.schedulePing = new SchedulePing(scheduleId, executionsRepository, this.logger); } /** @@ -19,11 +26,17 @@ export class MongoSchedule extends Schedule { * @param connectionOptions for the MongoDB connection to establish */ public static async connect(connectionOptions: MomoConnectionOptions): Promise { + const connection = await Connection.create(connectionOptions); + + const executionsRepository = connection.getExecutionsRepository(); + const scheduleId = uuid(); - const mongoSchedule = new MongoSchedule(scheduleId); - await connect(connectionOptions); - await getExecutionsRepository().addSchedule(scheduleId); + await executionsRepository.addSchedule(scheduleId); + + const mongoSchedule = new MongoSchedule(scheduleId, connection); + mongoSchedule.schedulePing.start(); + return mongoSchedule; } @@ -33,6 +46,6 @@ export class MongoSchedule extends Schedule { public async disconnect(): Promise { await this.cancel(); await this.schedulePing.stop(); - await disconnect(); + await this.disconnectFct(); } } diff --git a/src/schedule/MongoScheduleBuilder.ts b/src/schedule/MongoScheduleBuilder.ts index 81383305..783db461 100644 --- a/src/schedule/MongoScheduleBuilder.ts +++ b/src/schedule/MongoScheduleBuilder.ts @@ -1,4 +1,4 @@ -import { MomoConnectionOptions } from '../connect'; +import { MomoConnectionOptions } from '../Connection'; import { MomoJob } from '../job/MomoJob'; import { MongoSchedule } from './MongoSchedule'; diff --git a/src/schedule/Schedule.ts b/src/schedule/Schedule.ts index 70bdf71b..beeefa27 100644 --- a/src/schedule/Schedule.ts +++ b/src/schedule/Schedule.ts @@ -1,20 +1,23 @@ import { sum } from 'lodash'; - -import { ExecutionStatus, JobResult } from '../job/ExecutionInfo'; +import { ExecutionInfo, ExecutionStatus, JobResult } from '../job/ExecutionInfo'; +import { ExecutionsRepository } from '../repository/ExecutionsRepository'; +import { JobRepository } from '../repository/JobRepository'; import { JobScheduler } from '../scheduler/JobScheduler'; import { LogEmitter } from '../logging/LogEmitter'; import { MomoErrorType } from '../logging/error/MomoErrorType'; import { MomoJob } from '../job/MomoJob'; import { MomoJobDescription } from '../job/MomoJobDescription'; -import { define } from '../job/define'; -import { getJobRepository } from '../repository/getRepository'; +import { toJob } from '../job/Job'; import { validate } from '../job/validate'; -import { withDefaults } from '../job/withDefaults'; export class Schedule extends LogEmitter { private jobSchedulers: { [name: string]: JobScheduler } = {}; - constructor(private readonly scheduleId: string) { + constructor( + private readonly scheduleId: string, + private readonly executionsRepository: ExecutionsRepository, + private readonly jobRepository: JobRepository + ) { super(); } @@ -47,18 +50,27 @@ export class Schedule extends LogEmitter { * the scheduler has to be started again to pick up the change. * * @param momoJob the job to define + * @returns true if jobs was defined, false if the job was invalid */ - public async define(momoJob: MomoJob): Promise { - const job = withDefaults(momoJob); + public async define(momoJob: MomoJob): Promise { + const job = toJob(momoJob); if (!validate(job, this.logger)) { - return; + return false; } await this.stopJob(job.name); - await define(job, this.logger); + await this.jobRepository.define(job); + + this.jobSchedulers[job.name] = JobScheduler.forJob( + this.scheduleId, + job, + this.logger, + this.executionsRepository, + this.jobRepository + ); - this.jobSchedulers[job.name] = JobScheduler.forJob(this.scheduleId, job, this.logger); + return true; } /** @@ -171,7 +183,7 @@ export class Schedule extends LogEmitter { public async removeJob(name: string): Promise { await this.cancelJob(name); this.logger.debug('remove', { name }); - await getJobRepository().delete({ name }); + await this.jobRepository.deleteOne({ name }); } /** @@ -181,7 +193,7 @@ export class Schedule extends LogEmitter { const names = Object.keys(this.jobSchedulers); await this.cancel(); this.logger.debug('remove all jobs', { names: names.join(', ') }); - await getJobRepository().deleteMany({ name: { $in: names } }); + await this.jobRepository.delete({ name: { $in: names } }); } /** @@ -203,6 +215,26 @@ export class Schedule extends LogEmitter { await Promise.all(Object.values(this.jobSchedulers).map(async (jobScheduler) => jobScheduler.getJobDescription())) ).filter((jobDescription): jobDescription is MomoJobDescription => jobDescription !== undefined); } + /** + * Retrieves execution information about the job from the database. Returns undefined if the job cannot be found or was never executed. + * + * @param name the job to check + */ + public async check(name: string): Promise { + return this.jobRepository.check(name); + } + + /** + * Removes all jobs from the database. + * + * NOTE: + * This also removes jobs that are not on this schedule, but were defined by other schedules. + * However, does NOT stop job executions - this will cause currently running jobs to fail. + * Consider using stop/cancel/remove methods instead! + */ + public async clear(): Promise { + await this.jobRepository.delete(); + } /** * Returns the description of a job or undefined if no job with the given name is on the schedule. diff --git a/src/schedule/SchedulePing.ts b/src/schedule/SchedulePing.ts index bbcf9979..fa15a6dd 100644 --- a/src/schedule/SchedulePing.ts +++ b/src/schedule/SchedulePing.ts @@ -1,5 +1,5 @@ +import { ExecutionsRepository } from '../repository/ExecutionsRepository'; import { Logger } from '../logging/Logger'; -import { getExecutionsRepository } from '../repository/getRepository'; export const defaultInterval = 60 * 1000; @@ -7,16 +7,19 @@ export class SchedulePing { public static interval = defaultInterval; private handle?: NodeJS.Timeout; - constructor(private readonly scheduleId: string, private readonly logger: Logger) {} + constructor( + private readonly scheduleId: string, + private readonly executionsRepository: ExecutionsRepository, + private readonly logger: Logger + ) {} start(): void { if (this.handle !== undefined) { return; } - const executionsRepository = getExecutionsRepository(); this.handle = setInterval(async () => { - await executionsRepository.ping(this.scheduleId); - const deletedCount = await executionsRepository.clean(); + await this.executionsRepository.ping(this.scheduleId); + const deletedCount = await this.executionsRepository.clean(); if (deletedCount > 0) { this.logger.debug('removed dead executions', { schedules: deletedCount }); } @@ -28,6 +31,6 @@ export class SchedulePing { this.logger.debug('stop SchedulerPing', { scheduleId: this.scheduleId }); clearInterval(this.handle); } - await getExecutionsRepository().delete({ scheduleId: this.scheduleId }); + await this.executionsRepository.deleteOne({ scheduleId: this.scheduleId }); } } diff --git a/src/scheduler/JobScheduler.ts b/src/scheduler/JobScheduler.ts index 2e92b78d..25fb8596 100644 --- a/src/scheduler/JobScheduler.ts +++ b/src/scheduler/JobScheduler.ts @@ -3,14 +3,15 @@ import { min } from 'lodash'; import humanInterval from 'human-interval'; import { ExecutionStatus, JobResult } from '../job/ExecutionInfo'; +import { ExecutionsRepository } from '../repository/ExecutionsRepository'; import { Job } from '../job/Job'; import { JobExecutor } from '../executor/JobExecutor'; +import { JobRepository } from '../repository/JobRepository'; import { Logger } from '../logging/Logger'; import { MomoErrorType } from '../logging/error/MomoErrorType'; import { MomoJobDescription, jobDescriptionFromEntity } from '../job/MomoJobDescription'; import { TimeoutHandle, setIntervalWithDelay } from './setIntervalWithDelay'; import { calculateDelay } from './calculateDelay'; -import { getExecutionsRepository, getJobRepository } from '../repository/getRepository'; import { momoError } from '../logging/error/MomoError'; export class JobScheduler { @@ -23,12 +24,20 @@ export class JobScheduler { private readonly immediate: boolean, private readonly jobExecutor: JobExecutor, private readonly scheduleId: string, + private readonly executionsRepository: ExecutionsRepository, + private readonly jobRepository: JobRepository, private readonly logger: Logger ) {} - static forJob(scheduleId: string, job: Job, logger: Logger): JobScheduler { - const executor = new JobExecutor(job.handler, scheduleId, logger); - return new JobScheduler(job.name, job.immediate, executor, scheduleId, logger); + static forJob( + scheduleId: string, + job: Job, + logger: Logger, + executionsRepository: ExecutionsRepository, + jobRepository: JobRepository + ): JobScheduler { + const executor = new JobExecutor(job.handler, scheduleId, executionsRepository, jobRepository, logger); + return new JobScheduler(job.name, job.immediate, executor, scheduleId, executionsRepository, jobRepository, logger); } getUnexpectedErrorCount(): number { @@ -40,7 +49,7 @@ export class JobScheduler { } async getJobDescription(): Promise { - const jobEntity = await getJobRepository().findOne({ name: this.jobName }); + const jobEntity = await this.jobRepository.findOne({ name: this.jobName }); if (!jobEntity) { this.logger.error( 'get job description - job not found', @@ -51,7 +60,7 @@ export class JobScheduler { return; } - const running = await getExecutionsRepository().countRunningExecutions(jobEntity.name); + const running = await this.executionsRepository.countRunningExecutions(jobEntity.name); const schedulerStatus = this.interval !== undefined ? { interval: this.interval, running } : undefined; return { ...jobDescriptionFromEntity(jobEntity), schedulerStatus }; @@ -60,7 +69,7 @@ export class JobScheduler { async start(): Promise { await this.stop(); - const jobEntity = await getJobRepository().findOne({ name: this.jobName }); + const jobEntity = await this.jobRepository.findOne({ name: this.jobName }); if (!jobEntity) { this.logger.error( 'cannot schedule job', @@ -93,7 +102,7 @@ export class JobScheduler { if (this.jobHandle) { clearInterval(this.jobHandle.get()); this.jobExecutor.stop(); - await getExecutionsRepository().removeJob(this.scheduleId, this.jobName); + await this.executionsRepository.removeJob(this.scheduleId, this.jobName); this.jobHandle = undefined; this.interval = undefined; } @@ -101,7 +110,7 @@ export class JobScheduler { async executeOnce(): Promise { try { - const jobEntity = await getJobRepository().findOne({ name: this.jobName }); + const jobEntity = await this.jobRepository.findOne({ name: this.jobName }); if (!jobEntity) { this.logger.error( 'job not found, skip execution', @@ -125,7 +134,7 @@ export class JobScheduler { async executeConcurrently(): Promise { try { - const jobEntity = await getJobRepository().findOne({ name: this.jobName }); + const jobEntity = await this.jobRepository.findOne({ name: this.jobName }); if (!jobEntity) { this.logger.error( 'job not found, skip execution', @@ -136,7 +145,7 @@ export class JobScheduler { return; } - const running = await getExecutionsRepository().countRunningExecutions(jobEntity.name); + const running = await this.executionsRepository.countRunningExecutions(jobEntity.name); const numToExecute = jobEntity.maxRunning > 0 ? min([jobEntity.concurrency, jobEntity.maxRunning - running]) ?? jobEntity.concurrency diff --git a/test/connect.integration.spec.ts b/test/connect.integration.spec.ts deleted file mode 100644 index 3d7d0d04..00000000 --- a/test/connect.integration.spec.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { MongoMemoryServer } from 'mongodb-memory-server'; - -import { connect, disconnect } from '../src/connect'; -import { isConnected } from '../src'; - -describe('connect', () => { - let mongo: MongoMemoryServer; - let url: string; - - beforeAll(async () => { - mongo = await MongoMemoryServer.create(); - url = await mongo.getUri(); - }); - - afterAll(async () => mongo.stop()); - - it('connects mongo', async () => { - await connect({ url }); - - expect(isConnected()).toBe(true); - }); - - it('disconnects mongo', async () => { - await connect({ url }); - await disconnect(); - - expect(isConnected()).toBe(false); - }); -}); diff --git a/test/executor/JobExecutor.spec.ts b/test/executor/JobExecutor.spec.ts index 29b47e5c..825a3f22 100644 --- a/test/executor/JobExecutor.spec.ts +++ b/test/executor/JobExecutor.spec.ts @@ -1,16 +1,15 @@ -import { anything, capture, verify, when } from 'ts-mockito'; +import { anything, capture, instance, mock, verify, when } from 'ts-mockito'; import { ExecutionStatus, MomoErrorType } from '../../src'; import { ExecutionsRepository } from '../../src/repository/ExecutionsRepository'; import { Job } from '../../src/job/Job'; -import { JobEntity } from '../../src/repository/JobEntity'; import { JobExecutor } from '../../src/executor/JobExecutor'; import { JobRepository } from '../../src/repository/JobRepository'; import { loggerForTests } from '../utils/logging'; -import { mockRepositories } from '../utils/mockRepositories'; describe('JobExecutor', () => { const scheduleId = '123'; + const errorFn = jest.fn(); const handler = jest.fn(); const job: Job = { name: 'test', @@ -20,9 +19,7 @@ describe('JobExecutor', () => { maxRunning: 0, handler, }; - const savedJob = JobEntity.from(job); - const errorFn = jest.fn(); let jobRepository: JobRepository; let executionsRepository: ExecutionsRepository; let jobExecutor: JobExecutor; @@ -30,19 +27,25 @@ describe('JobExecutor', () => { beforeEach(() => { jest.clearAllMocks(); - const repositories = mockRepositories(); - jobRepository = repositories.jobRepository; - executionsRepository = repositories.executionsRepository; + jobRepository = mock(JobRepository); + executionsRepository = mock(ExecutionsRepository); + when(executionsRepository.addExecution(scheduleId, job.name, job.maxRunning)).thenResolve({ added: true, running: 0, }); - jobExecutor = new JobExecutor(job.handler, scheduleId, loggerForTests(errorFn)); + jobExecutor = new JobExecutor( + job.handler, + scheduleId, + instance(executionsRepository), + instance(jobRepository), + loggerForTests(errorFn) + ); }); it('executes job', async () => { - await jobExecutor.execute(savedJob); + await jobExecutor.execute(job); expect(job.handler).toHaveBeenCalled(); @@ -60,7 +63,7 @@ describe('JobExecutor', () => { running: 0, }); - await jobExecutor.execute(savedJob); + await jobExecutor.execute(job); expect(errorFn).not.toHaveBeenCalled(); expect(job.handler).not.toHaveBeenCalled(); @@ -74,7 +77,7 @@ describe('JobExecutor', () => { throw error; }); - await jobExecutor.execute(savedJob); + await jobExecutor.execute(job); expect(errorFn).toHaveBeenCalledWith('job failed', MomoErrorType.executeJob, { name: job.name }, error); @@ -88,7 +91,7 @@ describe('JobExecutor', () => { return handlerResult; }); - await jobExecutor.execute(savedJob); + await jobExecutor.execute(job); verify(jobRepository.updateJob(job.name, anything())).once(); const [, update] = capture(jobRepository.updateJob).last(); diff --git a/test/isConnected.spec.ts b/test/isConnected.spec.ts deleted file mode 100644 index d9e03bc4..00000000 --- a/test/isConnected.spec.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { isConnected } from '../src'; -import { mockRepositories } from './utils/mockRepositories'; - -describe('isConnected', () => { - beforeEach(() => jest.restoreAllMocks()); - - it('reports connection', () => { - mockRepositories(); - expect(isConnected()).toBe(true); - }); - - it('reports missing connection', () => { - expect(isConnected()).toBe(false); - }); -}); diff --git a/test/job/withDefaults.spec.ts b/test/job/Job.spec.ts similarity index 60% rename from test/job/withDefaults.spec.ts rename to test/job/Job.spec.ts index b5bb5a4c..d6898177 100644 --- a/test/job/withDefaults.spec.ts +++ b/test/job/Job.spec.ts @@ -1,9 +1,9 @@ -import { withDefaults } from '../../src/job/withDefaults'; +import { toJob } from '../../src/job/Job'; -describe('withDefaults', () => { +describe('fromMomoJob', () => { it('sets defaults', () => { const job = { name: 'test', interval: '1 second', handler: () => undefined }; - expect(withDefaults(job)).toMatchObject({ + expect(toJob(job)).toMatchObject({ ...job, immediate: false, concurrency: 1, diff --git a/test/job/check.spec.ts b/test/job/check.spec.ts deleted file mode 100644 index b4972e34..00000000 --- a/test/job/check.spec.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { DateTime } from 'luxon'; -import { deepEqual, when } from 'ts-mockito'; - -import { ExecutionInfo, ExecutionStatus, check } from '../../src'; -import { JobEntity } from '../../src/repository/JobEntity'; -import { mockRepositories } from '../utils/mockRepositories'; - -describe('check', () => { - const name = 'test'; - - afterEach(() => jest.resetAllMocks()); - - it('returns executionInfo', async () => { - const jobRepository = mockRepositories().jobRepository; - - const executionInfo: ExecutionInfo = { - lastStarted: DateTime.now().toISO(), - lastFinished: DateTime.now().toISO(), - lastResult: { status: ExecutionStatus.finished }, - }; - when(jobRepository.findOne(deepEqual({ name }))).thenResolve({ name, executionInfo } as JobEntity); - - const result = await check(name); - - expect(result).toEqual(executionInfo); - }); - - it('returns nothing if job not found', async () => { - mockRepositories(); - expect(await check(name)).toBeUndefined(); - }); - - it('returns nothing if not connected', async () => { - expect(await check(name)).toBeUndefined(); - }); -}); diff --git a/test/job/clear.integration.spec.ts b/test/job/clear.integration.spec.ts deleted file mode 100644 index 101bd405..00000000 --- a/test/job/clear.integration.spec.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { MongoMemoryServer } from 'mongodb-memory-server'; - -import { JobRepository } from '../../src/repository/JobRepository'; -import { clear } from '../../src'; -import { connect, disconnect } from '../../src/connect'; -import { createJobEntity } from '../utils/createJobEntity'; -import { getJobRepository } from '../../src/repository/getRepository'; - -describe('clear', () => { - let mongo: MongoMemoryServer; - let jobRepository: JobRepository; - - beforeAll(async () => { - mongo = await MongoMemoryServer.create(); - await connect({ url: mongo.getUri() }); - jobRepository = getJobRepository(); - }); - - beforeEach(async () => jobRepository.delete({})); - - afterAll(async () => { - await disconnect(); - await mongo.stop(); - }); - - it('removes all jobs from mongo', async () => { - await jobRepository.save(createJobEntity({ name: 'job 1', interval: '1 minute', handler: () => undefined })); - await jobRepository.save(createJobEntity({ name: 'job 2', interval: '1 minute', handler: () => undefined })); - - await clear(); - - expect(await jobRepository.find({})).toHaveLength(0); - }); -}); diff --git a/test/job/define.spec.ts b/test/job/define.spec.ts deleted file mode 100644 index 0a026b42..00000000 --- a/test/job/define.spec.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { DateTime } from 'luxon'; -import { deepEqual, verify, when } from 'ts-mockito'; - -import { ExecutionInfo } from '../../src'; -import { JobRepository } from '../../src/repository/JobRepository'; -import { createJobEntity } from '../utils/createJobEntity'; -import { define } from '../../src/job/define'; -import { mockRepositories } from '../utils/mockRepositories'; -import { withDefaults } from '../../src/job/withDefaults'; - -describe('define', () => { - const job = { name: 'test', interval: '1 minute', handler: () => 'finished' }; - - let jobRepository: JobRepository; - - it('saves a job', async () => { - jobRepository = mockRepositories().jobRepository; - - when(jobRepository.find(deepEqual({ name: job.name }))).thenResolve([]); - - await define(withDefaults(job)); - - verify(jobRepository.save(deepEqual(createJobEntity(job)))).once(); - }); - - it('updates a job', async () => { - jobRepository = mockRepositories().jobRepository; - - when(jobRepository.find(deepEqual({ name: job.name }))).thenResolve([createJobEntity(job)]); - - const newInterval = '2 minutes'; - await define(withDefaults({ ...job, interval: newInterval })); - - verify(jobRepository.updateJob(job.name, deepEqual(createJobEntity({ ...job, interval: newInterval })))).once(); - }); - - it('cleans up duplicate jobs but keeps latest job', async () => { - const duplicate = createJobEntity(job); - const latest = createJobEntity(job); - latest.executionInfo = { lastFinished: DateTime.now().toISO() } as ExecutionInfo; - when(jobRepository.find(deepEqual({ name: job.name }))).thenResolve([duplicate, latest]); - - const newInterval = 'two minutes'; - await define(withDefaults({ ...job, interval: newInterval })); - - verify(jobRepository.remove(deepEqual([duplicate]))).once(); - verify(jobRepository.updateJob(job.name, deepEqual(createJobEntity({ ...job, interval: newInterval })))).once(); - }); -}); diff --git a/test/job/findLatest.spec.ts b/test/job/findLatest.spec.ts index 337cef3a..d7223035 100644 --- a/test/job/findLatest.spec.ts +++ b/test/job/findLatest.spec.ts @@ -1,12 +1,11 @@ import { DateTime } from 'luxon'; import { ExecutionInfo } from '../../src'; -import { Job } from '../../src/job/Job'; import { JobEntity } from '../../src/repository/JobEntity'; import { findLatest } from '../../src/job/findLatest'; function createJob(lastFinished?: number): JobEntity { - const job = JobEntity.from({ name: 'test' } as Job); + const job = { name: 'test' } as JobEntity; if (lastFinished !== undefined) { job.executionInfo = { lastFinished: DateTime.fromMillis(lastFinished).toISO() } as ExecutionInfo; } diff --git a/test/job/keepLatest.spec.ts b/test/job/keepLatest.spec.ts deleted file mode 100644 index 25abb795..00000000 --- a/test/job/keepLatest.spec.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { DateTime } from 'luxon'; -import { anything, capture, deepEqual, verify, when } from 'ts-mockito'; - -import { ExecutionInfo } from '../../src'; -import { Job } from '../../src/job/Job'; -import { JobEntity } from '../../src/repository/JobEntity'; -import { JobRepository } from '../../src/repository/JobRepository'; -import { keepLatest } from '../../src/job/keepLatest'; -import { mockRepositories } from '../utils/mockRepositories'; - -describe('keepLatest', () => { - const name = 'test'; - let jobRepository: JobRepository; - - beforeAll(() => { - jobRepository = mockRepositories().jobRepository; - }); - - afterEach(() => jest.clearAllMocks()); - - it('removes all duplicate jobs except latest', async () => { - const duplicate = JobEntity.from({ name } as Job); - const latest = JobEntity.from({ name } as Job); - latest.executionInfo = { lastFinished: DateTime.now().toISO() } as ExecutionInfo; - - when(jobRepository.find(deepEqual({ name }))).thenResolve([duplicate, latest]); - - const kept = await keepLatest(name); - - expect(kept).toBe(latest); - verify(jobRepository.remove(anything())).once(); - const [removedJobs] = capture(jobRepository.remove).last(); - expect(removedJobs).toEqual([duplicate]); - }); - - it('returns a job without duplicates', async () => { - const job = JobEntity.from({ name } as Job); - when(jobRepository.find(deepEqual({ name }))).thenResolve([job]); - - expect(await keepLatest(name)).toEqual(job); - }); - - it('returns undefined when no job is found', async () => { - when(jobRepository.find(deepEqual({ name }))).thenResolve([]); - - expect(await keepLatest(name)).toBeUndefined(); - }); -}); diff --git a/test/job/list.spec.ts b/test/job/list.spec.ts deleted file mode 100644 index 1ec7b07c..00000000 --- a/test/job/list.spec.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { when } from 'ts-mockito'; - -import { ExecutionInfo, list } from '../../src'; -import { mockRepositories } from '../utils/mockRepositories'; - -describe('list', () => { - afterEach(() => jest.resetAllMocks()); - - it('returns jobs', async () => { - const jobRepository = mockRepositories().jobRepository; - - const job1 = { - name: 'job1', - interval: '1 minute', - executionInfo: {} as ExecutionInfo, - running: 2, - concurrency: 1, - maxRunning: 3, - }; - const job2 = { - name: 'job2', - interval: '2 minutes', - executionInfo: {} as ExecutionInfo, - running: 0, - concurrency: 1, - maxRunning: 0, - }; - when(jobRepository.find()).thenResolve([job1, job2]); - - const jobs = await list(); - - expect(jobs).toEqual([ - { - name: job1.name, - interval: job1.interval, - concurrency: job1.concurrency, - maxRunning: job1.maxRunning, - executionInfo: {}, - }, - { - name: job2.name, - interval: job2.interval, - concurrency: job2.concurrency, - maxRunning: job2.maxRunning, - executionInfo: {}, - }, - ]); - }); - - it('returns nothing if not connected', async () => { - expect(await list()).toHaveLength(0); - }); -}); diff --git a/test/job/validate.spec.ts b/test/job/validate.spec.ts index d9e59490..1647781a 100644 --- a/test/job/validate.spec.ts +++ b/test/job/validate.spec.ts @@ -1,8 +1,7 @@ -import { Job } from '../../src/job/Job'; +import { Job, toJob } from '../../src/job/Job'; import { Logger } from '../../src/logging/Logger'; import { MomoErrorType, momoError } from '../../src'; import { validate } from '../../src/job/validate'; -import { withDefaults } from '../../src/job/withDefaults'; describe('validate', () => { const logger: Logger = { @@ -13,12 +12,12 @@ describe('validate', () => { beforeEach(async () => jest.clearAllMocks()); it('validates a job', () => { - const job: Job = withDefaults({ name: 'test', interval: '1 minute', handler: () => 'finished' }); + const job: Job = toJob({ name: 'test', interval: '1 minute', handler: () => 'finished' }); expect(validate(job, logger)).toBe(true); }); it('reports error when interval cannot be parsed', () => { - const job: Job = withDefaults({ name: 'test', interval: 'not an interval', handler: () => 'finished' }); + const job: Job = toJob({ name: 'test', interval: 'not an interval', handler: () => 'finished' }); expect(validate(job, logger)).toBe(false); expect(logger.error).toHaveBeenCalledTimes(1); @@ -31,7 +30,7 @@ describe('validate', () => { }); it('reports error when interval is not positive', () => { - const job: Job = withDefaults({ name: 'test', interval: '-1 minute', handler: () => 'finished' }); + const job: Job = toJob({ name: 'test', interval: '-1 minute', handler: () => 'finished' }); expect(validate(job, logger)).toBe(false); expect(logger.error).toHaveBeenCalledTimes(1); @@ -44,7 +43,7 @@ describe('validate', () => { }); it('reports error when maxRunning is invalid', async () => { - const job: Job = withDefaults({ name: 'test', interval: '1 minute', handler: () => 'finished', maxRunning: -1 }); + const job: Job = toJob({ name: 'test', interval: '1 minute', handler: () => 'finished', maxRunning: -1 }); expect(validate(job, logger)).toBe(false); expect(logger.error).toHaveBeenCalledTimes(1); @@ -57,7 +56,7 @@ describe('validate', () => { }); it('reports error when concurrency is invalid', async () => { - const job: Job = withDefaults({ name: 'test', interval: '1 minute', handler: () => 'finished', concurrency: 0 }); + const job: Job = toJob({ name: 'test', interval: '1 minute', handler: () => 'finished', concurrency: 0 }); expect(validate(job, logger)).toBe(false); expect(logger.error).toHaveBeenCalledTimes(1); @@ -70,7 +69,7 @@ describe('validate', () => { }); it('reports error when concurrency > maxRunning', async () => { - const job: Job = withDefaults({ + const job: Job = toJob({ name: 'test', interval: '1 minute', handler: () => 'finished', diff --git a/test/repository/ExecutionsRepository.integration.spec.ts b/test/repository/ExecutionsRepository.integration.spec.ts index 4d66eac2..f56bac20 100644 --- a/test/repository/ExecutionsRepository.integration.spec.ts +++ b/test/repository/ExecutionsRepository.integration.spec.ts @@ -1,8 +1,7 @@ import { MongoMemoryServer } from 'mongodb-memory-server'; +import { Connection } from '../../src/Connection'; import { ExecutionsRepository } from '../../src/repository/ExecutionsRepository'; -import { connect, disconnect } from '../../src/connect'; -import { getExecutionsRepository } from '../../src/repository/getRepository'; import { sleep } from '../utils/sleep'; describe('ExecutionsRepository', () => { @@ -10,20 +9,21 @@ describe('ExecutionsRepository', () => { const name = 'test job'; let mongo: MongoMemoryServer; + let connection: Connection; let executionsRepository: ExecutionsRepository; beforeAll(async () => { ExecutionsRepository.deadScheduleThreshold = 1000; mongo = await MongoMemoryServer.create(); - await connect({ url: mongo.getUri() }); - executionsRepository = getExecutionsRepository(); + connection = await Connection.create({ url: mongo.getUri() }); + executionsRepository = connection.getExecutionsRepository(); }); - beforeEach(async () => executionsRepository.clear()); + beforeEach(async () => executionsRepository.delete()); afterAll(async () => { - await disconnect(); + await connection.disconnect(); await mongo.stop(); }); diff --git a/test/repository/JobRepository.integration.spec.ts b/test/repository/JobRepository.integration.spec.ts index ad8cd638..ddf5eea6 100644 --- a/test/repository/JobRepository.integration.spec.ts +++ b/test/repository/JobRepository.integration.spec.ts @@ -1,41 +1,146 @@ import { DateTime } from 'luxon'; import { MongoMemoryServer } from 'mongodb-memory-server'; -import { ExecutionStatus, MomoJob } from '../../src'; +import { Connection } from '../../src/Connection'; +import { ExecutionInfo, ExecutionStatus } from '../../src'; +import { JobEntity } from '../../src/repository/JobEntity'; import { JobRepository } from '../../src/repository/JobRepository'; -import { connect, disconnect } from '../../src/connect'; -import { createJobEntity } from '../utils/createJobEntity'; -import { getJobRepository } from '../../src/repository/getRepository'; +import { toJob, toJobDefinition } from '../../src/job/Job'; describe('JobRepository', () => { - const job: MomoJob = { + const job = toJob({ name: 'test job', interval: 'one minute', handler: () => undefined, - }; + }); + const jobDefinition = toJobDefinition(job); + let mongo: MongoMemoryServer; + let connection: Connection; let jobRepository: JobRepository; beforeAll(async () => { mongo = await MongoMemoryServer.create(); - await connect({ url: mongo.getUri() }); - jobRepository = getJobRepository(); + connection = await Connection.create({ url: mongo.getUri() }); + jobRepository = connection.getJobRepository(); }); - beforeEach(async () => jobRepository.delete({})); + beforeEach(async () => jobRepository.delete()); afterAll(async () => { - await disconnect(); + await connection.disconnect(); await mongo.stop(); }); - describe('updateJob', () => { - it('does not overwrite executionInfo', async () => { - const savedJob = createJobEntity(job); - savedJob.executionInfo = { + describe('check', () => { + const name = 'test'; + + it('returns executionInfo', async () => { + const executionInfo: ExecutionInfo = { lastStarted: DateTime.now().toISO(), lastFinished: DateTime.now().toISO(), - lastResult: { status: ExecutionStatus.finished, handlerResult: 'I was executed' }, + lastResult: { status: ExecutionStatus.finished }, + }; + await jobRepository.save({ name, executionInfo } as JobEntity); + + const result = await jobRepository.check(name); + + expect(result).toEqual(executionInfo); + }); + + it('returns nothing if job not found', async () => { + expect(await jobRepository.check(name)).toBeUndefined(); + }); + }); + + describe('define', () => { + it('saves a job', async () => { + await jobRepository.define(job); + + expect(await jobRepository.find({ name: job.name })).toEqual([{ ...jobDefinition, _id: expect.anything() }]); + }); + + it('updates a job', async () => { + await jobRepository.save(jobDefinition); + + const newInterval = '2 minutes'; + await jobRepository.define({ ...job, interval: newInterval }); + + expect(await jobRepository.find({ name: job.name })).toEqual([ + { ...jobDefinition, interval: newInterval, _id: expect.anything() }, + ]); + }); + + it('cleans up duplicate jobs but keeps latest job', async () => { + const latest = { ...jobDefinition, executionInfo: { lastFinished: DateTime.now().toISO() } as ExecutionInfo }; + await jobRepository.save(jobDefinition); + await jobRepository.save(latest); + + const newInterval = 'two minutes'; + await jobRepository.define({ ...job, interval: newInterval }); + + const actual = await jobRepository.find({ name: job.name }); + expect(actual).toEqual([ + { + ...latest, + interval: newInterval, + _id: expect.anything(), + }, + ]); + }); + }); + + describe('list', () => { + it('returns jobs', async () => { + const job1 = { + name: 'job1', + interval: '1 minute', + executionInfo: {} as ExecutionInfo, + running: 2, + concurrency: 1, + maxRunning: 3, + }; + const job2 = { + name: 'job2', + interval: '2 minutes', + executionInfo: {} as ExecutionInfo, + running: 0, + concurrency: 1, + maxRunning: 0, + }; + await jobRepository.save(job1); + await jobRepository.save(job2); + + const jobs = await jobRepository.list(); + + expect(jobs).toEqual([ + { + name: job1.name, + interval: job1.interval, + concurrency: job1.concurrency, + maxRunning: job1.maxRunning, + executionInfo: {}, + }, + { + name: job2.name, + interval: job2.interval, + concurrency: job2.concurrency, + maxRunning: job2.maxRunning, + executionInfo: {}, + }, + ]); + }); + }); + + describe('updateJob', () => { + it('does not overwrite executionInfo', async () => { + const savedJob = { + ...jobDefinition, + executionInfo: { + lastStarted: DateTime.now().toISO(), + lastFinished: DateTime.now().toISO(), + lastResult: { status: ExecutionStatus.finished, handlerResult: 'I was executed' }, + }, }; await jobRepository.save(savedJob); @@ -46,7 +151,7 @@ describe('JobRepository', () => { }); it('can update maxRunning to 0', async () => { - const savedJob = createJobEntity({ ...job, maxRunning: 3 }); + const savedJob = toJobDefinition({ ...job, maxRunning: 3 }); await jobRepository.save(savedJob); await jobRepository.updateJob(job.name, { maxRunning: 0 }); diff --git a/test/schedule/MongoSchedule.spec.ts b/test/schedule/MongoSchedule.spec.ts index e8604e3f..aa7a3ee9 100644 --- a/test/schedule/MongoSchedule.spec.ts +++ b/test/schedule/MongoSchedule.spec.ts @@ -1,16 +1,28 @@ -import { anyString, capture, deepEqual, verify } from 'ts-mockito'; +import { anyString, capture, deepEqual, instance, mock, verify } from 'ts-mockito'; import { ExecutionsRepository } from '../../src/repository/ExecutionsRepository'; -import { MongoSchedule } from '../../src'; -import { mockRepositories } from '../utils/mockRepositories'; +import { JobRepository } from '../../src/repository/JobRepository'; +import { MomoConnectionOptions, MongoSchedule } from '../../src'; + +const executionsRepository = mock(ExecutionsRepository); +jest.mock('../../src/Connection', () => { + return { + // eslint-disable-next-line @typescript-eslint/naming-convention + Connection: { + create: async (_options: MomoConnectionOptions) => { + return { + getJobRepository: () => instance(mock(JobRepository)), + getExecutionsRepository: () => instance(executionsRepository), + disconnect: async () => undefined, + }; + }, + }, + }; +}); describe('MongoSchedule', () => { - let executionsRepository: ExecutionsRepository; - beforeEach(async () => { jest.clearAllMocks(); - - executionsRepository = mockRepositories().executionsRepository; }); it('connects and starts the ping and disconnects and stops the ping', async () => { @@ -21,6 +33,6 @@ describe('MongoSchedule', () => { await mongoSchedule.disconnect(); - verify(executionsRepository.delete(deepEqual({ scheduleId }))); + verify(executionsRepository.deleteOne(deepEqual({ scheduleId }))); }); }); diff --git a/test/schedule/MongoScheduleBuilder.integration.spec.ts b/test/schedule/MongoScheduleBuilder.integration.spec.ts index 33838c41..314f9ccb 100644 --- a/test/schedule/MongoScheduleBuilder.integration.spec.ts +++ b/test/schedule/MongoScheduleBuilder.integration.spec.ts @@ -1,6 +1,7 @@ import { MongoMemoryServer } from 'mongodb-memory-server'; -import { MomoConnectionOptions, MomoJob, MongoSchedule, clear } from '../../src'; +import { Connection } from '../../src/Connection'; +import { MomoConnectionOptions, MomoJob, MongoSchedule } from '../../src'; import { MongoScheduleBuilder } from '../../src/schedule/MongoScheduleBuilder'; describe('MongoScheduleBuilder', () => { @@ -24,24 +25,25 @@ describe('MongoScheduleBuilder', () => { let mongo: MongoMemoryServer; let connectionOptions: MomoConnectionOptions; + let connection: Connection; beforeAll(async () => { mongo = await MongoMemoryServer.create(); connectionOptions = { url: mongo.getUri() }; - }); - - afterEach(async () => { - await clear(); + connection = await Connection.create(connectionOptions); }); afterAll(async () => { await mongo.stop(); + await connection.disconnect(); }); describe('build a mongoSchedule', () => { let mongoSchedule: MongoSchedule; afterEach(async () => { + await connection.getJobRepository().delete(); + await connection.getExecutionsRepository().delete(); await mongoSchedule.disconnect(); }); diff --git a/test/schedule/Schedule.integration.spec.ts b/test/schedule/Schedule.integration.spec.ts index ba07cb08..d29c2d06 100644 --- a/test/schedule/Schedule.integration.spec.ts +++ b/test/schedule/Schedule.integration.spec.ts @@ -1,11 +1,10 @@ import { MongoMemoryServer } from 'mongodb-memory-server'; -import { JobEntity } from '../../src/repository/JobEntity'; +import { Connection } from '../../src/Connection'; import { JobRepository } from '../../src/repository/JobRepository'; -import { MomoJob, MongoSchedule, clear } from '../../src'; -import { getJobRepository } from '../../src/repository/getRepository'; +import { MomoJob, MongoSchedule } from '../../src'; import { initLoggingForTests } from '../utils/logging'; -import { withDefaults } from '../../src/job/withDefaults'; +import { toJob } from '../../src/job/Job'; describe('Schedule', () => { const job: MomoJob = { @@ -15,24 +14,28 @@ describe('Schedule', () => { }; let mongo: MongoMemoryServer; + let connection: Connection; let jobRepository: JobRepository; let mongoSchedule: MongoSchedule; beforeAll(async () => { mongo = await MongoMemoryServer.create(); + connection = await Connection.create({ url: mongo.getUri() }); + jobRepository = connection.getJobRepository(); + mongoSchedule = await MongoSchedule.connect({ url: mongo.getUri() }); - jobRepository = getJobRepository(); initLoggingForTests(mongoSchedule); }); beforeEach(async () => { await mongoSchedule.cancel(); - await clear(); + await mongoSchedule.clear(); }); afterAll(async () => { await mongoSchedule.disconnect(); + await connection.disconnect(); await mongo.stop(); }); @@ -63,13 +66,11 @@ describe('Schedule', () => { it('lists jobs on the schedule', async () => { await jobRepository.save( - JobEntity.from( - withDefaults({ - name: 'some job that is in the database but not on the schedule', - handler: jest.fn(), - interval: 'one minute', - }) - ) + toJob({ + name: 'some job that is in the database but not on the schedule', + handler: jest.fn(), + interval: 'one minute', + }) ); await mongoSchedule.define(job); diff --git a/test/schedule/Schedule.spec.ts b/test/schedule/Schedule.spec.ts index 6e7febda..b54c01c2 100644 --- a/test/schedule/Schedule.spec.ts +++ b/test/schedule/Schedule.spec.ts @@ -1,33 +1,42 @@ -import { anyString, deepEqual, verify, when } from 'ts-mockito'; +import { anyString, deepEqual, instance, mock, when } from 'ts-mockito'; -import { ExecutionStatus, MomoEvent, MomoJob, MongoSchedule } from '../../src'; +import { ExecutionStatus, MomoConnectionOptions, MomoEvent, MomoJob, MongoSchedule } from '../../src'; import { ExecutionsRepository } from '../../src/repository/ExecutionsRepository'; -import { Job } from '../../src/job/Job'; -import { JobEntity } from '../../src/repository/JobEntity'; import { JobRepository } from '../../src/repository/JobRepository'; -import { createJobEntity } from '../utils/createJobEntity'; import { initLoggingForTests } from '../utils/logging'; -import { mockRepositories } from '../utils/mockRepositories'; +import { toJob, toJobDefinition } from '../../src/job/Job'; + +const executionsRepository = mock(ExecutionsRepository); +const jobRepository = mock(JobRepository); +jest.mock('../../src/Connection', () => { + return { + // eslint-disable-next-line @typescript-eslint/naming-convention + Connection: { + create: async (_options: MomoConnectionOptions) => { + return { + getJobRepository: () => instance(jobRepository), + getExecutionsRepository: () => instance(executionsRepository), + disconnect: async () => undefined, + }; + }, + }, + }; +}); describe('Schedule', () => { - const job: MomoJob = { + const momoJob: MomoJob = { name: 'test job', interval: 'one minute', handler: jest.fn(), }; + const jobDefinition = toJobDefinition(toJob(momoJob)); - let jobRepository: JobRepository; - let executionsRepository: ExecutionsRepository; let mongoSchedule: MongoSchedule; beforeEach(async () => { jest.clearAllMocks(); - const repositories = mockRepositories(); - jobRepository = repositories.jobRepository; - executionsRepository = repositories.executionsRepository; - - when(jobRepository.find(deepEqual({ name: job.name }))).thenResolve([]); + when(jobRepository.find(deepEqual({ name: momoJob.name }))).thenResolve([]); mongoSchedule = await MongoSchedule.connect({ url: 'mongodb://does.not/matter' }); initLoggingForTests(mongoSchedule); @@ -47,15 +56,13 @@ describe('Schedule', () => { }); it('does not report error when concurrency > maxRunning but maxRunning is not set', async () => { - await mongoSchedule.define({ ...job, concurrency: 3 }); + const defined = await mongoSchedule.define({ ...momoJob, concurrency: 3 }); - verify( - jobRepository.save(deepEqual(JobEntity.from({ ...job, maxRunning: 0, concurrency: 3, immediate: false } as Job))) - ).once(); + expect(defined).toBe(true); }); it('counts jobs', async () => { - await mongoSchedule.define(job); + await mongoSchedule.define(momoJob); expect(mongoSchedule.count()).toBe(1); }); @@ -64,13 +71,13 @@ describe('Schedule', () => { const name = 'not started'; when(jobRepository.find(deepEqual({ name }))).thenResolve([]); - const notStartedJob = { ...job, name }; + const notStartedJob = { ...momoJob, name }; await mongoSchedule.define(notStartedJob); - await mongoSchedule.define(job); + await mongoSchedule.define(momoJob); - when(jobRepository.findOne(deepEqual({ name: job.name }))).thenResolve(createJobEntity(job)); + when(jobRepository.findOne(deepEqual({ name: momoJob.name }))).thenResolve(jobDefinition); - await mongoSchedule.startJob(job.name); + await mongoSchedule.startJob(momoJob.name); expect(mongoSchedule.count()).toBe(2); expect(mongoSchedule.count(true)).toBe(1); @@ -84,15 +91,15 @@ describe('Schedule', () => { }); it('runs a job once', async () => { - await mongoSchedule.define(job); + await mongoSchedule.define(momoJob); - when(jobRepository.findOne(deepEqual({ name: job.name }))).thenResolve(createJobEntity(job)); - when(executionsRepository.addExecution(anyString(), job.name, 0)).thenResolve({ added: true, running: 0 }); + when(jobRepository.findOne(deepEqual({ name: momoJob.name }))).thenResolve(jobDefinition); + when(executionsRepository.addExecution(anyString(), momoJob.name, 0)).thenResolve({ added: true, running: 0 }); - const result = await mongoSchedule.run(job.name); + const result = await mongoSchedule.run(momoJob.name); expect(result).toEqual({ status: ExecutionStatus.finished, handlerResult: 'finished' }); - expect(job.handler).toHaveBeenCalledTimes(1); + expect(momoJob.handler).toHaveBeenCalledTimes(1); }); it('skips running job once when job is not found', async () => { @@ -102,27 +109,27 @@ describe('Schedule', () => { }); it('skips running job once when job is not found in repository', async () => { - await mongoSchedule.define(job); + await mongoSchedule.define(momoJob); - when(jobRepository.findOne(deepEqual({ name: job.name }))).thenResolve(undefined); + when(jobRepository.findOne(deepEqual({ name: momoJob.name }))).thenResolve(undefined); - const result = await mongoSchedule.run(job.name); + const result = await mongoSchedule.run(momoJob.name); expect(result).toEqual({ status: ExecutionStatus.notFound }); }); it('skips running job once when maxRunning is reached', async () => { - await mongoSchedule.define(job); + await mongoSchedule.define(momoJob); - when(jobRepository.findOne(deepEqual({ name: job.name }))).thenResolve(createJobEntity(job)); - when(executionsRepository.addExecution(anyString(), job.name, 0)).thenResolve({ + when(jobRepository.findOne(deepEqual({ name: momoJob.name }))).thenResolve(jobDefinition); + when(executionsRepository.addExecution(anyString(), momoJob.name, 0)).thenResolve({ added: false, running: 0, }); - const result = await mongoSchedule.run(job.name); + const result = await mongoSchedule.run(momoJob.name); expect(result).toEqual({ status: ExecutionStatus.maxRunningReached }); - expect(job.handler).toHaveBeenCalledTimes(0); + expect(momoJob.handler).toHaveBeenCalledTimes(0); }); }); diff --git a/test/schedule/SchedulePing.spec.ts b/test/schedule/SchedulePing.spec.ts index 61f325d6..6dbce513 100644 --- a/test/schedule/SchedulePing.spec.ts +++ b/test/schedule/SchedulePing.spec.ts @@ -1,20 +1,23 @@ -import { deepEqual, verify } from 'ts-mockito'; +import { deepEqual, instance, mock, verify } from 'ts-mockito'; import { ExecutionsRepository } from '../../src/repository/ExecutionsRepository'; import { SchedulePing } from '../../src/schedule/SchedulePing'; -import { mockRepositories } from '../utils/mockRepositories'; import { sleep } from '../utils/sleep'; describe('SchedulePing', () => { const scheduleId = '123'; - const schedulePing = new SchedulePing(scheduleId, { debug: jest.fn(), error: jest.fn() }); + let executionsRepository: ExecutionsRepository; + let schedulePing: SchedulePing; beforeAll(() => { SchedulePing.interval = 1000; - executionsRepository = mockRepositories().executionsRepository; + executionsRepository = mock(ExecutionsRepository); + schedulePing = new SchedulePing(scheduleId, instance(executionsRepository), { debug: jest.fn(), error: jest.fn() }); }); + beforeEach(jest.clearAllMocks); + it('starts, pings, cleans and stops', async () => { schedulePing.start(); await sleep(SchedulePing.interval); @@ -26,6 +29,6 @@ describe('SchedulePing', () => { await sleep(SchedulePing.interval); verify(executionsRepository.ping(scheduleId)).once(); - verify(executionsRepository.delete(deepEqual({ scheduleId }))).once(); + verify(executionsRepository.deleteOne(deepEqual({ scheduleId }))).once(); }); }); diff --git a/test/schedule/momo.integration.spec.ts b/test/schedule/momo.integration.spec.ts index 68fd15f4..781df2f6 100644 --- a/test/schedule/momo.integration.spec.ts +++ b/test/schedule/momo.integration.spec.ts @@ -2,13 +2,13 @@ import { DateTime } from 'luxon'; import { MongoMemoryServer } from 'mongodb-memory-server'; import { v4 as uuid } from 'uuid'; -import { ExecutionStatus, MomoErrorEvent, MomoErrorType, MomoJob, MongoSchedule, clear, momoError } from '../../src'; +import { Connection } from '../../src/Connection'; +import { ExecutionStatus, MomoErrorEvent, MomoErrorType, MomoJob, MongoSchedule, momoError } from '../../src'; import { ExecutionsRepository } from '../../src/repository/ExecutionsRepository'; import { JobRepository } from '../../src/repository/JobRepository'; -import { createJobEntity } from '../utils/createJobEntity'; -import { getExecutionsRepository, getJobRepository } from '../../src/repository/getRepository'; import { initLoggingForTests } from '../utils/logging'; import { sleep } from '../utils/sleep'; +import { toJob, toJobDefinition } from '../../src/job/Job'; import { waitFor } from '../utils/waitFor'; interface TestJobHandler { @@ -27,12 +27,16 @@ describe('Momo', () => { let jobRepository: JobRepository; let executionsRepository: ExecutionsRepository; let mongoSchedule: MongoSchedule; + let connection: Connection; beforeAll(async () => { mongo = await MongoMemoryServer.create(); + + connection = await Connection.create({ url: mongo.getUri() }); + jobRepository = connection.getJobRepository(); + executionsRepository = connection.getExecutionsRepository(); + mongoSchedule = await MongoSchedule.connect({ url: mongo.getUri() }); - jobRepository = getJobRepository(); - executionsRepository = getExecutionsRepository(); initLoggingForTests(mongoSchedule); @@ -44,11 +48,12 @@ describe('Momo', () => { // eslint-disable-next-line jest/no-standalone-expect expect(mongoSchedule.getUnexpectedErrorCount()).toBe(0); await mongoSchedule.cancel(); - await clear(); + await mongoSchedule.clear(); }); afterAll(async () => { await mongoSchedule.disconnect(); + await connection.disconnect(); await mongo.stop(); }); @@ -81,15 +86,15 @@ describe('Momo', () => { describe('single job', () => { let jobHandler: TestJobHandler; - let job: MomoJob; + let momoJob: MomoJob; beforeEach(() => { jobHandler = createTestJobHandler(); - job = createTestJob(jobHandler); + momoJob = createTestJob(jobHandler); }); it('executes job periodically', async () => { - await mongoSchedule.define(job); + await mongoSchedule.define(momoJob); await mongoSchedule.start(); @@ -99,7 +104,7 @@ describe('Momo', () => { }); it('executes an immediate job periodically', async () => { - await mongoSchedule.define({ ...job, immediate: true }); + await mongoSchedule.define({ ...momoJob, immediate: true }); await mongoSchedule.start(); @@ -108,18 +113,20 @@ describe('Momo', () => { }); it('executes job that was executed before', async () => { - const jobEntity = createJobEntity(job); - jobEntity.executionInfo = { - lastStarted: DateTime.now().toISO(), - lastFinished: DateTime.now().toISO(), - lastResult: { status: ExecutionStatus.finished, handlerResult: 'I was executed' }, + const jobEntity = { + ...toJobDefinition(toJob(momoJob)), + executionInfo: { + lastStarted: DateTime.now().toISO(), + lastFinished: DateTime.now().toISO(), + lastResult: { status: ExecutionStatus.finished, handlerResult: 'I was executed' }, + }, }; await jobRepository.save(jobEntity); await sleep(500); - await mongoSchedule.define(job); + await mongoSchedule.define(momoJob); - const jobs = await jobRepository.find({ name: job.name }); + const jobs = await jobRepository.find({ name: momoJob.name }); expect(jobs[0]?.executionInfo).toEqual(jobEntity.executionInfo); await mongoSchedule.start(); @@ -128,15 +135,15 @@ describe('Momo', () => { }); it('saves executionInfo in mongo', async () => { - await mongoSchedule.define(job); + await mongoSchedule.define(momoJob); - const jobs1 = await jobRepository.find({ name: job.name }); + const jobs1 = await jobRepository.find({ name: momoJob.name }); expect(jobs1[0]!.executionInfo).toBeUndefined(); await mongoSchedule.start(); await waitFor(() => expect(jobHandler.count).toBe(1)); - const jobs2 = await jobRepository.find({ name: job.name }); + const jobs2 = await jobRepository.find({ name: momoJob.name }); const executionInfo = jobs2[0]?.executionInfo; expect(executionInfo).toBeDefined(); @@ -149,13 +156,13 @@ describe('Momo', () => { it('updates and reports failing job in mongo', async () => { jobHandler.failJob = true; - await mongoSchedule.define(job); + await mongoSchedule.define(momoJob); await mongoSchedule.start(); await waitFor(() => expect(jobHandler.count).toBe(1)); const executionInfo = await waitFor(async () => { - const jobs = await jobRepository.find({ name: job.name }); + const jobs = await jobRepository.find({ name: momoJob.name }); const executionInfo = jobs[0]?.executionInfo; expect(executionInfo?.lastFinished).toBeDefined(); return executionInfo!; @@ -165,19 +172,19 @@ describe('Momo', () => { expect(receivedError).toEqual({ message: 'job failed', type: MomoErrorType.executeJob, - data: { name: job.name }, + data: { name: momoJob.name }, error: new Error(jobHandler.message), }); }); it('updates result message when job succeeds', async () => { jobHandler.failJob = true; - await mongoSchedule.define(job); + await mongoSchedule.define(momoJob); await mongoSchedule.start(); await waitFor(() => expect(jobHandler.count).toBe(1)); await waitFor(async () => { - const jobs = await jobRepository.find({ name: job.name }); + const jobs = await jobRepository.find({ name: momoJob.name }); expect(jobs[0]?.executionInfo?.lastResult).toEqual({ status: ExecutionStatus.failed, handlerResult: jobHandler.message, @@ -187,7 +194,7 @@ describe('Momo', () => { jobHandler.failJob = false; await waitFor(() => expect(jobHandler.count).toBe(2)); await waitFor(async () => { - const jobs = await jobRepository.find({ name: job.name }); + const jobs = await jobRepository.find({ name: momoJob.name }); expect(jobs[0]?.executionInfo?.lastResult).toEqual({ status: ExecutionStatus.finished, handlerResult: jobHandler.result, @@ -196,7 +203,7 @@ describe('Momo', () => { }); it('can be stopped and restarted', async () => { - await mongoSchedule.define(job); + await mongoSchedule.define(momoJob); await mongoSchedule.start(); await waitFor(() => expect(jobHandler.count).toBe(1)); @@ -213,11 +220,11 @@ describe('Momo', () => { }); it('updates already started job', async () => { - await mongoSchedule.define(job); + await mongoSchedule.define(momoJob); await mongoSchedule.start(); await waitFor(() => expect(jobHandler.count).toBe(1)); - await mongoSchedule.define({ ...job, interval: '2 seconds' }); + await mongoSchedule.define({ ...momoJob, interval: '2 seconds' }); await mongoSchedule.start(); // pick up the new interval await sleep(1100); @@ -227,25 +234,25 @@ describe('Momo', () => { }); it('does not execute a job that was removed from mongo', async () => { - await mongoSchedule.define(job); + await mongoSchedule.define(momoJob); await mongoSchedule.start(); - await jobRepository.clear(); + await jobRepository.delete(); await sleep(1500); expect(jobHandler.count).toBe(0); }); it('updates maxRunning and concurrency from mongo', async () => { - await mongoSchedule.define(job); + await mongoSchedule.define(momoJob); await mongoSchedule.start(); const updatedConcurrency = 5; const updatedMaxRunning = 10; - await jobRepository.update( - { name: job.name }, - { concurrency: updatedConcurrency, maxRunning: updatedMaxRunning } + await jobRepository.updateOne( + { name: momoJob.name }, + { $set: { concurrency: updatedConcurrency, maxRunning: updatedMaxRunning } } ); await waitFor(async () => { @@ -259,19 +266,19 @@ describe('Momo', () => { }); it('does not update interval of started job from mongo', async () => { - await mongoSchedule.define(job); + await mongoSchedule.define(momoJob); await mongoSchedule.start(); const updatedInterval = '2 seconds'; - await jobRepository.update({ name: job.name }, { interval: updatedInterval }); + await jobRepository.updateOne({ name: momoJob.name }, { $set: { interval: updatedInterval } }); await waitFor(() => expect(jobHandler.count).toBe(1)); const updatedJobs = await mongoSchedule.list(); const { interval, schedulerStatus } = updatedJobs[0]!; expect(interval).toEqual(updatedInterval); - expect(schedulerStatus?.interval).toBe(job.interval); + expect(schedulerStatus?.interval).toBe(momoJob.interval); expect(schedulerStatus?.running).toBeGreaterThanOrEqual(0); }); }); @@ -557,7 +564,7 @@ describe('Momo', () => { expect(running).toBe(1); }, 1100); - await jobRepository.clear(); + await jobRepository.delete(); await waitFor(async () => { expect(await jobRepository.find({ name: job.name })).toHaveLength(0); @@ -569,7 +576,7 @@ describe('Momo', () => { await mongoSchedule.start(); - await jobRepository.clear(); + await jobRepository.delete(); await waitFor(() => { expect(receivedError).toEqual({ diff --git a/test/scheduler/JobScheduler.spec.ts b/test/scheduler/JobScheduler.spec.ts index c680368f..bd2e8f37 100644 --- a/test/scheduler/JobScheduler.spec.ts +++ b/test/scheduler/JobScheduler.spec.ts @@ -1,15 +1,12 @@ import { anything, deepEqual, instance, mock, verify, when } from 'ts-mockito'; import { ExecutionsRepository } from '../../src/repository/ExecutionsRepository'; -import { Job } from '../../src/job/Job'; -import { JobEntity } from '../../src/repository/JobEntity'; +import { Job, toJobDefinition } from '../../src/job/Job'; import { JobExecutor } from '../../src/executor/JobExecutor'; import { JobRepository } from '../../src/repository/JobRepository'; import { JobScheduler } from '../../src/scheduler/JobScheduler'; import { MomoErrorType, momoError } from '../../src'; -import { createJobEntity } from '../utils/createJobEntity'; import { loggerForTests } from '../utils/logging'; -import { mockRepositories } from '../utils/mockRepositories'; import { sleep } from '../utils/sleep'; describe('JobScheduler', () => { @@ -24,19 +21,15 @@ describe('JobScheduler', () => { const errorFn = jest.fn(); const scheduleId = '123'; - let jobExecutor: JobExecutor; - let jobRepository: JobRepository; let executionsRepository: ExecutionsRepository; + let jobRepository: JobRepository; + let jobExecutor: JobExecutor; let jobScheduler: JobScheduler; beforeEach(() => { - jest.clearAllMocks(); - + executionsRepository = mock(ExecutionsRepository); + jobRepository = mock(JobRepository); jobExecutor = mock(JobExecutor); - const repositories = mockRepositories(); - jobRepository = repositories.jobRepository; - executionsRepository = repositories.executionsRepository; - when(jobExecutor.execute(anything())).thenResolve(); }); @@ -51,9 +44,11 @@ describe('JobScheduler', () => { job.immediate, instance(jobExecutor), scheduleId, + instance(executionsRepository), + instance(jobRepository), loggerForTests(errorFn) ); - when(jobRepository.findOne(deepEqual({ name: job.name }))).thenResolve(JobEntity.from(job)); + when(jobRepository.findOne(deepEqual({ name: job.name }))).thenResolve(job); when(executionsRepository.countRunningExecutions(job.name)).thenResolve(0); return job; } @@ -92,7 +87,7 @@ describe('JobScheduler', () => { it('returns job description', async () => { const job = createJob(); - when(jobRepository.findOne(deepEqual({ name: job.name }))).thenResolve(createJobEntity(job)); + when(jobRepository.findOne(deepEqual({ name: job.name }))).thenResolve(toJobDefinition(job)); const jobDescription = await jobScheduler.getJobDescription(); expect(jobDescription).toEqual({ @@ -107,7 +102,7 @@ describe('JobScheduler', () => { const job = createJob(); await jobScheduler.start(); - when(jobRepository.findOne(deepEqual({ name: job.name }))).thenResolve(createJobEntity(job)); + when(jobRepository.findOne(deepEqual({ name: job.name }))).thenResolve(toJobDefinition(job)); const jobDescription = await jobScheduler.getJobDescription(); expect(jobDescription).toEqual({ diff --git a/test/scheduler/calculateDelay.spec.ts b/test/scheduler/calculateDelay.spec.ts index 83ac8880..759fb5d7 100644 --- a/test/scheduler/calculateDelay.spec.ts +++ b/test/scheduler/calculateDelay.spec.ts @@ -2,7 +2,6 @@ import { Clock, install } from '@sinonjs/fake-timers'; import { DateTime } from 'luxon'; import { ExecutionInfo } from '../../src'; -import { Job } from '../../src/job/Job'; import { JobEntity } from '../../src/repository/JobEntity'; import { calculateDelay } from '../../src/scheduler/calculateDelay'; @@ -11,7 +10,12 @@ describe('calculateDelay', () => { let clock: Clock; beforeEach(() => { - job = JobEntity.from({ name: 'test', interval: 'one second' } as Job); + job = { + name: 'test', + interval: 'one second', + concurrency: 0, + maxRunning: 1, + }; clock = install(); }); diff --git a/test/utils/createJobEntity.ts b/test/utils/createJobEntity.ts deleted file mode 100644 index 2530cce7..00000000 --- a/test/utils/createJobEntity.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { JobEntity } from '../../src/repository/JobEntity'; -import { MomoJob } from '../../src'; -import { withDefaults } from '../../src/job/withDefaults'; - -export function createJobEntity(job: MomoJob): JobEntity { - return JobEntity.from(withDefaults(job)); -} diff --git a/test/utils/mockRepositories.ts b/test/utils/mockRepositories.ts deleted file mode 100644 index 32a4a4a0..00000000 --- a/test/utils/mockRepositories.ts +++ /dev/null @@ -1,26 +0,0 @@ -import * as typeorm from 'typeorm'; -import { Connection, MongoRepository } from 'typeorm'; -import { instance, mock } from 'ts-mockito'; - -import { ExecutionsRepository } from '../../src/repository/ExecutionsRepository'; -import { JobRepository } from '../../src/repository/JobRepository'; - -export function mockRepositories(): { jobRepository: JobRepository; executionsRepository: ExecutionsRepository } { - const jobRepository = mock(JobRepository); - const executionsRepository = mock(ExecutionsRepository); - jest.spyOn(typeorm, 'getConnection').mockReturnValue({ - isConnected: true, - close: jest.fn(), - getCustomRepository: (clazz: typeof MongoRepository) => { - switch (clazz) { - case JobRepository: - return instance(jobRepository); - case ExecutionsRepository: - return instance(executionsRepository); - default: - return undefined; - } - }, - } as unknown as Connection); - return { jobRepository, executionsRepository }; -}