diff --git a/.travis.yml b/.travis.yml index 17a970a..f4fe4e1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,33 +23,38 @@ script: - npm run build jobs: include: - - stage: test + - stage: lint name: lint script: npm run lint - name: openapi + - stage: test + name: test + script: npm run test + - stage: test openapi + name: test-openapi script: npm run test:openapi + - stage: generate openapi name: openapi typescript client generation script: bash client/generate.sh - stage: build name: docker test build script: /bin/bash travis/docker-functions.sh docker_build $VERSION node_js: 10 - - stage: publish + - stage: publish alpha docker name: docker publish alpha script: /bin/bash travis/docker-functions.sh docker_build $VERSION publish if: branch = env(DEV_BRANCH) AND type = push node_js: 10 - - stage: publish + - stage: publish alpha npm name: openapi typescript client alpha script: bash client/generate.sh publish if: branch = env(DEV_BRANCH) AND type = push node_js: 10 - - stage: release + - stage: release docker name: docker publish release script: /bin/bash travis/docker-functions.sh docker_build $VERSION release if: branch = env(RELEASE_BRANCH) AND type = api AND commit_message = env(RELEASE_MESSAGE) node_js: 10 - - stage: release + - stage: release npm name: openapi typescript client release script: bash client/generate.sh release if: branch = env(RELEASE_BRANCH) AND type = api AND commit_message = env(RELEASE_MESSAGE) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b119a6..dc533a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,19 @@ All notable changes to this project will be documented in this file. The changelog format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +## [v1.1.4][v1.1.4] - 18-Jan-2022 + +Package | Version | Link +---|---|--- +REST Core| v2.4.0 | [catapult-rest][catapult-rest@v2.4.0] +SDK Core| v1.0.3 | [symbol-sdk][symbol-sdk@v1.0.3] + +### Fix +- `tsconfig.build.json` added to exclude test files from build but keep for tests [#128](https://github.com/symbol/statistics-service/pull/128) +- `Config.KEEP_STALE_NODES_FOR_HOURS` is added to clean up stale nodes from database [#127](https://github.com/symbol/statistics-service/pull/127) +- Preferred nodes filter fixed, `Config.PREFERRED_NODES` is now empty by default (will be set in the testnet/mainnet environment) [#126](https://github.com/symbol/statistics-service/pull/126) +- Node discovery extended, empty host info bug fixed, logging enhanced [#121](https://github.com/symbol/statistics-service/pull/121) + ## [v1.1.3][v1.1.3] - 30-Nov-2021 Package | Version | Link @@ -142,6 +155,8 @@ REST Core| v2.1.0 | [catapult-rest](https://github.com/nemtech/catapult-rest/rel ### Fixes - Cors error. [#13](https://github.com/nemgrouplimited/symbol-statistics-service/issues/13) +[v1.1.4]: https://github.com/symbol/statistics-service/releases/tag/v1.1.4 +[v1.1.3]: https://github.com/symbol/statistics-service/releases/tag/v1.1.3 [v1.1.2]: https://github.com/symbol/statistics-service/releases/tag/v1.1.2 [v1.1.1]: https://github.com/symbol/statistics-service/releases/tag/v1.1.1 [v1.1.0]: https://github.com/symbol/statistics-service/releases/tag/v1.1.0 diff --git a/openapi/openapi.yml b/openapi/openapi.yml index 31a64dc..cc8a21f 100644 --- a/openapi/openapi.yml +++ b/openapi/openapi.yml @@ -1,7 +1,7 @@ openapi: 3.0.3 info: title: Symbol Statistic Service API Documentation - version: '1.1.3' + version: '1.1.4' servers: - url: 'https://testnet.symbol.services' description: Testnet server diff --git a/package-lock.json b/package-lock.json index 5b647e3..705b5a0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,7 +5,8 @@ "requires": true, "packages": { "": { - "version": "1.1.3", + "name": "symbol-statistics-service", + "version": "1.1.4", "dependencies": { "@types/cors": "^2.8.6", "@types/express": "^4.17.6", @@ -15,6 +16,7 @@ "cors": "^2.8.5", "dotenv": "^8.2.0", "express": "^4.17.1", + "humanize-duration": "^3.27.1", "module-alias": "^2.2.2", "mongoose": "^5.9.16", "symbol-sdk": "^1.0.3", @@ -25,20 +27,27 @@ }, "devDependencies": { "@openapitools/openapi-generator-cli": "^2.4.15", + "@types/chai": "^4.3.0", + "@types/humanize-duration": "^3.27.0", + "@types/mocha": "^9.0.0", "@types/mongoose": "^5.7.14", - "@types/node": "^14.14.10", + "@types/sinon": "^10.0.6", "@types/winston": "^2.4.4", "@types/ws": "^8.2.0", "@typescript-eslint/eslint-plugin": "^4.4.0", "@typescript-eslint/parser": "^4.4.0", + "chai": "^4.3.4", "eslint": "^7.10.0", "eslint-config-prettier": "^6.12.0", "eslint-plugin-prettier": "^3.1.4", + "mocha": "^8.4.0", "nodemon": "^2.0.3", "openapi-to-postmanv2": "^2.6.0", "prettier": "^2.1.2", "shx": "^0.3.3", + "sinon": "^12.0.1", "swagger-cli": "^4.0.4", + "ts-mocha": "^8.0.0", "ts-node": "^8.10.1", "tslint": "^6.1.2", "typescript": "^3.8.3", @@ -455,6 +464,41 @@ "node": ">=6" } }, + "node_modules/@sinonjs/commons": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", + "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-7.1.2.tgz", + "integrity": "sha512-iQADsW4LBMISqZ6Ci1dupJL9pprqwcVFTcOsEmQOEhW+KLCVn/Y4Jrvg2k19fIHCp+iFprriYPTdRcQR8NbUPg==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1.7.0" + } + }, + "node_modules/@sinonjs/samsam": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-6.0.2.tgz", + "integrity": "sha512-jxPRPp9n93ci7b8hMfJOFDPRLFYadN6FSpeROFTR4UNF4i5b+EK6m4QXPO46BDhFgRy1JuS87zAnFOzCUwMJcQ==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1.6.0", + "lodash.get": "^4.4.2", + "type-detect": "^4.0.8" + } + }, + "node_modules/@sinonjs/text-encoding": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", + "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", + "dev": true + }, "node_modules/@szmarczak/http-timer": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", @@ -484,6 +528,12 @@ "@types/node": "*" } }, + "node_modules/@types/chai": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.0.tgz", + "integrity": "sha512-/ceqdqeRraGolFTcfoXNiqjyQhZzbINDngeoAq9GoHa8PPK1yNzTaxWjA6BFWp5Ua9JpXEMSS4s5i9tS0hOJtw==", + "dev": true + }, "node_modules/@types/connect": { "version": "3.4.34", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.34.tgz", @@ -518,17 +568,36 @@ "@types/range-parser": "*" } }, + "node_modules/@types/humanize-duration": { + "version": "3.27.0", + "resolved": "https://registry.npmjs.org/@types/humanize-duration/-/humanize-duration-3.27.0.tgz", + "integrity": "sha512-ivv1EIdXz20vHPB9xftPvogUEjGLSSlgz2fipK2yyHQZvZeIgUQBZc23Kcuxa4zGbiUcRtr36Sw96CF+TO30Fw==", + "dev": true + }, "node_modules/@types/json-schema": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz", "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==", "dev": true }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "dev": true, + "optional": true + }, "node_modules/@types/mime": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.3.tgz", "integrity": "sha512-Jus9s4CDbqwocc5pOAnh8ShfrnMcPHuJYzVcSUU7lrh8Ni5HuIqX3oilL86p3dlTrk0LzHRCgA/GQ7uNCw6l2Q==" }, + "node_modules/@types/mocha": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.0.0.tgz", + "integrity": "sha512-scN0hAWyLVAvLR9AyW7HoFF5sJZglyBsbPuHO4fv7JRvfmPBMfp1ozWqOf/e4wwPNxezBZXRfWzMb6iFLgEVRA==", + "dev": true + }, "node_modules/@types/mongodb": { "version": "3.6.3", "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.6.3.tgz", @@ -549,9 +618,9 @@ } }, "node_modules/@types/node": { - "version": "14.14.21", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.21.tgz", - "integrity": "sha512-cHYfKsnwllYhjOzuC5q1VpguABBeecUp24yFluHpn/BQaVxB1CuQ1FSRZCzrPxrkIfWISXV2LbeoBthLWg0+0A==" + "version": "14.18.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.5.tgz", + "integrity": "sha512-LMy+vDDcQR48EZdEx5wRX1q/sEl6NdGuHXPnfeL8ixkwCOSZ2qnIyIZmcCbdX0MeRqHhAcHmX+haCbrS8Run+A==" }, "node_modules/@types/qs": { "version": "6.9.5", @@ -572,6 +641,15 @@ "@types/node": "*" } }, + "node_modules/@types/sinon": { + "version": "10.0.6", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.6.tgz", + "integrity": "sha512-6EF+wzMWvBNeGrfP3Nx60hhx+FfwSg1JJBLAAP/IdIUq0EYkqCYf70VT3PhuhPX9eLD+Dp+lNdpb/ZeHG8Yezg==", + "dev": true, + "dependencies": { + "@sinonjs/fake-timers": "^7.1.0" + } + }, "node_modules/@types/tcp-ping": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@types/tcp-ping/-/tcp-ping-0.1.1.tgz", @@ -888,6 +966,12 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "dev": true + }, "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -1013,9 +1097,9 @@ } }, "node_modules/anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", "dev": true, "dependencies": { "normalize-path": "^3.0.0", @@ -1054,6 +1138,24 @@ "node": ">=8" } }, + "node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/astral-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", @@ -1241,6 +1343,12 @@ "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, "node_modules/bson": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.6.tgz", @@ -1377,6 +1485,23 @@ "resolved": "https://registry.npmjs.org/catbuffer-typescript/-/catbuffer-typescript-1.0.1.tgz", "integrity": "sha512-IyC2bmBEMRY96/NMsAer+qMTSa6yAwKfGIbpYPDPnlSb4UguNOlSabbCFH0CDQfhWuO6wqH97xGCuB4qY3OCwA==" }, + "node_modules/chai": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.4.tgz", + "integrity": "sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA==", + "dev": true, + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^3.0.1", + "get-func-name": "^2.0.0", + "pathval": "^1.1.1", + "type-detect": "^4.0.5" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -1429,25 +1554,34 @@ "node": ">=4.0.0" } }, + "node_modules/check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/chokidar": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.0.tgz", - "integrity": "sha512-JgQM9JS92ZbFR4P90EvmzNpSGhpPBGBSj10PILeDyYFwp4h2/D9OM03wsJ4zW1fEp4ka2DGrnUeD7FuvQ2aZ2Q==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", "dev": true, "dependencies": { - "anymatch": "~3.1.1", + "anymatch": "~3.1.2", "braces": "~3.0.2", - "glob-parent": "~5.1.0", + "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" + "readdirp": "~3.6.0" }, "engines": { "node": ">= 8.10.0" }, "optionalDependencies": { - "fsevents": "~2.3.1" + "fsevents": "~2.3.2" } }, "node_modules/ci-info": { @@ -1843,6 +1977,18 @@ "node": ">=4" } }, + "node_modules/deep-eql": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "dev": true, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=0.12" + } + }, "node_modules/deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", @@ -2584,6 +2730,15 @@ "node": ">=8" } }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, "node_modules/flat-cache": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", @@ -2664,9 +2819,9 @@ "dev": true }, "node_modules/fsevents": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.1.tgz", - "integrity": "sha512-YR47Eg4hChJGAB1O3yEAOkGO+rlzutoICGqGo9EZ4lKWokzZRSyIW1QmTzqjtw8MJdj9srP869CuWw/hyzSiBw==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, "hasInstallScript": true, "optional": true, @@ -2700,6 +2855,15 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/get-stdin": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", @@ -2840,6 +3004,15 @@ "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", "dev": true }, + "node_modules/growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true, + "engines": { + "node": ">=4.x" + } + }, "node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -2917,6 +3090,15 @@ "minimalistic-assert": "^1.0.1" } }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, "node_modules/hmac-drbg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", @@ -2960,6 +3142,11 @@ "integrity": "sha512-EC2utToWl4RKfs5zd36Mxq7nzHHBuomZboI0yYL6Y0RmBgT7Sgkq4rQ0ezFTYoIsSs7Tm9SJe+o2FcAg6GBhGA==", "dev": true }, + "node_modules/humanize-duration": { + "version": "3.27.1", + "resolved": "https://registry.npmjs.org/humanize-duration/-/humanize-duration-3.27.1.tgz", + "integrity": "sha512-jCVkMl+EaM80rrMrAPl96SGG4NRac53UyI1o/yAzebDntEY6K6/Fj2HOjdPg8omTqIe5Y0wPBai2q5xXrIbarA==" + }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -3252,6 +3439,15 @@ "node": ">=8" } }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/is-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", @@ -3379,6 +3575,19 @@ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true }, + "node_modules/json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "optional": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, "node_modules/jsonfile": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", @@ -3391,6 +3600,12 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/just-extend": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", + "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", + "dev": true + }, "node_modules/kareem": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.2.tgz", @@ -3736,100 +3951,351 @@ "mkdirp": "bin/cmd.js" } }, - "node_modules/module-alias": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/module-alias/-/module-alias-2.2.2.tgz", - "integrity": "sha512-A/78XjoX2EmNvppVWEhM2oGk3x4lLxnkEA4jTbaK97QKSDjkIoOsKQlfylt/d3kKKi596Qy3NP5XrXJ6fZIC9Q==" + "node_modules/mocha": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.4.0.tgz", + "integrity": "sha512-hJaO0mwDXmZS4ghXsvPVriOhsxQ7ofcpQdm8dE+jISUOKopitvnXFQmpRR7jd2K6VBG6E26gU3IAbXXGIbu4sQ==", + "dev": true, + "dependencies": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.1", + "debug": "4.3.1", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.1.6", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "4.0.0", + "log-symbols": "4.0.0", + "minimatch": "3.0.4", + "ms": "2.1.3", + "nanoid": "3.1.20", + "serialize-javascript": "5.0.1", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "which": "2.0.2", + "wide-align": "1.1.3", + "workerpool": "6.1.0", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha" + }, + "engines": { + "node": ">= 10.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" + } }, - "node_modules/mongodb": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.7.2.tgz", - "integrity": "sha512-/Qi0LmOjzIoV66Y2JQkqmIIfFOy7ZKsXnQNlUXPFXChOw3FCdNqVD5zvci9ybm6pkMe/Nw+Rz9I0Zsk2a+05iQ==", + "node_modules/mocha/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/mocha/node_modules/chokidar": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", + "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", + "dev": true, "dependencies": { - "bl": "^2.2.1", - "bson": "^1.1.4", - "denque": "^1.4.1", - "optional-require": "^1.1.8", - "safe-buffer": "^5.1.2" + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.5.0" }, "engines": { - "node": ">=4" + "node": ">= 8.10.0" }, "optionalDependencies": { - "saslprep": "^1.0.0" + "fsevents": "~2.3.1" + } + }, + "node_modules/mocha/node_modules/debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" }, "peerDependenciesMeta": { - "aws4": { - "optional": true - }, - "bson-ext": { - "optional": true - }, - "kerberos": { - "optional": true - }, - "mongodb-client-encryption": { - "optional": true - }, - "mongodb-extjson": { - "optional": true - }, - "snappy": { + "supports-color": { "optional": true } } }, - "node_modules/mongodb/node_modules/optional-require": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/optional-require/-/optional-require-1.1.8.tgz", - "integrity": "sha512-jq83qaUb0wNg9Krv1c5OQ+58EK+vHde6aBPzLvPPqJm89UQWsvSuFy9X/OSNJnFeSOKo7btE0n8Nl2+nE+z5nA==", - "dependencies": { - "require-at": "^1.0.6" - }, + "node_modules/mocha/node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/mocha/node_modules/diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true, "engines": { - "node": ">=4" + "node": ">=0.3.1" } }, - "node_modules/mongoose": { - "version": "5.13.12", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.13.12.tgz", - "integrity": "sha512-ZEuZ3X/yop9XyOyuCYMz+oxJxXBclm9LIsjKHB0QX2eaNqKNqkvZFzkElbJCj8FDvYmBZFh0OFHlkREhtie6uA==", + "node_modules/mocha/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, "dependencies": { - "@types/bson": "1.x || 4.0.x", - "@types/mongodb": "^3.5.27", - "bson": "^1.1.4", - "kareem": "2.3.2", - "mongodb": "3.7.2", - "mongoose-legacy-pluralize": "1.0.2", - "mpath": "0.8.4", - "mquery": "3.2.5", - "ms": "2.1.2", - "optional-require": "1.0.x", - "regexp-clone": "1.0.0", - "safe-buffer": "5.2.1", - "sift": "13.5.2", - "sliced": "1.0.1" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": ">=4.0.0" + "node": ">=10" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mongoose" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mongoose-legacy-pluralize": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz", - "integrity": "sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ==", - "peerDependencies": { - "mongoose": "*" + "node_modules/mocha/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" } }, - "node_modules/mongoose/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "node_modules/mocha/node_modules/js-yaml": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz", + "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/mocha/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/log-symbols": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", + "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/mocha/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/readdirp": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/mocha/node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/module-alias": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/module-alias/-/module-alias-2.2.2.tgz", + "integrity": "sha512-A/78XjoX2EmNvppVWEhM2oGk3x4lLxnkEA4jTbaK97QKSDjkIoOsKQlfylt/d3kKKi596Qy3NP5XrXJ6fZIC9Q==" + }, + "node_modules/mongodb": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.7.2.tgz", + "integrity": "sha512-/Qi0LmOjzIoV66Y2JQkqmIIfFOy7ZKsXnQNlUXPFXChOw3FCdNqVD5zvci9ybm6pkMe/Nw+Rz9I0Zsk2a+05iQ==", + "dependencies": { + "bl": "^2.2.1", + "bson": "^1.1.4", + "denque": "^1.4.1", + "optional-require": "^1.1.8", + "safe-buffer": "^5.1.2" + }, + "engines": { + "node": ">=4" + }, + "optionalDependencies": { + "saslprep": "^1.0.0" + }, + "peerDependenciesMeta": { + "aws4": { + "optional": true + }, + "bson-ext": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "mongodb-extjson": { + "optional": true + }, + "snappy": { + "optional": true + } + } + }, + "node_modules/mongodb/node_modules/optional-require": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/optional-require/-/optional-require-1.1.8.tgz", + "integrity": "sha512-jq83qaUb0wNg9Krv1c5OQ+58EK+vHde6aBPzLvPPqJm89UQWsvSuFy9X/OSNJnFeSOKo7btE0n8Nl2+nE+z5nA==", + "dependencies": { + "require-at": "^1.0.6" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mongoose": { + "version": "5.13.12", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.13.12.tgz", + "integrity": "sha512-ZEuZ3X/yop9XyOyuCYMz+oxJxXBclm9LIsjKHB0QX2eaNqKNqkvZFzkElbJCj8FDvYmBZFh0OFHlkREhtie6uA==", + "dependencies": { + "@types/bson": "1.x || 4.0.x", + "@types/mongodb": "^3.5.27", + "bson": "^1.1.4", + "kareem": "2.3.2", + "mongodb": "3.7.2", + "mongoose-legacy-pluralize": "1.0.2", + "mpath": "0.8.4", + "mquery": "3.2.5", + "ms": "2.1.2", + "optional-require": "1.0.x", + "regexp-clone": "1.0.0", + "safe-buffer": "5.2.1", + "sift": "13.5.2", + "sliced": "1.0.1" + }, + "engines": { + "node": ">=4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mongoose" + } + }, + "node_modules/mongoose-legacy-pluralize": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz", + "integrity": "sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ==", + "peerDependencies": { + "mongoose": "*" + } + }, + "node_modules/mongoose/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/mongoose/node_modules/safe-buffer": { "version": "5.2.1", @@ -3892,6 +4358,18 @@ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, + "node_modules/nanoid": { + "version": "3.1.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz", + "integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==", + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -3906,6 +4384,34 @@ "node": ">= 0.6" } }, + "node_modules/nise": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.0.tgz", + "integrity": "sha512-W5WlHu+wvo3PaKLsJJkgPup2LrsXCcm7AWwyNZkUnn5rwPkuPBi3Iwk5SQtN0mv+K65k7nKKjwNQ30wg3wLAQQ==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1.7.0", + "@sinonjs/fake-timers": "^7.0.4", + "@sinonjs/text-encoding": "^0.7.1", + "just-extend": "^4.0.2", + "path-to-regexp": "^1.7.0" + } + }, + "node_modules/nise/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "node_modules/nise/node_modules/path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "dev": true, + "dependencies": { + "isarray": "0.0.1" + } + }, "node_modules/node-fetch": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", @@ -4428,6 +4934,15 @@ "node": ">=8" } }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/picomatch": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", @@ -4704,9 +5219,9 @@ } }, "node_modules/readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, "dependencies": { "picomatch": "^2.2.1" @@ -5017,6 +5532,15 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" }, + "node_modules/serialize-javascript": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", + "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, "node_modules/serve-static": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", @@ -5144,6 +5668,63 @@ "is-arrayish": "^0.3.1" } }, + "node_modules/sinon": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-12.0.1.tgz", + "integrity": "sha512-iGu29Xhym33ydkAT+aNQFBINakjq69kKO6ByPvTsm3yyIACfyQttRTP03aBP/I8GfhFmLzrnKwNNkr0ORb1udg==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1.8.3", + "@sinonjs/fake-timers": "^8.1.0", + "@sinonjs/samsam": "^6.0.2", + "diff": "^5.0.0", + "nise": "^5.1.0", + "supports-color": "^7.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/sinon" + } + }, + "node_modules/sinon/node_modules/@sinonjs/fake-timers": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", + "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1.7.0" + } + }, + "node_modules/sinon/node_modules/diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/sinon/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/sinon/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -5273,6 +5854,16 @@ "node": ">=8" } }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true, + "optional": true, + "engines": { + "node": ">=4" + } + }, "node_modules/strip-hex-prefix": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz", @@ -5610,6 +6201,67 @@ "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" }, + "node_modules/ts-mocha": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/ts-mocha/-/ts-mocha-8.0.0.tgz", + "integrity": "sha512-Kou1yxTlubLnD5C3unlCVO7nh0HERTezjoVhVw/M5S1SqoUec0WgllQvPk3vzPMc6by8m6xD1uR1yRf8lnVUbA==", + "dev": true, + "dependencies": { + "ts-node": "7.0.1" + }, + "bin": { + "ts-mocha": "bin/ts-mocha" + }, + "engines": { + "node": ">= 6.X.X" + }, + "optionalDependencies": { + "tsconfig-paths": "^3.5.0" + }, + "peerDependencies": { + "mocha": "^3.X.X || ^4.X.X || ^5.X.X || ^6.X.X || ^7.X.X || ^8.X.X" + } + }, + "node_modules/ts-mocha/node_modules/diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/ts-mocha/node_modules/ts-node": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-7.0.1.tgz", + "integrity": "sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==", + "dev": true, + "dependencies": { + "arrify": "^1.0.0", + "buffer-from": "^1.1.0", + "diff": "^3.1.0", + "make-error": "^1.1.1", + "minimist": "^1.2.0", + "mkdirp": "^0.5.1", + "source-map-support": "^0.5.6", + "yn": "^2.0.0" + }, + "bin": { + "ts-node": "dist/bin.js" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/ts-mocha/node_modules/yn": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz", + "integrity": "sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/ts-node": { "version": "8.10.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.10.2.tgz", @@ -5635,6 +6287,19 @@ "typescript": ">=2.7" } }, + "node_modules/tsconfig-paths": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.12.0.tgz", + "integrity": "sha512-e5adrnOYT6zqVnWqZu7i/BQ3BnhzvGbjEjejFXO20lKIKpwTaupkCPgEfv4GZK1IBciJUEhYs3J3p75FdaTFVg==", + "dev": true, + "optional": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.0", + "strip-bom": "^3.0.0" + } + }, "node_modules/tslib": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", @@ -5741,6 +6406,15 @@ "node": ">= 0.8.0" } }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/type-fest": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", @@ -6048,6 +6722,58 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, + "node_modules/wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dev": true, + "dependencies": { + "string-width": "^1.0.2 || 2" + } + }, + "node_modules/wide-align/node_modules/ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/wide-align/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/wide-align/node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/wide-align/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/widest-line": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", @@ -6113,6 +6839,12 @@ "node": ">=0.10.0" } }, + "node_modules/workerpool": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.0.tgz", + "integrity": "sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg==", + "dev": true + }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -6266,6 +6998,45 @@ "node": ">=10" } }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yargs-unparser/node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/yargs/node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -6284,6 +7055,18 @@ "node": ">=6" } }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/z-schema": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-4.2.3.tgz", @@ -6623,6 +7406,41 @@ "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", "dev": true }, + "@sinonjs/commons": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", + "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-7.1.2.tgz", + "integrity": "sha512-iQADsW4LBMISqZ6Ci1dupJL9pprqwcVFTcOsEmQOEhW+KLCVn/Y4Jrvg2k19fIHCp+iFprriYPTdRcQR8NbUPg==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.0" + } + }, + "@sinonjs/samsam": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-6.0.2.tgz", + "integrity": "sha512-jxPRPp9n93ci7b8hMfJOFDPRLFYadN6FSpeROFTR4UNF4i5b+EK6m4QXPO46BDhFgRy1JuS87zAnFOzCUwMJcQ==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.6.0", + "lodash.get": "^4.4.2", + "type-detect": "^4.0.8" + } + }, + "@sinonjs/text-encoding": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", + "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", + "dev": true + }, "@szmarczak/http-timer": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", @@ -6649,6 +7467,12 @@ "@types/node": "*" } }, + "@types/chai": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.0.tgz", + "integrity": "sha512-/ceqdqeRraGolFTcfoXNiqjyQhZzbINDngeoAq9GoHa8PPK1yNzTaxWjA6BFWp5Ua9JpXEMSS4s5i9tS0hOJtw==", + "dev": true + }, "@types/connect": { "version": "3.4.34", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.34.tgz", @@ -6683,17 +7507,36 @@ "@types/range-parser": "*" } }, + "@types/humanize-duration": { + "version": "3.27.0", + "resolved": "https://registry.npmjs.org/@types/humanize-duration/-/humanize-duration-3.27.0.tgz", + "integrity": "sha512-ivv1EIdXz20vHPB9xftPvogUEjGLSSlgz2fipK2yyHQZvZeIgUQBZc23Kcuxa4zGbiUcRtr36Sw96CF+TO30Fw==", + "dev": true + }, "@types/json-schema": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz", "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==", "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=", + "dev": true, + "optional": true + }, "@types/mime": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.3.tgz", "integrity": "sha512-Jus9s4CDbqwocc5pOAnh8ShfrnMcPHuJYzVcSUU7lrh8Ni5HuIqX3oilL86p3dlTrk0LzHRCgA/GQ7uNCw6l2Q==" }, + "@types/mocha": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.0.0.tgz", + "integrity": "sha512-scN0hAWyLVAvLR9AyW7HoFF5sJZglyBsbPuHO4fv7JRvfmPBMfp1ozWqOf/e4wwPNxezBZXRfWzMb6iFLgEVRA==", + "dev": true + }, "@types/mongodb": { "version": "3.6.3", "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.6.3.tgz", @@ -6714,9 +7557,9 @@ } }, "@types/node": { - "version": "14.14.21", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.21.tgz", - "integrity": "sha512-cHYfKsnwllYhjOzuC5q1VpguABBeecUp24yFluHpn/BQaVxB1CuQ1FSRZCzrPxrkIfWISXV2LbeoBthLWg0+0A==" + "version": "14.18.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.5.tgz", + "integrity": "sha512-LMy+vDDcQR48EZdEx5wRX1q/sEl6NdGuHXPnfeL8ixkwCOSZ2qnIyIZmcCbdX0MeRqHhAcHmX+haCbrS8Run+A==" }, "@types/qs": { "version": "6.9.5", @@ -6737,6 +7580,15 @@ "@types/node": "*" } }, + "@types/sinon": { + "version": "10.0.6", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.6.tgz", + "integrity": "sha512-6EF+wzMWvBNeGrfP3Nx60hhx+FfwSg1JJBLAAP/IdIUq0EYkqCYf70VT3PhuhPX9eLD+Dp+lNdpb/ZeHG8Yezg==", + "dev": true, + "requires": { + "@sinonjs/fake-timers": "^7.1.0" + } + }, "@types/tcp-ping": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@types/tcp-ping/-/tcp-ping-0.1.1.tgz", @@ -6936,6 +7788,12 @@ "eslint-visitor-keys": "^2.0.0" } }, + "@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "dev": true + }, "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -7024,9 +7882,9 @@ } }, "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", "dev": true, "requires": { "normalize-path": "^3.0.0", @@ -7059,6 +7917,18 @@ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true + }, "astral-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", @@ -7204,6 +8074,12 @@ "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, "bson": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.6.tgz", @@ -7301,6 +8177,20 @@ "resolved": "https://registry.npmjs.org/catbuffer-typescript/-/catbuffer-typescript-1.0.1.tgz", "integrity": "sha512-IyC2bmBEMRY96/NMsAer+qMTSa6yAwKfGIbpYPDPnlSb4UguNOlSabbCFH0CDQfhWuO6wqH97xGCuB4qY3OCwA==" }, + "chai": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.4.tgz", + "integrity": "sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA==", + "dev": true, + "requires": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^3.0.1", + "get-func-name": "^2.0.0", + "pathval": "^1.1.1", + "type-detect": "^4.0.5" + } + }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -7340,20 +8230,26 @@ "integrity": "sha512-6dVyOOYjpfFcL1Y4qChrAoQLRHvj2ziyhcm0QJlhOcAhykL/k1kTUPbeo+87MNRTRdk2OIIsIXbuF3x2wi5EXg==", "dev": true }, + "check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", + "dev": true + }, "chokidar": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.0.tgz", - "integrity": "sha512-JgQM9JS92ZbFR4P90EvmzNpSGhpPBGBSj10PILeDyYFwp4h2/D9OM03wsJ4zW1fEp4ka2DGrnUeD7FuvQ2aZ2Q==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", "dev": true, "requires": { - "anymatch": "~3.1.1", + "anymatch": "~3.1.2", "braces": "~3.0.2", - "fsevents": "~2.3.1", - "glob-parent": "~5.1.0", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" + "readdirp": "~3.6.0" } }, "ci-info": { @@ -7671,6 +8567,15 @@ "mimic-response": "^1.0.0" } }, + "deep-eql": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "dev": true, + "requires": { + "type-detect": "^4.0.0" + } + }, "deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", @@ -8260,6 +9165,12 @@ "path-exists": "^4.0.0" } }, + "flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true + }, "flat-cache": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", @@ -8314,9 +9225,9 @@ "dev": true }, "fsevents": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.1.tgz", - "integrity": "sha512-YR47Eg4hChJGAB1O3yEAOkGO+rlzutoICGqGo9EZ4lKWokzZRSyIW1QmTzqjtw8MJdj9srP869CuWw/hyzSiBw==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, "optional": true }, @@ -8337,6 +9248,12 @@ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, + "get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", + "dev": true + }, "get-stdin": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", @@ -8441,6 +9358,12 @@ "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", "dev": true }, + "growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -8494,6 +9417,12 @@ "minimalistic-assert": "^1.0.1" } }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, "hmac-drbg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", @@ -8534,6 +9463,11 @@ "integrity": "sha512-EC2utToWl4RKfs5zd36Mxq7nzHHBuomZboI0yYL6Y0RmBgT7Sgkq4rQ0ezFTYoIsSs7Tm9SJe+o2FcAg6GBhGA==", "dev": true }, + "humanize-duration": { + "version": "3.27.1", + "resolved": "https://registry.npmjs.org/humanize-duration/-/humanize-duration-3.27.1.tgz", + "integrity": "sha512-jCVkMl+EaM80rrMrAPl96SGG4NRac53UyI1o/yAzebDntEY6K6/Fj2HOjdPg8omTqIe5Y0wPBai2q5xXrIbarA==" + }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -8747,6 +9681,12 @@ "integrity": "sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==", "dev": true }, + "is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true + }, "is-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", @@ -8856,6 +9796,16 @@ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "optional": true, + "requires": { + "minimist": "^1.2.0" + } + }, "jsonfile": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", @@ -8866,6 +9816,12 @@ "universalify": "^2.0.0" } }, + "just-extend": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", + "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", + "dev": true + }, "kareem": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.2.tgz", @@ -9145,6 +10101,183 @@ "minimist": "^1.2.5" } }, + "mocha": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.4.0.tgz", + "integrity": "sha512-hJaO0mwDXmZS4ghXsvPVriOhsxQ7ofcpQdm8dE+jISUOKopitvnXFQmpRR7jd2K6VBG6E26gU3IAbXXGIbu4sQ==", + "dev": true, + "requires": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.1", + "debug": "4.3.1", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.1.6", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "4.0.0", + "log-symbols": "4.0.0", + "minimatch": "3.0.4", + "ms": "2.1.3", + "nanoid": "3.1.20", + "serialize-javascript": "5.0.1", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "which": "2.0.2", + "wide-align": "1.1.3", + "workerpool": "6.1.0", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "chokidar": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", + "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", + "dev": true, + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.3.1", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.5.0" + } + }, + "debug": { + "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" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "js-yaml": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz", + "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "log-symbols": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", + "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", + "dev": true, + "requires": { + "chalk": "^4.0.0" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "readdirp": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true + } + } + }, "module-alias": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/module-alias/-/module-alias-2.2.2.tgz", @@ -9250,6 +10383,12 @@ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, + "nanoid": { + "version": "3.1.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz", + "integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==", + "dev": true + }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -9261,6 +10400,36 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" }, + "nise": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.0.tgz", + "integrity": "sha512-W5WlHu+wvo3PaKLsJJkgPup2LrsXCcm7AWwyNZkUnn5rwPkuPBi3Iwk5SQtN0mv+K65k7nKKjwNQ30wg3wLAQQ==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.0", + "@sinonjs/fake-timers": "^7.0.4", + "@sinonjs/text-encoding": "^0.7.1", + "just-extend": "^4.0.2", + "path-to-regexp": "^1.7.0" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "dev": true, + "requires": { + "isarray": "0.0.1" + } + } + } + }, "node-fetch": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", @@ -9667,6 +10836,12 @@ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true }, + "pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true + }, "picomatch": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", @@ -9878,9 +11053,9 @@ } }, "readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, "requires": { "picomatch": "^2.2.1" @@ -10111,6 +11286,15 @@ } } }, + "serialize-javascript": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", + "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, "serve-static": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", @@ -10203,6 +11387,52 @@ "is-arrayish": "^0.3.1" } }, + "sinon": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-12.0.1.tgz", + "integrity": "sha512-iGu29Xhym33ydkAT+aNQFBINakjq69kKO6ByPvTsm3yyIACfyQttRTP03aBP/I8GfhFmLzrnKwNNkr0ORb1udg==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.8.3", + "@sinonjs/fake-timers": "^8.1.0", + "@sinonjs/samsam": "^6.0.2", + "diff": "^5.0.0", + "nise": "^5.1.0", + "supports-color": "^7.2.0" + }, + "dependencies": { + "@sinonjs/fake-timers": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", + "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.0" + } + }, + "diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -10305,6 +11535,13 @@ "ansi-regex": "^5.0.0" } }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true, + "optional": true + }, "strip-hex-prefix": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz", @@ -10563,6 +11800,46 @@ "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" }, + "ts-mocha": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/ts-mocha/-/ts-mocha-8.0.0.tgz", + "integrity": "sha512-Kou1yxTlubLnD5C3unlCVO7nh0HERTezjoVhVw/M5S1SqoUec0WgllQvPk3vzPMc6by8m6xD1uR1yRf8lnVUbA==", + "dev": true, + "requires": { + "ts-node": "7.0.1", + "tsconfig-paths": "^3.5.0" + }, + "dependencies": { + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, + "ts-node": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-7.0.1.tgz", + "integrity": "sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==", + "dev": true, + "requires": { + "arrify": "^1.0.0", + "buffer-from": "^1.1.0", + "diff": "^3.1.0", + "make-error": "^1.1.1", + "minimist": "^1.2.0", + "mkdirp": "^0.5.1", + "source-map-support": "^0.5.6", + "yn": "^2.0.0" + } + }, + "yn": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz", + "integrity": "sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=", + "dev": true + } + } + }, "ts-node": { "version": "8.10.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.10.2.tgz", @@ -10576,6 +11853,19 @@ "yn": "3.1.1" } }, + "tsconfig-paths": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.12.0.tgz", + "integrity": "sha512-e5adrnOYT6zqVnWqZu7i/BQ3BnhzvGbjEjejFXO20lKIKpwTaupkCPgEfv4GZK1IBciJUEhYs3J3p75FdaTFVg==", + "dev": true, + "optional": true, + "requires": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.0", + "strip-bom": "^3.0.0" + } + }, "tslib": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", @@ -10662,6 +11952,12 @@ "prelude-ls": "^1.2.1" } }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, "type-fest": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", @@ -10910,6 +12206,48 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dev": true, + "requires": { + "string-width": "^1.0.2 || 2" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, "widest-line": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", @@ -10962,6 +12300,12 @@ "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", "dev": true }, + "workerpool": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.0.tgz", + "integrity": "sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg==", + "dev": true + }, "wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -11088,12 +12432,44 @@ "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true }, + "yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "requires": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "dependencies": { + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true + }, + "decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true + } + } + }, "yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", "dev": true }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true + }, "z-schema": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-4.2.3.tgz", diff --git a/package.json b/package.json index aae1727..52b40d3 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,8 @@ "description": "", "scripts": { "dev": "nodemon", - "build": "shx rm -rf ./dist/ && tsc && shx cp -r openapi dist/openapi", + "debug": "node --inspect-brk=0.0.0.0:9229 --require ts-node/register src/app.ts", + "build": "shx rm -rf ./dist/ && tsc -p tsconfig.build.json && shx cp -r openapi dist/openapi", "build:clients": "bash scripts/build-clients.sh", "start": "node dist/app.js", "version": "echo $npm_package_version", @@ -12,7 +13,8 @@ "lint": "eslint --cache src/ --ext .ts", "lint:fix": "eslint src/ --ext .ts --fix", "style:fix": "npm run prettier && npm run lint:fix", - "test:openapi": "swagger-cli validate openapi/openapi.yml" + "test:openapi": "swagger-cli validate openapi/openapi.yml", + "test": "ts-mocha --paths ./test/**.test.ts" }, "main": "dist/app.js", "typings": "dist/index.d.ts", @@ -21,20 +23,27 @@ }, "devDependencies": { "@openapitools/openapi-generator-cli": "^2.4.15", + "@types/chai": "^4.3.0", + "@types/humanize-duration": "^3.27.0", + "@types/mocha": "^9.0.0", "@types/mongoose": "^5.7.14", - "@types/node": "^14.14.10", + "@types/sinon": "^10.0.6", "@types/winston": "^2.4.4", "@types/ws": "^8.2.0", "@typescript-eslint/eslint-plugin": "^4.4.0", "@typescript-eslint/parser": "^4.4.0", + "chai": "^4.3.4", "eslint": "^7.10.0", "eslint-config-prettier": "^6.12.0", "eslint-plugin-prettier": "^3.1.4", + "mocha": "^8.4.0", "nodemon": "^2.0.3", "openapi-to-postmanv2": "^2.6.0", "prettier": "^2.1.2", "shx": "^0.3.3", + "sinon": "^12.0.1", "swagger-cli": "^4.0.4", + "ts-mocha": "^8.0.0", "ts-node": "^8.10.1", "tslint": "^6.1.2", "typescript": "^3.8.3", @@ -49,6 +58,7 @@ "cors": "^2.8.5", "dotenv": "^8.2.0", "express": "^4.17.1", + "humanize-duration": "^3.27.1", "module-alias": "^2.2.2", "mongoose": "^5.9.16", "symbol-sdk": "^1.0.3", diff --git a/src/config/config.json b/src/config/config.json index f9dc274..fcb03a9 100644 --- a/src/config/config.json +++ b/src/config/config.json @@ -17,6 +17,9 @@ "PEER_NODE_PORT": 7900, "REQUEST_TIMEOUT": 5000, "NUMBER_OF_NODE_REQUEST_CHUNK": 10, - "PREFERRED_NODES": ["*.symboldev.network"], - "MIN_PARTNER_NODE_VERSION": 16777728 + "NODE_PEERS_REQUEST_CHUNK_SIZE": 50, + "CHAIN_HEIGHT_REQUEST_CHUNK_SIZE": 10, + "PREFERRED_NODES": [], + "MIN_PARTNER_NODE_VERSION": 16777728, + "KEEP_STALE_NODES_FOR_HOURS": 72 } diff --git a/src/config/index.ts b/src/config/index.ts index 4724bab..448d0af 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -18,11 +18,14 @@ interface Symbol { interface Monitor { NODE_MONITOR_SCHEDULE_INTERVAL: number; NUMBER_OF_NODE_REQUEST_CHUNK: number; + NODE_PEERS_REQUEST_CHUNK_SIZE: number; + CHAIN_HEIGHT_REQUEST_CHUNK_SIZE: number; CHAIN_HEIGHT_MONITOR_SCHEDULE_INTERVAL: number; GEOLOCATION_MONITOR_SCHEDULE_INTERVAL: number; API_NODE_PORT: number; PEER_NODE_PORT: number; REQUEST_TIMEOUT: number; + KEEP_STALE_NODES_FOR_HOURS: number; } export interface Config { @@ -49,6 +52,8 @@ export const symbol: Symbol = { export const monitor: Monitor = { NODE_MONITOR_SCHEDULE_INTERVAL: Number(process.env.NODE_MONITOR_SCHEDULE_INTERVAL) || config.NODE_MONITOR_SCHEDULE_INTERVAL, NUMBER_OF_NODE_REQUEST_CHUNK: Number(process.env.NUMBER_OF_NODE_REQUEST_CHUNK) || config.NUMBER_OF_NODE_REQUEST_CHUNK, + NODE_PEERS_REQUEST_CHUNK_SIZE: Number(process.env.NODE_PEERS_REQUEST_CHUNK_SIZE) || config.NODE_PEERS_REQUEST_CHUNK_SIZE, + CHAIN_HEIGHT_REQUEST_CHUNK_SIZE: Number(process.env.CHAIN_HEIGHT_REQUEST_CHUNK_SIZE) || config.CHAIN_HEIGHT_REQUEST_CHUNK_SIZE, CHAIN_HEIGHT_MONITOR_SCHEDULE_INTERVAL: Number(process.env.CHAIN_HEIGHT_MONITOR_SCHEDULE_INTERVAL) || config.CHAIN_HEIGHT_MONITOR_SCHEDULE_INTERVAL, GEOLOCATION_MONITOR_SCHEDULE_INTERVAL: @@ -56,6 +61,7 @@ export const monitor: Monitor = { API_NODE_PORT: Number(process.env.API_NODE_PORT) || config.API_NODE_PORT, PEER_NODE_PORT: Number(process.env.PEER_NODE_PORT) || config.PEER_NODE_PORT, REQUEST_TIMEOUT: Number(process.env.REQUEST_TIMEOUT) || config.REQUEST_TIMEOUT, + KEEP_STALE_NODES_FOR_HOURS: Number(process.env.KEEP_STALE_NODES_FOR_HOURS) || config.KEEP_STALE_NODES_FOR_HOURS, }; export const verifyConfig = (cfg: Config): boolean => { @@ -77,14 +83,18 @@ export const verifyConfig = (cfg: Config): boolean => { error = 'Invalid "NODES"'; } - if (cfg.symbol.PREFERRED_NODES.length === 0) error = 'Invalid "PREFERRED NODES"'; - if (isNaN(cfg.monitor.NODE_MONITOR_SCHEDULE_INTERVAL) || cfg.monitor.NODE_MONITOR_SCHEDULE_INTERVAL < 0) error = 'Invalid "NODE_MONITOR_SCHEDULE_INTERVAL"'; if (isNaN(cfg.monitor.NUMBER_OF_NODE_REQUEST_CHUNK) || cfg.monitor.NUMBER_OF_NODE_REQUEST_CHUNK < 0) error = 'Invalid "NUMBER_OF_NODE_REQUEST_CHUNK"'; + if (isNaN(cfg.monitor.NODE_PEERS_REQUEST_CHUNK_SIZE) || cfg.monitor.NODE_PEERS_REQUEST_CHUNK_SIZE < 0) + error = 'Invalid "NODE_PEERS_REQUEST_CHUNK_SIZE"'; + + if (isNaN(cfg.monitor.CHAIN_HEIGHT_REQUEST_CHUNK_SIZE) || cfg.monitor.CHAIN_HEIGHT_REQUEST_CHUNK_SIZE < 0) + error = 'Invalid "CHAIN_HEIGHT_REQUEST_CHUNK_SIZE"'; + if (isNaN(cfg.monitor.API_NODE_PORT) || cfg.monitor.API_NODE_PORT <= 0 || cfg.monitor.API_NODE_PORT >= 10000) error = 'Invalid "API_NODE_PORT"'; diff --git a/src/constants.ts b/src/constants.ts new file mode 100644 index 0000000..eafbad3 --- /dev/null +++ b/src/constants.ts @@ -0,0 +1,6 @@ +export class Constants { + public static TIME_UNIT_SECOND = 1000; + public static TIME_UNIT_MINUTE = Constants.TIME_UNIT_SECOND * 60; + public static TIME_UNIT_HOUR = Constants.TIME_UNIT_MINUTE * 60; + public static TIME_UNIT_DAY = Constants.TIME_UNIT_HOUR * 24; +} diff --git a/src/models/Node.ts b/src/models/Node.ts index 014c92f..c2aa647 100644 --- a/src/models/Node.ts +++ b/src/models/Node.ts @@ -16,6 +16,7 @@ export interface INode { peerStatus?: PeerStatus; apiStatus?: ApiStatus; hostDetail?: IHostDetail; + lastAvailable?: Date; } export interface NodeDocument extends INode, Document {} @@ -50,6 +51,7 @@ const NodeSchema: Schema = new Schema({ type: Number, required: false, }, + required: false, }, apiStatus: { webSocket: { @@ -210,6 +212,10 @@ const NodeSchema: Schema = new Schema({ }, required: false, }, + lastAvailable: { + type: Date, + required: false, + }, }); NodeSchema.index( @@ -236,7 +242,9 @@ NodeSchema.set('toObject', { export const Node = mongoose.model('Node', NodeSchema); export const validateNodeModel = (node: any): boolean => { - if (!node || typeof node !== 'object') return false; + if (!node || typeof node !== 'object') { + return false; + } return !new Node(node).validateSync(); }; diff --git a/src/routes/index.ts b/src/routes/index.ts index 33b5153..e4bc767 100644 --- a/src/routes/index.ts +++ b/src/routes/index.ts @@ -37,10 +37,13 @@ export class Routes { } // ?filter=preferred - // it filter by host / domain name config by admin. + // it filters by host / domain name config by admin. if (filter === NodeFilter.Preferred) { + if (!symbol.PREFERRED_NODES?.length) { + return Promise.resolve(res.send([])); + } Object.assign(searchCriteria.filter, { - host: { $in: symbol.PREFERRED_NODES.map((node) => new RegExp(`^.${node}`, 'i')) }, + host: { $in: symbol.PREFERRED_NODES.map((node) => new RegExp(`^${node}`, 'i')) }, 'apiStatus.isAvailable': true, 'apiStatus.nodeStatus.apiNode': 'up', 'apiStatus.nodeStatus.db': 'up', diff --git a/src/services/ApiNodeService.ts b/src/services/ApiNodeService.ts index e4dd96f..5b7c6c6 100644 --- a/src/services/ApiNodeService.ts +++ b/src/services/ApiNodeService.ts @@ -163,7 +163,7 @@ export class ApiNodeService { try { return (await HTTP.get(`${hostUrl}/node/info`)).data; } catch (e) { - logger.error(`Fail to request /node/info: ${hostUrl}`, e); + logger.error(`[getNodeInfo] Fail to request /node/info: ${hostUrl}`, e); return null; } }; @@ -172,7 +172,7 @@ export class ApiNodeService { try { return (await HTTP.get(`${hostUrl}/chain/info`)).data; } catch (e) { - logger.error(`Fail to request /chain/info: ${hostUrl}`, e); + logger.error(`[getNodeChainInfo] Fail to request /chain/info: ${hostUrl}`, e); return null; } }; @@ -183,7 +183,7 @@ export class ApiNodeService { return nodeServerInfo.serverInfo; } catch (e) { - logger.error(`Fail to request /node/server: ${hostUrl}`, e); + logger.error(`[getNodeServer] Fail to request /node/server: ${hostUrl}`, e); return null; } }; @@ -194,7 +194,7 @@ export class ApiNodeService { return health.status; } catch (e) { - logger.error(`Fail to request /node/health: ${hostUrl}`, e); + logger.error(`[getNodeHealth] Fail to request /node/health: ${hostUrl}`, e); return null; } }; diff --git a/src/services/ChainHeightMonitor.ts b/src/services/ChainHeightMonitor.ts index eb112cd..8f05942 100644 --- a/src/services/ChainHeightMonitor.ts +++ b/src/services/ChainHeightMonitor.ts @@ -5,8 +5,8 @@ import { Logger } from '@src/infrastructure'; import { INode } from '@src/models/Node'; import { INodeHeightStats } from '@src/models/NodeHeightStats'; -import { symbol } from '@src/config'; -import { isAPIRole, basename, sleep } from '@src/utils'; +import { symbol, monitor } from '@src/config'; +import { isAPIRole, basename, sleep, runTaskInChunks } from '@src/utils'; const logger: winston.Logger = Logger.getLogger(basename(__filename)); @@ -61,7 +61,7 @@ export class ChainHeightMonitor { try { this.nodeList = (await DataBase.getNodeList()).filter((node) => isAPIRole(node.roles)); } catch (e) { - logger.error('Failed to get node list. Use nodes from config'); + logger.error('[getNodeList] Failed to get node list. Use nodes from config'); for (const node of symbol.NODES) { const url = new URL(node); const hostUrl = await ApiNodeService.buildHostUrl(url.hostname); @@ -80,32 +80,32 @@ export class ChainHeightMonitor { private getNodeChainHeight = async () => { logger.info(`Getting height stats for ${this.nodeList.length} nodes`); - const nodes: INode[] = this.nodeList; - const nodeChainInfoPromises = nodes.map((node) => { - const isHttps = node.apiStatus?.isHttpsEnabled; - const protocol = isHttps ? 'https:' : 'http:'; - const port = isHttps ? 3001 : 3000; - const hostUrl = `${protocol}//${node.host}:${port}`; - - return ApiNodeService.getNodeChainInfo(hostUrl); - }); - const nodeChainInfoList = await Promise.all(nodeChainInfoPromises); - - for (let chainInfo of nodeChainInfoList) { - try { - if (chainInfo) { - if (this.heights[chainInfo.height]) this.heights[chainInfo.height]++; - else this.heights[chainInfo.height] = 1; - - if (this.finalizedHeights[chainInfo.latestFinalizedBlock.height]) - this.finalizedHeights[chainInfo.latestFinalizedBlock.height]++; - else this.finalizedHeights[chainInfo.latestFinalizedBlock.height] = 1; + await runTaskInChunks(this.nodeList, monitor.CHAIN_HEIGHT_REQUEST_CHUNK_SIZE, logger, 'getNodeChainHeight', async (nodes) => { + const nodeChainInfoPromises = nodes.map((node) => { + const isHttps = node.apiStatus?.isHttpsEnabled; + const protocol = isHttps ? 'https:' : 'http:'; + const port = isHttps ? 3001 : 3000; + + const hostUrl = `${protocol}//${node.host}:${port}`; + + return ApiNodeService.getNodeChainInfo(hostUrl); + }); + const nodeChainInfoList = await Promise.all(nodeChainInfoPromises); + + for (const chainInfo of nodeChainInfoList) { + try { + if (chainInfo) { + this.heights[chainInfo.height] = (this.heights[chainInfo.height] || 0) + 1; + this.finalizedHeights[chainInfo.latestFinalizedBlock.height] = + (this.finalizedHeights[chainInfo.latestFinalizedBlock.height] || 0) + 1; + } + } catch (e) { + logger.error(`Node chain height monitor failed. ${e.message}`); } - } catch (e) { - logger.error(`Node chain height monitor failed. ${e.message}`); } - } + return nodeChainInfoList; + }); }; private clear = () => { diff --git a/src/services/DataBase.ts b/src/services/DataBase.ts index 9b91526..48ddcf0 100644 --- a/src/services/DataBase.ts +++ b/src/services/DataBase.ts @@ -109,28 +109,30 @@ export class DataBase { collectionName: string, ) => { const prevState = await model.find().exec(); - let error = Error(); try { await model.deleteMany(); } catch (e) { - logger.error(`Update collection "${collectionName}" failed. Error during "model.deleteMany()". ${error.message}`); - throw error; + const msg = `Update collection "${collectionName}" failed. Error during "model.deleteMany()". ${e.message}`; + + logger.error(msg); + throw new Error(msg); } try { await model.insertMany(documents); } catch (e) { - logger.error(`Update collection "${collectionName}" failed. Error during "model.insertMany()". ${error.message}`); - await model.insertMany(prevState); - throw error; + const msg = `Update collection "${collectionName}" failed. Error during "model.insertMany()". ${e.message}`; + + logger.error(msg); + throw new Error(msg); } const currentState = await model.find().exec(); if (documents.length !== currentState.length) { logger.error( - `Update collection "${collectionName}" failed. Collectin.length(${currentState.length}) !== documentsToInsert.length(${documents.length})`, + `Update collection "${collectionName}" failed. Collection.length(${currentState.length}) !== documentsToInsert.length(${documents.length})`, ); await model.insertMany(prevState); throw new Error(`Failed to update collection "${collectionName}. Length verification failed`); diff --git a/src/services/HostInfo.ts b/src/services/HostInfo.ts index e929d69..bc2f499 100644 --- a/src/services/HostInfo.ts +++ b/src/services/HostInfo.ts @@ -43,7 +43,7 @@ export class HostInfo { zip: data.zip, }; } catch (e) { - logger.error(`Failed to get host ${host} info ${e.message}`); + logger.error(`[getHostDetail] Failed to get host ${host} info ${e.message}`); return null; } }; diff --git a/src/services/NodeMonitor.ts b/src/services/NodeMonitor.ts index a7a58df..0158ce8 100644 --- a/src/services/NodeMonitor.ts +++ b/src/services/NodeMonitor.ts @@ -13,7 +13,9 @@ import { Logger } from '@src/infrastructure'; import { INode, validateNodeModel } from '@src/models/Node'; import { symbol, monitor } from '@src/config'; -import { isAPIRole, isPeerRole, basename, splitArray, sleep } from '@src/utils'; +import { isAPIRole, isPeerRole, basename, showDuration, runTaskInChunks, splitByPredicate } from '@src/utils'; +import humanizeDuration = require('humanize-duration'); +import { Constants } from '@src/constants'; const logger: winston.Logger = Logger.getLogger(basename(__filename)); @@ -24,6 +26,7 @@ export class NodeMonitor { private isRunning: boolean; private interval: number; private nodeInfoChunks: number; + private nodePeersChunkSize: number; private nodeInfoDelay: number; private networkIdentifier: number; private generationHashSeed: string; @@ -39,6 +42,7 @@ export class NodeMonitor { this.isRunning = false; this.interval = _interval || 300000; this.nodeInfoChunks = monitor.NUMBER_OF_NODE_REQUEST_CHUNK; + this.nodePeersChunkSize = monitor.NODE_PEERS_REQUEST_CHUNK_SIZE; this.nodeInfoDelay = 1000; this.networkIdentifier = 0; this.generationHashSeed = ''; @@ -53,6 +57,8 @@ export class NodeMonitor { public start = async () => { logger.info(`Start`); + const startTime = new Date().getTime(); + try { this.isRunning = true; this.clear(); @@ -67,8 +73,13 @@ export class NodeMonitor { await this.cacheCollection(); setTimeout(() => this.start(), this.interval); } + logger.info(`[start] Node monitor task finished, time elapsed: [${showDuration(startTime - new Date().getTime())}]`); } catch (e) { - logger.error(`Unhandled error during a loop. ${e.message}. Restarting NodeMonitor..`); + logger.error( + `[start] Node monitor task failed [error: ${e.message}], time elapsed: [${showDuration( + startTime - new Date().getTime(), + )}], Restarting Node monitor task...`, + ); this.stop(); this.start(); } @@ -81,31 +92,29 @@ export class NodeMonitor { }; private getNodeList = async (): Promise => { - logger.info(`Getting node list`); + logger.info(`[getNodeList] Getting node list...`); + const startTime = new Date().getTime(); + + // Fetch node list from database + const nodesFromDb = (await DataBase.getNodeList().then((nodes) => nodes.map((n) => n.toJSON()))) || []; + + logger.info(`[getNodeList] Nodes count from DB: ${nodesFromDb.length}`); + // adding the nodes from DB to the node list + this.addNodesToList(nodesFromDb); // Fetch node list from config nodes + logger.info(`[getNodeList] Initial node list: ${symbol.NODES.join(', ')}`); for (const nodeUrl of symbol.NODES) { const peers = await this.fetchNodePeersByURL(nodeUrl); this.addNodesToList(peers); } - // Nested fetch node list from current nodeList[] - const nodeListPromises = this.nodeList.map(async (node) => { - if (isAPIRole(node.roles)) { - const hostUrl = await ApiNodeService.buildHostUrl(node.host); - - return this.fetchNodePeersByURL(hostUrl); - } - - return []; - }); - - const arrayOfNodeList = await Promise.all(nodeListPromises); - const nodeList: INode[] = arrayOfNodeList.reduce((accumulator, value) => accumulator.concat(value), []); - - this.addNodesToList(nodeList); + await this.fetchAndAddNodeListPeers(); + logger.info( + `[getNodeList] Total node count: ${this.nodeList.length}, time elapsed: [${showDuration(startTime - new Date().getTime())}]`, + ); return Promise.resolve(); }; @@ -124,49 +133,81 @@ export class NodeMonitor { if (Array.isArray(nodePeers.data)) nodeList = [...nodePeers.data]; } catch (e) { - logger.error(`FetchNodePeersByURL. Failed to get /node/peers from "${hostUrl}". ${e.message}`); + logger.error(`[FetchNodePeersByURL] Failed to get /node/peers from "${hostUrl}". ${e.message}`); } return nodeList; }; - private getNodeListInfo = async () => { - logger.info(`Getting node from peers total ${this.nodeList.length} nodes`); - const nodeListChunks = splitArray(this.nodeList, this.nodeInfoChunks); + /** + * Fetch peers from current node list and add them to the node list. + */ + private fetchAndAddNodeListPeers = async (): Promise => { + const apiNodeList = this.nodeList.filter((node) => isAPIRole(node.roles)); - this.nodeList = []; + logger.info( + `[fetchAndAddNodeListPeers] Getting peers from nodes, total nodes: ${this.nodeList.length}, api nodes: ${apiNodeList.length}`, + ); + + await runTaskInChunks(apiNodeList, this.nodePeersChunkSize, logger, 'fetchAndAddNodeListPeers', async (nodes) => { + const arrayOfPeerList = await Promise.all( + [...nodes].map(async (node) => this.fetchNodePeersByURL(await ApiNodeService.buildHostUrl(node.host))), + ); + const peers: INode[] = arrayOfPeerList.reduce((accumulator, value) => accumulator.concat(value), []); + + this.addNodesToList(peers); + return peers; + }); + }; + + private getNodeListInfo = async () => { + const startTime = new Date().getTime(); + const nodeCount = this.nodeList.length; - for (const nodes of nodeListChunks) { - logger.info(`Getting node info for chunk of ${nodes.length} nodes`); + logger.info(`[getNodeListInfo] Getting node from peers, total nodes: ${nodeCount}`); + await runTaskInChunks(this.nodeList, this.nodeInfoChunks, logger, 'getNodeListInfo', async (nodes) => { const nodeInfoPromises = [...nodes].map((node) => this.getNodeInfo(node)); + const arrayOfNodeInfo = await Promise.all(nodeInfoPromises); + + this.addNodesToList(arrayOfNodeInfo); + return arrayOfNodeInfo; + }); - this.addNodesToList((await Promise.all(nodeInfoPromises)) as INode[]); - await sleep(this.nodeInfoDelay); - } this.nodeList.forEach((node) => this.nodesStats.addToStats(node)); + logger.info( + `[getNodeListInfo] Total node count(after nodeInfo): ${this.nodeList.length}, time elapsed: [${showDuration( + startTime - new Date().getTime(), + )}]`, + ); }; private async getNodeInfo(node: INode): Promise { let nodeWithInfo: INode = { ...node }; + const nodeHost = node.host; try { - const hostDetail = await HostInfo.getHostDetailCached(node.host); + const hostDetail = await HostInfo.getHostDetailCached(nodeHost); - if (hostDetail) nodeWithInfo.hostDetail = hostDetail; + if (hostDetail) { + nodeWithInfo.hostDetail = hostDetail; + } if (isPeerRole(nodeWithInfo.roles)) { - nodeWithInfo.peerStatus = await PeerNodeService.getStatus(node.host, node.port); + nodeWithInfo.peerStatus = await PeerNodeService.getStatus(nodeHost, node.port); } if (isAPIRole(nodeWithInfo.roles)) { - const hostUrl = await ApiNodeService.buildHostUrl(nodeWithInfo.host); + const hostUrl = await ApiNodeService.buildHostUrl(nodeHost); // Get node info and overwrite info from /node/peers const nodeStatus = await ApiNodeService.getNodeInfo(hostUrl); if (nodeStatus) { Object.assign(nodeWithInfo, nodeStatus); + if (!nodeWithInfo.host) { + nodeWithInfo.host = nodeHost; + } } // Request API Status, if node belong to the network @@ -178,7 +219,7 @@ export class NodeMonitor { } } } catch (e) { - logger.error(`GetNodeInfo. Failed to fetch info for "${nodeWithInfo.host}". ${e.message}`); + logger.error(`[getNodeInfo] Failed to fetch info for "${nodeWithInfo.host}". ${e.message}`); } return nodeWithInfo; @@ -202,6 +243,7 @@ export class NodeMonitor { logger.info(`Update collection`); const prevNodeList = await DataBase.getNodeList(); + this.nodeList = this.removeStaleNodesAndUpdateLastAvailable(this.nodeList); try { await DataBase.updateNodeList(this.nodeList); await DataBase.updateNodesStats(this.nodesStats); @@ -212,7 +254,47 @@ export class NodeMonitor { } else logger.error(`Failed to update collection. Collection length = ${this.nodeList.length}`); }; - private cacheCollection = async (): Promise => { + private removeStaleNodesAndUpdateLastAvailable(nodes: INode[]): INode[] { + let { filtered: availableNodes, unfiltered: unavailableNodes } = splitByPredicate((n) => this.checkNodeAvailable(n), nodes); + + // set last available time for available nodes + availableNodes = availableNodes.map((n) => ({ ...n, lastAvailable: new Date() })); + + let { filtered: staleNodes, unfiltered: soonTobeStaleNodes } = splitByPredicate((n) => this.checkNodeStale(n), unavailableNodes); + + logger.info( + `[updateCollection] Removing stale nodes[${staleNodes + .map((n) => n.host) + .join(', ')}], available ones in the last ${humanizeDuration( + monitor.KEEP_STALE_NODES_FOR_HOURS * Constants.TIME_UNIT_HOUR, + )} are kept.`, + ); + return [...availableNodes, ...soonTobeStaleNodes]; + } + + private checkNodeStale = (node: INode): boolean => { + return ( + !this.checkNodeAvailable(node) && + !!node.lastAvailable && + new Date().getTime() > node.lastAvailable.getTime() + monitor.KEEP_STALE_NODES_FOR_HOURS * Constants.TIME_UNIT_HOUR + ); + }; + + private checkNodeAvailable = (node: INode): boolean => { + let available = true; + + if (isAPIRole(node.roles) && isPeerRole(node.roles)) { + // in dual node mode, we consider the node is available if any of the two (REST and Peer) is available + available = !!node.apiStatus?.isAvailable || !!node.peerStatus?.isAvailable; + } else if (isAPIRole(node.roles)) { + available = !!node.apiStatus?.isAvailable; + } else if (isPeerRole(node.roles)) { + available = !!node.peerStatus?.isAvailable; + } + return available; + }; + + private async cacheCollection(): Promise { try { const nodeList = await DataBase.getNodeList(); @@ -220,9 +302,9 @@ export class NodeMonitor { } catch (e) { logger.error('Failed to cache Node collection to memory. ' + e.message); } - }; + } - private fetchAndSetNetworkInfo = async (): Promise => { + private async fetchAndSetNetworkInfo(): Promise { for (const nodeUrl of symbol.NODES) { const url = new URL(nodeUrl); const hostUrl = await ApiNodeService.buildHostUrl(url.hostname); @@ -239,19 +321,34 @@ export class NodeMonitor { } logger.info(`Network identifier not found in ${symbol.NODES}, using default ${this.networkIdentifier}`); - }; + } private addNodesToList = (nodes: INode[]) => { nodes.forEach((node: INode) => { if ( node.networkIdentifier !== this.networkIdentifier || node.networkGenerationHashSeed !== this.generationHashSeed || - !!this.nodeList.find((addedNode) => addedNode.publicKey === node.publicKey) || !validateNodeModel(node) - ) + ) { return; + } + const nodeInx = this.nodeList.findIndex((addedNode) => addedNode.publicKey === node.publicKey); + + if (nodeInx > -1) { + // already in the list then update and keep the last available time + let lastAvailable = this.nodeList[nodeInx].lastAvailable || node.lastAvailable; + + if (lastAvailable === undefined && !this.checkNodeAvailable(node)) { + lastAvailable = new Date(); + } - this.nodeList.push(node); + this.nodeList[nodeInx] = { ...node, lastAvailable }; + } else { + if (!node.lastAvailable && !this.checkNodeAvailable(node)) { + node.lastAvailable = new Date(); + } + this.nodeList.push(node); + } }); }; } diff --git a/src/utils.ts b/src/utils.ts index 18a9918..20a07c1 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,5 +1,7 @@ import { INode } from '@src/models/Node'; import * as path from 'path'; +import * as humanizeDuration from 'humanize-duration'; +import winston = require('winston'); export const stringToArray = (str: string | undefined): Array => { let result = null; @@ -58,3 +60,55 @@ export const splitArray = (array: Array, chunks: number): Array => all[ch] = [].concat(all[ch] || [], one); return all; }, []); + +export const showDuration = (durationMs: number): string => { + return humanizeDuration(durationMs); +}; + +export const runTaskInChunks = async ( + list: T[], + chunkSize: number, + logger: winston.Logger, + loggingMethod: string, + asyncTask: (subList: T[]) => Promise, +) => { + if (!list?.length) { + return []; + } + if (chunkSize < 1) { + throw new Error(`Invalid chunkSize value[${chunkSize}]`); + } + const chunks: T[][] = splitArray(list, chunkSize); + const listSize = list.length; + + logger.info( + `[${loggingMethod}] Running the task for chunks, Total Size: ${listSize}, Chunk size: ${chunkSize}, Chunk count: ${Math.ceil( + listSize / chunkSize, + )}`, + ); + + let numOfNodesProcessed = 0, + i = 0; + + for (const chunk of chunks) { + logger.info( + `[${loggingMethod}] Working on chunk #${++i}/${chunks.length}, size: ${ + chunk.length + }, progress: ${numOfNodesProcessed}/${listSize}`, + ); + const arrayOfTaskResults = await asyncTask(chunk); + + logger.info(`[${loggingMethod}] Number of results:${arrayOfTaskResults.length} in the chunk of ${chunk.length}`); + numOfNodesProcessed += chunk.length; + } +}; + +export const splitByPredicate = (predicate: (item: T) => boolean, arr: T[]): { filtered: T[]; unfiltered: T[] } => { + return arr.reduce( + (res, item: T) => { + res[predicate(item) ? 'filtered' : 'unfiltered'].push(item); + return res; + }, + { filtered: [] as T[], unfiltered: [] as T[] }, + ); +}; diff --git a/test/NodeMonitor.test.ts b/test/NodeMonitor.test.ts new file mode 100644 index 0000000..0217094 --- /dev/null +++ b/test/NodeMonitor.test.ts @@ -0,0 +1,160 @@ +import { monitor } from '@src/config'; +import { Constants } from '@src/constants'; +import { NodeMonitor } from '@src/services/NodeMonitor'; +import { expect } from 'chai'; +import { stub } from 'sinon'; +import { RoleType } from 'symbol-sdk'; + +describe('NodeMonitor', () => { + describe('removeStaleNodesAndUpdateLastAvailable when KEEP_STALE_NODES_FOR_HOURS is 3 days', () => { + stub(NodeMonitor.prototype, 'cacheCollection' as any); + stub(monitor, 'KEEP_STALE_NODES_FOR_HOURS').value(72); // 3 days + + const nodeMonitor = new NodeMonitor(0); + + it('should remove stale nodes and keep fresh ones', () => { + const freshNodeLastAvailable = new Date(new Date().getTime() - 1 * Constants.TIME_UNIT_DAY); + const nodes = [ + { + publicKey: 'pkFresh', + host: 'hostFresh', + lastAvailable: freshNodeLastAvailable, + roles: RoleType.ApiNode + RoleType.PeerNode, + apiStatus: { + isAvailable: false, + }, + }, + { + publicKey: 'pkStale', + host: 'hostStale', + lastAvailable: new Date(new Date().getTime() - 4 * Constants.TIME_UNIT_DAY), + roles: RoleType.ApiNode + RoleType.PeerNode, + apiStatus: { + isAvailable: false, + }, + }, + ]; + const result = (nodeMonitor as any).removeStaleNodesAndUpdateLastAvailable(nodes); + + expect(result.length).to.be.equal(1); + expect(result[0].publicKey).to.be.equal('pkFresh'); + expect(result[0].lastAvailable).to.be.eq(freshNodeLastAvailable); + }); + + it("dual node - should refresh the stale node if at least one of two(API, Peer)'s status is available else remove the node", () => { + const getNode = (apiAvailable: boolean, peerAvailable: boolean) => [ + { + publicKey: 'pkFresh', + host: 'hostFresh', + lastAvailable: new Date(new Date().getTime() - 4 * Constants.TIME_UNIT_DAY), + roles: RoleType.ApiNode + RoleType.PeerNode, + apiStatus: { + isAvailable: apiAvailable, + }, + peerStatus: { + isAvailable: peerAvailable, + }, + }, + ]; + const resultApiOK_PeerNOT = (nodeMonitor as any).removeStaleNodesAndUpdateLastAvailable(getNode(true, false)); + + expect(resultApiOK_PeerNOT.length).to.be.equal(1); + expect(resultApiOK_PeerNOT[0].publicKey).to.be.equal('pkFresh'); + expect(resultApiOK_PeerNOT[0].lastAvailable).to.be.greaterThan(new Date(new Date().getTime() - 1 * Constants.TIME_UNIT_MINUTE)); + + const resultApiNOT_PeerOK = (nodeMonitor as any).removeStaleNodesAndUpdateLastAvailable(getNode(false, true)); + + expect(resultApiNOT_PeerOK.length).to.be.equal(1); + expect(resultApiNOT_PeerOK[0].publicKey).to.be.equal('pkFresh'); + expect(resultApiNOT_PeerOK[0].lastAvailable).to.be.greaterThan(new Date(new Date().getTime() - 1 * Constants.TIME_UNIT_MINUTE)); + + const resultApiOK_PeerOK = (nodeMonitor as any).removeStaleNodesAndUpdateLastAvailable(getNode(true, true)); + + expect(resultApiOK_PeerOK.length).to.be.equal(1); + expect(resultApiOK_PeerOK[0].publicKey).to.be.equal('pkFresh'); + expect(resultApiOK_PeerOK[0].lastAvailable).to.be.greaterThan(new Date(new Date().getTime() - 1 * Constants.TIME_UNIT_MINUTE)); + + const resultApiNOT_PeerNOT = (nodeMonitor as any).removeStaleNodesAndUpdateLastAvailable(getNode(false, false)); + + expect(resultApiNOT_PeerNOT.length).to.be.equal(0); + }); + + it('api only - should keep the node in the list when fresh and available', () => { + const nodes = [ + { + publicKey: 'pkApiOnly', + host: 'apiOnly', + lastAvailable: new Date(), + roles: RoleType.ApiNode, + apiStatus: { + isAvailable: true, + }, + }, + ]; + const result = (nodeMonitor as any).removeStaleNodesAndUpdateLastAvailable(nodes); + + expect(result.length).to.be.equal(1); + expect(result[0].publicKey).to.be.equal('pkApiOnly'); + }); + + it('peer only - should keep the node in the list when fresh and available', () => { + const nodes = [ + { + publicKey: 'pkPeerOnly', + host: 'peerOnly', + lastAvailable: new Date(), + roles: RoleType.PeerNode, + peerStatus: { + isAvailable: true, + }, + }, + ]; + const result = (nodeMonitor as any).removeStaleNodesAndUpdateLastAvailable(nodes); + + expect(result.length).to.be.equal(1); + expect(result[0].publicKey).to.be.equal('pkPeerOnly'); + }); + + it('voting only - should keep the node in the list', () => { + const nodes = [ + { + publicKey: 'pkVotingOnly', + host: 'votingOnly', + lastAvailable: new Date(new Date().getTime() - 3 * Constants.TIME_UNIT_DAY + 1 * Constants.TIME_UNIT_HOUR), + roles: RoleType.VotingNode, + }, + ]; + const result = (nodeMonitor as any).removeStaleNodesAndUpdateLastAvailable(nodes); + + expect(result.length).to.be.equal(1); + expect(result[0].publicKey).to.be.equal('pkVotingOnly'); + }); + + it('should keep a resurrecting node', () => { + const nodes = [ + { + publicKey: 'pkFresh', + host: 'hostFresh', + lastAvailable: new Date(new Date().getTime() - 3 * Constants.TIME_UNIT_DAY + 1 * Constants.TIME_UNIT_HOUR), + roles: RoleType.ApiNode + RoleType.PeerNode, + apiStatus: { + isAvailable: true, + }, + }, + { + publicKey: 'pkStale', + host: 'hostStale', + lastAvailable: new Date(new Date().getTime() - 4 * Constants.TIME_UNIT_DAY), + roles: RoleType.ApiNode + RoleType.PeerNode, + apiStatus: { + isAvailable: false, + }, + }, + ]; + const result = (nodeMonitor as any).removeStaleNodesAndUpdateLastAvailable(nodes); + + expect(result.length).to.be.equal(1); + expect(result[0].publicKey).to.be.equal('pkFresh'); + }); + }); +}); diff --git a/tsconfig.build.json b/tsconfig.build.json new file mode 100644 index 0000000..f092552 --- /dev/null +++ b/tsconfig.build.json @@ -0,0 +1,31 @@ +{ + "compilerOptions": { + "module": "commonjs", + "strict": true, + "baseUrl": "./", + "outDir": "dist", + "removeComments": true, + "experimentalDecorators": true, + "target": "es6", + "emitDecoratorMetadata": true, + "moduleResolution": "node", + "resolveJsonModule": true, + "importHelpers": true, + "types": [ + "node", + "mocha" + ], + "typeRoots": [ + "node_modules/@types" + ], + "paths": { + "@src/*": [ "src/*" ] + } + }, + "include": [ + "./src/**/*.ts", + ], + "exclude": [ + "./src/public/" + ] +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index eb9c35a..a63b5e9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,30 +1,6 @@ { - "compilerOptions": { - "module": "commonjs", - "strict": true, - "baseUrl": "./", - "outDir": "dist", - "removeComments": true, - "experimentalDecorators": true, - "target": "es6", - "emitDecoratorMetadata": true, - "moduleResolution": "node", - "resolveJsonModule": true, - "importHelpers": true, - "types": [ - "node", - ], - "typeRoots": [ - "node_modules/@types" - ], - "paths": { - "@src/*": [ "src/*" ], - } - }, + "extends": "./tsconfig.build.json", "include": [ - "./src/**/*.ts" + "./test/**/*.test.ts", ], - "exclude": [ - "./src/public/" - ] } \ No newline at end of file