diff --git a/jest.config.js b/jest.config.js index 252dcf2..3be977f 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,3 +1,6 @@ +const { createDefaultPreset } = require('ts-jest'); +const tsJestTransformCfg = createDefaultPreset().transform; + /** @type {import("jest").Config} **/ module.exports = { testEnvironment: 'node', diff --git a/package-lock.json b/package-lock.json index 8ff1a7f..b34e531 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "@aws-sdk/client-s3": "^3.1095.0", "@aws-sdk/s3-request-presigner": "^3.1095.0", "@stellar/stellar-sdk": "13.1.0", + "awilix": "^10.0.0", "axios": "^1.6.0", "bcryptjs": "2.4.3", "compression": "1.7.4", @@ -38,6 +39,8 @@ }, "devDependencies": { "@jest/globals": "^30.4.1", + "@stryker-mutator/core": "^7.3.0", + "@stryker-mutator/typescript-checker": "^7.3.0", "@types/axios": "^0.14.0", "@types/bcryptjs": "2.4.6", "@types/compression": "1.7.5", @@ -74,6 +77,20 @@ "typescript": "^5.2.2" } }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@apidevtools/json-schema-ref-parser": { "version": "14.0.1", "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-14.0.1.tgz", @@ -127,7 +144,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -515,7 +531,6 @@ "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", @@ -568,6 +583,19 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.29.7.tgz", + "integrity": "sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-compilation-targets": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", @@ -595,6 +623,38 @@ "semver": "bin/semver.js" } }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.29.7.tgz", + "integrity": "sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-member-expression-to-functions": "^7.29.7", + "@babel/helper-optimise-call-expression": "^7.29.7", + "@babel/helper-replace-supers": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", + "@babel/traverse": "^7.29.7", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/@babel/helper-globals": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", @@ -605,6 +665,20 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.29.7.tgz", + "integrity": "sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-module-imports": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", @@ -637,6 +711,19 @@ "@babel/core": "^7.0.0" } }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.29.7.tgz", + "integrity": "sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-plugin-utils": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", @@ -647,6 +734,38 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.29.7.tgz", + "integrity": "sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.29.7", + "@babel/helper-optimise-call-expression": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.29.7.tgz", + "integrity": "sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-string-parser": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", @@ -707,6 +826,24 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/plugin-proposal-decorators": { + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.23.9.tgz", + "integrity": "sha512-hJhBCb0+NnTWybvWq2WpbCYDOcflSbx0t+BYP65e5R9GVnukiDTi+on5bFkk4p7QGuv190H6KfNiV9Knf/3cZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.23.9", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-decorators": "^7.23.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-syntax-async-generators": { "version": "7.8.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", @@ -762,6 +899,22 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-syntax-decorators": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.29.7.tgz", + "integrity": "sha512-9MTTLbF39X6sqM92JPEsoI7++26hjZvzkxKZy64aMhWLH2mPkJ/Q3AV4QLmls3R14FpSpkOwQQfUh962JGQxxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-syntax-import-attributes": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.29.7.tgz", @@ -946,6 +1099,63 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.29.7.tgz", + "integrity": "sha512-j0vCldybPC5b5dwCQOJ21uKtHzt7hxLygJTg9eF1ScfaikEDNfzn94XoW5Fi+seBR0nCyL23xaBFFkq7dTM8XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.29.7.tgz", + "integrity": "sha512-jK52h8LaLc7JarhQV2ofeFMts4H7vnOXnqZNA6fYglBTZewRBE51KWt3BUltW1P+KoPsYkHoJeXePuz4zo2LMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", + "@babel/plugin-syntax-typescript": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-typescript": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.23.3.tgz", + "integrity": "sha512-17oIGVlqz6CchO9RFYn5U6ZpWRZIngayYCtrPRSgANSwC2V1Jb+iP74nVxzzXJte8b8BYxrL1yY96xfhTBrNNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-option": "^7.22.15", + "@babel/plugin-syntax-jsx": "^7.23.3", + "@babel/plugin-transform-modules-commonjs": "^7.23.3", + "@babel/plugin-transform-typescript": "^7.23.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/template": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", @@ -1045,6 +1255,29 @@ "kuler": "^2.0.0" } }, + "node_modules/@emnapi/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@emnapi/wasi-threads": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", @@ -1729,6 +1962,16 @@ "url": "https://opencollective.com/libvips" } }, + "node_modules/@inquirer/figures": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.15.tgz", + "integrity": "sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/@ioredis/commands": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-2.0.0.tgz", @@ -2290,6 +2533,19 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@ljharb/through": { + "version": "2.3.14", + "resolved": "https://registry.npmjs.org/@ljharb/through/-/through-2.3.14.tgz", + "integrity": "sha512-ajBvlKpWucBB17FuQYUShqpqy8GRgYEpJW0vWJbUu1CV9lWyrDCapy0lScU8T8Z6qn49sSwJB3+M+evYIdGg+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/@mongodb-js/saslprep": { "version": "1.4.11", "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.4.11.tgz", @@ -2336,7 +2592,6 @@ "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.5", @@ -2350,7 +2605,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, "license": "MIT", "engines": { "node": ">= 8" @@ -2360,7 +2614,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.5", @@ -2421,7 +2674,6 @@ "resolved": "https://registry.npmjs.org/@redis/client/-/client-6.2.1.tgz", "integrity": "sha512-LzxBY7SIBvvJiyCgcaJZZakE3fJrZZ++i24+EDW9fKpCl68D35uJcKFpZZwCfOoG9WZTbyZlMzMeM0gtOAMU9Q==", "license": "MIT", - "peer": true, "dependencies": { "cluster-key-slot": "1.1.2" }, @@ -2640,26 +2892,507 @@ "engines": { "node": ">=18.0.0" }, - "optionalDependencies": { - "sodium-native": "^4.3.3" + "optionalDependencies": { + "sodium-native": "^4.3.3" + } + }, + "node_modules/@stellar/stellar-sdk": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/@stellar/stellar-sdk/-/stellar-sdk-13.1.0.tgz", + "integrity": "sha512-ARQkUdyGefXdTgwSF0eONkzv/geAqUfyfheJ9Nthz6GAr5b41fNwWW9UtE8JpXC4IpvE3t5elIUN5hKJzASN9w==", + "license": "Apache-2.0", + "dependencies": { + "@stellar/stellar-base": "^13.0.1", + "axios": "^1.7.9", + "bignumber.js": "^9.1.2", + "eventsource": "^2.0.2", + "feaxios": "^0.0.23", + "randombytes": "^2.1.0", + "toml": "^3.0.0", + "urijs": "^1.19.1" + } + }, + "node_modules/@stryker-mutator/api": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/@stryker-mutator/api/-/api-7.3.0.tgz", + "integrity": "sha512-0tiQF0E38ypgg2fb2a4wbr2wpu4ugY7HwwsgrI9NttY1EojOS0BtaKHo1DIrj5SVMRXq0kaMgl5h2ohSuysvRA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "mutation-testing-metrics": "2.0.3", + "mutation-testing-report-schema": "2.0.3", + "tslib": "~2.6.0", + "typed-inject": "~4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@stryker-mutator/api/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@stryker-mutator/core": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/@stryker-mutator/core/-/core-7.3.0.tgz", + "integrity": "sha512-O9m2jEnJXbKBlj27/ps9nGCpm0HtQC0YlNV/aenocmERnySnvqEM6bwxvQ4apK5bad8ZyGJyhDIyJrwoVGmfVQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@stryker-mutator/api": "7.3.0", + "@stryker-mutator/instrumenter": "7.3.0", + "@stryker-mutator/util": "7.3.0", + "ajv": "~8.12.0", + "chalk": "~5.3.0", + "commander": "~11.1.0", + "diff-match-patch": "1.0.5", + "emoji-regex": "~10.2.1", + "execa": "~8.0.0", + "file-url": "~4.0.0", + "get-port": "~7.0.0", + "glob": "~10.3.0", + "inquirer": "~9.2.0", + "lodash.groupby": "~4.6.0", + "log4js": "~6.9.0", + "minimatch": "~9.0.1", + "mutation-testing-elements": "2.0.3", + "mutation-testing-metrics": "2.0.3", + "mutation-testing-report-schema": "2.0.3", + "npm-run-path": "~5.1.0", + "progress": "~2.0.0", + "rxjs": "~7.8.0", + "semver": "^7.3.5", + "source-map": "~0.7.3", + "tree-kill": "~1.2.2", + "tslib": "2.6.2", + "typed-inject": "~4.0.0", + "typed-rest-client": "~1.8.0" + }, + "bin": { + "stryker": "bin/stryker.js" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@stryker-mutator/core/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@stryker-mutator/core/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@stryker-mutator/core/node_modules/brace-expansion": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz", + "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@stryker-mutator/core/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@stryker-mutator/core/node_modules/emoji-regex": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.2.1.tgz", + "integrity": "sha512-97g6QgOk8zlDRdgq1WxwgTMgEWGVAQvB5Fdpgc1MkNy56la5SKP9GsMXKDOdqwn90/41a8yPwIGk1Y6WVbeMQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@stryker-mutator/core/node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/@stryker-mutator/core/node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@stryker-mutator/core/node_modules/glob": { + "version": "10.3.16", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.16.tgz", + "integrity": "sha512-JDKXl1DiuuHJ6fVS2FXjownaavciiHNUU4mOvV/B793RLh05vZL1rcPnCSaOgv1hDT6RDlY7AB7ZUvFYAtPgAw==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.1", + "minipass": "^7.0.4", + "path-scurry": "^1.11.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@stryker-mutator/core/node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/@stryker-mutator/core/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@stryker-mutator/core/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/@stryker-mutator/core/node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@stryker-mutator/core/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@stryker-mutator/core/node_modules/npm-run-path": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", + "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@stryker-mutator/core/node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@stryker-mutator/core/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@stryker-mutator/core/node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, + "node_modules/@stryker-mutator/core/node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@stryker-mutator/core/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@stryker-mutator/instrumenter": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/@stryker-mutator/instrumenter/-/instrumenter-7.3.0.tgz", + "integrity": "sha512-RdfQF08GclNdKldG3rH9YztapPhfTYsc90p8Tev+b6yZJSpk1j8mKZRMjxk/mylDtXFZZ2IVhI9txAt2YYT+OQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@babel/core": "~7.23.0", + "@babel/generator": "~7.23.0", + "@babel/parser": "~7.23.0", + "@babel/plugin-proposal-decorators": "~7.23.0", + "@babel/preset-typescript": "~7.23.0", + "@stryker-mutator/api": "7.3.0", + "@stryker-mutator/util": "7.3.0", + "angular-html-parser": "~4.0.0", + "weapon-regex": "~1.1.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@stryker-mutator/instrumenter/node_modules/@babel/core": { + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.9.tgz", + "integrity": "sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.23.9", + "@babel/parser": "^7.23.9", + "@babel/template": "^7.23.9", + "@babel/traverse": "^7.23.9", + "@babel/types": "^7.23.9", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@stryker-mutator/instrumenter/node_modules/@babel/generator": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", + "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.23.6", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@stryker-mutator/instrumenter/node_modules/@babel/parser": { + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.9.tgz", + "integrity": "sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA==", + "dev": true, + "license": "MIT", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@stryker-mutator/instrumenter/node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@stryker-mutator/instrumenter/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@stryker-mutator/typescript-checker": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/@stryker-mutator/typescript-checker/-/typescript-checker-7.3.0.tgz", + "integrity": "sha512-WNYeDRJNeyA6r6eSQLO9HPd0t88QD77Nv422EGzInGT9DIgpRffH46JijFsdcLZtzAq1OACFZZ25bgdHTEjCrQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@stryker-mutator/api": "7.3.0", + "@stryker-mutator/util": "7.3.0", + "semver": "~7.5.0" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@stryker-mutator/core": "~7.3.0", + "typescript": ">=3.6" + } + }, + "node_modules/@stryker-mutator/typescript-checker/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" } }, - "node_modules/@stellar/stellar-sdk": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/@stellar/stellar-sdk/-/stellar-sdk-13.1.0.tgz", - "integrity": "sha512-ARQkUdyGefXdTgwSF0eONkzv/geAqUfyfheJ9Nthz6GAr5b41fNwWW9UtE8JpXC4IpvE3t5elIUN5hKJzASN9w==", - "license": "Apache-2.0", + "node_modules/@stryker-mutator/typescript-checker/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "license": "ISC", "dependencies": { - "@stellar/stellar-base": "^13.0.1", - "axios": "^1.7.9", - "bignumber.js": "^9.1.2", - "eventsource": "^2.0.2", - "feaxios": "^0.0.23", - "randombytes": "^2.1.0", - "toml": "^3.0.0", - "urijs": "^1.19.1" + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, + "node_modules/@stryker-mutator/typescript-checker/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/@stryker-mutator/util": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/@stryker-mutator/util/-/util-7.3.0.tgz", + "integrity": "sha512-bdFvuw7F3LC05dOFqgGjuipLt8ng5uXyjjdKeqqeTowm1wAyeDt0GTQKBuiINSAtcZxN75wTXq4DsCZXb/LMjw==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/@tsconfig/node10": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz", @@ -2962,7 +3695,6 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz", "integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==", "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -3190,7 +3922,6 @@ "integrity": "sha512-MUkcC+7Wt/QOGeVlM8aGGJZy1XV5YKjTpq9jK6r6/iLsGXhBVaGP5N0UYvFsu9BFlSpwY9kMretzdBH01rkRXg==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "6.13.2", "@typescript-eslint/types": "6.13.2", @@ -3685,7 +4416,6 @@ "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3743,6 +4473,19 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/angular-html-parser": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/angular-html-parser/-/angular-html-parser-4.0.1.tgz", + "integrity": "sha512-x9SLf2jNNh3nG+haVIwKX/GVW8PcvSRmkeT9WqTDYSAVuwT9IzwEyVm09FCZpOo/dtFRxE9vaNXqcAf/MIxphg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -3924,6 +4667,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/awilix": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/awilix/-/awilix-10.0.2.tgz", + "integrity": "sha512-hFatb7eZFdtiWjjmGRSm/K/uxZpmcBlM+YoeMB3VpOPXk3xa6+7zctg3LRbUzoimom5bwGrePF0jXReO6b4zNQ==", + "license": "MIT", + "dependencies": { + "camel-case": "^4.1.2", + "fast-glob": "^3.3.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/axios": { "version": "1.18.1", "resolved": "https://registry.npmjs.org/axios/-/axios-1.18.1.tgz", @@ -4109,7 +4865,6 @@ "integrity": "sha512-Z0oHEHAFDZkffN8Qc39zNZjQlMDkPJRyyyZieU1VH7u8c5S+qHZ2S8ixdKIAxEjfHO7FJxXmJWgteOghVanIsg==", "dev": true, "license": "Apache-2.0", - "peer": true, "peerDependencies": { "bare-abort-controller": "*" }, @@ -4166,7 +4921,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.1.1.tgz", "integrity": "sha512-JprUlveX3QjApC1cTpsUOiscADftCGVWkzitbHsRqv84hzYwYHw2mbluddsq5TvI8mH/8Ov1f4BiMAdcB0oYnQ==", - "devOptional": true, + "dev": true, "license": "Apache-2.0" }, "node_modules/bare-semver": { @@ -4208,9 +4963,8 @@ "version": "2.4.6", "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.4.6.tgz", "integrity": "sha512-iQxPClE07hETVpbRoX7JXX3v/ZQViCxe/SYCxylRLzdEx1xJAufPptfiOqR8tqiCtmbtMDANKWszzjLu1PMAZQ==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "bare-path": "^3.0.0" } @@ -4294,6 +5048,43 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bl/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, "node_modules/body-parser": { "version": "1.20.5", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz", @@ -4355,7 +5146,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, "license": "MIT", "dependencies": { "fill-range": "^7.1.1" @@ -4384,7 +5174,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.10.44", "caniuse-lite": "^1.0.30001806", @@ -4560,6 +5349,16 @@ "node": ">=6" } }, + "node_modules/camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "license": "MIT", + "dependencies": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, "node_modules/camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", @@ -4618,6 +5417,13 @@ "node": ">=10" } }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true, + "license": "MIT" + }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -4695,6 +5501,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/cli-truncate": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", @@ -4766,6 +5585,16 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 12" + } + }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -4831,6 +5660,16 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, "node_modules/cluster-key-slot": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.1.tgz", @@ -5120,6 +5959,16 @@ "node": ">= 8" } }, + "node_modules/date-format": { + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz", + "integrity": "sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -5169,6 +6018,19 @@ "node": ">=0.10.0" } }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -5263,6 +6125,13 @@ "node": ">=0.3.1" } }, + "node_modules/diff-match-patch": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz", + "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -5538,7 +6407,6 @@ "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -5595,7 +6463,6 @@ "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", - "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -5874,7 +6741,6 @@ "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz", "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==", "license": "MIT", - "peer": true, "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", @@ -5943,6 +6809,21 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "license": "MIT", + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -5967,7 +6848,6 @@ "version": "3.3.3", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -5984,7 +6864,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, "license": "ISC", "dependencies": { "is-glob": "^4.0.1" @@ -6034,7 +6913,6 @@ "version": "1.20.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", - "dev": true, "license": "ISC", "dependencies": { "reusify": "^1.0.4" @@ -6088,11 +6966,23 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/file-url": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/file-url/-/file-url-4.0.0.tgz", + "integrity": "sha512-vRCdScQ6j3Ku6Kd7W1kZk9c++5SqD6Xz5Jotrjr/nkY714M14RFHy/AAVA2WQvpsqVAVgTbDrYyBpU205F0cLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" @@ -6326,6 +7216,21 @@ "node": ">= 0.6" } }, + "node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -6424,6 +7329,19 @@ "node": ">=8.0.0" } }, + "node_modules/get-port": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-7.0.0.tgz", + "integrity": "sha512-mDHFgApoQd+azgMdwylJrv2DX47ywGq1i5VFJE7fZ0dttNq3iQMfsU4IvEgBHojA3KqEudyu7Vq+oN8kNaNkWw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", @@ -6832,34 +7750,155 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/inquirer": { + "version": "9.2.23", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.2.23.tgz", + "integrity": "sha512-kod5s+FBPIDM2xiy9fu+6wdU/SkK5le5GS9lh4FEBjBHqiMgD9lLFbCbuqFNAjNL2ZOy9Wd9F694IOzN9pZHBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/figures": "^1.0.3", + "@ljharb/through": "^2.3.13", + "ansi-escapes": "^4.3.2", + "chalk": "^5.3.0", + "cli-cursor": "^3.1.0", + "cli-width": "^4.1.0", + "external-editor": "^3.1.0", + "lodash": "^4.17.21", + "mute-stream": "1.0.0", + "ora": "^5.4.1", + "run-async": "^3.0.0", + "rxjs": "^7.8.1", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^6.2.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/inquirer/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/inquirer/node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/inquirer/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/inquirer/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/inquirer/node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/inquirer/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/inquirer/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, "engines": { - "node": ">=0.8.19" + "node": ">=8" } }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "node_modules/inquirer/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "once": "^1.3.0", - "wrappy": "1" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, "node_modules/ioredis": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-6.0.0.tgz", @@ -6935,7 +7974,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -6968,7 +8006,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" @@ -6977,11 +8014,20 @@ "node": ">=0.10.0" } }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.12.0" @@ -7036,6 +8082,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/isarray": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", @@ -7141,7 +8200,6 @@ "integrity": "sha512-Yi1jqNC/Oq0N4hBgNH/YvBpP1P57QqundgytzYqy3yqAa7NZPNjSoi4SGbRAXDMdBzNE6xBCi5U7RgfrvMEUVQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/core": "30.4.2", "@jest/types": "30.4.1", @@ -7799,6 +8857,16 @@ "node": ">=6" } }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, "node_modules/jsonwebtoken": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", @@ -8251,6 +9319,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.groupby": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.groupby/-/lodash.groupby-4.6.0.tgz", + "integrity": "sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", @@ -8313,6 +9395,23 @@ "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", "license": "MIT" }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/log-update": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", @@ -8467,6 +9566,23 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/log4js": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.9.1.tgz", + "integrity": "sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "date-format": "^4.0.14", + "debug": "^4.3.4", + "flatted": "^3.2.7", + "rfdc": "^1.3.0", + "streamroller": "^3.1.5" + }, + "engines": { + "node": ">=8.0" + } + }, "node_modules/logform": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", @@ -8484,6 +9600,15 @@ "node": ">= 12.0.0" } }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.3" + } + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -8572,7 +9697,6 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 8" @@ -8591,7 +9715,6 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, "license": "MIT", "dependencies": { "braces": "^3.0.3", @@ -8605,7 +9728,6 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", - "dev": true, "license": "MIT", "engines": { "node": ">=8.6" @@ -8884,6 +10006,40 @@ "url": "https://opencollective.com/express" } }, + "node_modules/mutation-testing-elements": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/mutation-testing-elements/-/mutation-testing-elements-2.0.3.tgz", + "integrity": "sha512-V00F5dVriVZTPoDcflX2Lp+/cA1LrkX9RwPntrrAEmM8OLEUG+jSZIJeYImTGK/opW5yD+q9ugykVjHbw2KQTg==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/mutation-testing-metrics": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/mutation-testing-metrics/-/mutation-testing-metrics-2.0.3.tgz", + "integrity": "sha512-pvrrE8Qf5xuimkm+TYUwX3g6Op6K4jE2/tD4NX8UZdTzuT/NHwAJw/YUXI7UJSA9M9Jpz9+VCjB31YnAX6wm7Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "mutation-testing-report-schema": "2.0.3" + } + }, + "node_modules/mutation-testing-report-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/mutation-testing-report-schema/-/mutation-testing-report-schema-2.0.3.tgz", + "integrity": "sha512-+x6ssyq4xVkUyHbbbbiU1pCla7QHO/VRaxfHsOb4JGCw+56EtCJ4w4wQuQ24J5DYTRCAZ5y2oBk7DwP8UXWbwg==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/mute-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", + "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/napi-postinstall": { "version": "0.3.4", "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz", @@ -8936,6 +10092,16 @@ "node": ">=12.22.0" } }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "license": "MIT", + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, "node_modules/node-abort-controller": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", @@ -9133,13 +10299,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/openapi-types": { - "version": "12.1.3", - "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz", - "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==", - "license": "MIT", - "peer": true - }, "node_modules/opossum": { "version": "8.1.2", "resolved": "https://registry.npmjs.org/opossum/-/opossum-8.1.2.tgz", @@ -9167,6 +10326,74 @@ "node": ">= 0.8.0" } }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ora/node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ora/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -9256,6 +10483,16 @@ "node": ">= 0.8" } }, + "node_modules/pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -9469,7 +10706,6 @@ "integrity": "sha512-22UbSzg8luF4UuZtzgiUOfcGM8s4tjBv6dJRT7j275NXsy2jb4aJa4NNveul5x4eqlF1wuhuR2RElK71RvmVaw==", "dev": true, "license": "MIT", - "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -9522,6 +10758,16 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -9597,7 +10843,6 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, "funding": [ { "type": "github", @@ -9842,7 +11087,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "dev": true, "license": "MIT", "engines": { "iojs": ">=1.0.0", @@ -9926,11 +11170,20 @@ "node": "*" } }, + "node_modules/run-async": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz", + "integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, "funding": [ { "type": "github", @@ -9950,6 +11203,16 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -10505,6 +11768,21 @@ "node": ">= 0.8" } }, + "node_modules/streamroller": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.5.tgz", + "integrity": "sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "date-format": "^4.0.14", + "debug": "^4.3.4", + "fs-extra": "^8.1.0" + }, + "engines": { + "node": ">=8.0" + } + }, "node_modules/streamsearch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", @@ -11035,6 +12313,19 @@ "dev": true, "license": "MIT" }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -11060,7 +12351,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, "license": "MIT", "dependencies": { "is-number": "^7.0.0" @@ -11106,6 +12396,16 @@ "node": ">=12" } }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, "node_modules/triple-beam": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", @@ -11200,7 +12500,6 @@ "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -11245,6 +12544,16 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, + "node_modules/tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.11 <=0.7.0 || >=0.7.3" + } + }, "node_modules/tweetnacl": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", @@ -11314,6 +12623,28 @@ "node": ">= 0.4" } }, + "node_modules/typed-inject": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/typed-inject/-/typed-inject-4.0.0.tgz", + "integrity": "sha512-OuBL3G8CJlS/kjbGV/cN8Ni32+ktyyi6ADDZpKvksbX0fYBV5WcukhRCYa7WqLce7dY/Br2dwtmJ9diiadLFpg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=16" + } + }, + "node_modules/typed-rest-client": { + "version": "1.8.11", + "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.8.11.tgz", + "integrity": "sha512-5UvfMpd1oelmUPRbbaVnq+rHP7ng2cE4qoQkQeAqxRL6PklkxsM0g32/HL0yfvruK6ojQ5x8EE+HF4YV6DtuCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "qs": "^6.9.1", + "tunnel": "0.0.6", + "underscore": "^1.12.1" + } + }, "node_modules/typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", @@ -11326,7 +12657,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -11356,12 +12686,29 @@ "dev": true, "license": "MIT" }, + "node_modules/underscore": { + "version": "1.13.8", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.8.tgz", + "integrity": "sha512-DXtD3ZtEQzc7M8m4cXotyHR+FAS18C64asBYY5vqZexfYryNNnDc02W4hKg3rdQuqOYas1jkseX0+nZXjTXnvQ==", + "dev": true, + "license": "MIT" + }, "node_modules/undici-types": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "license": "MIT" }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -11526,6 +12873,23 @@ "makeerror": "1.0.12" } }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/weapon-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/weapon-regex/-/weapon-regex-1.1.1.tgz", + "integrity": "sha512-b0RmqduiSUKyKFamrpU+UK78Jm65/6CgKq1zoWFaS9PM7vwNK4RWrjmX1jREs3pLmG7botsgMLVOltxDR7RGRw==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", diff --git a/package.json b/package.json index 4091527..605728e 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "@aws-sdk/client-s3": "^3.1095.0", "@aws-sdk/s3-request-presigner": "^3.1095.0", "@stellar/stellar-sdk": "13.1.0", - "awilix": "^10.2.2", + "awilix": "^10.0.0", "axios": "^1.6.0", "bcryptjs": "2.4.3", "compression": "1.7.4", @@ -46,8 +46,8 @@ }, "devDependencies": { "@jest/globals": "^30.4.1", - "@stryker-mutator/core": "^7.3.1", - "@stryker-mutator/typescript-checker": "^7.3.1", + "@stryker-mutator/core": "^7.3.0", + "@stryker-mutator/typescript-checker": "^7.3.0", "@types/axios": "^0.14.0", "@types/bcryptjs": "2.4.6", "@types/compression": "1.7.5", diff --git a/src/config/redis.ts b/src/config/redis.ts index 7ce80d4..53612c0 100644 --- a/src/config/redis.ts +++ b/src/config/redis.ts @@ -17,6 +17,13 @@ export const redisClient = new Redis(env.REDIS_URL, { lazyConnect: true, }); +/** + * Helper to safely get the active Redis client. + */ +export function getRedisClient(): Redis | null { + return redisClient; +} + /** * Redlock instance for distributed lock management across multiple Redis nodes. * Currently configured with a single Redis instance, but can be extended to diff --git a/src/controllers/dashboardController.ts b/src/controllers/dashboardController.ts new file mode 100644 index 0000000..9be1570 --- /dev/null +++ b/src/controllers/dashboardController.ts @@ -0,0 +1,58 @@ +import { Request, Response, NextFunction } from 'express'; +import { StatusCodes } from 'http-status-codes'; +import { dashboardService, DashboardService } from '../services/dashboardService'; +import type { IUser } from '../interfaces/IUser'; +import AppError from '../utils/AppError'; +import { sendSuccess } from '../utils/responseWrapper'; + +export class DashboardController { + private readonly service: DashboardService; + + constructor(service: DashboardService = dashboardService) { + this.service = service; + } + + /** + * GET /api/v1/admin/dashboard + * + * Retrieves aggregated system metrics for the admin dashboard: + * - Active deliveries (total + breakdown by status) + * - Online drivers (total active accounts + recently active locations) + * - Total escrow volume (total, locked, released, refunded) + * - Soroban RPC connectivity info + * + * Protected by `authenticate` + `requireRole(UserRole.ADMIN)`. + * Query params: + * - refresh {boolean} optional: set to true to force bypass of cache. + */ + public getDashboardMetrics = async ( + req: Request, + res: Response, + next: NextFunction, + ): Promise => { + try { + const adminUser = (req as Request & { user?: IUser }).user; + + if (!adminUser) { + throw new AppError('Authentication required.', StatusCodes.UNAUTHORIZED); + } + + const forceRefresh = req.query.refresh === 'true' || req.query.refresh === '1'; + + const metrics = await this.service.getAdminDashboardMetrics({ forceRefresh }); + + sendSuccess( + res, + metrics, + 'Admin dashboard metrics retrieved successfully', + StatusCodes.OK, + ); + } catch (error) { + next(error); + } + }; +} + +export const dashboardController = new DashboardController(); +export const getDashboardMetrics = dashboardController.getDashboardMetrics; +export default dashboardController; diff --git a/src/di/container.ts b/src/di/container.ts index d838049..9e0a4e1 100644 --- a/src/di/container.ts +++ b/src/di/container.ts @@ -45,6 +45,7 @@ import { fleetService } from '../services/fleetService'; import escrowService from '../services/escrowService'; import { disputeService } from '../services/disputeService'; import adminService from '../services/adminService'; +import dashboardService from '../services/dashboardService'; import eventLogService from '../services/eventLogService'; import profilePictureService from '../services/profilePicture.service'; import storageService from '../services/storage.service'; @@ -72,6 +73,7 @@ import { escrowController } from '../controllers/escrow.controller'; import escrowControllerInstance from '../controllers/escrowController'; import { disputeController } from '../controllers/disputeController'; import { adminController } from '../controllers/adminController'; +import dashboardController from '../controllers/dashboardController'; import { eventLogController } from '../controllers/eventLogController'; import { profileController } from '../controllers/profileController'; import { uploadController } from '../controllers/uploadController'; @@ -81,7 +83,6 @@ import circuitBreakerController from '../controllers/circuitBreakerController'; import indexerController from '../controllers/indexerController'; import { indexerController as indexerController2 } from '../controllers/indexer.controller'; import monitorController from '../controllers/monitorController'; -import stellarController from '../controllers/stellarController'; import { stellarController as stellarController2 } from '../controllers/stellar.controller'; import { TOKENS } from './tokens'; @@ -133,6 +134,7 @@ export function createDIContainer(): AwilixContainer { [TOKENS.escrow_service]: asValue(escrowService), // Alternate name [TOKENS.disputeService]: asValue(disputeService), [TOKENS.adminService]: asValue(adminService), + [TOKENS.dashboardService]: asValue(dashboardService), [TOKENS.eventLogService]: asValue(eventLogService), [TOKENS.profilePictureService]: asValue(profilePictureService), [TOKENS.storageService]: asValue(storageService), @@ -166,6 +168,7 @@ export function createDIContainer(): AwilixContainer { [TOKENS.escrow_controller]: asValue(escrowController), [TOKENS.disputeController]: asValue(disputeController), [TOKENS.adminController]: asValue(adminController), + [TOKENS.dashboardController]: asValue(dashboardController), [TOKENS.eventLogController]: asValue(eventLogController), [TOKENS.profileController]: asValue(profileController), [TOKENS.uploadController]: asValue(uploadController), @@ -175,7 +178,7 @@ export function createDIContainer(): AwilixContainer { [TOKENS.indexerController]: asValue(indexerController), [TOKENS.indexer_controller]: asValue(indexerController2), [TOKENS.monitorController]: asValue(monitorController), - [TOKENS.stellarController]: asValue(stellarController), + [TOKENS.stellarController]: asValue(stellarController2), [TOKENS.stellar_controller]: asValue(stellarController2), }); diff --git a/src/di/tokens.ts b/src/di/tokens.ts index aa9f858..159c5a7 100644 --- a/src/di/tokens.ts +++ b/src/di/tokens.ts @@ -16,6 +16,7 @@ export const TOKENS = { escrow_service: 'escrow_service', // Alternate export (escrow.service.ts) disputeService: 'disputeService', adminService: 'adminService', + dashboardService: 'dashboardService', eventLogService: 'eventLogService', profilePictureService: 'profilePictureService', storageService: 'storageService', @@ -63,6 +64,7 @@ export const TOKENS = { escrow_controller: 'escrow_controller', // Alternate export (escrow.controller.ts) disputeController: 'disputeController', adminController: 'adminController', + dashboardController: 'dashboardController', eventLogController: 'eventLogController', profileController: 'profileController', uploadController: 'uploadController', diff --git a/src/routes/adminRoutes.ts b/src/routes/adminRoutes.ts index 6b682cf..274708a 100644 --- a/src/routes/adminRoutes.ts +++ b/src/routes/adminRoutes.ts @@ -2,7 +2,7 @@ import { Router } from 'express'; import authenticate from '../middleware/authenticate'; import requireRole from '../middleware/requireRole'; import { suspendUser, getDisputes } from '../controllers/adminController'; -import { dlqController } from '../controllers/dlqController'; +import { getDashboardMetrics } from '../controllers/dashboardController'; import { UserRole } from '../interfaces/IUser'; const router = Router(); @@ -11,6 +11,85 @@ const router = Router(); router.use(authenticate); router.use(requireRole(UserRole.ADMIN)); +/** + * @openapi + * /v1/admin/dashboard: + * get: + * tags: [Admin] + * summary: Retrieve real-time admin dashboard system metrics + * description: > + * Admin-only. Aggregates active deliveries, online drivers, total escrow volume, + * and Soroban RPC status. Results are cached in Redis to minimize database load. + * security: + * - bearerAuth: [] + * parameters: + * - in: query + * name: refresh + * schema: + * type: boolean + * description: Set to true to bypass cache and force fresh aggregation + * responses: + * 200: + * description: Successfully retrieved system metrics + * content: + * application/json: + * schema: + * type: object + * properties: + * status: + * type: string + * example: success + * message: + * type: string + * example: Admin dashboard metrics retrieved successfully + * data: + * type: object + * properties: + * activeDeliveries: + * type: object + * properties: + * total: + * type: integer + * byStatus: + * type: object + * onlineDrivers: + * type: object + * properties: + * totalActiveDrivers: + * type: integer + * recentlyActiveDrivers: + * type: integer + * escrow: + * type: object + * properties: + * totalVolume: + * type: number + * lockedVolume: + * type: number + * releasedVolume: + * type: number + * refundedVolume: + * type: number + * activeCount: + * type: integer + * totalCount: + * type: integer + * metadata: + * type: object + * properties: + * timestamp: + * type: string + * cached: + * type: boolean + * cacheTtlSeconds: + * type: integer + * 401: + * $ref: '#/components/responses/Unauthorized' + * 403: + * description: Requester is not an admin + */ +router.get('/dashboard', getDashboardMetrics); + /** * @openapi * /v1/admin/disputes: diff --git a/src/routes/index.ts b/src/routes/index.ts index f60bf81..1023ec3 100644 --- a/src/routes/index.ts +++ b/src/routes/index.ts @@ -1,5 +1,6 @@ import { Router } from 'express'; import authRoutes from './authRoutes'; +import bulkDeliveryRoutes from './bulkDeliveryRoutes'; import deliveryCrudRoutes from './delivery.routes'; import deliveryEtaRoutes from './deliveryRoutes'; import deliveryStatusRoutes from './deliveries'; @@ -9,6 +10,7 @@ import fleetRoutes from './fleetRoutes'; import disputeRoutes from './disputeRoutes'; import eventLogRoutes from './eventLogRoutes'; import profileRoutes from './profileRoutes'; +import notificationRoutes from './notificationRoutes'; import healthRoutes from './healthRoutes'; import userRoutes from './userRoutes'; import socketMetricsRoutes from './socketMetricsRoutes'; diff --git a/src/services/dashboardService.ts b/src/services/dashboardService.ts new file mode 100644 index 0000000..a249c7a --- /dev/null +++ b/src/services/dashboardService.ts @@ -0,0 +1,276 @@ +import { StatusCodes } from 'http-status-codes'; +import Delivery, { DeliveryStatus } from '../models/Delivery'; +import User from '../models/User'; +import Escrow, { EscrowStatus } from '../models/Escrow'; +import { LocationUpdate } from '../models/LocationUpdate'; +import { UserRole, UserStatus } from '../interfaces/IUser'; +import { getRedisClient } from '../config/redis'; +import { sorobanService } from '../blockchain/soroban.service'; +import logger from '../config/logger'; + +export interface ActiveDeliveriesMetrics { + total: number; + byStatus: { + pending: number; + funded: number; + assigned: number; + in_progress: number; + }; +} + +export interface OnlineDriversMetrics { + totalActiveDrivers: number; + recentlyActiveDrivers: number; +} + +export interface EscrowMetrics { + totalVolume: number; + lockedVolume: number; + releasedVolume: number; + refundedVolume: number; + activeCount: number; + totalCount: number; +} + +export interface AdminDashboardMetrics { + activeDeliveries: ActiveDeliveriesMetrics; + onlineDrivers: OnlineDriversMetrics; + escrow: EscrowMetrics; + sorobanRpc?: { + connected: boolean; + latestLedger?: number; + network?: string; + }; + metadata: { + timestamp: string; + cached: boolean; + cacheTtlSeconds: number; + }; +} + +export interface GetDashboardMetricsOptions { + forceRefresh?: boolean; +} + +export class DashboardService { + private readonly CACHE_KEY = 'admin:dashboard:metrics'; + + private getCacheTtl(): number { + const parsed = parseInt(process.env.ADMIN_DASHBOARD_CACHE_TTL_SECONDS ?? '60', 10); + return Number.isFinite(parsed) && parsed > 0 ? parsed : 60; + } + + /** + * Aggregate active deliveries, online drivers, and total escrow volume metrics. + * Leverages Redis caching to reduce database load. + */ + public async getAdminDashboardMetrics( + options: GetDashboardMetricsOptions = {}, + ): Promise { + const ttlSeconds = this.getCacheTtl(); + const redis = getRedisClient(); + + // 1. Try reading from cache unless forceRefresh is true + if (!options.forceRefresh && redis) { + try { + const cachedRaw = await redis.get(this.CACHE_KEY); + if (cachedRaw) { + const cachedMetrics = JSON.parse(cachedRaw) as AdminDashboardMetrics; + logger.debug('[DashboardService] Cache hit for admin metrics'); + return { + ...cachedMetrics, + metadata: { + ...cachedMetrics.metadata, + cached: true, + }, + }; + } + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + logger.warn(`[DashboardService] Cache read error: ${message}`); + } + } + + // 2. Aggregate metrics from database and blockchain in parallel + const [activeDeliveries, onlineDrivers, escrow, sorobanInfo] = await Promise.all([ + this.getActiveDeliveriesMetrics(), + this.getOnlineDriversMetrics(), + this.getEscrowMetrics(), + this.getSorobanInfo(), + ]); + + const metrics: AdminDashboardMetrics = { + activeDeliveries, + onlineDrivers, + escrow, + sorobanRpc: sorobanInfo, + metadata: { + timestamp: new Date().toISOString(), + cached: false, + cacheTtlSeconds: ttlSeconds, + }, + }; + + // 3. Store aggregated metrics in cache + if (redis) { + try { + await redis.set(this.CACHE_KEY, JSON.stringify(metrics), 'EX', ttlSeconds); + logger.debug(`[DashboardService] Cached admin metrics with TTL=${ttlSeconds}s`); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + logger.warn(`[DashboardService] Cache write error: ${message}`); + } + } + + return metrics; + } + + /** + * Aggregate active delivery metrics from database. + */ + private async getActiveDeliveriesMetrics(): Promise { + const activeStatuses = [ + DeliveryStatus.PENDING, + DeliveryStatus.FUNDED, + DeliveryStatus.ASSIGNED, + DeliveryStatus.IN_PROGRESS, + ]; + + const counts = await Delivery.aggregate<{ _id: string; count: number }>([ + { + $match: { + status: { $in: activeStatuses }, + isDeleted: { $ne: true }, + }, + }, + { + $group: { + _id: '$status', + count: { $sum: 1 }, + }, + }, + ]); + + const byStatus = { + pending: 0, + funded: 0, + assigned: 0, + in_progress: 0, + }; + + let total = 0; + for (const item of counts) { + if (item._id in byStatus) { + byStatus[item._id as keyof typeof byStatus] = item.count; + } + total += item.count; + } + + return { + total, + byStatus, + }; + } + + /** + * Aggregate driver counts and recently online drivers from database. + */ + private async getOnlineDriversMetrics(): Promise { + const fifteenMinutesAgo = new Date(Date.now() - 15 * 60 * 1000); + + const [totalActiveDrivers, recentlyActiveDriversList] = await Promise.all([ + User.countDocuments({ + role: UserRole.DRIVER, + status: UserStatus.ACTIVE, + isActive: true, + isDeleted: { $ne: true }, + }), + LocationUpdate.distinct('driverId', { + capturedAt: { $gte: fifteenMinutesAgo }, + }), + ]); + + return { + totalActiveDrivers, + recentlyActiveDrivers: Array.isArray(recentlyActiveDriversList) + ? recentlyActiveDriversList.length + : 0, + }; + } + + /** + * Aggregate total escrow volume and lock state metrics from database. + */ + private async getEscrowMetrics(): Promise { + const aggregationResult = await Escrow.aggregate<{ + _id: string; + totalVolume: number; + count: number; + }>([ + { + $group: { + _id: '$status', + totalVolume: { $sum: '$amount' }, + count: { $sum: 1 }, + }, + }, + ]); + + let totalVolume = 0; + let lockedVolume = 0; + let releasedVolume = 0; + let refundedVolume = 0; + let activeCount = 0; + let totalCount = 0; + + for (const item of aggregationResult) { + totalCount += item.count; + totalVolume += item.totalVolume; + + if (item._id === EscrowStatus.LOCKED || item._id === EscrowStatus.DISPUTED) { + lockedVolume += item.totalVolume; + activeCount += item.count; + } else if (item._id === EscrowStatus.RELEASED) { + releasedVolume += item.totalVolume; + } else if (item._id === EscrowStatus.REFUNDED) { + refundedVolume += item.totalVolume; + } + } + + return { + totalVolume: Math.round(totalVolume * 100) / 100, + lockedVolume: Math.round(lockedVolume * 100) / 100, + releasedVolume: Math.round(releasedVolume * 100) / 100, + refundedVolume: Math.round(refundedVolume * 100) / 100, + activeCount, + totalCount, + }; + } + + /** + * Fetch Soroban connectivity status resiliently. + */ + private async getSorobanInfo(): Promise { + try { + const conn = await sorobanService.checkConnectivity(); + if (conn.connected) { + return { + connected: true, + latestLedger: conn.latestLedger, + network: conn.network, + }; + } + return { + connected: false, + network: conn.network, + }; + } catch { + return { + connected: false, + }; + } + } +} + +export const dashboardService = new DashboardService(); +export default dashboardService; diff --git a/tests/dashboard.test.ts b/tests/dashboard.test.ts new file mode 100644 index 0000000..487f259 --- /dev/null +++ b/tests/dashboard.test.ts @@ -0,0 +1,163 @@ +import { StatusCodes } from 'http-status-codes'; +import { dashboardService } from '../src/services/dashboardService'; +import { dashboardController } from '../src/controllers/dashboardController'; +import Delivery, { DeliveryStatus } from '../src/models/Delivery'; +import User from '../src/models/User'; +import Escrow, { EscrowStatus } from '../src/models/Escrow'; +import { LocationUpdate } from '../src/models/LocationUpdate'; +import { UserRole } from '../src/interfaces/IUser'; +import { getRedisClient } from '../src/config/redis'; + +jest.mock('../src/models/Delivery'); +jest.mock('../src/models/User'); +jest.mock('../src/models/Escrow'); +jest.mock('../src/models/LocationUpdate'); +jest.mock('../src/config/redis', () => ({ + getRedisClient: jest.fn(), + redisClient: null, +})); +jest.mock('../src/blockchain/soroban.service', () => ({ + sorobanService: { + checkConnectivity: jest.fn().mockResolvedValue({ + connected: true, + network: 'testnet', + latestLedger: 123456, + }), + }, +})); + +describe('Admin Dashboard Metrics API', () => { + beforeEach(() => { + jest.clearAllMocks(); + (getRedisClient as jest.Mock).mockReturnValue(null); + }); + + describe('DashboardService', () => { + it('should aggregate active deliveries, online drivers, and total escrow volume', async () => { + (Delivery.aggregate as jest.Mock).mockResolvedValue([ + { _id: DeliveryStatus.PENDING, count: 2 }, + { _id: DeliveryStatus.IN_PROGRESS, count: 3 }, + ]); + + (User.countDocuments as jest.Mock).mockResolvedValue(10); + (LocationUpdate.distinct as jest.Mock).mockResolvedValue(['driver1', 'driver2']); + + (Escrow.aggregate as jest.Mock).mockResolvedValue([ + { _id: EscrowStatus.LOCKED, totalVolume: 1500, count: 3 }, + { _id: EscrowStatus.RELEASED, totalVolume: 500, count: 2 }, + ]); + + const metrics = await dashboardService.getAdminDashboardMetrics(); + + expect(metrics.activeDeliveries.total).toBe(5); + expect(metrics.activeDeliveries.byStatus.pending).toBe(2); + expect(metrics.activeDeliveries.byStatus.in_progress).toBe(3); + expect(metrics.activeDeliveries.byStatus.funded).toBe(0); + + expect(metrics.onlineDrivers.totalActiveDrivers).toBe(10); + expect(metrics.onlineDrivers.recentlyActiveDrivers).toBe(2); + + expect(metrics.escrow.totalVolume).toBe(2000); + expect(metrics.escrow.lockedVolume).toBe(1500); + expect(metrics.escrow.releasedVolume).toBe(500); + expect(metrics.escrow.activeCount).toBe(3); + expect(metrics.escrow.totalCount).toBe(5); + + expect(metrics.sorobanRpc?.connected).toBe(true); + expect(metrics.metadata.cached).toBe(false); + }); + + it('should return cached metrics if present in Redis and not forceRefreshed', async () => { + const mockCachedMetrics = { + activeDeliveries: { total: 4, byStatus: { pending: 4, funded: 0, assigned: 0, in_progress: 0 } }, + onlineDrivers: { totalActiveDrivers: 5, recentlyActiveDrivers: 1 }, + escrow: { totalVolume: 100, lockedVolume: 100, releasedVolume: 0, refundedVolume: 0, activeCount: 1, totalCount: 1 }, + metadata: { timestamp: new Date().toISOString(), cached: true, cacheTtlSeconds: 60 }, + }; + + const mockRedis = { + get: jest.fn().mockResolvedValue(JSON.stringify(mockCachedMetrics)), + set: jest.fn(), + }; + (getRedisClient as jest.Mock).mockReturnValue(mockRedis); + + const metrics = await dashboardService.getAdminDashboardMetrics(); + + expect(mockRedis.get).toHaveBeenCalledWith('admin:dashboard:metrics'); + expect(metrics.metadata.cached).toBe(true); + expect(Delivery.aggregate).not.toHaveBeenCalled(); + }); + + it('should bypass cache when forceRefresh is true', async () => { + (Delivery.aggregate as jest.Mock).mockResolvedValue([ + { _id: DeliveryStatus.PENDING, count: 1 }, + ]); + (User.countDocuments as jest.Mock).mockResolvedValue(2); + (LocationUpdate.distinct as jest.Mock).mockResolvedValue([]); + (Escrow.aggregate as jest.Mock).mockResolvedValue([]); + + const mockRedis = { + get: jest.fn(), + set: jest.fn().mockResolvedValue('OK'), + }; + (getRedisClient as jest.Mock).mockReturnValue(mockRedis); + + const metrics = await dashboardService.getAdminDashboardMetrics({ forceRefresh: true }); + + expect(mockRedis.get).not.toHaveBeenCalled(); + expect(Delivery.aggregate).toHaveBeenCalled(); + expect(mockRedis.set).toHaveBeenCalled(); + expect(metrics.metadata.cached).toBe(false); + }); + }); + + describe('DashboardController Unit Handler', () => { + it('should throw UNAUTHORIZED if req.user is missing', async () => { + const req = { query: {} } as any; + const res = {} as any; + const next = jest.fn(); + + await dashboardController.getDashboardMetrics(req, res, next); + + expect(next).toHaveBeenCalledWith( + expect.objectContaining({ + statusCode: StatusCodes.UNAUTHORIZED, + message: 'Authentication required.', + }), + ); + }); + + it('should send success response with metrics when user is authenticated', async () => { + const mockMetrics = { + activeDeliveries: { total: 2, byStatus: { pending: 1, funded: 0, assigned: 0, in_progress: 1 } }, + onlineDrivers: { totalActiveDrivers: 3, recentlyActiveDrivers: 1 }, + escrow: { totalVolume: 300, lockedVolume: 300, releasedVolume: 0, refundedVolume: 0, activeCount: 1, totalCount: 1 }, + metadata: { timestamp: new Date().toISOString(), cached: false, cacheTtlSeconds: 60 }, + }; + + jest.spyOn(dashboardService, 'getAdminDashboardMetrics').mockResolvedValue(mockMetrics as any); + + const req = { + user: { _id: 'admin123', role: UserRole.ADMIN }, + query: { refresh: 'true' }, + } as any; + + const res = { + status: jest.fn().mockReturnThis(), + json: jest.fn(), + } as any; + const next = jest.fn(); + + await dashboardController.getDashboardMetrics(req, res, next); + + expect(dashboardService.getAdminDashboardMetrics).toHaveBeenCalledWith({ forceRefresh: true }); + expect(res.status).toHaveBeenCalledWith(StatusCodes.OK); + expect(res.json).toHaveBeenCalledWith( + expect.objectContaining({ + success: true, + data: mockMetrics, + }), + ); + }); + }); +}); diff --git a/tests/integration/dashboard.integration.test.ts b/tests/integration/dashboard.integration.test.ts new file mode 100644 index 0000000..fdd7676 --- /dev/null +++ b/tests/integration/dashboard.integration.test.ts @@ -0,0 +1,113 @@ +import request from 'supertest'; +import jwt from 'jsonwebtoken'; +import { StatusCodes } from 'http-status-codes'; +import app from '../../src/app'; +import User from '../../src/models/User'; +import Delivery, { DeliveryStatus } from '../../src/models/Delivery'; +import Escrow, { EscrowStatus } from '../../src/models/Escrow'; +import { LocationUpdate } from '../../src/models/LocationUpdate'; +import { UserRole, UserStatus } from '../../src/interfaces/IUser'; +import env from '../../src/config/env'; + +jest.mock('../../src/models/User'); +jest.mock('../../src/models/Delivery'); +jest.mock('../../src/models/Escrow'); +jest.mock('../../src/models/LocationUpdate'); +jest.mock('../../src/config/redis', () => ({ + getRedisClient: jest.fn().mockReturnValue(null), + redisClient: null, +})); +jest.mock('../../src/blockchain/soroban.service', () => ({ + sorobanService: { + checkConnectivity: jest.fn().mockResolvedValue({ + connected: true, + network: 'testnet', + latestLedger: 1000, + }), + }, +})); + +describe('Admin Dashboard Endpoint GET /api/v1/admin/dashboard', () => { + const adminId = '507f1f77bcf86cd799439011'; + const userId = '507f1f77bcf86cd799439022'; + + let adminToken: string; + let userToken: string; + + beforeAll(() => { + adminToken = jwt.sign( + { userId: adminId, role: UserRole.ADMIN }, + env.JWT_SECRET, + { expiresIn: '1h' }, + ); + + userToken = jwt.sign( + { userId: userId, role: UserRole.USER }, + env.JWT_SECRET, + { expiresIn: '1h' }, + ); + }); + + beforeEach(() => { + jest.clearAllMocks(); + (LocationUpdate.distinct as jest.Mock).mockResolvedValue(['driver1']); + }); + + it('should return 401 Unauthorized when Bearer token is missing', async () => { + const res = await request(app).get('/api/v1/admin/dashboard'); + expect(res.status).toBe(StatusCodes.UNAUTHORIZED); + }); + + it('should return 403 Forbidden when authenticated user is not an admin', async () => { + (User.findById as jest.Mock).mockReturnValue({ + select: jest.fn().mockResolvedValue({ + _id: userId, + role: UserRole.USER, + status: UserStatus.ACTIVE, + isActive: true, + }), + }); + + const res = await request(app) + .get('/api/v1/admin/dashboard') + .set('Authorization', `Bearer ${userToken}`); + + expect(res.status).toBe(StatusCodes.FORBIDDEN); + }); + + it('should return 200 OK with aggregated metrics for admin user', async () => { + (User.findById as jest.Mock).mockReturnValue({ + select: jest.fn().mockResolvedValue({ + _id: adminId, + role: UserRole.ADMIN, + status: UserStatus.ACTIVE, + isActive: true, + }), + }); + + (Delivery.aggregate as jest.Mock).mockResolvedValue([ + { _id: DeliveryStatus.PENDING, count: 5 }, + { _id: DeliveryStatus.IN_PROGRESS, count: 2 }, + ]); + + (User.countDocuments as jest.Mock).mockResolvedValue(15); + + (Escrow.aggregate as jest.Mock).mockResolvedValue([ + { _id: EscrowStatus.LOCKED, totalVolume: 2500, count: 4 }, + ]); + + const res = await request(app) + .get('/api/v1/admin/dashboard') + .set('Authorization', `Bearer ${adminToken}`); + + expect(res.status).toBe(StatusCodes.OK); + expect(res.body.success).toBe(true); + expect(res.body.data).toHaveProperty('activeDeliveries'); + expect(res.body.data.activeDeliveries.total).toBe(7); + expect(res.body.data).toHaveProperty('onlineDrivers'); + expect(res.body.data.onlineDrivers.totalActiveDrivers).toBe(15); + expect(res.body.data.onlineDrivers.recentlyActiveDrivers).toBe(1); + expect(res.body.data).toHaveProperty('escrow'); + expect(res.body.data.escrow.totalVolume).toBe(2500); + }); +});