diff --git a/README.md b/README.md index 8985ad3e0..5b3921916 100644 --- a/README.md +++ b/README.md @@ -108,6 +108,48 @@ NOTE: Only POSITION.PREPARE is supported at this time, with additional event-typ } ``` +### Batch Processing Configuration Guide + +Batch processing can be enabled in the transfer execution flow. Follow the steps below to enable batch processing for a more efficient transfer execution: + +- **Step 1:** **Create a New Kafka Topic** + + Create a new Kafka topic named `topic-transfer-position-batch` to handle batch processing events. +- **Step 2:** **Configure Action Type Mapping** + + Point the prepare handler to the newly created topic for the action type `prepare` using the `KAFKA.EVENT_TYPE_ACTION_TOPIC_MAP` configuration as shown below: + ``` + "KAFKA": { + "EVENT_TYPE_ACTION_TOPIC_MAP" : { + "POSITION":{ + "PREPARE": "topic-transfer-position-batch", + "BULK_PREPARE": "topic-transfer-position" + } + } + } + ``` + _NOTE_: `BULK_PREPARE` configuration property is added to aid routing of `bulk-prepare` events to non-batch handler since the batch handler does not support `bulk-prepare` events. + +- **Step 3:** **Run Batch Processing Handlers** + + Run the position batch handler along with the existing position handler using the following configuration options: + - **Configure Event Type Action Topic Map for Batch Handler:** + + Configure the `EVENT_TYPE_ACTION_TOPIC_MAP` parameter for the position batch handler to consume events from the new topic (topic-transfer-position-batch). + + - **Set Batch Size:** + + Adjust the batch size using the parameter `KAFKA.CONSUMER.TRANSFER.POSITION_BATCH.config.options.batchSize`. This parameter determines the number of messages fetched in each batch. + + - **Set Consume Timeout:** + + Configure the consume timeout using the parameter `KAFKA.CONSUMER.TRANSFER.POSITION_BATCH.config.options.consumeTimeout`. This parameter specifies the number of milliseconds to wait for a batch of messages to be fetched if the specified batch size of messages is not immediately available. + +Example Command to Run Handlers: +``` +node src/handlers/index.js handler --positionbatch +``` + ## API For endpoint documentation, see the [API documentation](API.md). @@ -187,6 +229,25 @@ If you want to run integration tests in a repetitive manner, you can startup the If you want to run override position topic tests you can repeat the above and use `npm run test:int-override` after configuring settings found [here](#kafka-position-event-type-action-topic-map) +#### For running integration tests for batch processing interactively +- Run dependecies +``` +docker-compose up -d mysql kafka init-kafka kafka-debug-console +npm run wait-4-docker +``` +- Run central-ledger services +``` +nvm use +npm run migrate +env "CLEDG_KAFKA__EVENT_TYPE_ACTION_TOPIC_MAP__POSITION__PREPARE=topic-transfer-position-batch" npm start +``` +- Additionally, run position batch handler in a new terminal +``` +env "CLEDG_KAFKA__EVENT_TYPE_ACTION_TOPIC_MAP__POSITION__PREPARE=topic-transfer-position-batch" "CLEDG_HANDLERS__API__DISABLED=true" node src/handlers/index.js handler --positionbatch +``` +- Run tests using `npx tape 'test/integration-override/**/handlerBatch.test.js'` + + If you want to just run all of the integration suite non-interactively then use npm run `test:integration`. It will handle docker start up, migration, service starting and testing. Be sure to exit any previously ran handlers or docker commands. diff --git a/config/default.json b/config/default.json index c9ca7fcd2..dfebaedb7 100644 --- a/config/default.json +++ b/config/default.json @@ -87,7 +87,8 @@ "KAFKA": { "EVENT_TYPE_ACTION_TOPIC_MAP" : { "POSITION":{ - "PREPARE": null + "PREPARE": null, + "BULK_PREPARE": null } }, "TOPIC_TEMPLATES": { @@ -289,7 +290,35 @@ "group.id": "cl-group-transfer-position", "metadata.broker.list": "localhost:9092", "socket.keepalive.enable": true, - "allow.auto.create.topics": true + "allow.auto.create.topics": true, + "partition.assignment.strategy": "cooperative-sticky", + "enable.auto.commit": false + }, + "topicConf": { + "auto.offset.reset": "earliest" + } + } + }, + "POSITION_BATCH": { + "config": { + "options": { + "mode": 2, + "batchSize": 10, + "pollFrequency": 10, + "recursiveTimeout": 100, + "messageCharset": "utf8", + "messageAsJSON": true, + "sync": true, + "consumeTimeout": 10 + }, + "rdkafkaConf": { + "client.id": "cl-con-transfer-position-batch", + "group.id": "cl-group-transfer-position-batch", + "metadata.broker.list": "localhost:9092", + "socket.keepalive.enable": true, + "allow.auto.create.topics": true, + "partition.assignment.strategy": "cooperative-sticky", + "enable.auto.commit": false }, "topicConf": { "auto.offset.reset": "earliest" diff --git a/package-lock.json b/package-lock.json index 421ba208a..9c4158054 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@mojaloop/central-ledger", - "version": "17.3.2", + "version": "17.4.0-snapshot.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@mojaloop/central-ledger", - "version": "17.3.2", + "version": "17.4.0-snapshot.7", "license": "Apache-2.0", "dependencies": { "@hapi/catbox-memory": "6.0.1", @@ -19,7 +19,7 @@ "@mojaloop/central-services-health": "14.0.2", "@mojaloop/central-services-logger": "11.2.2", "@mojaloop/central-services-metrics": "12.0.8", - "@mojaloop/central-services-shared": "18.1.3", + "@mojaloop/central-services-shared": "18.2.0", "@mojaloop/central-services-stream": "11.2.0", "@mojaloop/database-lib": "11.0.3", "@mojaloop/event-sdk": "14.0.0", @@ -39,7 +39,7 @@ "glob": "10.3.10", "hapi-auth-basic": "5.0.0", "hapi-auth-bearer-token": "8.0.0", - "hapi-swagger": "17.1.0", + "hapi-swagger": "17.2.0", "ilp-packet": "2.2.0", "knex": "3.0.1", "lodash": "4.17.21", @@ -54,8 +54,8 @@ "get-port": "5.1.1", "jsdoc": "4.0.2", "jsonpath": "1.1.1", - "nodemon": "3.0.1", - "npm-check-updates": "16.14.6", + "nodemon": "3.0.2", + "npm-check-updates": "16.14.11", "nyc": "15.1.0", "pre-commit": "1.2.2", "proxyquire": "2.1.3", @@ -95,30 +95,21 @@ } }, "node_modules/@apidevtools/json-schema-ref-parser": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.1.2.tgz", - "integrity": "sha512-r1w81DpR+KyRWd3f+rk6TNqMgedmAxZP5v5KWlXQWlgMUUtyEJch0DKEci1SorPMiSeM8XPl7MZ3miJ60JIpQg==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-11.1.0.tgz", + "integrity": "sha512-g/VW9ZQEFJAOwAyUb8JFf7MLiLy2uEB4rU270rGzDwICxnxMlPy0O11KVePSgS36K1NI29gSlK84n5INGhd4Ag==", "dependencies": { "@jsdevtools/ono": "^7.1.3", - "@types/json-schema": "^7.0.6", - "call-me-maybe": "^1.0.1", - "js-yaml": "^4.1.0" - } - }, - "node_modules/@apidevtools/json-schema-ref-parser/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, - "node_modules/@apidevtools/json-schema-ref-parser/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dependencies": { - "argparse": "^2.0.1" + "@types/json-schema": "^7.0.13", + "@types/lodash.clonedeep": "^4.5.7", + "js-yaml": "^4.1.0", + "lodash.clonedeep": "^4.5.0" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/philsturgeon" } }, "node_modules/@apidevtools/openapi-schemas": { @@ -150,6 +141,17 @@ "openapi-types": ">=7" } }, + "node_modules/@apidevtools/swagger-parser/node_modules/@apidevtools/json-schema-ref-parser": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.1.2.tgz", + "integrity": "sha512-r1w81DpR+KyRWd3f+rk6TNqMgedmAxZP5v5KWlXQWlgMUUtyEJch0DKEci1SorPMiSeM8XPl7MZ3miJ60JIpQg==", + "dependencies": { + "@jsdevtools/ono": "^7.1.3", + "@types/json-schema": "^7.0.6", + "call-me-maybe": "^1.0.1", + "js-yaml": "^4.1.0" + } + }, "node_modules/@babel/code-frame": { "version": "7.22.13", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", @@ -227,31 +229,31 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.9.tgz", - "integrity": "sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.3.tgz", + "integrity": "sha512-BmR4bWbDIoFJmJ9z2cZ8Gmm2MXgEDgjdWgpKmKWUt54UGFJdlj31ECtbaDvCG/qVdG3AQ1SfpZEs01lUFbzLOQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.11.tgz", - "integrity": "sha512-lh7RJrtPdhibbxndr6/xx0w8+CVlY5FJZiaSz908Fpy+G0xkBFTvwLcKJFF4PJxVfGhVWNebikpWGnOoC71juQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.3.tgz", + "integrity": "sha512-Jg+msLuNuCJDyBvFv5+OKOUjWMZgd85bKjbICd3zWrKAo+bJ49HJufi7CQE0q0uR8NGyO6xkCACScNqyjHSZew==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.22.10", - "@babel/generator": "^7.22.10", - "@babel/helper-compilation-targets": "^7.22.10", - "@babel/helper-module-transforms": "^7.22.9", - "@babel/helpers": "^7.22.11", - "@babel/parser": "^7.22.11", - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.11", - "@babel/types": "^7.22.11", - "convert-source-map": "^1.7.0", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.3", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.23.2", + "@babel/parser": "^7.23.3", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.3", + "@babel/types": "^7.23.3", + "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", @@ -265,6 +267,12 @@ "url": "https://opencollective.com/babel" } }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, "node_modules/@babel/core/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -275,12 +283,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.10.tgz", - "integrity": "sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.3.tgz", + "integrity": "sha512-keeZWAV4LU3tW0qRi19HRpabC/ilM0HRBBzf9/k8FFiG4KVpiv0FIy4hHfLfFQZNhziCTPTmd59zoyv6DNISzg==", "dev": true, "dependencies": { - "@babel/types": "^7.22.10", + "@babel/types": "^7.23.3", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -290,13 +298,13 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.10.tgz", - "integrity": "sha512-JMSwHD4J7SLod0idLq5PKgI+6g/hLD/iuWBq08ZX49xE14VpVEojJ5rHWptpirV2j020MvypRLAXAO50igCJ5Q==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", + "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", "dev": true, "dependencies": { "@babel/compat-data": "^7.22.9", - "@babel/helper-validator-option": "^7.22.5", + "@babel/helper-validator-option": "^7.22.15", "browserslist": "^4.21.9", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -330,22 +338,22 @@ "dev": true }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", - "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", - "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, "dependencies": { - "@babel/template": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" @@ -364,28 +372,28 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz", - "integrity": "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", "dev": true, "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz", - "integrity": "sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", + "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", "@babel/helper-simple-access": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.5" + "@babel/helper-validator-identifier": "^7.22.20" }, "engines": { "node": ">=6.9.0" @@ -428,42 +436,42 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", - "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz", - "integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", + "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.11.tgz", - "integrity": "sha512-vyOXC8PBWaGc5h7GMsNx68OH33cypkEDJCHvYVVgVbbxJDROYVtexSk0gK5iCF1xNjRIN2s8ai7hwkWDq5szWg==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.2.tgz", + "integrity": "sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ==", "dev": true, "dependencies": { - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.11", - "@babel/types": "^7.22.11" + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.2", + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.13.tgz", - "integrity": "sha512-C/BaXcnnvBCmHTpz/VGZ8jgtE2aYlW4hxDhseJAWZb7gqGM/qtCK6iZUb0TyKFf7BOUsBH7Q7fkRsDRhg1XklQ==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", "dependencies": { - "@babel/helper-validator-identifier": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, @@ -536,9 +544,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.13.tgz", - "integrity": "sha512-3l6+4YOvc9wx7VlCSw4yQfcBo01ECA8TicQfbnCPuCEpRQrf+gTUyGdxNw+pyTUyywp6JRD1w0YQs9TpBXYlkw==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.3.tgz", + "integrity": "sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -548,9 +556,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.11.tgz", - "integrity": "sha512-ee7jVNlWN09+KftVOu9n7S8gQzD/Z6hN/I8VBRXW4P1+Xe7kJGXMwu8vds4aGIMHZnNbdpSWCfZZtinytpcAvA==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.2.tgz", + "integrity": "sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -559,33 +567,33 @@ } }, "node_modules/@babel/template": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", - "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.22.5", - "@babel/parser": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.11.tgz", - "integrity": "sha512-mzAenteTfomcB7mfPtyi+4oe5BZ6MXxWcn4CX+h4IRJ+OOGXBrWU6jDQavkQI9Vuc5P+donFabBfFCcmWka9lQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.3.tgz", + "integrity": "sha512-+K0yF1/9yR0oHdE0StHuEj3uTPzwwbrLGfNOndVJVV2TqA5+j3oljJUb4nmB954FLGjNem976+B+eDuLIjesiQ==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.22.10", - "@babel/generator": "^7.22.10", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.3", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.22.11", - "@babel/types": "^7.22.11", + "@babel/parser": "^7.23.3", + "@babel/types": "^7.23.3", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -594,13 +602,13 @@ } }, "node_modules/@babel/types": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.11.tgz", - "integrity": "sha512-siazHiGuZRz9aB9NpHy9GOs9xiQPKnMzgdr493iI1M67vRXpnEq8ZOOKzezC5q7zwuQ6sDhdSp4SD9ixKSqKZg==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.3.tgz", + "integrity": "sha512-OZnvoH2l8PK5eUvEcUyCt/sXgr/h+UWpVuBbOljwcrAgUl6lpchoQ++PHGyQy1AtYnVA6CEq3y5xeEI10brpXw==", "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" }, "engines": { @@ -641,18 +649,18 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.8.0.tgz", - "integrity": "sha512-JylOEEzDiOryeUnFbQz+oViCXS0KsvR1mvHkoMiu5+UiBvy+RYX7tzlIIIEstF/gVa2tj9AQXk3dgnxv6KxhFg==", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/eslintrc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", - "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.3.tgz", + "integrity": "sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==", "dev": true, "dependencies": { "ajv": "^6.12.4", @@ -688,12 +696,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@eslint/eslintrc/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -705,9 +707,9 @@ } }, "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.21.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", - "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", + "version": "13.23.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", + "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -719,18 +721,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@eslint/eslintrc/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -762,9 +752,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.48.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.48.0.tgz", - "integrity": "sha512-ZSjtmelB7IJfWD2Fvb7+Z+ChTIKWq6kjda95fLcQKNS5aheVHn4IkfgRQE3sIIzTcSLwLcLZUD9UBt+V7+h+Pw==", + "version": "8.53.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.53.0.tgz", + "integrity": "sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -775,6 +765,12 @@ "resolved": "https://registry.npmjs.org/@exodus/schemasafe/-/schemasafe-1.3.0.tgz", "integrity": "sha512-5Aap/GaRupgNx/feGBwLLTVv8OQFfv3pq2lPRzPg9R+IOBnDgghTGW7l7EuVXOvg5cc/xSAlRW8rBrjIC3Nvqw==" }, + "node_modules/@gar/promisify": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", + "dev": true + }, "node_modules/@grpc/grpc-js": { "version": "1.9.9", "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.9.tgz", @@ -804,64 +800,10 @@ "node": ">=6" } }, - "node_modules/@grpc/proto-loader/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@grpc/proto-loader/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/@grpc/proto-loader/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@grpc/proto-loader/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "engines": { - "node": ">=12" - } - }, "node_modules/@hapi/accept": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@hapi/accept/-/accept-6.0.2.tgz", - "integrity": "sha512-xaTLf29Zeph/B32hekmgxLFsEPuX1xQYyZu0gJ4ZCHKU6nXmBRXfBymtWNEK0souOJcX2XHWUaZU6JzccuuMpg==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@hapi/accept/-/accept-6.0.3.tgz", + "integrity": "sha512-p72f9k56EuF0n3MwlBNThyVE5PXX40g+aQh+C/xbKrfzahM2Oispv3AXmOIU51t3j77zay1qrX7IIziZXspMlw==", "dependencies": { "@hapi/boom": "^10.0.1", "@hapi/hoek": "^11.0.2" @@ -1335,12 +1277,12 @@ } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.11", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz", - "integrity": "sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==", + "version": "0.11.13", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", + "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", + "@humanwhocodes/object-schema": "^2.0.1", "debug": "^4.1.1", "minimatch": "^3.0.5" }, @@ -1384,9 +1326,9 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", + "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", "dev": true }, "node_modules/@hutson/parse-repository-url": { @@ -1414,52 +1356,6 @@ "node": ">=12" } }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -1476,6 +1372,28 @@ "node": ">=8" } }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", @@ -1489,6 +1407,19 @@ "node": ">=8" } }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -1576,9 +1507,9 @@ "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.19", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz", - "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==", + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", + "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -1659,13 +1590,13 @@ } }, "node_modules/@mojaloop/central-services-shared": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/@mojaloop/central-services-shared/-/central-services-shared-18.1.3.tgz", - "integrity": "sha512-XqpxFlPYhXd4wXm3IJN6MGrUaGfbUiebygM6xHcxt3n4tux5w7zFVbQLMDC09MjfKRg9juB+b//RdBPLh+Wtnw==", + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/@mojaloop/central-services-shared/-/central-services-shared-18.2.0.tgz", + "integrity": "sha512-qhfUsNusL2EdxCtnChTUXD6/m+iIGMgE/SM8/TNLgcgo1sEqM87/DJ4992AwLsH3a8ErRy2Q3e8vDPixmtLdAg==", "dependencies": { "@hapi/catbox": "12.1.1", "@hapi/catbox-memory": "5.0.1", - "axios": "1.6.0", + "axios": "1.6.2", "clone": "2.1.2", "dotenv": "16.3.1", "env-var": "7.4.1", @@ -1679,7 +1610,7 @@ "shins": "2.6.0", "uuid4": "2.0.3", "widdershins": "^4.0.1", - "yaml": "2.3.3" + "yaml": "2.3.4" }, "peerDependencies": { "@mojaloop/central-services-error-handling": ">=12.x.x", @@ -1710,6 +1641,14 @@ } } }, + "node_modules/@mojaloop/central-services-shared/node_modules/@hapi/boom": { + "version": "9.1.4", + "resolved": "https://registry.npmjs.org/@hapi/boom/-/boom-9.1.4.tgz", + "integrity": "sha512-Ls1oH8jaN1vNsqcaHVYJrKmgMcKsC1wcp8bujvXrHaAqD2iDYq3HoOwsxwo09Cuda5R5nC0o0IxlrlTuvPuzSw==", + "dependencies": { + "@hapi/hoek": "9.x.x" + } + }, "node_modules/@mojaloop/central-services-shared/node_modules/@hapi/catbox-memory": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/@hapi/catbox-memory/-/catbox-memory-5.0.1.tgz", @@ -1719,15 +1658,7 @@ "@hapi/hoek": "9.x.x" } }, - "node_modules/@mojaloop/central-services-shared/node_modules/@hapi/catbox-memory/node_modules/@hapi/boom": { - "version": "9.1.4", - "resolved": "https://registry.npmjs.org/@hapi/boom/-/boom-9.1.4.tgz", - "integrity": "sha512-Ls1oH8jaN1vNsqcaHVYJrKmgMcKsC1wcp8bujvXrHaAqD2iDYq3HoOwsxwo09Cuda5R5nC0o0IxlrlTuvPuzSw==", - "dependencies": { - "@hapi/hoek": "9.x.x" - } - }, - "node_modules/@mojaloop/central-services-shared/node_modules/@hapi/catbox-memory/node_modules/@hapi/hoek": { + "node_modules/@mojaloop/central-services-shared/node_modules/@hapi/hoek": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" @@ -1931,9 +1862,9 @@ } }, "node_modules/@mongodb-js/saslprep": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.0.tgz", - "integrity": "sha512-Xfijy7HvfzzqiOAhAepF4SGN5e9leLkMvg/OPOF97XemjfVCYN/oWa75wnkc6mltMSTwY+XlbhWgUOJmkFspSw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.1.tgz", + "integrity": "sha512-t7c5K033joZZMspnHg/gWPE4kandgc2OxE74aYOtGKfgB9VPuVJPix0H6fhmm2erj5PBJ21mqcx34lpIGtUCsQ==", "optional": true, "dependencies": { "sparse-bitfield": "^3.0.3" @@ -2115,40 +2046,111 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/@npmcli/node-gyp": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-3.0.0.tgz", - "integrity": "sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA==", - "dev": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@npmcli/promise-spawn": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-6.0.2.tgz", - "integrity": "sha512-gGq0NJkIGSwdbUt4yhdF8ZrmkGKVz9vAdVzpOfnom+V8PLSmSOVhZwbNvZZS1EYcJN5hzzKBxmmVVAInM6HQLg==", + "node_modules/@npmcli/move-file": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-2.0.1.tgz", + "integrity": "sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==", + "deprecated": "This functionality has been moved to @npmcli/fs", "dev": true, "dependencies": { - "which": "^3.0.0" + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/@npmcli/promise-spawn/node_modules/which": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/which/-/which-3.0.1.tgz", - "integrity": "sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==", + "node_modules/@npmcli/move-file/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/which.js" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@npmcli/move-file/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@npmcli/move-file/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@npmcli/move-file/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@npmcli/node-gyp": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-3.0.0.tgz", + "integrity": "sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/promise-spawn": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-6.0.2.tgz", + "integrity": "sha512-gGq0NJkIGSwdbUt4yhdF8ZrmkGKVz9vAdVzpOfnom+V8PLSmSOVhZwbNvZZS1EYcJN5hzzKBxmmVVAInM6HQLg==", + "dev": true, + "dependencies": { + "which": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/promise-spawn/node_modules/which": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/which/-/which-3.0.1.tgz", + "integrity": "sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/@npmcli/run-script": { @@ -2457,15 +2459,15 @@ } }, "node_modules/@types/http-cache-semantics": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", - "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", + "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", "dev": true }, "node_modules/@types/json-schema": { - "version": "7.0.14", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.14.tgz", - "integrity": "sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw==" + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" }, "node_modules/@types/json5": { "version": "0.0.29", @@ -2474,28 +2476,28 @@ "dev": true }, "node_modules/@types/linkify-it": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.2.tgz", - "integrity": "sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.5.tgz", + "integrity": "sha512-yg6E+u0/+Zjva+buc3EIb+29XEg4wltq7cSmd4Uc2EE/1nUVmxyzpX6gUXD0V8jIrG0r7YeOGVIbYRkxeooCtw==", "dev": true }, "node_modules/@types/lodash": { - "version": "4.14.197", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.197.tgz", - "integrity": "sha512-BMVOiWs0uNxHVlHBgzTIqJYmj+PgCo4euloGF+5m4okL3rEYzM2EEv78mw8zWSMM57dM7kVIgJ2QDvwHSoCI5g==" + "version": "4.14.201", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.201.tgz", + "integrity": "sha512-y9euML0cim1JrykNxADLfaG0FgD1g/yTHwUs/Jg9ZIU7WKj2/4IW9Lbb1WZbvck78W/lfGXFfe+u2EGfIJXdLQ==" }, "node_modules/@types/lodash.clonedeep": { - "version": "4.5.7", - "resolved": "https://registry.npmjs.org/@types/lodash.clonedeep/-/lodash.clonedeep-4.5.7.tgz", - "integrity": "sha512-ccNqkPptFIXrpVqUECi60/DFxjNKsfoQxSQsgcBJCX/fuX1wgyQieojkcWH/KpE3xzLoWN/2k+ZeGqIN3paSvw==", + "version": "4.5.9", + "resolved": "https://registry.npmjs.org/@types/lodash.clonedeep/-/lodash.clonedeep-4.5.9.tgz", + "integrity": "sha512-19429mWC+FyaAhOLzsS8kZUsI+/GmBAQ0HFiCPsKGU+7pBXOQWhyrY6xNNDwUSX8SMZMJvuFVMF9O5dQOlQK9Q==", "dependencies": { "@types/lodash": "*" } }, "node_modules/@types/luxon": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.3.2.tgz", - "integrity": "sha512-l5cpE57br4BIjK+9BSkFBOsWtwv6J9bJpC7gdXIzZyI0vuKvNTk0wZZrkQxMGsUAuGW9+WMNWF2IJMD7br2yeQ==" + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.3.4.tgz", + "integrity": "sha512-H9OXxv4EzJwE75aTPKpiGXJq+y4LFxjpsdgKwSmr503P5DkWc3AG7VAFYrFNVvqemT5DfgZJV9itYhqBHSGujA==" }, "node_modules/@types/markdown-it": { "version": "12.2.3", @@ -2508,37 +2510,40 @@ } }, "node_modules/@types/mdurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.2.tgz", - "integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.5.tgz", + "integrity": "sha512-6L6VymKTzYSrEf4Nev4Xa1LCHKrlTlYCBMTlQKFuddo1CvQcE52I0mwfOJayueUC7MJuXOeHTcIU683lzd0cUA==", "dev": true }, "node_modules/@types/minimist": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", - "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==", "dev": true }, "node_modules/@types/node": { - "version": "20.5.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.7.tgz", - "integrity": "sha512-dP7f3LdZIysZnmvP3ANJYTSwg+wLLl8p7RqniVlV7j+oXSXAbt9h0WIBFmJy5inWZoX9wZN6eXx+YXd9Rh3RBA==" + "version": "20.9.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.0.tgz", + "integrity": "sha512-nekiGu2NDb1BcVofVcEKMIwzlx4NjHlcjhoxxKBNLtz15Y1z7MYf549DFvkHSId02Ax6kGwWntIBPC3l/JZcmw==", + "dependencies": { + "undici-types": "~5.26.4" + } }, "node_modules/@types/normalize-package-data": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", - "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", "dev": true }, "node_modules/@types/triple-beam": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.2.tgz", - "integrity": "sha512-txGIh+0eDFzKGC25zORnswy+br1Ha7hj5cMVwKIU7+s0U2AxxJru/jZSMU6OC9MJWP6+pc/hc6ZjyZShpsyY2g==" + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==" }, "node_modules/@types/webidl-conversions": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.1.tgz", - "integrity": "sha512-8hKOnOan+Uu+NgMaCouhg3cT9x5fFZ92Jwf+uDLXLu/MFRbXxlWwGeQY7KVHkeSft6RvY+tdxklUBuyY9eIEKg==" + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==" }, "node_modules/@types/whatwg-url": { "version": "8.2.2", @@ -2549,6 +2554,12 @@ "@types/webidl-conversions": "*" } }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -2568,9 +2579,9 @@ } }, "node_modules/acorn": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", - "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", + "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -2706,6 +2717,38 @@ "string-width": "^4.1.0" } }, + "node_modules/ansi-align/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 + }, + "node_modules/ansi-align/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, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-align/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -2764,14 +2807,38 @@ "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", "dev": true }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "node_modules/are-we-there-yet": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", + "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", + "dev": true, "dependencies": { - "sprintf-js": "~1.0.2" + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/are-we-there-yet/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" } }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, "node_modules/array-buffer-byte-length": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", @@ -2797,15 +2864,15 @@ "dev": true }, "node_modules/array-includes": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", - "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz", + "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "get-intrinsic": "^1.1.3", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", "is-string": "^1.0.7" }, "engines": { @@ -2843,14 +2910,14 @@ } }, "node_modules/array.prototype.flat": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", - "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", + "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", "es-shim-unscopables": "^1.0.0" }, "engines": { @@ -2861,14 +2928,14 @@ } }, "node_modules/array.prototype.flatmap": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz", - "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", + "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", "es-shim-unscopables": "^1.0.0" }, "engines": { @@ -2879,27 +2946,28 @@ } }, "node_modules/array.prototype.tosorted": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.1.tgz", - "integrity": "sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.2.tgz", + "integrity": "sha512-HuQCHOlk1Weat5jzStICBCd83NxiIMwqDg/dHEsoefabn/hJRj5pVdWcPUSpRrwhwxZOsQassMpgN/xRYFBMIg==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", "es-shim-unscopables": "^1.0.0", - "get-intrinsic": "^1.1.3" + "get-intrinsic": "^1.2.1" } }, "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.1.tgz", - "integrity": "sha512-09x0ZWFEjj4WD8PDbykUwo3t9arLn8NIzmmYEJFpYekOAQjpkGSyrQhNoRTcwwcFRu+ycWF78QZ63oWTqSjBcw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz", + "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==", "dev": true, "dependencies": { "array-buffer-byte-length": "^1.0.0", "call-bind": "^1.0.2", "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", "get-intrinsic": "^1.2.1", "is-array-buffer": "^3.0.2", "is-shared-array-buffer": "^1.0.2" @@ -2980,64 +3048,6 @@ "node": ">=12.9.0" } }, - "node_modules/audit-ci/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/audit-ci/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/audit-ci/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/audit-ci/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, "node_modules/available-typed-arrays": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", @@ -3051,9 +3061,9 @@ } }, "node_modules/axios": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.0.tgz", - "integrity": "sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz", + "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==", "dependencies": { "follow-redirects": "^1.15.0", "form-data": "^4.0.0", @@ -3223,6 +3233,12 @@ "hoek": "6.x.x" } }, + "node_modules/boom/node_modules/hoek": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-6.1.3.tgz", + "integrity": "sha512-YXXAAhmF9zpQbC7LEcREFtXfGq5K1fmd+4PHkBq8NUqmzW3G+Dq10bI/i0KucLRwss3YYFQ0fSfoxBZYiGUqtQ==", + "deprecated": "This module has moved and is now available at @hapi/hoek. Please update your dependencies as this version is no longer maintained an may contain bugs and security issues." + }, "node_modules/boxen": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/boxen/-/boxen-7.1.1.tgz", @@ -3245,25 +3261,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/boxen/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "node_modules/boxen/node_modules/camelcase": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.1.tgz", + "integrity": "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==", "dev": true, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/boxen/node_modules/camelcase": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.1.tgz", - "integrity": "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==", - "dev": true, - "engines": { - "node": ">=14.16" + "node": ">=14.16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -3281,44 +3285,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/boxen/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true - }, - "node_modules/boxen/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/boxen/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, "node_modules/boxen/node_modules/type-fest": { "version": "2.19.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", @@ -3356,9 +3322,9 @@ "integrity": "sha512-UcQusNAX7nnuXf9tvvLRC6DtZ8/YkDJRtTIbiA5ayb8MehwtSwtkvd5ZTXNLUTTtU6J/yJsi+1LJXqgRz1obwg==" }, "node_modules/browserslist": { - "version": "4.21.10", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz", - "integrity": "sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==", + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz", + "integrity": "sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==", "dev": true, "funding": [ { @@ -3375,10 +3341,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001517", - "electron-to-chromium": "^1.4.477", + "caniuse-lite": "^1.0.30001541", + "electron-to-chromium": "^1.4.535", "node-releases": "^2.0.13", - "update-browserslist-db": "^1.0.11" + "update-browserslist-db": "^1.0.13" }, "bin": { "browserslist": "cli.js" @@ -3388,9 +3354,9 @@ } }, "node_modules/bson": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/bson/-/bson-5.5.0.tgz", - "integrity": "sha512-B+QB4YmDx9RStKv8LLSl/aVIEV3nYJc3cJNNTK2Cd1TL+7P+cNpw9mAPeCgc5K+j01Dv6sxUzcITXDx7ZU3F0w==", + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/bson/-/bson-5.5.1.tgz", + "integrity": "sha512-ix0EwukN2EpC0SRWIj/7B5+A6uQMQy6KMREI9qQqvgpkV2frH63T0UDVd1SYedL6dNCmDBYB3QtXi4ISk9YT+g==", "engines": { "node": ">=14.20.1" } @@ -3452,15 +3418,6 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/cacache/node_modules/minipass": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.3.tgz", - "integrity": "sha512-LhbbwCfz3vsb12j/WkWQPZfKTsgqIe1Nf/ti1pKjYESGLHIVjWU96G9/ljLH4F9mWNVhlQOm0VySdAWzf05dpg==", - "dev": true, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, "node_modules/cacheable-lookup": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", @@ -3471,12 +3428,12 @@ } }, "node_modules/cacheable-request": { - "version": "10.2.13", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.13.tgz", - "integrity": "sha512-3SD4rrMu1msNGEtNSt8Od6enwdo//U9s4ykmXfA2TD58kcLkCobtCDiby7kNyj7a/Q7lz/mAesAFI54rTdnvBA==", + "version": "10.2.14", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.14.tgz", + "integrity": "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==", "dev": true, "dependencies": { - "@types/http-cache-semantics": "^4.0.1", + "@types/http-cache-semantics": "^4.0.2", "get-stream": "^6.0.1", "http-cache-semantics": "^4.1.1", "keyv": "^4.5.3", @@ -3504,12 +3461,13 @@ } }, "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", + "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.1", + "set-function-length": "^1.1.1" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3554,9 +3512,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001524", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001524.tgz", - "integrity": "sha512-Jj917pJtYg9HSJBF95HVX3Cdr89JUyLT4IZ8SvM5aDRni95swKgYi3TgYLH5hnGfPE/U1dg6IfZ50UsIlLkwSA==", + "version": "1.0.30001561", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001561.tgz", + "integrity": "sha512-NTt0DNoKe958Q0BE0j0c1V9jbUzhBxHIEJy7asmGrpE0yG63KTV7PLHPnK2E1O9RsQrQ081I3NLuXGS6zht3cw==", "dev": true, "funding": [ { @@ -3684,9 +3642,9 @@ } }, "node_modules/ci-info": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", - "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", "dev": true, "funding": [ { @@ -3734,22 +3692,84 @@ "@colors/colors": "1.5.0" } }, - "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "node_modules/cli-table3/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 + }, + "node_modules/cli-table3/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, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-table3/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dependencies": { "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", + "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/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==" + }, + "node_modules/cliui/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==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, "node_modules/cliui/node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -4411,9 +4431,9 @@ "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, "node_modules/core-js": { - "version": "3.32.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.32.1.tgz", - "integrity": "sha512-lqufgNn9NLnESg5mQeYsxQP5w7wrViSj0jr/kv6ECQiByzQkrn1MKvV0L3acttpDqfQrHLwr2KCMgX5b8X+lyQ==", + "version": "3.33.2", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.33.2.tgz", + "integrity": "sha512-XeBzWI6QL3nJQiHmdzbAOiMYqjrb7hwU7A39Qhvd/POSa/t9E1AeZyEZx3fNvp/vtM8zXwhoL0FsiS0hD0pruQ==", "hasInstallScript": true, "funding": { "type": "opencollective", @@ -4534,6 +4554,11 @@ } } }, + "node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, "node_modules/decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", @@ -4600,17 +4625,20 @@ } }, "node_modules/deep-equal": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", - "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.2.tgz", + "integrity": "sha512-5tdhKF6DbU7iIzrIOa1AOUt39ZRm13cmL1cGEh//aqR8x9+tNfbywRf0n5FD/18OKMdo7DNEtrX2t22ZAkI+eg==", "dev": true, "dependencies": { - "is-arguments": "^1.0.4", - "is-date-object": "^1.0.1", - "is-regex": "^1.0.4", - "object-is": "^1.0.1", + "is-arguments": "^1.1.1", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "object-is": "^1.1.5", "object-keys": "^1.1.1", - "regexp.prototype.flags": "^1.2.0" + "regexp.prototype.flags": "^1.5.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -4675,12 +4703,26 @@ "node": ">=10" } }, + "node_modules/define-data-property": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", + "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "dependencies": { + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/define-properties": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", - "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "dev": true, "dependencies": { + "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" }, @@ -5088,15 +5130,15 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.505", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.505.tgz", - "integrity": "sha512-0A50eL5BCCKdxig2SsCXhpuztnB9PfUgRMojj5tMvt8O54lbwz3t6wNgnpiTRosw5QjlJB7ixhVyeg8daLQwSQ==", + "version": "1.4.579", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.579.tgz", + "integrity": "sha512-bJKvA+awBIzYR0xRced7PrQuRIwGQPpo6ZLP62GAShahU9fWpsNN2IP6BSP1BLDDSbxvBVRGAMWlvVVq3npmLA==", "dev": true }, "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==" + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" }, "node_modules/enabled": { "version": "2.0.0", @@ -5177,26 +5219,26 @@ } }, "node_modules/es-abstract": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.1.tgz", - "integrity": "sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw==", + "version": "1.22.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", + "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==", "dev": true, "dependencies": { "array-buffer-byte-length": "^1.0.0", - "arraybuffer.prototype.slice": "^1.0.1", + "arraybuffer.prototype.slice": "^1.0.2", "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", + "call-bind": "^1.0.5", "es-set-tostringtag": "^2.0.1", "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.2.1", + "function.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.2", "get-symbol-description": "^1.0.0", "globalthis": "^1.0.3", "gopd": "^1.0.1", - "has": "^1.0.3", "has-property-descriptors": "^1.0.0", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", + "hasown": "^2.0.0", "internal-slot": "^1.0.5", "is-array-buffer": "^3.0.2", "is-callable": "^1.2.7", @@ -5204,23 +5246,23 @@ "is-regex": "^1.1.4", "is-shared-array-buffer": "^1.0.2", "is-string": "^1.0.7", - "is-typed-array": "^1.1.10", + "is-typed-array": "^1.1.12", "is-weakref": "^1.0.2", - "object-inspect": "^1.12.3", + "object-inspect": "^1.13.1", "object-keys": "^1.1.1", "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.0", - "safe-array-concat": "^1.0.0", + "regexp.prototype.flags": "^1.5.1", + "safe-array-concat": "^1.0.1", "safe-regex-test": "^1.0.0", - "string.prototype.trim": "^1.2.7", - "string.prototype.trimend": "^1.0.6", - "string.prototype.trimstart": "^1.0.6", + "string.prototype.trim": "^1.2.8", + "string.prototype.trimend": "^1.0.7", + "string.prototype.trimstart": "^1.0.7", "typed-array-buffer": "^1.0.0", "typed-array-byte-length": "^1.0.0", "typed-array-byte-offset": "^1.0.0", "typed-array-length": "^1.0.4", "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.10" + "which-typed-array": "^1.1.13" }, "engines": { "node": ">= 0.4" @@ -5230,14 +5272,14 @@ } }, "node_modules/es-iterator-helpers": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.14.tgz", - "integrity": "sha512-JgtVnwiuoRuzLvqelrvN3Xu7H9bu2ap/kQ2CrM62iidP8SKuD99rWU3CJy++s7IVL2qb/AjXPGR/E7i9ngd/Cw==", + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.15.tgz", + "integrity": "sha512-GhoY8uYqd6iwUl2kgjTm4CZAf6oo5mHK7BPqx3rKgx893YSsy0LGHV6gfqqQvZt/8xM8xeOnfXBCfqclMKkJ5g==", "dev": true, "dependencies": { "asynciterator.prototype": "^1.0.0", "call-bind": "^1.0.2", - "define-properties": "^1.2.0", + "define-properties": "^1.2.1", "es-abstract": "^1.22.1", "es-set-tostringtag": "^2.0.1", "function-bind": "^1.1.1", @@ -5247,31 +5289,31 @@ "has-proto": "^1.0.1", "has-symbols": "^1.0.3", "internal-slot": "^1.0.5", - "iterator.prototype": "^1.1.0", - "safe-array-concat": "^1.0.0" + "iterator.prototype": "^1.1.2", + "safe-array-concat": "^1.0.1" } }, "node_modules/es-set-tostringtag": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", - "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz", + "integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==", "dev": true, "dependencies": { - "get-intrinsic": "^1.1.3", - "has": "^1.0.3", - "has-tostringtag": "^1.0.0" + "get-intrinsic": "^1.2.2", + "has-tostringtag": "^1.0.0", + "hasown": "^2.0.0" }, "engines": { "node": ">= 0.4" } }, "node_modules/es-shim-unscopables": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", - "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", "dev": true, "dependencies": { - "has": "^1.0.3" + "hasown": "^2.0.0" } }, "node_modules/es-to-primitive": { @@ -5361,6 +5403,19 @@ "source-map": "~0.6.1" } }, + "node_modules/escodegen/node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/escodegen/node_modules/estraverse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", @@ -5422,18 +5477,19 @@ } }, "node_modules/eslint": { - "version": "8.48.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.48.0.tgz", - "integrity": "sha512-sb6DLeIuRXxeM1YljSe1KEx9/YYeZFQWcV8Rq9HfigmdDEugjLEVEa1ozDjL6YDjBpQHPJxJzze+alxi4T3OLg==", + "version": "8.53.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.53.0.tgz", + "integrity": "sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.2", - "@eslint/js": "8.48.0", - "@humanwhocodes/config-array": "^0.11.10", + "@eslint/eslintrc": "^2.1.3", + "@eslint/js": "8.53.0", + "@humanwhocodes/config-array": "^0.11.13", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", @@ -5618,26 +5674,26 @@ } }, "node_modules/eslint-plugin-import": { - "version": "2.28.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.28.1.tgz", - "integrity": "sha512-9I9hFlITvOV55alzoKBI+K9q74kv0iKMeY6av5+umsNwayt59fz692daGyjR+oStBQgx6nwR9rXldDev3Clw+A==", + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.0.tgz", + "integrity": "sha512-QPOO5NO6Odv5lpoTkddtutccQjysJuFxoPS7fAHO+9m9udNHvTCPSAMW9zGAYj8lAIdr40I8yPCdUYrncXtrwg==", "dev": true, "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.findlastindex": "^1.2.2", - "array.prototype.flat": "^1.3.1", - "array.prototype.flatmap": "^1.3.1", + "array-includes": "^3.1.7", + "array.prototype.findlastindex": "^1.2.3", + "array.prototype.flat": "^1.3.2", + "array.prototype.flatmap": "^1.3.2", "debug": "^3.2.7", "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.7", + "eslint-import-resolver-node": "^0.3.9", "eslint-module-utils": "^2.8.0", - "has": "^1.0.3", - "is-core-module": "^2.13.0", + "hasown": "^2.0.0", + "is-core-module": "^2.13.1", "is-glob": "^4.0.3", "minimatch": "^3.1.2", - "object.fromentries": "^2.0.6", - "object.groupby": "^1.0.0", - "object.values": "^1.1.6", + "object.fromentries": "^2.0.7", + "object.groupby": "^1.0.1", + "object.values": "^1.1.7", "semver": "^6.3.1", "tsconfig-paths": "^3.14.2" }, @@ -5824,12 +5880,12 @@ } }, "node_modules/eslint-plugin-react/node_modules/resolve": { - "version": "2.0.0-next.4", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", - "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==", + "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", "dev": true, "dependencies": { - "is-core-module": "^2.9.0", + "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -5920,12 +5976,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/eslint/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, "node_modules/eslint/node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -5949,9 +5999,9 @@ } }, "node_modules/eslint/node_modules/globals": { - "version": "13.21.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", - "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", + "version": "13.23.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", + "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -5963,18 +6013,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/eslint/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -5993,6 +6031,18 @@ "node": "*" } }, + "node_modules/eslint/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/eslint/node_modules/type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", @@ -6031,16 +6081,16 @@ } }, "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.2.2.tgz", + "integrity": "sha512-+JpPZam9w5DuJ3Q67SqsMGtiHKENSMRVoxvArfJZK01/BfLEObtZ6orJa/MtoGNR/rfMgp5837T41PAmTwAv/A==", "dev": true, "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" }, "engines": { - "node": ">=4" + "node": ">=0.4.0" } }, "node_modules/esquery": { @@ -6207,6 +6257,11 @@ "node": ">=0.10.0" } }, + "node_modules/execa/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==" + }, "node_modules/execa/node_modules/which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", @@ -6303,9 +6358,9 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fast-glob": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", - "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -6493,12 +6548,12 @@ } }, "node_modules/flat-cache": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.0.tgz", - "integrity": "sha512-OHx4Qwrrt0E4jEIcI5/Xb+f+QmJYNj2rrK8wiIdQOIrB9WrrJL8cjZvXdXuBTkkEwEqLycb5BeZDV1o2i9bTew==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.1.tgz", + "integrity": "sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q==", "dev": true, "dependencies": { - "flatted": "^3.2.7", + "flatted": "^3.2.9", "keyv": "^4.5.3", "rimraf": "^3.0.2" }, @@ -6506,30 +6561,87 @@ "node": ">=12.0.0" } }, - "node_modules/flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", - "dev": true - }, - "node_modules/fn.name": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", - "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" + "node_modules/flat-cache/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } }, - "node_modules/follow-redirects": { - "version": "1.15.3", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", - "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, + "node_modules/flat-cache/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/flat-cache/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/flat-cache/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/flatted": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", + "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", + "dev": true + }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" + }, + "node_modules/follow-redirects": { + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", + "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, "peerDependenciesMeta": { "debug": { "optional": true @@ -6551,16 +6663,18 @@ "integrity": "sha512-k6GAGDyqLe9JaebCsFCoudPPWfihKu8pylYXRlqP1J7ms39iPoTtk2fviNglIeQEwdh0bQeKJ01ZPyuyQvKzwg==" }, "node_modules/foreground-child": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", - "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", - "dev": true, + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", "dependencies": { "cross-spawn": "^7.0.0", - "signal-exit": "^3.0.2" + "signal-exit": "^4.0.1" }, "engines": { - "node": ">=8.0.0" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/form-data": { @@ -6647,15 +6761,6 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/fs-minipass/node_modules/minipass": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.3.tgz", - "integrity": "sha512-LhbbwCfz3vsb12j/WkWQPZfKTsgqIe1Nf/ti1pKjYESGLHIVjWU96G9/ljLH4F9mWNVhlQOm0VySdAWzf05dpg==", - "dev": true, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, "node_modules/fs-readfile-promise": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/fs-readfile-promise/-/fs-readfile-promise-2.0.1.tgz", @@ -6701,23 +6806,13 @@ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/function.prototype.name": { "version": "1.1.6", @@ -6746,6 +6841,63 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/gauge": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", + "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "dev": true, + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/gauge/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 + }, + "node_modules/gauge/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 + }, + "node_modules/gauge/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, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/gauge/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -6764,14 +6916,14 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", - "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", + "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", + "function-bind": "^1.1.2", "has-proto": "^1.0.1", - "has-symbols": "^1.0.3" + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -6808,6 +6960,23 @@ "node": ">=6.9.0" } }, + "node_modules/get-pkg-repo/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/get-pkg-repo/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 + }, "node_modules/get-pkg-repo/node_modules/hosted-git-info": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", @@ -6832,6 +7001,32 @@ "node": ">=10" } }, + "node_modules/get-pkg-repo/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, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/get-pkg-repo/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/get-pkg-repo/node_modules/through2": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", @@ -6842,6 +7037,41 @@ "xtend": "~4.0.1" } }, + "node_modules/get-pkg-repo/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/get-pkg-repo/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/get-port": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", @@ -7012,32 +7242,6 @@ "node": ">= 6" } }, - "node_modules/glob/node_modules/foreground-child": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", - "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", - "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/global-dirs": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", @@ -7109,7 +7313,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dev": true, "dependencies": { "get-intrinsic": "^1.1.3" }, @@ -7222,15 +7425,6 @@ "hapi": ">=17.x.x" } }, - "node_modules/hapi-auth-basic/node_modules/hoek": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-5.0.4.tgz", - "integrity": "sha512-Alr4ZQgoMlnere5FZJsIyfIjORBqZll5POhDsF4q64dPuJR6rNxXdDxtHSQq8OXRurhmx+PWYEE8bXRROY8h0w==", - "deprecated": "This version has been deprecated in accordance with the hapi support policy (hapi.im/support). Please upgrade to the latest version to get the best features, bug fixes, and security patches. If you are unable to upgrade at this time, paid support is available for older versions (hapi.im/commercial).", - "engines": { - "node": ">=8.9.0" - } - }, "node_modules/hapi-auth-bearer-token": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/hapi-auth-bearer-token/-/hapi-auth-bearer-token-8.0.0.tgz", @@ -7253,17 +7447,17 @@ "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" }, "node_modules/hapi-swagger": { - "version": "17.1.0", - "resolved": "https://registry.npmjs.org/hapi-swagger/-/hapi-swagger-17.1.0.tgz", - "integrity": "sha512-5aMFTqbpvLs6xYiHUv/VTImPS9GAInudOGJ7Sl6QAbEwQhSrqvJysAlcIgVkc5sD6XM0b3lTYvc0YWdZLN3sRA==", + "version": "17.2.0", + "resolved": "https://registry.npmjs.org/hapi-swagger/-/hapi-swagger-17.2.0.tgz", + "integrity": "sha512-vcLz3OK7WLFsuY7cLgPJAulnuvkGSIE3XVbeD1XzoPXtb2jmuDUTg2yvrXx32EwlhSsyT/RP1MIVzHuc8KxvQw==", "dependencies": { - "@apidevtools/json-schema-ref-parser": "^10.1.0", + "@apidevtools/json-schema-ref-parser": "^11.1.0", "@hapi/boom": "^10.0.1", "@hapi/hoek": "^11.0.2", - "handlebars": "^4.7.7", - "http-status": "^1.6.2", + "handlebars": "^4.7.8", + "http-status": "^1.7.3", "swagger-parser": "^10.0.3", - "swagger-ui-dist": "^5.1.0" + "swagger-ui-dist": "^5.9.1" }, "engines": { "node": ">=16.0.0" @@ -7273,40 +7467,6 @@ "joi": "17.x" } }, - "node_modules/hapi-swagger/node_modules/@apidevtools/json-schema-ref-parser": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-10.1.0.tgz", - "integrity": "sha512-3e+viyMuXdrcK8v5pvP+SDoAQ77FH6OyRmuK48SZKmdHJRFm87RsSs8qm6kP39a/pOPURByJw+OXzQIqcfmKtA==", - "dependencies": { - "@jsdevtools/ono": "^7.1.3", - "@types/json-schema": "^7.0.11", - "@types/lodash.clonedeep": "^4.5.7", - "js-yaml": "^4.1.0", - "lodash.clonedeep": "^4.5.0" - }, - "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://github.com/sponsors/philsturgeon" - } - }, - "node_modules/hapi-swagger/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, - "node_modules/hapi-swagger/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/hapi/node_modules/accept": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/accept/-/accept-3.1.3.tgz", @@ -7526,12 +7686,10 @@ } }, "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dependencies": { - "function-bind": "^1.1.1" - }, + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.4.tgz", + "integrity": "sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==", + "dev": true, "engines": { "node": ">= 0.4.0" } @@ -7573,12 +7731,11 @@ } }, "node_modules/has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", - "dev": true, + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", + "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", "dependencies": { - "get-intrinsic": "^1.1.1" + "get-intrinsic": "^1.2.2" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -7655,6 +7812,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/highlight.js": { "version": "10.7.3", "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", @@ -7670,10 +7838,13 @@ "deprecated": "Use the 'highlight.js' package instead https://npm.im/highlight.js" }, "node_modules/hoek": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-6.1.3.tgz", - "integrity": "sha512-YXXAAhmF9zpQbC7LEcREFtXfGq5K1fmd+4PHkBq8NUqmzW3G+Dq10bI/i0KucLRwss3YYFQ0fSfoxBZYiGUqtQ==", - "deprecated": "This module has moved and is now available at @hapi/hoek. Please update your dependencies as this version is no longer maintained an may contain bugs and security issues." + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-5.0.4.tgz", + "integrity": "sha512-Alr4ZQgoMlnere5FZJsIyfIjORBqZll5POhDsF4q64dPuJR6rNxXdDxtHSQq8OXRurhmx+PWYEE8bXRROY8h0w==", + "deprecated": "This version has been deprecated in accordance with the hapi support policy (hapi.im/support). Please upgrade to the latest version to get the best features, bug fixes, and security patches. If you are unable to upgrade at this time, paid support is available for older versions (hapi.im/commercial).", + "engines": { + "node": ">=8.9.0" + } }, "node_modules/hosted-git-info": { "version": "5.2.1", @@ -7758,9 +7929,9 @@ } }, "node_modules/http-status": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/http-status/-/http-status-1.6.2.tgz", - "integrity": "sha512-oUExvfNckrpTpDazph7kNG8sQi5au3BeTo0idaZFXEhTaJKu7GNJCLHI0rYY2wljm548MSTM+Ljj/c6anqu2zQ==", + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/http-status/-/http-status-1.7.3.tgz", + "integrity": "sha512-GS8tL1qHT2nBCMJDYMHGkkkKQLNkIAHz37vgO68XKvzv+XyqB4oh/DfmMHdtRzfqSJPj1xKG2TaELZtlCz6BEQ==", "engines": { "node": ">= 0.4.0" } @@ -8101,6 +8272,12 @@ "node": ">=8" } }, + "node_modules/infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "dev": true + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -8126,13 +8303,13 @@ } }, "node_modules/internal-slot": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", - "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz", + "integrity": "sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==", "dev": true, "dependencies": { - "get-intrinsic": "^1.2.0", - "has": "^1.0.3", + "get-intrinsic": "^1.2.2", + "hasown": "^2.0.0", "side-channel": "^1.0.4" }, "engines": { @@ -8147,6 +8324,14 @@ "node": ">= 0.10" } }, + "node_modules/invert-kv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", + "engines": { + "node": ">=4" + } + }, "node_modules/ip": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", @@ -8280,11 +8465,11 @@ } }, "node_modules/is-core-module": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", - "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==", + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", "dependencies": { - "has": "^1.0.3" + "hasown": "^2.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -8675,9 +8860,9 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "node_modules/istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true, "engines": { "node": ">=8" @@ -8719,21 +8904,63 @@ "semver": "bin/semver.js" } }, - "node_modules/istanbul-lib-processinfo": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz", - "integrity": "sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg==", + "node_modules/istanbul-lib-processinfo": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz", + "integrity": "sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg==", + "dev": true, + "dependencies": { + "archy": "^1.0.0", + "cross-spawn": "^7.0.3", + "istanbul-lib-coverage": "^3.2.0", + "p-map": "^3.0.0", + "rimraf": "^3.0.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-processinfo/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/istanbul-lib-processinfo/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/istanbul-lib-processinfo/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "dependencies": { - "archy": "^1.0.0", - "cross-spawn": "^7.0.3", - "istanbul-lib-coverage": "^3.2.0", - "p-map": "^3.0.0", - "rimraf": "^3.0.0", - "uuid": "^8.3.2" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=8" + "node": "*" } }, "node_modules/istanbul-lib-processinfo/node_modules/p-map": { @@ -8748,6 +8975,21 @@ "node": ">=8" } }, + "node_modules/istanbul-lib-processinfo/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/istanbul-lib-report": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", @@ -8805,16 +9047,16 @@ } }, "node_modules/iterator.prototype": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.0.tgz", - "integrity": "sha512-rjuhAk1AJ1fssphHD0IFV6TWL40CwRZ53FrztKx43yk2v6rguBYsY4Bj1VU4HmoMmKwZUlx7mfnhDf9cOp4YTw==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz", + "integrity": "sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==", "dev": true, "dependencies": { - "define-properties": "^1.1.4", - "get-intrinsic": "^1.1.3", + "define-properties": "^1.2.1", + "get-intrinsic": "^1.2.1", "has-symbols": "^1.0.3", - "has-tostringtag": "^1.0.0", - "reflect.getprototypeof": "^1.0.3" + "reflect.getprototypeof": "^1.0.4", + "set-function-name": "^2.0.1" } }, "node_modules/jackspeak": { @@ -8851,9 +9093,9 @@ "dev": true }, "node_modules/joi": { - "version": "17.10.0", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.10.0.tgz", - "integrity": "sha512-hrazgRSlhzacZ69LdcKfhi3Vu13z2yFfoAzmEov3yFIJlatTdVGUW6vle1zjH8qkzdCn/qGw8rapjqsObbYXAg==", + "version": "17.11.0", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.11.0.tgz", + "integrity": "sha512-NgB+lZLNoqISVy1rZocE9PZI36bL/77ie924Ri43yEvi9GUUMPeyVIr8KdFTMUlby1p0PBYMk9spIxEUQYqrJQ==", "dependencies": { "@hapi/hoek": "^9.0.0", "@hapi/topo": "^5.0.0", @@ -8881,13 +9123,11 @@ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" @@ -9057,19 +9297,6 @@ "underscore": "1.12.1" } }, - "node_modules/jsonpath/node_modules/esprima": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.2.2.tgz", - "integrity": "sha512-+JpPZam9w5DuJ3Q67SqsMGtiHKENSMRVoxvArfJZK01/BfLEObtZ6orJa/MtoGNR/rfMgp5837T41PAmTwAv/A==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/jsonpath/node_modules/underscore": { "version": "1.12.1", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz", @@ -9188,9 +9415,9 @@ } }, "node_modules/keyv": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz", - "integrity": "sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==", + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, "dependencies": { "json-buffer": "3.0.1" @@ -9309,6 +9536,17 @@ "node": ">=0.10.0" } }, + "node_modules/lcid": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "dependencies": { + "invert-kv": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -9471,16 +9709,27 @@ "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" }, "node_modules/logform": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.5.1.tgz", - "integrity": "sha512-9FyqAm9o9NKKfiAKfZoYo9bGXXuwMkxQiQttkT4YjjVtQVIQtK6LmVtlxmCaFswo6N4AfEkHqZTV0taDtPotNg==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.6.0.tgz", + "integrity": "sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ==", "dependencies": { - "@colors/colors": "1.5.0", + "@colors/colors": "1.6.0", "@types/triple-beam": "^1.3.2", "fecha": "^4.2.0", "ms": "^2.1.1", "safe-stable-stringify": "^2.3.1", "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/logform/node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "engines": { + "node": ">=0.1.90" } }, "node_modules/long": { @@ -9586,6 +9835,15 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/make-fetch-happen/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/map-age-cleaner": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", @@ -9660,11 +9918,6 @@ "resolved": "https://registry.npmjs.org/markdown-it-lazy-headers/-/markdown-it-lazy-headers-0.1.3.tgz", "integrity": "sha512-65BxqvmYLpVifv6MvTElthY8zvZ/TpZBCdshr/mTpsFkqwcwWtfD3YoSE7RYSn7ugnEAAaj2gywszq+hI/Pxgg==" }, - "node_modules/markdown-it/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, "node_modules/marked": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", @@ -10072,11 +10325,11 @@ } }, "node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", "engines": { - "node": ">=8" + "node": ">=16 || 14 >=14.17" } }, "node_modules/minipass-collect": { @@ -10120,15 +10373,6 @@ "encoding": "^0.1.13" } }, - "node_modules/minipass-fetch/node_modules/minipass": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.3.tgz", - "integrity": "sha512-LhbbwCfz3vsb12j/WkWQPZfKTsgqIe1Nf/ti1pKjYESGLHIVjWU96G9/ljLH4F9mWNVhlQOm0VySdAWzf05dpg==", - "dev": true, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, "node_modules/minipass-flush": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", @@ -10311,11 +10555,11 @@ } }, "node_modules/mongodb": { - "version": "5.8.1", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-5.8.1.tgz", - "integrity": "sha512-wKyh4kZvm6NrCPH8AxyzXm3JBoEf4Xulo0aUWh3hCgwgYJxyQ1KLST86ZZaSWdj6/kxYUA3+YZuyADCE61CMSg==", + "version": "5.9.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-5.9.0.tgz", + "integrity": "sha512-g+GCMHN1CoRUA+wb1Agv0TI4YTSiWr42B5ulkiAfLLHitGK1R+PkSAf3Lr5rPZwi/3F04LiaZEW0Kxro9Fi2TA==", "dependencies": { - "bson": "^5.4.0", + "bson": "^5.5.0", "mongodb-connection-string-url": "^2.6.0", "socks": "^2.7.1" }, @@ -10360,13 +10604,13 @@ } }, "node_modules/mongoose": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-7.5.3.tgz", - "integrity": "sha512-QyYzhZusux0wIJs+4rYyHvel0kJm0CT887trNd1WAB3iQnDuJow0xEnjETvuS/cTjHQUVPihOpN7OHLlpJc52w==", + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-7.6.4.tgz", + "integrity": "sha512-kadPkS/f5iZJrrMxxOvSoOAErXmdnb28lMvHmuYgmV1ZQTpRqpp132PIPHkJMbG4OC2H0eSXYw/fNzYTH+LUcw==", "dependencies": { - "bson": "^5.4.0", + "bson": "^5.5.0", "kareem": "2.5.1", - "mongodb": "5.8.1", + "mongodb": "5.9.0", "mpath": "0.9.0", "mquery": "5.0.0", "ms": "2.1.3", @@ -10380,11 +10624,6 @@ "url": "https://opencollective.com/mongoose" } }, - "node_modules/mongoose/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, "node_modules/mpath": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", @@ -10405,9 +10644,9 @@ } }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/mustache": { "version": "4.2.0", @@ -10445,9 +10684,9 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "node_modules/nan": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz", - "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==" + "version": "2.18.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.18.0.tgz", + "integrity": "sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==" }, "node_modules/natural-compare": { "version": "1.4.0", @@ -10578,16 +10817,16 @@ } }, "node_modules/node-gyp": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-9.4.0.tgz", - "integrity": "sha512-dMXsYP6gc9rRbejLXmTbVRYjAHw7ppswsKyMxuxJxxOHzluIO1rGp9TOQgjFJ+2MCqcOcQTOPB/8Xwhr+7s4Eg==", + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-9.4.1.tgz", + "integrity": "sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ==", "dev": true, "dependencies": { "env-paths": "^2.2.0", "exponential-backoff": "^3.1.1", "glob": "^7.1.4", "graceful-fs": "^4.2.6", - "make-fetch-happen": "^11.0.3", + "make-fetch-happen": "^10.0.3", "nopt": "^6.0.0", "npmlog": "^6.0.0", "rimraf": "^3.0.2", @@ -10602,14 +10841,14 @@ "node": "^12.13 || ^14.13 || >=16" } }, - "node_modules/node-gyp/node_modules/are-we-there-yet": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", - "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", + "node_modules/node-gyp/node_modules/@npmcli/fs": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.2.tgz", + "integrity": "sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==", "dev": true, "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" + "@gar/promisify": "^1.1.3", + "semver": "^7.3.5" }, "engines": { "node": "^12.13.0 || ^14.15.0 || >=16.0.0" @@ -10625,25 +10864,87 @@ "concat-map": "0.0.1" } }, - "node_modules/node-gyp/node_modules/gauge": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", - "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "node_modules/node-gyp/node_modules/cacache": { + "version": "16.1.3", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-16.1.3.tgz", + "integrity": "sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==", "dev": true, "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.3", - "console-control-strings": "^1.1.0", - "has-unicode": "^2.0.1", - "signal-exit": "^3.0.7", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.5" + "@npmcli/fs": "^2.1.0", + "@npmcli/move-file": "^2.0.0", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "glob": "^8.0.1", + "infer-owner": "^1.0.4", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "mkdirp": "^1.0.4", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^9.0.0", + "tar": "^6.1.11", + "unique-filename": "^2.0.0" }, "engines": { "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/node-gyp/node_modules/cacache/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/node-gyp/node_modules/cacache/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/node-gyp/node_modules/cacache/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-gyp/node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/node-gyp/node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -10664,6 +10965,33 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/node-gyp/node_modules/make-fetch-happen": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz", + "integrity": "sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==", + "dev": true, + "dependencies": { + "agentkeepalive": "^4.2.1", + "cacache": "^16.1.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^2.0.3", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^9.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, "node_modules/node-gyp/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -10676,48 +11004,84 @@ "node": "*" } }, - "node_modules/node-gyp/node_modules/nopt": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz", - "integrity": "sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==", + "node_modules/node-gyp/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/node-gyp/node_modules/minipass-fetch": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-2.1.2.tgz", + "integrity": "sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==", + "dev": true, + "dependencies": { + "minipass": "^3.1.6", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/node-gyp/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/node-gyp/node_modules/ssri": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz", + "integrity": "sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==", "dev": true, "dependencies": { - "abbrev": "^1.0.0" - }, - "bin": { - "nopt": "bin/nopt.js" + "minipass": "^3.1.1" }, "engines": { "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/node-gyp/node_modules/npmlog": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", - "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", + "node_modules/node-gyp/node_modules/unique-filename": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-2.0.1.tgz", + "integrity": "sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==", "dev": true, "dependencies": { - "are-we-there-yet": "^3.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^4.0.3", - "set-blocking": "^2.0.0" + "unique-slug": "^3.0.0" }, "engines": { "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/node-gyp/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "node_modules/node-gyp/node_modules/unique-slug": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-3.0.0.tgz", + "integrity": "sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==", "dev": true, "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "imurmurhash": "^0.1.4" }, "engines": { - "node": ">= 6" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, "node_modules/node-preload": { @@ -10760,13 +11124,13 @@ "dev": true }, "node_modules/nodemon": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.0.1.tgz", - "integrity": "sha512-g9AZ7HmkhQkqXkRc20w+ZfQ73cHLbE8hnPbtaFbFtCumZsjyMhKk9LajQ07U5Ux28lvFjZ5X7HvWR1xzU8jHVw==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.0.2.tgz", + "integrity": "sha512-9qIN2LNTrEzpOPBaWHTm4Asy1LxXLSickZStAQ4IZe7zsoIpD/A7LWxhZV3t4Zu352uBcqVnRsDXSMR2Sc3lTA==", "dev": true, "dependencies": { "chokidar": "^3.5.2", - "debug": "^3.2.7", + "debug": "^4", "ignore-by-default": "^1.0.1", "minimatch": "^3.1.2", "pstree.remy": "^1.1.8", @@ -10797,15 +11161,6 @@ "concat-map": "0.0.1" } }, - "node_modules/nodemon/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, "node_modules/nodemon/node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -10839,6 +11194,21 @@ "node": ">=4" } }, + "node_modules/nopt": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz", + "integrity": "sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==", + "dev": true, + "dependencies": { + "abbrev": "^1.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, "node_modules/normalize-package-data": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-5.0.0.tgz", @@ -10899,9 +11269,9 @@ } }, "node_modules/npm-check-updates": { - "version": "16.14.6", - "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-16.14.6.tgz", - "integrity": "sha512-sJ6w4AmSDP7YzBXah94Ul2JhiIbjBDfx9XYgib15um2wtiQkOyjE7Lov3MNUSQ84Ry7T81mE4ynMbl/mGbK4HQ==", + "version": "16.14.11", + "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-16.14.11.tgz", + "integrity": "sha512-0MMWGbGci22Pu77bR9jRsy5qgxdQSJVqNtSyyFeubDPtbcU36z4gjEDITu26PMabFWPNkAoVfKF31M3uKUvzFg==", "dev": true, "dependencies": { "chalk": "^5.3.0", @@ -10945,24 +11315,6 @@ "node": ">=14.14" } }, - "node_modules/npm-check-updates/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/npm-check-updates/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, "node_modules/npm-check-updates/node_modules/chalk": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", @@ -10984,51 +11336,6 @@ "node": ">=14" } }, - "node_modules/npm-check-updates/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/npm-check-updates/node_modules/rimraf": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.5.tgz", - "integrity": "sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==", - "dev": true, - "dependencies": { - "glob": "^10.3.7" - }, - "bin": { - "rimraf": "dist/esm/bin.mjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm-check-updates/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, "node_modules/npm-check-updates/node_modules/strip-json-comments": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.1.tgz", @@ -11042,9 +11349,9 @@ } }, "node_modules/npm-install-checks": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-6.2.0.tgz", - "integrity": "sha512-744wat5wAAHsxa4590mWO0tJ8PKxR8ORZsH9wGpQc3nWTzozMAgBN/XyqYw7mg3yqLM8dLwEnwSfKMmXAjF69g==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-6.3.0.tgz", + "integrity": "sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw==", "dev": true, "dependencies": { "semver": "^7.1.1" @@ -11134,6 +11441,15 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/npm-registry-fetch/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/npm-run-path": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", @@ -11153,6 +11469,21 @@ "node": ">=4" } }, + "node_modules/npmlog": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", + "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", + "dev": true, + "dependencies": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, "node_modules/nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", @@ -11234,6 +11565,12 @@ "wrap-ansi": "^6.2.0" } }, + "node_modules/nyc/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 + }, "node_modules/nyc/node_modules/find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", @@ -11247,6 +11584,19 @@ "node": ">=8" } }, + "node_modules/nyc/node_modules/foreground-child": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", + "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/nyc/node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -11330,6 +11680,53 @@ "node": ">=8" } }, + "node_modules/nyc/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/nyc/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 + }, + "node_modules/nyc/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, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/nyc/node_modules/wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", @@ -11432,35 +11829,6 @@ "url": "https://github.com/Mermade/oas-kit?sponsor=1" } }, - "node_modules/oas-resolver/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/oas-resolver/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/oas-resolver/node_modules/yaml": { "version": "1.10.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", @@ -11469,31 +11837,6 @@ "node": ">= 6" } }, - "node_modules/oas-resolver/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/oas-resolver/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "engines": { - "node": ">=12" - } - }, "node_modules/oas-schema-walker": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/oas-schema-walker/-/oas-schema-walker-1.1.5.tgz", @@ -11642,9 +11985,9 @@ } }, "node_modules/object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -11822,44 +12165,10 @@ "qs": "^6.9.3" }, "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/anttiviljami" - } - }, - "node_modules/openapi-backend/node_modules/@apidevtools/json-schema-ref-parser": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-11.1.0.tgz", - "integrity": "sha512-g/VW9ZQEFJAOwAyUb8JFf7MLiLy2uEB4rU270rGzDwICxnxMlPy0O11KVePSgS36K1NI29gSlK84n5INGhd4Ag==", - "dependencies": { - "@jsdevtools/ono": "^7.1.3", - "@types/json-schema": "^7.0.13", - "@types/lodash.clonedeep": "^4.5.7", - "js-yaml": "^4.1.0", - "lodash.clonedeep": "^4.5.0" - }, - "engines": { - "node": ">= 16" + "node": ">=12.0.0" }, "funding": { - "url": "https://github.com/sponsors/philsturgeon" - } - }, - "node_modules/openapi-backend/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, - "node_modules/openapi-backend/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "url": "https://github.com/sponsors/anttiviljami" } }, "node_modules/openapi-sampler": { @@ -11915,6 +12224,19 @@ "node": ">= 0.8.0" } }, + "node_modules/os-locale": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", + "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", + "dependencies": { + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/os-shim": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/os-shim/-/os-shim-0.1.3.tgz", @@ -12075,6 +12397,15 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/pacote/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/parent-module": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-2.0.0.tgz", @@ -12740,9 +13071,9 @@ } }, "node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "engines": { "node": ">=6" } @@ -12878,24 +13209,6 @@ "require-from-string": "^2.0.2" } }, - "node_modules/rc-config-loader/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/rc-config-loader/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/rc/node_modules/ini": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", @@ -13179,15 +13492,15 @@ } }, "node_modules/reflect.getprototypeof": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.3.tgz", - "integrity": "sha512-TTAOZpkJ2YLxl7mVHWrNo3iDMEkYlva/kgFcXndqMgbo/AZUmmavEkdXV+hXtE4P8xdyEKRzalaFqZVuwIk/Nw==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.4.tgz", + "integrity": "sha512-ECkTw8TmJwW60lOTR+ZkODISW6RQ8+2CL3COqtiJKLd6MmB45hN51HprHFziKLGkAuTGQhBb91V8cy+KHlaCjw==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "get-intrinsic": "^1.1.1", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", "globalthis": "^1.0.3", "which-builtin-type": "^1.1.3" }, @@ -13212,14 +13525,14 @@ "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" }, "node_modules/regexp.prototype.flags": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", - "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", + "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", - "functions-have-names": "^1.2.3" + "set-function-name": "^2.0.0" }, "engines": { "node": ">= 0.4" @@ -13376,6 +13689,12 @@ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, + "node_modules/replace/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 + }, "node_modules/replace/node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", @@ -13458,6 +13777,32 @@ "node": ">=8" } }, + "node_modules/replace/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, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/replace/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/replace/node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -13613,9 +13958,9 @@ } }, "node_modules/resolve": { - "version": "1.22.4", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz", - "integrity": "sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==", + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", @@ -13696,62 +14041,23 @@ } }, "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.5.tgz", + "integrity": "sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==", "dev": true, "dependencies": { - "glob": "^7.1.3" + "glob": "^10.3.7" }, "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rimraf/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/rimraf/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "rimraf": "dist/esm/bin.mjs" }, "engines": { - "node": "*" + "node": ">=14" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/rimraf/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -13775,13 +14081,13 @@ } }, "node_modules/safe-array-concat": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.0.tgz", - "integrity": "sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz", + "integrity": "sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", + "get-intrinsic": "^1.2.1", "has-symbols": "^1.0.3", "isarray": "^2.0.5" }, @@ -14016,11 +14322,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, "node_modules/serialize-error": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-8.1.0.tgz", @@ -14065,6 +14366,34 @@ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" }, + "node_modules/set-function-length": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", + "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", + "dependencies": { + "define-data-property": "^1.1.1", + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", + "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", @@ -14115,6 +14444,14 @@ "shins": "shins.js" } }, + "node_modules/shins/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, "node_modules/shins/node_modules/camelcase": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", @@ -14187,14 +14524,6 @@ "uglify-to-browserify": "~1.0.0" } }, - "node_modules/shins/node_modules/window-size": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", - "integrity": "sha512-1pTPQDKTdd61ozlKGNCjhNRd+KPmgLSGa3mZTHoOliaGcESD8G1PXhh7c1fgiPjVbNVfgy2Faw4BI8/m0cC8Mg==", - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/shins/node_modules/wordwrap": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", @@ -14289,9 +14618,15 @@ "integrity": "sha512-Wv6BjQ5zbhW7VFefWusVP33T/EM0vYikCaQ2qR8yULbsilAT8/wQaXvuQ3ptGLpoKx+lihJE3y2UTgKDyyNHZQ==" }, "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==" + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, "node_modules/sigstore": { "version": "1.9.0", @@ -14461,17 +14796,93 @@ "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", "dev": true, "dependencies": { - "foreground-child": "^2.0.0", - "is-windows": "^1.0.2", - "make-dir": "^3.0.0", - "rimraf": "^3.0.0", - "signal-exit": "^3.0.2", - "which": "^2.0.1" + "foreground-child": "^2.0.0", + "is-windows": "^1.0.2", + "make-dir": "^3.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "which": "^2.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/spawn-wrap/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/spawn-wrap/node_modules/foreground-child": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", + "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/spawn-wrap/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/spawn-wrap/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/spawn-wrap/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" }, - "engines": { - "node": ">=8" + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/spawn-wrap/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 + }, "node_modules/spdx-correct": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", @@ -14499,9 +14910,9 @@ } }, "node_modules/spdx-license-ids": { - "version": "3.0.13", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz", - "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==", + "version": "3.0.16", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.16.tgz", + "integrity": "sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==", "dev": true }, "node_modules/split": { @@ -14563,15 +14974,6 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/ssri/node_modules/minipass": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.3.tgz", - "integrity": "sha512-LhbbwCfz3vsb12j/WkWQPZfKTsgqIe1Nf/ti1pKjYESGLHIVjWU96G9/ljLH4F9mWNVhlQOm0VySdAWzf05dpg==", - "dev": true, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, "node_modules/stack-trace": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", @@ -14700,6 +15102,17 @@ "node": ">=4" } }, + "node_modules/standard-version/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, "node_modules/standard-version/node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -14715,6 +15128,12 @@ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, + "node_modules/standard-version/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 + }, "node_modules/standard-version/node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", @@ -14733,6 +15152,32 @@ "node": ">=4" } }, + "node_modules/standard-version/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, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/standard-version/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/standard-version/node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -14745,6 +15190,74 @@ "node": ">=4" } }, + "node_modules/standard-version/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/standard-version/node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/standard-version/node_modules/wrap-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/standard-version/node_modules/wrap-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/standard-version/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/static-eval": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/static-eval/-/static-eval-2.0.2.tgz", @@ -14790,16 +15303,19 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "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==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/string-width-cjs": { @@ -14816,10 +15332,26 @@ "node": ">=8" } }, + "node_modules/string-width-cjs/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==" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/string.prototype.matchall": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.9.tgz", - "integrity": "sha512-6i5hL3MqG/K2G43mWXWgP+qizFW/QH/7kCNN13JrJS5q48FN5IKksLDscexKP3dnmB6cdm9jlNgAsWNLpSykmA==", + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.10.tgz", + "integrity": "sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ==", "dev": true, "dependencies": { "call-bind": "^1.0.2", @@ -14829,6 +15361,7 @@ "has-symbols": "^1.0.3", "internal-slot": "^1.0.5", "regexp.prototype.flags": "^1.5.0", + "set-function-name": "^2.0.0", "side-channel": "^1.0.4" }, "funding": { @@ -14836,14 +15369,14 @@ } }, "node_modules/string.prototype.trim": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz", - "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==", + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", + "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" }, "engines": { "node": ">= 0.4" @@ -14853,28 +15386,28 @@ } }, "node_modules/string.prototype.trimend": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", - "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", + "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/string.prototype.trimstart": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", - "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", + "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -14909,14 +15442,17 @@ "dev": true }, "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dependencies": { - "ansi-regex": "^5.0.1" + "ansi-regex": "^6.0.1" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, "node_modules/strip-ansi-cjs": { @@ -14931,6 +15467,17 @@ "node": ">=8" } }, + "node_modules/strip-ansi/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, "node_modules/strip-bom": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", @@ -15006,9 +15553,9 @@ } }, "node_modules/swagger-ui-dist": { - "version": "5.4.2", - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.4.2.tgz", - "integrity": "sha512-vT5QxP/NOr9m4gLZl+SpavWI3M9Fdh30+Sdw9rEtZbkqNmNNEPhjXas2xTD9rsJYYdLzAiMfwXvtooWH3xbLJA==" + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.9.3.tgz", + "integrity": "sha512-/OgHfO96RWXF+p/EOjEnvKNEh94qAG/VHukgmVKh5e6foX9kas1WbjvQnDDj0sSTAMr9MHRBqAWytDcQi0VOrg==" }, "node_modules/swagger2openapi": { "version": "6.2.3", @@ -15116,6 +15663,11 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, + "node_modules/swagger2openapi/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==" + }, "node_modules/swagger2openapi/node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", @@ -15186,6 +15738,30 @@ "node": ">=8" } }, + "node_modules/swagger2openapi/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==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/swagger2openapi/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/swagger2openapi/node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -15349,21 +15925,56 @@ "safe-buffer": "~5.1.0" } }, - "node_modules/tap-parser": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tap-parser/-/tap-parser-1.2.2.tgz", - "integrity": "sha512-uXKcosa0qoSjeh73dhmX+OpJvpigDxUciOhBcbGUKtmwzEFJjUT1Ql5dpg4M9I1UjXT9b+6n1W05FB8QmKossA==", + "node_modules/tap-parser": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tap-parser/-/tap-parser-1.2.2.tgz", + "integrity": "sha512-uXKcosa0qoSjeh73dhmX+OpJvpigDxUciOhBcbGUKtmwzEFJjUT1Ql5dpg4M9I1UjXT9b+6n1W05FB8QmKossA==", + "dev": true, + "dependencies": { + "events-to-array": "^1.0.1", + "inherits": "~2.0.1", + "js-yaml": "^3.2.7" + }, + "bin": { + "tap-parser": "bin/cmd.js" + }, + "optionalDependencies": { + "readable-stream": "^2" + } + }, + "node_modules/tap-parser/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/tap-parser/node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/tap-parser/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", "dev": true, "dependencies": { - "events-to-array": "^1.0.1", - "inherits": "~2.0.1", - "js-yaml": "^3.2.7" + "argparse": "^1.0.7", + "esprima": "^4.0.0" }, "bin": { - "tap-parser": "bin/cmd.js" - }, - "optionalDependencies": { - "readable-stream": "^2" + "js-yaml": "bin/js-yaml.js" } }, "node_modules/tap-spec": { @@ -15572,6 +16183,15 @@ "node": "*" } }, + "node_modules/tape/node_modules/object-inspect": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/tapes": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/tapes/-/tapes-4.1.0.tgz", @@ -15630,9 +16250,9 @@ } }, "node_modules/tar": { - "version": "6.1.15", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.15.tgz", - "integrity": "sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz", + "integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==", "dev": true, "dependencies": { "chownr": "^2.0.0", @@ -15670,6 +16290,15 @@ "node": ">=8" } }, + "node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/tarn": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/tarn/-/tarn-3.0.2.tgz", @@ -16130,6 +16759,11 @@ "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==", "dev": true }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, "node_modules/unique-filename": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz", @@ -16187,9 +16821,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", - "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", "dev": true, "funding": [ { @@ -16473,13 +17107,13 @@ "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==" }, "node_modules/which-typed-array": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz", - "integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz", + "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==", "dev": true, "dependencies": { "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", + "call-bind": "^1.0.4", "for-each": "^0.3.3", "gopd": "^1.0.1", "has-tostringtag": "^1.0.0" @@ -16526,6 +17160,14 @@ "node": ">=4" } }, + "node_modules/widdershins/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, "node_modules/widdershins/node_modules/cliui": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", @@ -16557,14 +17199,6 @@ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" }, - "node_modules/widdershins/node_modules/invert-kv": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", - "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", - "engines": { - "node": ">=4" - } - }, "node_modules/widdershins/node_modules/is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", @@ -16573,17 +17207,6 @@ "node": ">=4" } }, - "node_modules/widdershins/node_modules/lcid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", - "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", - "dependencies": { - "invert-kv": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/widdershins/node_modules/linkify-it": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz", @@ -16619,19 +17242,6 @@ "markdown-it": "bin/markdown-it.js" } }, - "node_modules/widdershins/node_modules/os-locale": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", - "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", - "dependencies": { - "execa": "^1.0.0", - "lcid": "^2.0.0", - "mem": "^4.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/widdershins/node_modules/p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", @@ -16798,48 +17408,45 @@ "string-width": "^1.0.2 || 2 || 3 || 4" } }, - "node_modules/widest-line": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", - "integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==", + "node_modules/wide-align/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 + }, + "node_modules/wide-align/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, "dependencies": { - "string-width": "^5.0.1" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, - "node_modules/widest-line/node_modules/ansi-regex": { + "node_modules/wide-align/node_modules/strip-ansi": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, - "engines": { - "node": ">=12" + "dependencies": { + "ansi-regex": "^5.0.1" }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "engines": { + "node": ">=8" } }, - "node_modules/widest-line/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true - }, - "node_modules/widest-line/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "node_modules/widest-line": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", + "integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==", "dev": true, "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" + "string-width": "^5.0.1" }, "engines": { "node": ">=12" @@ -16848,19 +17455,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/widest-line/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, + "node_modules/window-size": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha512-1pTPQDKTdd61ozlKGNCjhNRd+KPmgLSGa3mZTHoOliaGcESD8G1PXhh7c1fgiPjVbNVfgy2Faw4BI8/m0cC8Mg==", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "node": ">= 0.8.0" } }, "node_modules/winston": { @@ -16885,16 +17485,16 @@ } }, "node_modules/winston-transport": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.5.0.tgz", - "integrity": "sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.6.0.tgz", + "integrity": "sha512-wbBA9PbPAHxKiygo7ub7BYRiKxms0tpfU2ljtWzb3SjRjv5yl6Ozuy/TkXf00HTAt+Uylo3gSkNwzc4ME0wiIg==", "dependencies": { "logform": "^2.3.2", "readable-stream": "^3.6.0", "triple-beam": "^1.3.0" }, "engines": { - "node": ">= 6.4.0" + "node": ">= 12.0.0" } }, "node_modules/winston-transport/node_modules/readable-stream": { @@ -16970,61 +17570,44 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } + "node_modules/wrap-ansi-cjs/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==" }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "engines": { - "node": ">=12" + "node_modules/wrap-ansi-cjs/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==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "engines": { + "node": ">=8" } }, - "node_modules/wrap-ansi/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" - }, - "node_modules/wrap-ansi/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dependencies": { - "ansi-regex": "^6.0.1" - }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "engines": { "node": ">=12" }, "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/wrappy": { @@ -17044,6 +17627,12 @@ "typedarray-to-buffer": "^3.1.5" } }, + "node_modules/write-file-atomic/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 + }, "node_modules/xdg-basedir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", @@ -17094,29 +17683,28 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/yaml": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.3.tgz", - "integrity": "sha512-zw0VAJxgeZ6+++/su5AFoqBbZbrEakwu+X0M5HmcwUiBL7AzcuPKjj5we4xfQLp78LkEMpD0cOnUhmgOVy3KdQ==", + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", + "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", "engines": { "node": ">= 14" } }, "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dependencies": { - "cliui": "^7.0.2", + "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "string-width": "^4.2.0", + "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "yargs-parser": "^21.1.1" }, "engines": { - "node": ">=10" + "node": ">=12" } }, "node_modules/yargs-parser": { @@ -17128,6 +17716,43 @@ "node": ">=10" } }, + "node_modules/yargs/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==" + }, + "node_modules/yargs/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==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "engines": { + "node": ">=12" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index c194becc6..013a06ee8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@mojaloop/central-ledger", - "version": "17.3.2", + "version": "17.4.0-snapshot.7", "description": "Central ledger hosted by a scheme to record and settle transfers", "license": "Apache-2.0", "author": "ModusBox", @@ -90,7 +90,7 @@ "@mojaloop/central-services-health": "14.0.2", "@mojaloop/central-services-logger": "11.2.2", "@mojaloop/central-services-metrics": "12.0.8", - "@mojaloop/central-services-shared": "18.1.3", + "@mojaloop/central-services-shared": "18.2.0", "@mojaloop/central-services-stream": "11.2.0", "@mojaloop/event-sdk": "14.0.0", "@mojaloop/ml-number": "11.2.3", @@ -109,7 +109,7 @@ "glob": "10.3.10", "hapi-auth-basic": "5.0.0", "hapi-auth-bearer-token": "8.0.0", - "hapi-swagger": "17.1.0", + "hapi-swagger": "17.2.0", "ilp-packet": "2.2.0", "knex": "3.0.1", "lodash": "4.17.21", @@ -127,8 +127,8 @@ "get-port": "5.1.1", "jsdoc": "4.0.2", "jsonpath": "1.1.1", - "nodemon": "3.0.1", - "npm-check-updates": "16.14.6", + "nodemon": "3.0.2", + "npm-check-updates": "16.14.11", "nyc": "15.1.0", "pre-commit": "1.2.2", "proxyquire": "2.1.3", diff --git a/src/domain/position/binProcessor.js b/src/domain/position/binProcessor.js new file mode 100644 index 000000000..3aff2df60 --- /dev/null +++ b/src/domain/position/binProcessor.js @@ -0,0 +1,249 @@ +/***** + License + -------------- + Copyright © 2017 Bill & Melinda Gates Foundation + The Mojaloop files are made available by the Bill & Melinda Gates Foundation under the Apache License, Version 2.0 (the "License") and you may not use these files except in compliance with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, the Mojaloop files are distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + + Contributors + -------------- + This is the official list of the Mojaloop project contributors for this file. + Names of the original copyright holders (individuals or organizations) + should be listed with a '*' in the first column. People who have + contributed from an organization can be listed under the organization + that actually holds the copyright for their contributions (see the + Gates Foundation organization for an example). Those individuals should have + their names indented and be marked with a '-'. Email address can be added + optionally within square brackets . + + * Gates Foundation + - Name Surname + + * INFITX + - Vijay Kumar Guthi + - Steven Oderayi + + -------------- + ******/ +'use strict' + +const Logger = require('@mojaloop/central-services-logger') +const BatchPositionModel = require('../../models/position/batch') +const BatchPositionModelCached = require('../../models/position/batchCached') +const PositionPrepareDomain = require('./prepare') +const SettlementModelCached = require('../../models/settlement/settlementModelCached') +const Enum = require('@mojaloop/central-services-shared').Enum +const ErrorHandler = require('@mojaloop/central-services-error-handling') +const participantFacade = require('../../models/participant/facade') + +/** + * @function processBins + * + * @async + * @description This is the domain function to process a list bins containing position messages grouped by participant account. + * + * @param {array} bins - a list of account-bins to process + * @param {object} trx - Database transaction object + * + * @returns {results} - Returns a list of bins with results or throws an error if failed + */ +const processBins = async (bins, trx) => { + const transferIdList = [] + iterateThroughBins(bins, (_accountID, _action, item) => { + if (item.decodedPayload?.transferId) { + transferIdList.push(item.decodedPayload.transferId) + } + }) + // Pre fetch latest transferStates for all the transferIds in the account-bin + const latestTransferStateChanges = await BatchPositionModel.getLatestTransferStateChangesByTransferIdList(trx, transferIdList) + const latestTransferStates = {} + for (const key in latestTransferStateChanges) { + latestTransferStates[key] = latestTransferStateChanges[key].transferStateId + } + + const accountIds = Object.keys(bins) + + // Pre fetch all settlement accounts corresponding to the position accounts + // Get all participantIdMap for the accountIds + const participantCurrencyIds = await BatchPositionModelCached.getParticipantCurrencyByIds(trx, accountIds) + + // Validate that participantCurrencyIds exist for each of the accountIds + // i.e every unique accountId has a corresponding entry in participantCurrencyIds + const participantIdsHavingCurrencyIdsList = [...new Set(participantCurrencyIds.map(item => item.participantCurrencyId))] + const allAccountIdsHaveParticipantCurrencyIds = accountIds.every(accountId => { + return participantIdsHavingCurrencyIdsList.includes(Number(accountId)) + }) + if (!allAccountIdsHaveParticipantCurrencyIds) { + throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.INTERNAL_SERVER_ERROR, 'Not all accountIds have corresponding participantCurrencyIds') + } + + const allSettlementModels = await SettlementModelCached.getAll() + + // Construct objects participantIdMap, accountIdMap and currencyIdMap + const participantIdMap = {} + const accountIdMap = {} + const currencyIdMap = {} + for (const item of participantCurrencyIds) { + const { participantId, currencyId, participantCurrencyId } = item + if (!participantIdMap[participantId]) { + participantIdMap[participantId] = {} + } + if (!currencyIdMap[currencyId]) { + currencyIdMap[currencyId] = { + settlementModel: _getSettlementModelForCurrency(currencyId, allSettlementModels) + } + } + participantIdMap[participantId][currencyId] = participantCurrencyId + accountIdMap[participantCurrencyId] = { participantId, currencyId } + } + + // Get all participantCurrencyIds for the participantIdMap + const allParticipantCurrencyIds = await BatchPositionModelCached.getParticipantCurrencyByParticipantIds(trx, Object.keys(participantIdMap)) + const settlementCurrencyIds = [] + for (const pc of allParticipantCurrencyIds) { + const correspondingParticipantCurrencyId = participantIdMap[pc.participantId][pc.currencyId] + if (correspondingParticipantCurrencyId) { + const settlementModel = currencyIdMap[pc.currencyId].settlementModel + if (pc.ledgerAccountTypeId === settlementModel.settlementAccountTypeId) { + settlementCurrencyIds.push(pc) + accountIdMap[correspondingParticipantCurrencyId].settlementCurrencyId = pc.participantCurrencyId + } + } + } + + // Pre fetch all position account balances for the account-bin and acquire lock on position + const positions = await BatchPositionModel.getPositionsByAccountIdsForUpdate(trx, [ + ...accountIds, + ...settlementCurrencyIds.map(pc => pc.participantCurrencyId) + ]) + + let notifyMessages = [] + let limitAlarms = [] + + // For each account-bin in the list + for (const accountID in bins) { + const accountBin = bins[accountID] + const actions = Object.keys(accountBin) + + const settlementParticipantPosition = positions[accountIdMap[accountID].settlementCurrencyId].value + const settlementModel = currencyIdMap[accountIdMap[accountID].currencyId].settlementModel + + // Story #3657: The following SQL query/lookup can be optimized for performance + const participantLimit = await participantFacade.getParticipantLimitByParticipantCurrencyLimit( + accountIdMap[accountID].participantId, + accountIdMap[accountID].currencyId, + Enum.Accounts.LedgerAccountType.POSITION, + Enum.Accounts.ParticipantLimitType.NET_DEBIT_CAP + ) + // Initialize accumulated values + // These values will be passed across various actions in the bin + let accumulatedPositionValue = positions[accountID].value + let accumulatedPositionReservedValue = positions[accountID].reservedValue + let accumulatedTransferStates = latestTransferStates + let accumulatedTransferStateChanges = [] + let accumulatedPositionChanges = [] + + // If non-prepare action found, log error + // We need to remove this once we implement all the actions + if (actions.length > 1 || (actions.length === 1 && actions[0] !== 'prepare')) { + Logger.isErrorEnabled && Logger.error('Only prepare action is allowed in a batch') + // throw new Error('Only prepare action is allowed in a batch') + } + // If prepare action found then call processPositionPrepareBin function + const prepareActionResult = await PositionPrepareDomain.processPositionPrepareBin( + accountBin.prepare, + accumulatedPositionValue, + accumulatedPositionReservedValue, + accumulatedTransferStates, + settlementParticipantPosition, + settlementModel, + participantLimit + ) + + // Update accumulated values + accumulatedPositionValue = prepareActionResult.accumulatedPositionValue + accumulatedPositionReservedValue = prepareActionResult.accumulatedPositionReservedValue + accumulatedTransferStates = prepareActionResult.accumulatedTransferStates + // Append accumulated arrays + accumulatedTransferStateChanges = accumulatedTransferStateChanges.concat(prepareActionResult.accumulatedTransferStateChanges) + accumulatedPositionChanges = accumulatedPositionChanges.concat(prepareActionResult.accumulatedPositionChanges) + notifyMessages = notifyMessages.concat(prepareActionResult.notifyMessages) + + // Update accumulated position values by calling a facade function + await BatchPositionModel.updateParticipantPosition(trx, positions[accountID].participantPositionId, accumulatedPositionValue, accumulatedPositionReservedValue) + + // Bulk insert accumulated transferStateChanges by calling a facade function + await BatchPositionModel.bulkInsertTransferStateChanges(trx, accumulatedTransferStateChanges) + + // Bulk get the transferStateChangeIds for transferids using select whereIn + const fetchedTransferStateChanges = await BatchPositionModel.getLatestTransferStateChangesByTransferIdList(trx, accumulatedTransferStateChanges.map(item => item.transferId)) + // Mutate accumulated positionChanges with transferStateChangeIds + for (const positionChange of accumulatedPositionChanges) { + positionChange.transferStateChangeId = fetchedTransferStateChanges[positionChange.transferId].transferStateChangeId + positionChange.participantPositionId = positions[accountID].participantPositionId + delete positionChange.transferId + } + // Bulk insert accumulated positionChanges by calling a facade function + await BatchPositionModel.bulkInsertParticipantPositionChanges(trx, accumulatedPositionChanges) + + limitAlarms = limitAlarms.concat(prepareActionResult.limitAlarms) + } + + // Return results + return { + notifyMessages, + limitAlarms + } +} + +/** + * @function iterateThroughBins + * + * @async + * @description Helper function to iterate though all messages in bins. + * + * @param {array} bins - a list of account-bins to iterate + * @param {async function} cb - callback function to call for each item + * @param {async function} errCb - callback function to call incase of any error + * + * @returns {void} - Doesn't return anything + */ + +const iterateThroughBins = async (bins, cb, errCb) => { + for (const accountID in bins) { + const accountBin = bins[accountID] + for (const action in accountBin) { + const actionBin = accountBin[action] + for (const item of actionBin) { + try { + await cb(accountID, action, item) + } catch (err) { + if (errCb === undefined) { + Logger.isErrorEnabled && Logger.error(err) + } else { + await errCb(accountID, action, item) + } + } + } + } + } +} + +const _getSettlementModelForCurrency = (currencyId, allSettlementModels) => { + let settlementModels = allSettlementModels.filter(model => model.currencyId === currencyId) + if (settlementModels.length === 0) { + settlementModels = allSettlementModels.filter(model => model.currencyId === null) // Default settlement model + if (settlementModels.length === 0) { + throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.GENERIC_SETTLEMENT_ERROR, 'Unable to find a matching or default, Settlement Model') + } + } + return settlementModels.find(sm => sm.ledgerAccountTypeId === Enum.Accounts.LedgerAccountType.POSITION) +} + +module.exports = { + processBins, + iterateThroughBins +} diff --git a/src/domain/position/prepare.js b/src/domain/position/prepare.js new file mode 100644 index 000000000..3f6df96c4 --- /dev/null +++ b/src/domain/position/prepare.js @@ -0,0 +1,262 @@ +const { Enum } = require('@mojaloop/central-services-shared') +const ErrorHandler = require('@mojaloop/central-services-error-handling') +const Config = require('../../lib/config') +const Utility = require('@mojaloop/central-services-shared').Util +const MLNumber = require('@mojaloop/ml-number') +const Logger = require('@mojaloop/central-services-logger') + +/** + * @function processPositionPrepareBin + * + * @async + * @description This is the domain function to process a bin of position-prepare messages of a single participant account. + * + * @param {array} binItems - an array of objects that contain a position prepare message and its span. {message, span} + * @param {number} accumulatedPositionValue - value of position accumulated so far from previous bin processing + * @param {number} accumulatedPositionReservedValue - value of position reserved accumulated so far, not used but kept for consistency + * @param {object} accumulatedTransferStates - object with transfer id keys and transfer state id values. Used to check if transfer is in correct state for processing. Clone and update states for output. + * @param {number} settlementParticipantPosition - position value of the participants settlement account + * @param {object} settlementModel - settlement model object for the currency + * @param {object} participantLimit - participant limit object for the currency + * @returns {object} - Returns an object containing accumulatedPositionValue, accumulatedPositionReservedValue, accumulatedTransferStateChanges, accumulatedTransferStates, resultMessages, limitAlarms or throws an error if failed + */ +const processPositionPrepareBin = async ( + binItems, + accumulatedPositionValue, + accumulatedPositionReservedValue, + accumulatedTransferStates, + settlementParticipantPosition, + settlementModel, + participantLimit +) => { + const transferStateChanges = [] + const participantPositionChanges = [] + const resultMessages = [] + const limitAlarms = [] + const accumulatedTransferStatesCopy = Object.assign({}, accumulatedTransferStates) + + let currentPosition = new MLNumber(accumulatedPositionValue) + const reservedPosition = new MLNumber(accumulatedPositionReservedValue) + const effectivePosition = new MLNumber(currentPosition.add(reservedPosition).toFixed(Config.AMOUNT.SCALE)) + const liquidityCover = new MLNumber(settlementParticipantPosition).multiply(-1) + const payerLimit = new MLNumber(participantLimit.value) + let availablePositionBasedOnLiquidityCover = new MLNumber(liquidityCover.subtract(effectivePosition).toFixed(Config.AMOUNT.SCALE)) + Logger.isInfoEnabled && Logger.info(`processPositionPrepareBin::availablePositionBasedOnLiquidityCover: ${availablePositionBasedOnLiquidityCover}`) + let availablePositionBasedOnPayerLimit = new MLNumber(payerLimit.subtract(effectivePosition).toFixed(Config.AMOUNT.SCALE)) + Logger.isDebugEnabled && Logger.debug(`processPositionPrepareBin::availablePositionBasedOnPayerLimit: ${availablePositionBasedOnPayerLimit}`) + + if (binItems && binItems.length > 0) { + for (const binItem of binItems) { + let transferStateId + let reason + let resultMessage + const transfer = binItem.decodedPayload + Logger.isDebugEnabled && Logger.debug(`processPositionPrepareBin::transfer:processingMessage: ${JSON.stringify(transfer)}`) + + // Check if transfer is in correct state for processing, produce an internal error message + if (accumulatedTransferStates[transfer.transferId] !== Enum.Transfers.TransferInternalState.RECEIVED_PREPARE) { + Logger.isDebugEnabled && Logger.debug(`processPositionPrepareBin::transferState: ${accumulatedTransferStates[transfer.transferId]} !== ${Enum.Transfers.TransferInternalState.RECEIVED_PREPARE}`) + + transferStateId = Enum.Transfers.TransferInternalState.ABORTED_REJECTED + reason = 'Transfer in incorrect state' + + // forward same headers from the prepare message, except the content-length header + // set destination to payerfsp and source to switch + const headers = { ...binItem.message.value.content.headers } + headers[Enum.Http.Headers.FSPIOP.DESTINATION] = transfer.payerFsp + headers[Enum.Http.Headers.FSPIOP.SOURCE] = Enum.Http.Headers.FSPIOP.SWITCH.value + delete headers['content-length'] + + const fspiopError = ErrorHandler.Factory.createFSPIOPError( + ErrorHandler.Enums.FSPIOPErrorCodes.INTERNAL_SERVER_ERROR + ).toApiErrorObject(Config.ERROR_HANDLING) + + const state = Utility.StreamingProtocol.createEventState( + Enum.Events.EventStatus.FAILURE.status, + fspiopError.errorInformation.errorCode, + fspiopError.errorInformation.errorDescription + ) + + const metadata = Utility.StreamingProtocol.createMetadataWithCorrelatedEvent( + transfer.transferId, + Enum.Kafka.Topics.NOTIFICATION, + Enum.Events.Event.Action.PREPARE, + state + ) + + resultMessage = Utility.StreamingProtocol.createMessage( + transfer.transferId, + transfer.payerFsp, + Enum.Http.Headers.FSPIOP.SWITCH.value, + metadata, + headers, + fspiopError, + { id: transfer.transferId }, + 'application/json' + ) + + binItem.result = { success: false } + + // Check if payer has insufficient liquidity, produce an error message and abort transfer + } else if (availablePositionBasedOnLiquidityCover.toNumber() < transfer.amount.amount) { + transferStateId = Enum.Transfers.TransferInternalState.ABORTED_REJECTED + reason = ErrorHandler.Enums.FSPIOPErrorCodes.PAYER_FSP_INSUFFICIENT_LIQUIDITY.message + + // forward same headers from the prepare message, except the content-length header + // set destination to payerfsp and source to switch + const headers = { ...binItem.message.value.content.headers } + headers[Enum.Http.Headers.FSPIOP.DESTINATION] = transfer.payerFsp + headers[Enum.Http.Headers.FSPIOP.SOURCE] = Enum.Http.Headers.FSPIOP.SWITCH.value + delete headers['content-length'] + + const fspiopError = ErrorHandler.Factory.createFSPIOPError( + ErrorHandler.Enums.FSPIOPErrorCodes.PAYER_FSP_INSUFFICIENT_LIQUIDITY + ).toApiErrorObject(Config.ERROR_HANDLING) + + const state = Utility.StreamingProtocol.createEventState( + Enum.Events.EventStatus.FAILURE.status, + fspiopError.errorInformation.errorCode, + fspiopError.errorInformation.errorDescription + ) + + const metadata = Utility.StreamingProtocol.createMetadataWithCorrelatedEvent( + transfer.transferId, + Enum.Kafka.Topics.NOTIFICATION, + Enum.Events.Event.Action.PREPARE, + state + ) + + resultMessage = Utility.StreamingProtocol.createMessage( + transfer.transferId, + transfer.payerFsp, + Enum.Http.Headers.FSPIOP.SWITCH.value, + metadata, + headers, + fspiopError, + { id: transfer.transferId }, + 'application/json' + ) + + binItem.result = { success: false } + + // Check if payer has surpassed their limit, produce an error message and abort transfer + } else if (availablePositionBasedOnPayerLimit.toNumber() < transfer.amount.amount) { + transferStateId = Enum.Transfers.TransferInternalState.ABORTED_REJECTED + reason = ErrorHandler.Enums.FSPIOPErrorCodes.PAYER_LIMIT_ERROR.message + + // forward same headers from the prepare message, except the content-length header + // set destination to payerfsp and source to switch + const headers = { ...binItem.message.value.content.headers } + headers[Enum.Http.Headers.FSPIOP.DESTINATION] = transfer.payerFsp + headers[Enum.Http.Headers.FSPIOP.SOURCE] = Enum.Http.Headers.FSPIOP.SWITCH.value + delete headers['content-length'] + + const fspiopError = ErrorHandler.Factory.createFSPIOPError( + ErrorHandler.Enums.FSPIOPErrorCodes.PAYER_LIMIT_ERROR + ).toApiErrorObject(Config.ERROR_HANDLING) + + const state = Utility.StreamingProtocol.createEventState( + Enum.Events.EventStatus.FAILURE.status, + fspiopError.errorInformation.errorCode, + fspiopError.errorInformation.errorDescription + ) + + const metadata = Utility.StreamingProtocol.createMetadataWithCorrelatedEvent( + transfer.transferId, + Enum.Kafka.Topics.NOTIFICATION, + Enum.Events.Event.Action.PREPARE, + state + ) + + resultMessage = Utility.StreamingProtocol.createMessage( + transfer.transferId, + transfer.payerFsp, + Enum.Http.Headers.FSPIOP.SWITCH.value, + metadata, + headers, + fspiopError, + { id: transfer.transferId }, + 'application/json' + ) + + binItem.result = { success: false } + + // Payer has sufficient liquidity and limit + } else { + transferStateId = Enum.Transfers.TransferState.RESERVED + currentPosition = currentPosition.add(transfer.amount.amount) + availablePositionBasedOnLiquidityCover = availablePositionBasedOnLiquidityCover.add(transfer.amount.amount) + availablePositionBasedOnPayerLimit = availablePositionBasedOnPayerLimit.add(transfer.amount.amount) + + // forward same headers from the prepare message, except the content-length header + const headers = { ...binItem.message.value.content.headers } + delete headers['content-length'] + + const state = Utility.StreamingProtocol.createEventState( + Enum.Events.EventStatus.SUCCESS.status, + null, + null + ) + const metadata = Utility.StreamingProtocol.createMetadataWithCorrelatedEvent( + transfer.transferId, + Enum.Kafka.Topics.TRANSFER, + Enum.Events.Event.Action.PREPARE, + state + ) + + resultMessage = Utility.StreamingProtocol.createMessage( + transfer.transferId, + transfer.payeeFsp, + transfer.payerFsp, + metadata, + headers, + transfer, + {}, + 'application/json' + ) + + const participantPositionChange = { + transferId: transfer.transferId, // Need to delete this in bin processor while updating transferStateChangeId + transferStateChangeId: null, // Need to update this in bin processor while executing queries + value: currentPosition.toNumber(), + reservedValue: accumulatedPositionReservedValue + } + participantPositionChanges.push(participantPositionChange) + Logger.isDebugEnabled && Logger.debug(`processPositionPrepareBin::participantPositionChange: ${JSON.stringify(participantPositionChange)}`) + binItem.result = { success: true } + } + + resultMessages.push({ binItem, message: resultMessage }) + + const transferStateChange = { + transferId: transfer.transferId, + transferStateId, + reason + } + transferStateChanges.push(transferStateChange) + Logger.isDebugEnabled && Logger.debug(`processPositionPrepareBin::transferStateChange: ${JSON.stringify(transferStateChange)}`) + + Logger.isDebugEnabled && Logger.debug(`processPositionPrepareBin::limitAlarm: ${currentPosition.toNumber()} > ${liquidityCover.multiply(participantLimit.thresholdAlarmPercentage)}`) + if (currentPosition.toNumber() > liquidityCover.multiply(participantLimit.thresholdAlarmPercentage).toNumber()) { + limitAlarms.push(participantLimit) + } + + accumulatedTransferStatesCopy[transfer.transferId] = transferStateId + Logger.isDebugEnabled && Logger.debug(`processPositionPrepareBin::accumulatedTransferStatesCopy:finalizedTransferState ${JSON.stringify(transferStateId)}`) + } + } + + return { + accumulatedPositionValue: currentPosition.toNumber(), + accumulatedTransferStates: accumulatedTransferStatesCopy, // finalized transfer state after prepare processing + accumulatedPositionReservedValue, // not used but kept for consistency + accumulatedTransferStateChanges: transferStateChanges, // transfer state changes to be persisted in order + limitAlarms, // array of participant limits that have been breached + accumulatedPositionChanges: participantPositionChanges, // participant position changes to be persisted in order + notifyMessages: resultMessages // array of objects containing bin item and result message. {binItem, message} + } +} + +module.exports = { + processPositionPrepareBin +} diff --git a/src/handlers/index.js b/src/handlers/index.js index b1b281730..ee1a6a706 100644 --- a/src/handlers/index.js +++ b/src/handlers/index.js @@ -52,6 +52,7 @@ Program.command('handler') // sub-command name, coffeeType = type, required .description('Start a specified Handler') // command description .option('--prepare', 'Start the Prepare Handler') .option('--position', 'Start the Position Handler') + .option('--positionbatch', 'Start the Position Batch Handler') .option('--get', 'Start the Transfer Get Handler') .option('--fulfil', 'Start the Fulfil Handler') .option('--timeout', 'Start the Timeout Handler') @@ -81,6 +82,14 @@ Program.command('handler') // sub-command name, coffeeType = type, required } handlerList.push(handler) } + if (args.positionbatch) { + Logger.isDebugEnabled && Logger.debug('CLI: Executing --positionbatch') + const handler = { + type: 'positionbatch', + enabled: true + } + handlerList.push(handler) + } if (args.get) { Logger.isDebugEnabled && Logger.debug('CLI: Executing --get') const handler = { diff --git a/src/handlers/positions/handlerBatch.js b/src/handlers/positions/handlerBatch.js new file mode 100644 index 000000000..c4f6b3c17 --- /dev/null +++ b/src/handlers/positions/handlerBatch.js @@ -0,0 +1,232 @@ +/***** + License + -------------- + Copyright © 2017 Bill & Melinda Gates Foundation + The Mojaloop files are made available by the Bill & Melinda Gates Foundation under the Apache License, Version 2.0 (the "License") and you may not use these files except in compliance with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, the Mojaloop files are distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + + Contributors + -------------- + This is the official list of the Mojaloop project contributors for this file. + Names of the original copyright holders (individuals or organizations) + should be listed with a '*' in the first column. People who have + contributed from an organization can be listed under the organization + that actually holds the copyright for their contributions (see the + Gates Foundation organization for an example). Those individuals should have + their names indented and be marked with a '-'. Email address can be added + optionally within square brackets . + + * Gates Foundation + - Name Surname + + * INFITX + - Vijay Kumar Guthi + + -------------- + ******/ +'use strict' + +/** + * @module src/handlers/positions + */ + +const Logger = require('@mojaloop/central-services-logger') +const EventSdk = require('@mojaloop/event-sdk') +const BinProcessor = require('../../domain/position/binProcessor') +const SettlementModelCached = require('../../models/settlement/settlementModelCached') +const Utility = require('@mojaloop/central-services-shared').Util +const Kafka = require('@mojaloop/central-services-shared').Util.Kafka +const Producer = require('@mojaloop/central-services-stream').Util.Producer +const Consumer = require('@mojaloop/central-services-stream').Util.Consumer +const Enum = require('@mojaloop/central-services-shared').Enum +const Metrics = require('@mojaloop/central-services-metrics') +const Config = require('../../lib/config') +const { randomUUID } = require('crypto') +const ErrorHandler = require('@mojaloop/central-services-error-handling') +const BatchPositionModel = require('../../models/position/batch') +const decodePayload = require('@mojaloop/central-services-shared').Util.StreamingProtocol.decodePayload + +const consumerCommit = true + +/** + * @function positions + * + * @async + * @description This is the consumer callback function that gets registered to a topic. This then gets a list of messages. + * + * @param {error} error - error thrown if something fails within Kafka + * @param {array} messages - a list of messages to consume for the relevant topic + * + * @returns {object} - Returns a boolean: true if successful, or throws and error if failed + */ + +const positions = async (error, messages) => { + const histTimerEnd = Metrics.getHistogram( + 'transfer_position_batch', + 'Consume a batch of prepare transfer messages from the kafka topic and process them', + ['success'] + ).startTimer() + + if (error) { + histTimerEnd({ success: false }) + throw ErrorHandler.Factory.reformatFSPIOPError(error) + } + let consumedMessages = [] + + if (Array.isArray(messages)) { + consumedMessages = Array.from(messages) + } else { + consumedMessages = [Object.assign({}, Utility.clone(messages))] + } + + const firstMessageOffset = consumedMessages[0]?.offset + const lastMessageOffset = consumedMessages[consumedMessages.length - 1]?.offset + const binId = `${firstMessageOffset}-${lastMessageOffset}` + + // Iterate through consumedMessages + const bins = {} + for (const message of consumedMessages) { + const histTimerMsgEnd = Metrics.getHistogram( + 'transfer_position', + 'Process a prepare transfer message', + ['success', 'action'] + ).startTimer() + + // Create a span for each message + const contextFromMessage = EventSdk.Tracer.extractContextFromMessage(message.value) + const span = EventSdk.Tracer.createChildSpanFromContext('cl_transfer_position', contextFromMessage) + span.setTags({ + processedAsBatch: true, + binId + }) + + // Assign message to account-bin by accountID and child action-bin by action + // (References to the messages to be stored in bins, no duplication of messages) + const accountID = message.key.toString() + const action = message.value.metadata.event.action + const accountBin = bins[accountID] || (bins[accountID] = {}) + const actionBin = accountBin[action] || (accountBin[action] = []) + + // Decode the payload and pass it as a separate parameter + const decodedPayload = decodePayload(message.value.content.payload) + actionBin.push({ + message, + decodedPayload, + span, + result: {}, + histTimerMsgEnd + }) + + await span.audit(message, EventSdk.AuditEventAction.start) + } + + // Start DB Transaction + const trx = await BatchPositionModel.startDbTransaction() + + try { + // Call Bin Processor with the list of account-bins and trx + const result = await BinProcessor.processBins(bins, trx) + + // If Bin Processor processed bins successfully, commit Kafka offset + // Commit the offset of last message in the array + const lastMessageToCommit = consumedMessages[consumedMessages.length - 1] + const params = { message: lastMessageToCommit, kafkaTopic: lastMessageToCommit.topic, consumer: Consumer } + + // We are using Kafka.proceed() to just commit the offset of the last message in the array + await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit }) + + // Commit DB transaction + await trx.commit() + + // Loop through results and produce notification messages and audit messages + for (const item of result.notifyMessages) { + // Produce notification message and audit message + const action = item.binItem.message?.value.metadata.event.action + const eventStatus = item?.message.metadata.event.state.status === Enum.Events.EventStatus.SUCCESS.status ? Enum.Events.EventStatus.SUCCESS : Enum.Events.EventStatus.FAILURE + await Kafka.produceGeneralMessage(Config.KAFKA_CONFIG, Producer, Enum.Events.Event.Type.NOTIFICATION, action, item.message, eventStatus, null, item.binItem.span) + } + histTimerEnd({ success: true }) + } catch (err) { + // If Bin Processor returns failure + // - Rollback DB transaction + await trx.rollback() + + // - Audit Error for each message + const fspiopError = ErrorHandler.Factory.reformatFSPIOPError(err) + const state = new EventSdk.EventStateMetadata(EventSdk.EventStatusType.failed, fspiopError.apiErrorCode.code, fspiopError.apiErrorCode.message) + await BinProcessor.iterateThroughBins(bins, async (_accountID, _action, item) => { + const span = item.span + await span.error(fspiopError, state) + }) + histTimerEnd({ success: false }) + } finally { + // Finish span for each message + await BinProcessor.iterateThroughBins(bins, async (_accountID, action, item) => { + item.histTimerMsgEnd({ ...item.result, action }) + const span = item.span + if (!span.isFinished) { + await span.finish() + } + }) + } +} + +/** + * @function registerPositionHandler + * + * @async + * @description Registers the handler for position topic. Gets Kafka config from default.json + * + * @returns {boolean} - Returns a boolean: true if successful, or throws and error if failed + */ +const registerPositionHandler = async () => { + try { + await SettlementModelCached.initialize() + // If there is no mapping, use default transformGeneralTopicName + const topicName = + Config.KAFKA_CONFIG.EVENT_TYPE_ACTION_TOPIC_MAP?.POSITION?.PREPARE || + Kafka.transformGeneralTopicName( + Config.KAFKA_CONFIG.TOPIC_TEMPLATES.GENERAL_TOPIC_TEMPLATE.TEMPLATE, + Enum.Events.Event.Type.POSITION, + Enum.Events.Event.Action.PREPARE + ) + const positionHandler = { + command: positions, + topicName, + // There is no corresponding action for POSITION_BATCH, so using straight value + config: Kafka.getKafkaConfig(Config.KAFKA_CONFIG, Enum.Kafka.Config.CONSUMER, Enum.Events.Event.Type.TRANSFER.toUpperCase(), 'POSITION_BATCH') + } + positionHandler.config.rdkafkaConf['client.id'] = `${positionHandler.config.rdkafkaConf['client.id']}-${randomUUID()}` + await Consumer.createHandler(positionHandler.topicName, positionHandler.config, positionHandler.command) + return true + } catch (err) { + Logger.isErrorEnabled && Logger.error(err) + throw ErrorHandler.Factory.reformatFSPIOPError(err) + } +} + +/** + * @function RegisterAllHandlers + * + * @async + * @description Registers all handlers in positions + * + * @returns {boolean} - Returns a boolean: true if successful, or throws and error if failed + */ +const registerAllHandlers = async () => { + try { + return await registerPositionHandler() + } catch (err) { + Logger.isErrorEnabled && Logger.error(err) + throw ErrorHandler.Factory.reformatFSPIOPError(err) + } +} + +module.exports = { + registerPositionHandler, + registerAllHandlers, + positions +} diff --git a/src/handlers/register.js b/src/handlers/register.js index 6acecdc33..ae89f1394 100644 --- a/src/handlers/register.js +++ b/src/handlers/register.js @@ -50,6 +50,7 @@ const Logger = require('@mojaloop/central-services-logger') const requireGlob = require('require-glob') const TransferHandlers = require('./transfers/handler') const PositionHandlers = require('./positions/handler') +const PositionHandlersBatch = require('./positions/handlerBatch') const TimeoutHandlers = require('./timeouts/handler') const AdminHandlers = require('./admin/handler') const BulkHandlers = require('./bulk') @@ -90,6 +91,10 @@ module.exports = { registerAllHandlers: PositionHandlers.registerAllHandlers, registerPositionHandler: PositionHandlers.registerPositionHandler }, + positionsBatch: { + registerAllHandlers: PositionHandlersBatch.registerAllHandlers, + registerPositionHandler: PositionHandlersBatch.registerPositionHandler + }, timeouts: { registerAllHandlers: TimeoutHandlers.registerAllHandlers, registerTimeoutHandler: TimeoutHandlers.registerTimeoutHandler diff --git a/src/handlers/transfers/handler.js b/src/handlers/transfers/handler.js index 84c295506..e82eb9ea2 100644 --- a/src/handlers/transfers/handler.js +++ b/src/handlers/transfers/handler.js @@ -200,7 +200,14 @@ const prepare = async (error, messages) => { const eventDetail = { functionality, action } // Key position prepare message with payer account id const payerAccount = await Participant.getAccountByNameAndCurrency(payload.payerFsp, payload.amount.currency, Enum.Accounts.LedgerAccountType.POSITION) - await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, eventDetail, messageKey: payerAccount.participantCurrencyId.toString(), topicNameOverride: Config.KAFKA_CONFIG.EVENT_TYPE_ACTION_TOPIC_MAP?.POSITION?.PREPARE }) + // We route bulk-prepare and prepare messages differently based on the topic configured for it. + // Note: The batch handler does not currently support bulk-prepare messages, only prepare messages are supported. + // Therefore, it is necessary to check the action to determine the topic to route to. + const topicNameOverride = + action === TransferEventAction.BULK_PREPARE + ? Config.KAFKA_CONFIG.EVENT_TYPE_ACTION_TOPIC_MAP?.POSITION?.BULK_PREPARE + : Config.KAFKA_CONFIG.EVENT_TYPE_ACTION_TOPIC_MAP?.POSITION?.PREPARE + await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, eventDetail, messageKey: payerAccount.participantCurrencyId.toString(), topicNameOverride }) histTimerEnd({ success: true, fspId: Config.INSTRUMENTATION_METRICS_LABELS.fspId }) return true } else { diff --git a/src/models/participant/facade.js b/src/models/participant/facade.js index 987858acf..cf68cc666 100644 --- a/src/models/participant/facade.js +++ b/src/models/participant/facade.js @@ -308,7 +308,7 @@ const getParticipantLimitByParticipantCurrencyLimit = async (participantId, curr /* Checkpoint #1: participant found and is active */ if ((participant) && (participant.isActive)) { - /* use the paricipant id and incoming params to prepare the filter */ + /* use the participant id and incoming params to prepare the filter */ const searchFilter = { participantId: participant.participantId, currencyId, diff --git a/src/models/position/batch.js b/src/models/position/batch.js new file mode 100644 index 000000000..08f287684 --- /dev/null +++ b/src/models/position/batch.js @@ -0,0 +1,125 @@ +/***** + License + -------------- + Copyright © 2017 Bill & Melinda Gates Foundation + The Mojaloop files are made available by the Bill & Melinda Gates Foundation under the Apache License, Version 2.0 (the "License") and you may not use these files except in compliance with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, the Mojaloop files are distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + + Contributors + -------------- + This is the official list of the Mojaloop project contributors for this file. + Names of the original copyright holders (individuals or organizations) + should be listed with a '*' in the first column. People who have + contributed from an organization can be listed under the organization + that actually holds the copyright for their contributions (see the + Gates Foundation organization for an example). Those individuals should have + their names indented and be marked with a '-'. Email address can be added + optionally within square brackets . + + * Gates Foundation + - Name Surname + + * INFITX + - Vijay Kumar Guthi + + -------------- + ******/ + +'use strict' + +const Db = require('../../lib/db') +const Logger = require('@mojaloop/central-services-logger') + +const startDbTransaction = async () => { + const knex = await Db.getKnex() + const trx = await knex.transaction() + return trx +} + +const getLatestTransferStateChangesByTransferIdList = async (trx, transfersIdList) => { + const knex = await Db.getKnex() + try { + const latestTransferStateChanges = {} + const results = await knex('transferStateChange') + .transacting(trx) + .whereIn('transferStateChange.transferId', transfersIdList) + .orderBy('transferStateChangeId', 'desc') + .select('*') + + for (const result of results) { + if (!latestTransferStateChanges[result.transferId]) { + latestTransferStateChanges[result.transferId] = result + } + } + return latestTransferStateChanges + } catch (err) { + Logger.isErrorEnabled && Logger.error(err) + throw err + } +} + +const getAllParticipantCurrency = async (trx) => { + const knex = await Db.getKnex() + if (trx) { + const result = await knex('participantCurrency') + .transacting(trx) + .select('*') + return result + } else { + const result = await knex('participantCurrency') + .select('*') + return result + } +} + +const getPositionsByAccountIdsForUpdate = async (trx, accountIds) => { + const knex = await Db.getKnex() + const participantPositions = await knex('participantPosition') + .transacting(trx) + .whereIn('participantCurrencyId', accountIds) + .forUpdate() + .select('*') + const positions = {} + for (const position of participantPositions) { + positions[position.participantCurrencyId] = position + } + return positions +} + +const updateParticipantPosition = async (trx, participantPositionId, participantPositionValue, participantPositionReservedValue = null) => { + const knex = await Db.getKnex() + const optionalValues = {} + if (participantPositionReservedValue !== null) { + optionalValues.reservedValue = participantPositionReservedValue + } + return await knex('participantPosition').transacting(trx) + .where({ participantPositionId }) + .update({ + value: participantPositionValue, + ...optionalValues, + changedDate: new Date() + }) +} + +const bulkInsertTransferStateChanges = async (trx, transferStateChangeList) => { + const knex = await Db.getKnex() + return await knex.batchInsert('transferStateChange', transferStateChangeList).transacting(trx) +} + +const bulkInsertParticipantPositionChanges = async (trx, participantPositionChangeList) => { + const knex = await Db.getKnex() + return await knex.batchInsert('participantPositionChange', participantPositionChangeList).transacting(trx) +} + +module.exports = { + startDbTransaction, + getLatestTransferStateChangesByTransferIdList, + getPositionsByAccountIdsForUpdate, + updateParticipantPosition, + bulkInsertTransferStateChanges, + bulkInsertParticipantPositionChanges, + getAllParticipantCurrency +} diff --git a/src/models/position/batchCached.js b/src/models/position/batchCached.js new file mode 100644 index 000000000..4adcd5a54 --- /dev/null +++ b/src/models/position/batchCached.js @@ -0,0 +1,134 @@ +/***** + License + -------------- + Copyright © 2017 Bill & Melinda Gates Foundation + The Mojaloop files are made available by the Bill & Melinda Gates Foundation under the Apache License, Version 2.0 (the "License") and you may not use these files except in compliance with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, the Mojaloop files are distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + + Contributors + -------------- + This is the official list of the Mojaloop project contributors for this file. + Names of the original copyright holders (individuals or organizations) + should be listed with a '*' in the first column. People who have + contributed from an organization can be listed under the organization + that actually holds the copyright for their contributions (see the + Gates Foundation organization for an example). Those individuals should have + their names indented and be marked with a '-'. Email address can be added + optionally within square brackets . + + * Gates Foundation + - Name Surname + + * INFITX + - Kevin Leyow + + -------------- + ******/ + +'use strict' + +const ErrorHandler = require('@mojaloop/central-services-error-handling') +const Cache = require('../../lib/cache') +const Metrics = require('@mojaloop/central-services-metrics') +const BatchPositionModel = require('./batch') + +let cacheClient +let participantCurrencyAllCacheKey + +/* + Private API +*/ + +const buildUnifiedParticipantCurrencyData = (allParticipantCurrency) => { + // build indexes (or indices?) - optimization for byId and byName access + const indexById = {} + const indexByParticipantId = {} + + for (const oneParticipantCurrency of allParticipantCurrency) { + // Participant API returns Date type, but cache internals will serialize it to String + // by calling JSON.stringify(), which calls .toISOString() on a Date object. + // Let's ensure all places return same kind of String. + oneParticipantCurrency.createdDate = JSON.stringify(oneParticipantCurrency.createdDate) + + // Add to indexes + if (!(oneParticipantCurrency.participantId in indexByParticipantId)) { + indexByParticipantId[oneParticipantCurrency.participantId] = [] + } + indexByParticipantId[oneParticipantCurrency.participantId].push(oneParticipantCurrency) + indexById[oneParticipantCurrency.participantCurrencyId] = oneParticipantCurrency + } + + // build unified structure - indexes + data + const unifiedParticipantsCurrency = { + indexById, + indexByParticipantId, + allParticipantCurrency + } + return unifiedParticipantsCurrency +} + +const getParticipantCurrencyCached = async (trx) => { + const histTimer = Metrics.getHistogram( + 'model_participant_batch', + 'model_getParticipantsCached - Metrics for participant model', + ['success', 'queryName', 'hit'] + ).startTimer() + // Do we have valid participants list in the cache ? + let cachedParticipantCurrency = cacheClient.get(participantCurrencyAllCacheKey) + if (!cachedParticipantCurrency) { + const allParticipantCurrency = await BatchPositionModel.getAllParticipantCurrency(trx) + cachedParticipantCurrency = buildUnifiedParticipantCurrencyData(allParticipantCurrency) + + // store in cache + cacheClient.set(participantCurrencyAllCacheKey, cachedParticipantCurrency) + histTimer({ success: true, queryName: 'model_getParticipantCurrencyBatchCached', hit: false }) + } else { + // unwrap participants list from catbox structure + cachedParticipantCurrency = cachedParticipantCurrency.item + histTimer({ success: true, queryName: 'model_getParticipantCurrencyBatchCached', hit: true }) + } + return cachedParticipantCurrency +} + +/* + Public API +*/ +exports.initialize = async () => { + /* Register as cache client */ + const participantCurrencyCacheClientMeta = { + id: 'participantCurrency', + preloadCache: getParticipantCurrencyCached + } + + cacheClient = Cache.registerCacheClient(participantCurrencyCacheClientMeta) + participantCurrencyAllCacheKey = cacheClient.createKey('participantCurrency') +} + +exports.getParticipantCurrencyByIds = async (trx, participantCurrencyIds) => { + try { + let participantCurrencies = [] + const cachedParticipantCurrency = await getParticipantCurrencyCached(trx) + for (const participantCurrencyId of participantCurrencyIds) { + participantCurrencies = participantCurrencies.concat(cachedParticipantCurrency.indexById[participantCurrencyId]) + } + return participantCurrencies + } catch (err) { + throw ErrorHandler.Factory.reformatFSPIOPError(err) + } +} + +exports.getParticipantCurrencyByParticipantIds = async (trx, participantIds) => { + try { + let participantCurrencies = [] + const cachedParticipantCurrency = await getParticipantCurrencyCached(trx) + for (const participantId of participantIds) { + participantCurrencies = participantCurrencies.concat(cachedParticipantCurrency.indexByParticipantId[participantId]) + } + return participantCurrencies + } catch (err) { + throw ErrorHandler.Factory.reformatFSPIOPError(err) + } +} diff --git a/src/models/settlement/settlementModelCached.js b/src/models/settlement/settlementModelCached.js index f6541a85d..7a1f36769 100644 --- a/src/models/settlement/settlementModelCached.js +++ b/src/models/settlement/settlementModelCached.js @@ -42,12 +42,12 @@ const buildUnifiedSettlementModelsData = (allSettlementModels) => { const indexByName = {} const indexByLedgerAccountTypeId = {} - allSettlementModels.forEach((oneSettlementModel) => { + for (const oneSettlementModel of allSettlementModels) { // Add to indexes indexById[oneSettlementModel.settlementModelId] = oneSettlementModel indexByName[oneSettlementModel.name] = oneSettlementModel indexByLedgerAccountTypeId[oneSettlementModel.ledgerAccountTypeId] = oneSettlementModel - }) + } // build unified structure - indexes + data const unifiedSettlementModels = { diff --git a/src/shared/setup.js b/src/shared/setup.js index db4b50412..19fd3b2e7 100644 --- a/src/shared/setup.js +++ b/src/shared/setup.js @@ -51,6 +51,7 @@ const EnumCached = require('../lib/enumCached') const ParticipantCached = require('../models/participant/participantCached') const ParticipantCurrencyCached = require('../models/participant/participantCurrencyCached') const ParticipantLimitCached = require('../models/participant/participantLimitCached') +const BatchPositionModelCached = require('../models/position/batchCached') const MongoUriBuilder = require('mongo-uri-builder') const migrate = (runMigrations) => { @@ -175,6 +176,10 @@ const createHandlers = async (handlers) => { // } break } + case 'positionbatch': { + await RegisterHandlers.positionsBatch.registerPositionHandler() + break + } case 'fulfil': { await RegisterHandlers.transfers.registerFulfilHandler() break @@ -230,6 +235,7 @@ const initializeCache = async () => { await ParticipantCached.initialize() await ParticipantCurrencyCached.initialize() await ParticipantLimitCached.initialize() + await BatchPositionModelCached.initialize() await Cache.initCache() } diff --git a/test/integration-override/handlers/positions/handlerBatch.test.js b/test/integration-override/handlers/positions/handlerBatch.test.js new file mode 100644 index 000000000..4e0e2547b --- /dev/null +++ b/test/integration-override/handlers/positions/handlerBatch.test.js @@ -0,0 +1,984 @@ +/***** + License + -------------- + Copyright © 2017 Bill & Melinda Gates Foundation + The Mojaloop files are made available by the Bill & Melinda Gates Foundation under the Apache License, Version 2.0 (the "License") and you may not use these files except in compliance with the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, the Mojaloop files are distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + Contributors + -------------- + This is the official list of the Mojaloop project contributors for this file. + Names of the original copyright holders (individuals or organizations) + should be listed with a '*' in the first column. People who have + contributed from an organization can be listed under the organization + that actually holds the copyright for their contributions (see the + Gates Foundation organization for an example). Those individuals should have + their names indented and be marked with a '-'. Email address can be added + optionally within square brackets . + * Gates Foundation + - Name Surname + + * Vijay Kumar Guthi + -------------- + **********/ + +'use strict' + +const Test = require('tape') +const { randomUUID } = require('crypto') +const Logger = require('@mojaloop/central-services-logger') +const Config = require('#src/lib/config') +const Db = require('@mojaloop/database-lib').Db +const Cache = require('#src/lib/cache') +const Producer = require('@mojaloop/central-services-stream').Util.Producer +const Utility = require('@mojaloop/central-services-shared').Util.Kafka +const Enum = require('@mojaloop/central-services-shared').Enum +const ParticipantHelper = require('#test/integration/helpers/participant') +const ParticipantLimitHelper = require('#test/integration/helpers/participantLimit') +const ParticipantFundsInOutHelper = require('#test/integration/helpers/participantFundsInOut') +const ParticipantEndpointHelper = require('#test/integration/helpers/participantEndpoint') +const SettlementHelper = require('#test/integration/helpers/settlementModels') +const HubAccountsHelper = require('#test/integration/helpers/hubAccounts') +const TransferService = require('#src/domain/transfer/index') +const ParticipantService = require('#src/domain/participant/index') +const Util = require('@mojaloop/central-services-shared').Util +const ErrorHandler = require('@mojaloop/central-services-error-handling') +const { + wrapWithRetries +} = require('#test/util/helpers') +const TestConsumer = require('#test/integration/helpers/testConsumer') +const KafkaHelper = require('#test/integration/helpers/kafkaHelper') + +const ParticipantCached = require('#src/models/participant/participantCached') +const ParticipantCurrencyCached = require('#src/models/participant/participantCurrencyCached') +const ParticipantLimitCached = require('#src/models/participant/participantLimitCached') +const SettlementModelCached = require('#src/models/settlement/settlementModelCached') + +const Handlers = { + index: require('#src/handlers/register'), + positions: require('#src/handlers/positions/handler'), + transfers: require('#src/handlers/transfers/handler'), + timeouts: require('#src/handlers/timeouts/handler') +} + +const TransferState = Enum.Transfers.TransferState +const TransferInternalState = Enum.Transfers.TransferInternalState +const TransferEventType = Enum.Events.Event.Type +const TransferEventAction = Enum.Events.Event.Action + +const debug = process?.env?.TEST_INT_DEBUG || false +// const rebalanceDelay = process?.env?.TEST_INT_REBALANCE_DELAY || 10000 +const retryDelay = process?.env?.TEST_INT_RETRY_DELAY || 2 +const retryCount = process?.env?.TEST_INT_RETRY_COUNT || 40 +const retryOpts = { + retries: retryCount, + minTimeout: retryDelay, + maxTimeout: retryDelay +} + +const testData = { + currencies: ['USD', 'ZAR'], + transfers: [ + { + amount: { + currency: 'USD', + amount: 5 + } + }, + { + amount: { + currency: 'USD', + amount: 5 + } + }, + { + amount: { + currency: 'USD', + amount: 5 + } + }, + { + amount: { + currency: 'USD', + amount: 5 + } + }, + { + amount: { + currency: 'USD', + amount: 5 + } + }, + { + amount: { + currency: 'USD', + amount: 5 + } + }, + { + amount: { + currency: 'USD', + amount: 5 + } + }, + { + amount: { + currency: 'USD', + amount: 5 + } + }, + { + amount: { + currency: 'USD', + amount: 5 + } + }, + { + amount: { + currency: 'USD', + amount: 5 + } + } + ], + payer: { + name: 'payerFsp', + limit: 1000, + number: 2, + fundsIn: 10000 + }, + payee: { + name: 'payeeFsp', + number: 2, + limit: 1000 + }, + endpoint: { + base: 'http://localhost:1080', + email: 'test@example.com' + }, + now: new Date(), + expiration: new Date((new Date()).getTime() + (24 * 60 * 60 * 1000)) // tomorrow +} + +const testDataLimitExceeded = { + currencies: ['USD', 'ZAR'], + transfers: [ + { + amount: { + currency: 'USD', + amount: 5 + } + }, + { + amount: { + currency: 'USD', + amount: 5 + } + }, + { + amount: { + currency: 'USD', + amount: 5 + } + }, + { + amount: { + currency: 'USD', + amount: 5 + } + }, + { + amount: { + currency: 'USD', + amount: 5 + } + }, + { + amount: { + currency: 'USD', + amount: 5 + } + }, + { + amount: { + currency: 'USD', + amount: 5 + } + }, + { + amount: { + currency: 'USD', + amount: 5 + } + }, + { + amount: { + currency: 'USD', + amount: 5 + } + }, + { + amount: { + currency: 'USD', + amount: 5 + } + } + ], + payer: { + name: 'payerFsp', + limit: 1, // Limit set low + number: 1, + fundsIn: 10000 + }, + payee: { + name: 'payeeFsp', + number: 2, + limit: 0 + }, + endpoint: { + base: 'http://localhost:1080', + email: 'test@example.com' + }, + now: new Date(), + expiration: new Date((new Date()).getTime() + (24 * 60 * 60 * 1000)) // tomorrow +} + +const testDataLimitNoLiquidity = { + currencies: ['USD', 'ZAR'], + transfers: [ + { + amount: { + currency: 'ZAR', + amount: 5 + } + }, + { + amount: { + currency: 'ZAR', + amount: 5 + } + }, + { + amount: { + currency: 'ZAR', + amount: 5 + } + }, + { + amount: { + currency: 'ZAR', + amount: 5 + } + }, + { + amount: { + currency: 'ZAR', + amount: 5 + } + }, + { + amount: { + currency: 'ZAR', + amount: 5 + } + }, + { + amount: { + currency: 'ZAR', + amount: 5 + } + }, + { + amount: { + currency: 'ZAR', + amount: 5 + } + }, + { + amount: { + currency: 'ZAR', + amount: 5 + } + }, + { + amount: { + currency: 'ZAR', + amount: 5 + } + } + ], + payer: { + name: 'payerFsp', + limit: 10000, + number: 1, + fundsIn: 1 // Low liquidity + }, + payee: { + name: 'payeeFsp', + number: 2, + limit: 0 + }, + endpoint: { + base: 'http://localhost:1080', + email: 'test@example.com' + }, + now: new Date(), + expiration: new Date((new Date()).getTime() + (24 * 60 * 60 * 1000)) // tomorrow +} + +const testDataMixedWithLimitExceeded = { + currencies: ['USD', 'ZAR'], + transfers: [ + { + amount: { + currency: 'USD', + amount: 5 + } + }, + { + amount: { + currency: 'USD', + amount: 5000 + } + }, + { + amount: { + currency: 'USD', + amount: 6 + } + } + ], + payer: { + name: 'payerFsp', + limit: 1000, + number: 1, + fundsIn: 10000 + }, + payee: { + name: 'payeeFsp', + number: 2, + limit: 1000 + }, + endpoint: { + base: 'http://localhost:1080', + email: 'test@example.com' + }, + now: new Date(), + expiration: new Date((new Date()).getTime() + (24 * 60 * 60 * 1000)) // tomorrow +} + +const testDataWithMixedCurrencies = { + currencies: ['USD', 'ZAR'], + transfers: [ + { + amount: { + currency: 'USD', + amount: 2 + } + }, + { + amount: { + currency: 'ZAR', + amount: 3 + } + }, + { + amount: { + currency: 'USD', + amount: 4 + } + + }, + { + amount: { + currency: 'ZAR', + amount: 5 + } + + }, + { + amount: { + currency: 'USD', + amount: 6 + } + + }, + { + amount: { + currency: 'ZAR', + amount: 7 + } + + }, + { + amount: { + currency: 'USD', + amount: 8 + } + + }, + { + amount: { + currency: 'ZAR', + amount: 9 + } + + } + ], + payer: { + name: 'payerFsp', + limit: 1000, + number: 1, + fundsIn: 10000 + }, + payee: { + name: 'payeeFsp', + number: 2, + limit: 1000 + }, + endpoint: { + base: 'http://localhost:1080', + email: 'test@example.com' + }, + now: new Date(), + expiration: new Date((new Date()).getTime() + (24 * 60 * 60 * 1000)) // tomorrow +} + +const _endpointSetup = async (participantName, baseURL) => { + await ParticipantEndpointHelper.prepareData(participantName, 'FSPIOP_CALLBACK_URL_TRANSFER_POST', `${baseURL}/transfers`) + await ParticipantEndpointHelper.prepareData(participantName, 'FSPIOP_CALLBACK_URL_TRANSFER_PUT', `${baseURL}/transfers/{{transferId}}`) + await ParticipantEndpointHelper.prepareData(participantName, 'FSPIOP_CALLBACK_URL_TRANSFER_ERROR', `${baseURL}/transfers/{{transferId}}/error`) + await ParticipantEndpointHelper.prepareData(participantName, 'FSPIOP_CALLBACK_URL_BULK_TRANSFER_POST', `${baseURL}/bulkTransfers`) + await ParticipantEndpointHelper.prepareData(participantName, 'FSPIOP_CALLBACK_URL_BULK_TRANSFER_PUT', `${baseURL}/bulkTransfers/{{id}}`) + await ParticipantEndpointHelper.prepareData(participantName, 'FSPIOP_CALLBACK_URL_BULK_TRANSFER_ERROR', `${baseURL}/bulkTransfers/{{id}}/error`) + await ParticipantEndpointHelper.prepareData(participantName, 'FSPIOP_CALLBACK_URL_QUOTES', `${baseURL}`) +} + +const prepareTestData = async (dataObj) => { + try { + const payerList = [] + const payeeList = [] + + // Create Payers + for (let i = 0; i < dataObj.payer.number; i++) { + // Create payer + const payer = await ParticipantHelper.prepareData(dataObj.payer.name, dataObj.currencies[0], dataObj.currencies[1]) + // limit,initial position and funds in + payer.payerLimitAndInitialPosition = await ParticipantLimitHelper.prepareLimitAndInitialPosition(payer.participant.name, { + currency: dataObj.currencies[0], + limit: { value: dataObj.payer.limit } + }) + payer.payerLimitAndInitialPositionSecondaryCurrency = await ParticipantLimitHelper.prepareLimitAndInitialPosition(payer.participant.name, { + currency: dataObj.currencies[1], + limit: { value: dataObj.payer.limit } + }) + await ParticipantFundsInOutHelper.recordFundsIn(payer.participant.name, payer.participantCurrencyId2, { + currency: dataObj.currencies[0], + amount: dataObj.payer.fundsIn + }) + await ParticipantFundsInOutHelper.recordFundsIn(payer.participant.name, payer.participantCurrencyIdSecondary2, { + currency: dataObj.currencies[1], + amount: dataObj.payer.fundsIn + }) + // endpoint setup + await _endpointSetup(payer.participant.name, dataObj.endpoint.base) + + payerList.push(payer) + } + + // Create Payees + for (let i = 0; i < dataObj.payee.number; i++) { + // Create payee + const payee = await ParticipantHelper.prepareData(dataObj.payee.name, dataObj.currencies[0], dataObj.currencies[1]) + // limit,initial position + payee.payeeLimitAndInitialPosition = await ParticipantLimitHelper.prepareLimitAndInitialPosition(payee.participant.name, { + currency: dataObj.currencies[0], + limit: { value: dataObj.payee.limit } + }) + payee.payeeLimitAndInitialPositionSecondaryCurrency = await ParticipantLimitHelper.prepareLimitAndInitialPosition(payee.participant.name, { + currency: dataObj.currencies[1], + limit: { value: dataObj.payee.limit } + }) + // endpoint setup + await _endpointSetup(payee.participant.name, dataObj.endpoint.base) + payeeList.push(payee) + } + + const kafkacat = 'GROUP=abc; T=topic; TR=transfer; kafkacat -b localhost -G $GROUP $T-$TR-prepare $T-$TR-position $T-$TR-position-batch $T-$TR-fulfil $T-$TR-get $T-admin-$TR $T-notification-event $T-bulk-prepare' + if (debug) console.error(kafkacat) + + // Create payloads for number of transfers + const transfersArray = [] + for (let i = 0; i < dataObj.transfers.length; i++) { + const payer = payerList[i % payerList.length] + const payee = payeeList[i % payeeList.length] + + const transferPayload = { + transferId: randomUUID(), + payerFsp: payer.participant.name, + payeeFsp: payee.participant.name, + amount: { + currency: dataObj.transfers[i].amount.currency, + amount: dataObj.transfers[i].amount.amount + }, + ilpPacket: 'AYIBgQAAAAAAAASwNGxldmVsb25lLmRmc3AxLm1lci45T2RTOF81MDdqUUZERmZlakgyOVc4bXFmNEpLMHlGTFGCAUBQU0svMS4wCk5vbmNlOiB1SXlweUYzY3pYSXBFdzVVc05TYWh3CkVuY3J5cHRpb246IG5vbmUKUGF5bWVudC1JZDogMTMyMzZhM2ItOGZhOC00MTYzLTg0NDctNGMzZWQzZGE5OGE3CgpDb250ZW50LUxlbmd0aDogMTM1CkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbgpTZW5kZXItSWRlbnRpZmllcjogOTI4MDYzOTEKCiJ7XCJmZWVcIjowLFwidHJhbnNmZXJDb2RlXCI6XCJpbnZvaWNlXCIsXCJkZWJpdE5hbWVcIjpcImFsaWNlIGNvb3BlclwiLFwiY3JlZGl0TmFtZVwiOlwibWVyIGNoYW50XCIsXCJkZWJpdElkZW50aWZpZXJcIjpcIjkyODA2MzkxXCJ9IgA', + condition: 'GRzLaTP7DJ9t4P-a_BA0WA9wzzlsugf00-Tn6kESAfM', + expiration: dataObj.expiration, + extensionList: { + extension: [ + { + key: 'key1', + value: 'value1' + }, + { + key: 'key2', + value: 'value2' + } + ] + } + } + + const prepareHeaders = { + 'fspiop-source': payer.participant.name, + 'fspiop-destination': payee.participant.name, + 'content-type': 'application/vnd.interoperability.transfers+json;version=1.1' + } + const fulfilAbortRejectHeaders = { + 'fspiop-source': payee.participant.name, + 'fspiop-destination': payer.participant.name, + 'content-type': 'application/vnd.interoperability.transfers+json;version=1.1' + } + + const fulfilPayload = { + fulfilment: 'UNlJ98hZTY_dsw0cAqw4i_UN3v4utt7CZFB4yfLbVFA', + completedTimestamp: dataObj.now, + transferState: 'COMMITTED', + extensionList: { + extension: [] + } + } + + const rejectPayload = Object.assign({}, fulfilPayload, { transferState: TransferInternalState.ABORTED_REJECTED }) + + const errorPayload = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.PAYEE_FSP_REJECTED_TXN).toApiErrorObject() + errorPayload.errorInformation.extensionList = { extension: [{ key: 'errorDetail', value: 'This is an abort extension' }] } + + const messageProtocolPrepare = { + id: randomUUID(), + from: transferPayload.payerFsp, + to: transferPayload.payeeFsp, + type: 'application/json', + content: { + headers: prepareHeaders, + payload: transferPayload + }, + metadata: { + event: { + id: randomUUID(), + type: TransferEventAction.PREPARE, + action: TransferEventType.PREPARE, + createdAt: dataObj.now, + state: { + status: 'success', + code: 0 + } + } + } + } + + const messageProtocolFulfil = Util.clone(messageProtocolPrepare) + messageProtocolFulfil.id = randomUUID() + messageProtocolFulfil.from = transferPayload.payeeFsp + messageProtocolFulfil.to = transferPayload.payerFsp + messageProtocolFulfil.content.headers = fulfilAbortRejectHeaders + messageProtocolFulfil.content.uriParams = { id: transferPayload.transferId } + messageProtocolFulfil.content.payload = fulfilPayload + messageProtocolFulfil.metadata.event.id = randomUUID() + messageProtocolFulfil.metadata.event.type = TransferEventType.FULFIL + messageProtocolFulfil.metadata.event.action = TransferEventAction.COMMIT + + const messageProtocolReject = Util.clone(messageProtocolFulfil) + messageProtocolReject.id = randomUUID() + messageProtocolFulfil.content.uriParams = { id: transferPayload.transferId } + messageProtocolReject.content.payload = rejectPayload + messageProtocolReject.metadata.event.action = TransferEventAction.REJECT + + const messageProtocolError = Util.clone(messageProtocolFulfil) + messageProtocolError.id = randomUUID() + messageProtocolFulfil.content.uriParams = { id: transferPayload.transferId } + messageProtocolError.content.payload = errorPayload + messageProtocolError.metadata.event.action = TransferEventAction.ABORT + transfersArray.push({ + transferPayload, + fulfilPayload, + rejectPayload, + errorPayload, + messageProtocolPrepare, + messageProtocolFulfil, + messageProtocolReject, + messageProtocolError, + payer, + payee + }) + } + const topicConfTransferPrepare = Utility.createGeneralTopicConf(Config.KAFKA_CONFIG.TOPIC_TEMPLATES.GENERAL_TOPIC_TEMPLATE.TEMPLATE, TransferEventType.TRANSFER, TransferEventType.PREPARE) + const topicConfTransferFulfil = Utility.createGeneralTopicConf(Config.KAFKA_CONFIG.TOPIC_TEMPLATES.GENERAL_TOPIC_TEMPLATE.TEMPLATE, TransferEventType.TRANSFER, TransferEventType.FULFIL) + return { + payerList, + payeeList, + topicConfTransferPrepare, + topicConfTransferFulfil, + transfersArray + } + } catch (err) { + Logger.error(err) + throw ErrorHandler.Factory.reformatFSPIOPError(err) + } +} + +Test('Handlers test', async handlersTest => { + const startTime = new Date() + await Db.connect(Config.DATABASE) + await ParticipantCached.initialize() + await ParticipantCurrencyCached.initialize() + await ParticipantLimitCached.initialize() + await SettlementModelCached.initialize() + await Cache.initCache() + await SettlementHelper.prepareData() + await HubAccountsHelper.prepareData() + + const wrapWithRetriesConf = { + remainingRetries: retryOpts?.retries || 10, // default 10 + timeout: retryOpts?.maxTimeout || 2 // default 2 + } + + // Start a testConsumer to monitor events that our handlers emit + const testConsumer = new TestConsumer([ + { + topicName: Utility.transformGeneralTopicName( + Config.KAFKA_CONFIG.TOPIC_TEMPLATES.GENERAL_TOPIC_TEMPLATE.TEMPLATE, + Enum.Events.Event.Type.TRANSFER, + Enum.Events.Event.Action.FULFIL + ), + config: Utility.getKafkaConfig( + Config.KAFKA_CONFIG, + Enum.Kafka.Config.CONSUMER, + Enum.Events.Event.Type.TRANSFER.toUpperCase(), + Enum.Events.Event.Action.FULFIL.toUpperCase() + ) + }, + { + topicName: Utility.transformGeneralTopicName( + Config.KAFKA_CONFIG.TOPIC_TEMPLATES.GENERAL_TOPIC_TEMPLATE.TEMPLATE, + Enum.Events.Event.Type.NOTIFICATION, + Enum.Events.Event.Action.EVENT + ), + config: Utility.getKafkaConfig( + Config.KAFKA_CONFIG, + Enum.Kafka.Config.CONSUMER, + Enum.Events.Event.Type.NOTIFICATION.toUpperCase(), + Enum.Events.Event.Action.EVENT.toUpperCase() + ) + }, + { + topicName: Utility.transformGeneralTopicName( + Config.KAFKA_CONFIG.TOPIC_TEMPLATES.GENERAL_TOPIC_TEMPLATE.TEMPLATE, + Enum.Events.Event.Type.TRANSFER, + Enum.Events.Event.Action.POSITION + ), + config: Utility.getKafkaConfig( + Config.KAFKA_CONFIG, + Enum.Kafka.Config.CONSUMER, + Enum.Events.Event.Type.TRANSFER.toUpperCase(), + Enum.Events.Event.Action.POSITION.toUpperCase() + ) + } + ]) + + await handlersTest.test('setup', async setupTests => { + await setupTests.test('start testConsumer', async (test) => { + // Set up the testConsumer here + await testConsumer.startListening() + test.pass('done') + test.end() + }) + + await setupTests.end() + }) + + await handlersTest.test('position batch handler should', async transferPositionPrepare => { + const prepareConfig = Utility.getKafkaConfig( + Config.KAFKA_CONFIG, + Enum.Kafka.Config.PRODUCER, + TransferEventType.TRANSFER.toUpperCase(), + TransferEventType.PREPARE.toUpperCase()) + prepareConfig.logger = Logger + + await transferPositionPrepare.test('process batch of messages with mixed keys (accountIds) and update transfer state to RESERVED', async (test) => { + // Construct test data for 10 transfers. Default object contains 10 transfers. + const td = await prepareTestData(testData) + + // Produce prepare messages for transfersArray + for (const transfer of td.transfersArray) { + await Producer.produceMessage(transfer.messageProtocolPrepare, td.topicConfTransferPrepare, prepareConfig) + } + + try { + const positionPrepare = await wrapWithRetries(() => testConsumer.getEventsForFilter({ + topicFilter: 'topic-notification-event', + action: 'event' + }), wrapWithRetriesConf.remainingRetries, wrapWithRetriesConf.timeout) + + // filter positionPrepare messages where destination is not Hub + const positionPrepareFiltered = positionPrepare.filter((notification) => notification.to !== 'Hub') + test.equal(positionPrepareFiltered.length, 10, 'Notification Messages received for all 10 transfers') + } catch (err) { + test.notOk('Error should not be thrown') + console.error(err) + } + const tests = async (totalTransferAmounts) => { + for (const value of Object.values(totalTransferAmounts)) { + const payerCurrentPosition = await ParticipantService.getPositionByParticipantCurrencyId(value.payer.participantCurrencyId) || {} + const payerInitialPosition = value.payer.payerLimitAndInitialPosition.participantPosition.value + const payerExpectedPosition = payerInitialPosition + value.totalTransferAmount + const payerPositionChange = await ParticipantService.getPositionChangeByParticipantPositionId(payerCurrentPosition.participantPositionId) || {} + test.equal(payerCurrentPosition.value, payerExpectedPosition, 'Payer position incremented by transfer amount and updated in participantPosition') + test.equal(payerPositionChange.value, payerCurrentPosition.value, 'Payer position change value inserted and matches the updated participantPosition value') + } + } + + try { + const totalTransferAmounts = {} + for (const tdTest of td.transfersArray) { + const transfer = await TransferService.getById(tdTest.messageProtocolPrepare.content.payload.transferId) || {} + if (transfer?.transferState !== TransferState.RESERVED) { + if (debug) console.log(`retrying in ${retryDelay / 1000}s..`) + throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.INTERNAL_SERVER_ERROR, `#1 Max retry count ${retryCount} reached after ${retryCount * retryDelay / 1000}s. Tests fail`) + } + totalTransferAmounts[tdTest.payer.participantCurrencyId] = { + payer: tdTest.payer, + totalTransferAmount: ( + (totalTransferAmounts[tdTest.payer.participantCurrencyId] && + totalTransferAmounts[tdTest.payer.participantCurrencyId].totalTransferAmount) || 0 + ) + tdTest.transferPayload.amount.amount + } + } + await tests(totalTransferAmounts) + } catch (err) { + Logger.error(err) + test.fail(err.message) + } + testConsumer.clearEvents() + test.end() + }) + + await transferPositionPrepare.test('process batch of messages with payer limit reached and update transfer state to ABORTED_REJECTED', async (test) => { + // Construct test data for 10 transfers. Default object contains 10 transfers. + const td = await prepareTestData(testDataLimitExceeded) + + // Produce prepare messages for transfersArray + for (const transfer of td.transfersArray) { + await Producer.produceMessage(transfer.messageProtocolPrepare, td.topicConfTransferPrepare, prepareConfig) + } + + try { + const positionPrepare = await wrapWithRetries(() => testConsumer.getEventsForFilter({ + topicFilter: 'topic-notification-event', + action: 'event', + errorCodeFilter: ErrorHandler.Enums.FSPIOPErrorCodes.PAYER_LIMIT_ERROR.code + }), wrapWithRetriesConf.remainingRetries, wrapWithRetriesConf.timeout) + + // filter positionPrepare messages where destination is not Hub + const positionPrepareFiltered = positionPrepare.filter((notification) => notification.to !== 'Hub') + test.equal(positionPrepareFiltered.length, 10, 'Notification Messages received for all 10 transfers payer limit aborts') + } catch (err) { + test.notOk('Error should not be thrown') + console.error(err) + } + + try { + for (const tdTest of td.transfersArray) { + const transfer = await TransferService.getById(tdTest.messageProtocolPrepare.content.payload.transferId) || {} + test.equal(transfer?.transferState, TransferInternalState.ABORTED_REJECTED, 'Transfer state updated to ABORTED_REJECTED') + } + } catch (err) { + Logger.error(err) + test.fail(err.message) + } + + const payerCurrentPosition = await ParticipantService.getPositionByParticipantCurrencyId(td.transfersArray[0].payer.participantCurrencyIdSecondary) || {} + const payerExpectedPosition = td.transfersArray[0].payer.payerLimitAndInitialPositionSecondaryCurrency.participantPosition.value + test.equal(payerCurrentPosition.value, payerExpectedPosition, 'Payer position should not have changed') + testConsumer.clearEvents() + test.end() + }) + + await transferPositionPrepare.test('process batch of messages with not enough liquidity and update transfer state to ABORTED_REJECTED', async (test) => { + // Construct test data for 10 transfers. Default object contains 10 transfers. + const td = await prepareTestData(testDataLimitNoLiquidity) + + // Produce prepare messages for transfersArray + for (const transfer of td.transfersArray) { + await Producer.produceMessage(transfer.messageProtocolPrepare, td.topicConfTransferPrepare, prepareConfig) + } + + try { + const positionPrepare = await wrapWithRetries(() => testConsumer.getEventsForFilter({ + topicFilter: 'topic-notification-event', + action: 'event', + errorCodeFilter: ErrorHandler.Enums.FSPIOPErrorCodes.PAYER_FSP_INSUFFICIENT_LIQUIDITY.code + }), wrapWithRetriesConf.remainingRetries, wrapWithRetriesConf.timeout) + + // filter positionPrepare messages where destination is not Hub + const positionPrepareFiltered = positionPrepare.filter((notification) => notification.to !== 'Hub') + test.equal(positionPrepareFiltered.length, 10, 'Notification Messages received for all 10 transfers payer insufficient liquidity aborts') + } catch (err) { + test.notOk('Error should not be thrown') + console.error(err) + } + + try { + for (const tdTest of td.transfersArray) { + const transfer = await TransferService.getById(tdTest.messageProtocolPrepare.content.payload.transferId) || {} + test.equal(transfer?.transferState, TransferInternalState.ABORTED_REJECTED, 'Transfer state updated to ABORTED_REJECTED') + } + } catch (err) { + Logger.error(err) + test.fail(err.message) + } + + const payerCurrentPosition = await ParticipantService.getPositionByParticipantCurrencyId(td.transfersArray[0].payer.participantCurrencyIdSecondary) || {} + const payerExpectedPosition = td.transfersArray[0].payer.payerLimitAndInitialPositionSecondaryCurrency.participantPosition.value + test.equal(payerCurrentPosition.value, payerExpectedPosition, 'Payer position should not have changed') + + testConsumer.clearEvents() + test.end() + }) + + await transferPositionPrepare.test('process batch of messages with some transfers having amount that exceeds NDC. Those transfers should be ABORTED', async (test) => { + // Construct test data for 10 transfers. Default object contains 10 transfers. + const td = await prepareTestData(testDataMixedWithLimitExceeded) + + // filter out the transferId for the transfer that will be aborted + const transferIdForLimitExceeded = td.transfersArray.filter(transfer => transfer.transferPayload.amount.amount === 5000)[0].transferPayload.transferId + console.log('transferIdForLimitExceeded:', transferIdForLimitExceeded) + + // Produce prepare messages for transfersArray + for (const transfer of td.transfersArray) { + await Producer.produceMessage(transfer.messageProtocolPrepare, td.topicConfTransferPrepare, prepareConfig) + } + + // Consume messages from notification topic + const positionPrepare = await wrapWithRetries(() => testConsumer.getEventsForFilter({ + topicFilter: 'topic-notification-event', + action: 'event' + }), wrapWithRetriesConf.remainingRetries, wrapWithRetriesConf.timeout) + + // filter positionPrepare messages where destination is not Hub + const positionPrepareFiltered = positionPrepare.filter((notification) => notification.to !== 'Hub') + test.equal(positionPrepareFiltered.length, 3, 'Notification Messages received for all 3 transfers') + + // Check error code for the transfer that exceeded NDC + positionPrepare.forEach((notification) => { + console.log('notification:', notification) + if (notification.content?.payload?.transferId === transferIdForLimitExceeded) { + test.equal(notification.content.payload.errorInformation.errorCode, ErrorHandler.Enums.FSPIOPErrorCodes.PAYER_LIMIT_ERROR.code, 'Notification Messages received for transfer that exceeded NDC') + } + }) + + // Check that payer position is only updated by sum of transfers that did not exceed NDC + const payerCurrentPosition = await ParticipantService.getPositionByParticipantCurrencyId(td.transfersArray[0].payer.participantCurrencyId) || {} + const payerExpectedPosition = testDataMixedWithLimitExceeded.transfers[0].amount.amount + testDataMixedWithLimitExceeded.transfers[2].amount.amount + test.equal(payerCurrentPosition.value, payerExpectedPosition, 'Payer position should only increase by the amounts that did not exceed NDC') + + // Check that the transfer state for transfers that exceeded NDC is ABORTED_REJECTED and for transfers that did not exceed NDC is RESERVED + try { + for (const tdTest of td.transfersArray) { + const transfer = await TransferService.getById(tdTest.messageProtocolPrepare.content.payload.transferId) || {} + // check if transferId is not transferIdForLimitExceeded + if (tdTest.messageProtocolPrepare.content.payload.transferId === transferIdForLimitExceeded) { + test.equal(transfer?.transferState, TransferInternalState.ABORTED_REJECTED, 'Transfer state updated to ABORTED_REJECTED') + } else { + test.equal(transfer?.transferState, TransferInternalState.RESERVED, 'Transfer state updated to RESERVED') + } + } + } catch (err) { + Logger.error(err) + test.fail(err.message) + } + + testConsumer.clearEvents() + test.end() + }) + + await transferPositionPrepare.test('process batch of transfers with mixed currencies', async (test) => { + // Construct test data for 10 transfers. Default object contains 10 transfers. + const td = await prepareTestData(testDataWithMixedCurrencies) + + // Produce prepare messages for transfersArray + for (const transfer of td.transfersArray) { + await Producer.produceMessage(transfer.messageProtocolPrepare, td.topicConfTransferPrepare, prepareConfig) + } + + // Consume messages from notification topic + const positionPrepare = await wrapWithRetries(() => testConsumer.getEventsForFilter({ + topicFilter: 'topic-notification-event', + action: 'event' + }), wrapWithRetriesConf.remainingRetries, wrapWithRetriesConf.timeout) + + // filter positionPrepare messages where destination is not Hub + const positionPrepareFiltered = positionPrepare.filter((notification) => notification.to !== 'Hub') + test.equal(positionPrepareFiltered.length, 8, 'Notification Messages received for all 8 transfers') + + // Check that payer position is only updated by sum of transfers relevant to the currency + const payerCurrentPositionForUSD = await ParticipantService.getPositionByParticipantCurrencyId(td.transfersArray[0].payer.participantCurrencyId) || {} + const payerExpectedPositionForUSD = 20 // Sum of USD transfers in testDataWithMixedCurrencies + test.equal(payerCurrentPositionForUSD.value, payerExpectedPositionForUSD, 'Payer position increases for USD transfers') + + const payerCurrentPositionForZAR = await ParticipantService.getPositionByParticipantCurrencyId(td.transfersArray[0].payer.participantCurrencyIdSecondary) || {} + const payerExpectedPositionForZAR = 24 // Sum of ZAR transfers in testDataWithMixedCurrencies + test.equal(payerCurrentPositionForZAR.value, payerExpectedPositionForZAR, 'Payer position increases for ZAR transfers') + + // Check that the transfer state for transfers is RESERVED + try { + for (const tdTest of td.transfersArray) { + const transfer = await TransferService.getById(tdTest.messageProtocolPrepare.content.payload.transferId) || {} + test.equal(transfer?.transferState, TransferInternalState.RESERVED, 'Transfer state updated to RESERVED') + } + } catch (err) { + Logger.error(err) + test.fail(err.message) + } + + testConsumer.clearEvents() + test.end() + }) + + transferPositionPrepare.end() + }) + + await handlersTest.test('teardown', async (assert) => { + try { + await Handlers.timeouts.stop() + await Cache.destroyCache() + await Db.disconnect() + assert.pass('database connection closed') + await testConsumer.destroy() + + await KafkaHelper.producers.disconnect() + await KafkaHelper.consumers.disconnect() + + if (debug) { + const elapsedTime = Math.round(((new Date()) - startTime) / 100) / 10 + console.log(`handlers.test.js finished in (${elapsedTime}s)`) + } + + assert.end() + } catch (err) { + Logger.error(`teardown failed with error - ${err}`) + assert.fail() + assert.end() + } + }) + + handlersTest.end() +}) diff --git a/test/integration/domain/participant/index.test.js b/test/integration/domain/participant/index.test.js index bc1deb9c2..2fb95ddf4 100644 --- a/test/integration/domain/participant/index.test.js +++ b/test/integration/domain/participant/index.test.js @@ -110,16 +110,16 @@ Test('Participant service', async (participantTest) => { try { let getByNameResult, result getByNameResult = await ParticipantService.getByName(testData.fsp1Name) - result = await ParticipantHelper.prepareData(testData.fsp1Name, testData.currency, !!getByNameResult) + result = await ParticipantHelper.prepareData(testData.fsp1Name, testData.currency, null, !!getByNameResult) participantFixtures.push(result.participant) getByNameResult = await ParticipantService.getByName(testData.fsp2Name) - result = await ParticipantHelper.prepareData(testData.fsp2Name, testData.currency, !!getByNameResult) + result = await ParticipantHelper.prepareData(testData.fsp2Name, testData.currency, null, !!getByNameResult) participantFixtures.push(result.participant) getByNameResult = await ParticipantService.getByName(testData.fsp3Name) - result = await ParticipantHelper.prepareData(testData.fsp3Name, testData.currency, !!getByNameResult) + result = await ParticipantHelper.prepareData(testData.fsp3Name, testData.currency, null, !!getByNameResult) participantFixtures.push(result.participant) getByNameResult = await ParticipantService.getByName(testData.fsp4Name) - result = await ParticipantHelper.prepareData(testData.fsp4Name, testData.currency, !!getByNameResult) + result = await ParticipantHelper.prepareData(testData.fsp4Name, testData.currency, null, !!getByNameResult) participantFixtures.push(result.participant) for (const participant of participantFixtures) { const read = await ParticipantService.getById(participant.participantId) diff --git a/test/integration/helpers/participant.js b/test/integration/helpers/participant.js index 7ceacf699..f1bc818c0 100644 --- a/test/integration/helpers/participant.js +++ b/test/integration/helpers/participant.js @@ -42,7 +42,7 @@ const testParticipant = { createdDate: new Date() } -exports.prepareData = async (name, currencyId = 'USD', isUnique = true) => { +exports.prepareData = async (name, currencyId = 'USD', secondaryCurrencyId = 'ZAR', isUnique = true) => { try { const participantId = await Model.create(Object.assign( {}, @@ -53,11 +53,15 @@ exports.prepareData = async (name, currencyId = 'USD', isUnique = true) => { )) const participantCurrencyId = await ParticipantCurrencyModel.create(participantId, currencyId, Enum.Accounts.LedgerAccountType.POSITION, false) const participantCurrencyId2 = await ParticipantCurrencyModel.create(participantId, currencyId, Enum.Accounts.LedgerAccountType.SETTLEMENT, false) + const participantCurrencyIdSecondary = await ParticipantCurrencyModel.create(participantId, secondaryCurrencyId, Enum.Accounts.LedgerAccountType.POSITION, false) + const participantCurrencyIdSecondary2 = await ParticipantCurrencyModel.create(participantId, secondaryCurrencyId, Enum.Accounts.LedgerAccountType.SETTLEMENT, false) const participant = await Model.getById(participantId) return { participant, participantCurrencyId, - participantCurrencyId2 + participantCurrencyId2, + participantCurrencyIdSecondary, + participantCurrencyIdSecondary2 } } catch (err) { throw ErrorHandler.Factory.reformatFSPIOPError(err) diff --git a/test/integration/helpers/testConsumer.js b/test/integration/helpers/testConsumer.js index 66b05a55a..d3a5f1e67 100644 --- a/test/integration/helpers/testConsumer.js +++ b/test/integration/helpers/testConsumer.js @@ -116,11 +116,12 @@ class TestConsumer { * @param {string} filters.valueFromFilter - String matching filter for `event.value.from` * @param {string} filters.valueToFilter - String matching filter for `event.value.to` * @param {string} filters.keyFilter - String matching filter for `event.key` + * @param {string} filters.errorCodeFilter - String matching filter for `event.value.content.payload.errorInformation.errorCode` * @returns {Array} A list of the events found for the eventId * @throws {Error} If no events could be found for the given set of filters */ getEventsForFilter (filters) { - const { action, topicFilter, valueFromFilter, valueToFilter, keyFilter } = filters + const { action, topicFilter, valueFromFilter, valueToFilter, keyFilter, errorCodeFilter } = filters let events = this.eventLog if (topicFilter !== undefined) { @@ -143,6 +144,10 @@ class TestConsumer { events = events.filter(e => e.key.toString() === keyFilter) } + if (errorCodeFilter !== undefined) { + events = events.filter(e => e.value.content.payload.errorInformation.errorCode === errorCodeFilter) + } + if (events.length === 0) { throw new Error(`No events found for given filters" ${JSON.stringify(filters)}`) } diff --git a/test/unit/domain/position/binProcessor.test.js b/test/unit/domain/position/binProcessor.test.js new file mode 100644 index 000000000..19af9c208 --- /dev/null +++ b/test/unit/domain/position/binProcessor.test.js @@ -0,0 +1,667 @@ +/***** + License + -------------- + Copyright © 2017 Bill & Melinda Gates Foundation + The Mojaloop files are made available by the Bill & Melinda Gates Foundation under the Apache License, Version 2.0 (the "License") and you may not use these files except in compliance with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, the Mojaloop files are distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + + Contributors + -------------- + This is the official list of the Mojaloop project contributors for this file. + Names of the original copyright holders (individuals or organizations) + should be listed with a '*' in the first column. People who have + contributed from an organization can be listed under the organization + that actually holds the copyright for their contributions (see the + Gates Foundation organization for an example). Those individuals should have + their names indented and be marked with a '-'. Email address can be added + optionally within square brackets . + + * Gates Foundation + - Name Surname + + * INFITX + - Vijay Kumar Guthi + + -------------- + ******/ +'use strict' + +const Sinon = require('sinon') +const Test = require('tapes')(require('tape')) +const Enum = require('@mojaloop/central-services-shared').Enum +const BinProcessor = require('../../../../src/domain/position/binProcessor') +const BatchPositionModel = require('../../../../src/models/position/batch') +const BatchPositionModelCached = require('../../../../src/models/position/batchCached') +const SettlementModelCached = require('../../../../src/models/settlement/settlementModelCached') +const participantFacade = require('../../../../src/models/participant/facade') +const sampleBins = require('./sampleBins') + +const trx = {} + +const prepareTransfersBin1 = [ + '6c2c09c3-19b6-48ba-becc-cbdffcaadd7e', + '1cf6981b-25d8-4bd7-b9d9-b1c0fc8cdeaf', + '5dff336f-62c0-4619-92c6-9ccd7c8f0369', + 'ccf68c01-fe1e-494e-8736-42a9107d3ba0' +] + +const prepareTransfersBin2 = [ + '605ce9e6-a320-4a25-a4c4-397ac6d2544b', + '42da6ac4-6c42-469d-930a-1213149f41fe', + '64b91a07-99a7-4c68-b7dd-d6b3211ad09c' +] + +const prepareTransfers = [ + ...prepareTransfersBin1, + ...prepareTransfersBin2 +] + +const fulfillTransfers = [ + '4830fa00-0c2a-4de1-9640-5ad4e68f5f62', + '33d42717-1dc9-4224-8c9b-45aab4fe6457', + 'f33add51-38b1-4715-9876-83d8a08c485d' +] + +Test('BinProcessor', async (binProcessorTest) => { + let sandbox + binProcessorTest.beforeEach(async test => { + sandbox = Sinon.createSandbox() + sandbox.stub(BatchPositionModel) + sandbox.stub(BatchPositionModelCached) + sandbox.stub(SettlementModelCached) + sandbox.stub(participantFacade) + + const prepareTransfersStates = Object.fromEntries(prepareTransfers.map((transferId) => [transferId, { transferStateChangeId: 1, transferStateId: Enum.Transfers.TransferInternalState.RECEIVED_PREPARE }])) + const fulfillTransfersStates = Object.fromEntries(fulfillTransfers.map((transferId) => [transferId, { transferStateChangeId: 1, transferStateId: Enum.Transfers.TransferInternalState.RECEIVED_FULFIL }])) + BatchPositionModel.getLatestTransferStateChangesByTransferIdList.returns({ + ...prepareTransfersStates, + ...fulfillTransfersStates + }) + + BatchPositionModelCached.getParticipantCurrencyByIds.returns([ + { + participantCurrencyId: 7, + participantId: 2, + currencyId: 'USD', + ledgerAccountTypeId: 1, + isActive: 1, + createdDate: '2023-08-17T09:36:27.000Z', + createdBy: 'unknown' + }, + { + participantCurrencyId: 15, + participantId: 3, + currencyId: 'USD', + ledgerAccountTypeId: 1, + isActive: 1, + createdDate: '2023-08-17T09:36:37.000Z', + createdBy: 'unknown' + } + ]) + + SettlementModelCached.getAll.returns([ + { + settlementModelId: 1, + name: 'DEFERREDNETUSD', + isActive: 1, + settlementGranularityId: 2, + settlementInterchangeId: 2, + settlementDelayId: 2, + currencyId: 'USD', + requireLiquidityCheck: 1, + ledgerAccountTypeId: 1, + autoPositionReset: 1, + adjustPosition: 0, + settlementAccountTypeId: 2 + }, + { + settlementModelId: 2, + name: 'DEFAULTDEFERREDNET', + isActive: 1, + settlementGranularityId: 2, + settlementInterchangeId: 2, + settlementDelayId: 2, + currencyId: null, + requireLiquidityCheck: 1, + ledgerAccountTypeId: 1, + autoPositionReset: 1, + adjustPosition: 0, + settlementAccountTypeId: 2 + }, + { + settlementModelId: 3, + name: 'CGS', + isActive: 1, + settlementGranularityId: 1, + settlementInterchangeId: 1, + settlementDelayId: 1, + currencyId: 'INR', + requireLiquidityCheck: 1, + ledgerAccountTypeId: 1, + autoPositionReset: 0, + adjustPosition: 0, + settlementAccountTypeId: 2 + }, + { + settlementModelId: 4, + name: 'INTERCHANGEFEE', + isActive: 1, + settlementGranularityId: 2, + settlementInterchangeId: 2, + settlementDelayId: 2, + currencyId: 'INR', + requireLiquidityCheck: 0, + ledgerAccountTypeId: 5, + autoPositionReset: 1, + adjustPosition: 0, + settlementAccountTypeId: 6 + } + ]) + + BatchPositionModelCached.getParticipantCurrencyByParticipantIds.returns([ + { + participantCurrencyId: 9, + participantId: 2, + currencyId: 'BGN', + ledgerAccountTypeId: 1, + isActive: 1, + createdDate: '2023-08-17T09:36:31.000Z', + createdBy: 'unknown' + }, + { + participantCurrencyId: 10, + participantId: 2, + currencyId: 'BGN', + ledgerAccountTypeId: 2, + isActive: 1, + createdDate: '2023-08-17T09:36:31.000Z', + createdBy: 'unknown' + }, + { + participantCurrencyId: 11, + participantId: 2, + currencyId: 'INR', + ledgerAccountTypeId: 1, + isActive: 1, + createdDate: '2023-08-17T09:36:32.000Z', + createdBy: 'unknown' + }, + { + participantCurrencyId: 12, + participantId: 2, + currencyId: 'INR', + ledgerAccountTypeId: 2, + isActive: 1, + createdDate: '2023-08-17T09:36:32.000Z', + createdBy: 'unknown' + }, + { + participantCurrencyId: 13, + participantId: 2, + currencyId: 'INR', + ledgerAccountTypeId: 5, + isActive: 1, + createdDate: '2023-08-17T09:36:32.000Z', + createdBy: 'unknown' + }, + { + participantCurrencyId: 14, + participantId: 2, + currencyId: 'INR', + ledgerAccountTypeId: 6, + isActive: 1, + createdDate: '2023-08-17T09:36:32.000Z', + createdBy: 'unknown' + }, + { + participantCurrencyId: 7, + participantId: 2, + currencyId: 'USD', + ledgerAccountTypeId: 1, + isActive: 1, + createdDate: '2023-08-17T09:36:27.000Z', + createdBy: 'unknown' + }, + { + participantCurrencyId: 8, + participantId: 2, + currencyId: 'USD', + ledgerAccountTypeId: 2, + isActive: 1, + createdDate: '2023-08-17T09:36:27.000Z', + createdBy: 'unknown' + }, + { + participantCurrencyId: 17, + participantId: 3, + currencyId: 'BGN', + ledgerAccountTypeId: 1, + isActive: 1, + createdDate: '2023-08-17T09:36:41.000Z', + createdBy: 'unknown' + }, + { + participantCurrencyId: 18, + participantId: 3, + currencyId: 'BGN', + ledgerAccountTypeId: 2, + isActive: 1, + createdDate: '2023-08-17T09:36:41.000Z', + createdBy: 'unknown' + }, + { + participantCurrencyId: 19, + participantId: 3, + currencyId: 'INR', + ledgerAccountTypeId: 1, + isActive: 1, + createdDate: '2023-08-17T09:36:41.000Z', + createdBy: 'unknown' + }, + { + participantCurrencyId: 20, + participantId: 3, + currencyId: 'INR', + ledgerAccountTypeId: 2, + isActive: 1, + createdDate: '2023-08-17T09:36:41.000Z', + createdBy: 'unknown' + }, + { + participantCurrencyId: 21, + participantId: 3, + currencyId: 'INR', + ledgerAccountTypeId: 5, + isActive: 1, + createdDate: '2023-08-17T09:36:41.000Z', + createdBy: 'unknown' + }, + { + participantCurrencyId: 22, + participantId: 3, + currencyId: 'INR', + ledgerAccountTypeId: 6, + isActive: 1, + createdDate: '2023-08-17T09:36:41.000Z', + createdBy: 'unknown' + }, + { + participantCurrencyId: 15, + participantId: 3, + currencyId: 'USD', + ledgerAccountTypeId: 1, + isActive: 1, + createdDate: '2023-08-17T09:36:37.000Z', + createdBy: 'unknown' + }, + { + participantCurrencyId: 16, + participantId: 3, + currencyId: 'USD', + ledgerAccountTypeId: 2, + isActive: 1, + createdDate: '2023-08-17T09:36:37.000Z', + createdBy: 'unknown' + } + ]) + + BatchPositionModel.getPositionsByAccountIdsForUpdate.returns({ + 7: { + participantPositionId: 7, + participantCurrencyId: 7, + value: 0, + reservedValue: 0, + changedDate: '2023-08-17T09:36:27.000Z' + }, + 8: { + participantPositionId: 8, + participantCurrencyId: 8, + value: -5000000, + reservedValue: 0, + changedDate: '2023-08-17T09:36:33.000Z' + }, + 15: { + participantPositionId: 15, + participantCurrencyId: 15, + value: 0, + reservedValue: 0, + changedDate: '2023-08-17T09:36:37.000Z' + }, + 16: { + participantPositionId: 16, + participantCurrencyId: 16, + value: -5000000, + reservedValue: 0, + changedDate: '2023-08-17T09:36:43.000Z' + } + }) + + BatchPositionModel.updateParticipantPosition.returns(true) + + test.end() + }) + + binProcessorTest.afterEach(async test => { + sandbox.restore() + test.end() + }) + + binProcessorTest.test('binProcessor should', prepareActionTest => { + prepareActionTest.test('processBins should process a bin of positions and return the expected results', async (test) => { + const sampleParticipantLimitReturnValues = [ + { + participantId: 2, + currencyId: 'USD', + participantLimitTypeId: 1, + value: 1000000 + }, + { + participantId: 3, + currencyId: 'USD', + participantLimitTypeId: 1, + value: 1000000 + } + ] + + participantFacade.getParticipantLimitByParticipantCurrencyLimit.returns(sampleParticipantLimitReturnValues.shift()) + + const result = await BinProcessor.processBins(sampleBins, trx) + + // Assert on result.notifyMessages + test.equal(result.notifyMessages.length, 7, 'processBins should return the expected number of notify messages') + + // Assert on result.limitAlarms + // test.equal(result.limitAlarms.length, 1, 'processBin should return the expected number of limit alarms') + + // Assert on number of function calls for DB update on position value + test.ok(BatchPositionModel.updateParticipantPosition.calledTwice, 'updateParticipantPosition should be called twice') + + // TODO: Assert on number of function calls for DB bulk insert of transferStateChanges + // TODO: Assert on number of function calls for DB bulk insert of positionChanges + + // Assert on DB update for position values of all accounts in each function call + test.deepEqual(BatchPositionModel.updateParticipantPosition.getCalls().map(call => call.args), [ + [{}, 7, 8, 0], + [{}, 15, 6, 0] + ], 'updateParticipantPosition should be called with the expected arguments') + + // TODO: Assert on DB bulk insert of transferStateChanges in each function call + // TODO: Assert on DB bulk insert of positionChanges in each function call + + test.end() + }) + + prepareActionTest.test('processBins should handle no prepare messages', async (test) => { + const sampleParticipantLimitReturnValues = [ + { + participantId: 2, + currencyId: 'USD', + participantLimitTypeId: 1, + value: 1000000 + }, + { + participantId: 3, + currencyId: 'USD', + participantLimitTypeId: 1, + value: 1000000 + } + ] + participantFacade.getParticipantLimitByParticipantCurrencyLimit.returns(sampleParticipantLimitReturnValues.shift()) + const sampleBinsDeepCopy = JSON.parse(JSON.stringify(sampleBins)) + sampleBinsDeepCopy[7].prepare = [] + sampleBinsDeepCopy[15].prepare = [] + const result = await BinProcessor.processBins(sampleBinsDeepCopy, trx) + + // Assert on result.notifyMessages + test.equal(result.notifyMessages.length, 0, 'processBins should return no messages') + + // TODO: What if there are no position changes in a batch? + // Assert on number of function calls for DB update on position value + // test.ok(BatchPositionModel.updateParticipantPosition.notCalled, 'updateParticipantPosition should not be called') + + // TODO: Assert on number of function calls for DB bulk insert of transferStateChanges + // TODO: Assert on number of function calls for DB bulk insert of positionChanges + + // Assert on DB update for position values of all accounts in each function call + test.deepEqual(BatchPositionModel.updateParticipantPosition.getCalls().map(call => call.args), [ + [{}, 7, 0, 0], + [{}, 15, 0, 0] + ], 'updateParticipantPosition should be called with the expected arguments') + + // TODO: Assert on DB bulk insert of transferStateChanges in each function call + // TODO: Assert on DB bulk insert of positionChanges in each function call + + test.end() + }) + + prepareActionTest.test('processBins should throw error if any accountId cannot be matched to atleast one participantCurrencyId', async (test) => { + const sampleParticipantLimitReturnValues = [ + { + participantId: 2, + currencyId: 'USD', + participantLimitTypeId: 1, + value: 1000000 + }, + { + participantId: 3, + currencyId: 'USD', + participantLimitTypeId: 1, + value: 1000000 + } + ] + participantFacade.getParticipantLimitByParticipantCurrencyLimit.returns(sampleParticipantLimitReturnValues.shift()) + const sampleBinsDeepCopy = JSON.parse(JSON.stringify(sampleBins)) + sampleBinsDeepCopy[100] = sampleBinsDeepCopy[15] + delete sampleBinsDeepCopy[15] + + try { + await BinProcessor.processBins(sampleBinsDeepCopy, trx) + test.fail('Error not thrown!') + } catch (err) { + test.pass('Error thrown') + } + test.end() + }) + + prepareActionTest.test('processBins should throw error if no settlement model is found', async (test) => { + SettlementModelCached.getAll.returns([]) + const sampleParticipantLimitReturnValues = [ + { + participantId: 2, + currencyId: 'USD', + participantLimitTypeId: 1, + value: 1000000 + }, + { + participantId: 3, + currencyId: 'USD', + participantLimitTypeId: 1, + value: 1000000 + } + ] + participantFacade.getParticipantLimitByParticipantCurrencyLimit.returns(sampleParticipantLimitReturnValues.shift()) + try { + await BinProcessor.processBins(sampleBins, trx) + test.fail('Error not thrown!') + } catch (err) { + test.pass('Error thrown') + } + test.end() + }) + + prepareActionTest.test('processBins should throw error if no default settlement model if currency model is missing', async (test) => { + SettlementModelCached.getAll.returns([ + { + settlementModelId: 3, + name: 'CGS', + isActive: 1, + settlementGranularityId: 1, + settlementInterchangeId: 1, + settlementDelayId: 1, + currencyId: 'INR', + requireLiquidityCheck: 1, + ledgerAccountTypeId: 1, + autoPositionReset: 0, + adjustPosition: 0, + settlementAccountTypeId: 2 + } + ]) + const sampleParticipantLimitReturnValues = [ + { + participantId: 2, + currencyId: 'USD', + participantLimitTypeId: 1, + value: 1000000 + }, + { + participantId: 3, + currencyId: 'USD', + participantLimitTypeId: 1, + value: 1000000 + } + ] + participantFacade.getParticipantLimitByParticipantCurrencyLimit.returns(sampleParticipantLimitReturnValues.shift()) + try { + await BinProcessor.processBins(sampleBins, trx) + test.fail('Error not thrown!') + } catch (err) { + test.pass('Error thrown') + } + test.end() + }) + + prepareActionTest.test('processBins should use default settlement model if currency model is missing', async (test) => { + SettlementModelCached.getAll.returns([ + { + settlementModelId: 2, + name: 'DEFAULTDEFERREDNET', + isActive: 1, + settlementGranularityId: 2, + settlementInterchangeId: 2, + settlementDelayId: 2, + currencyId: null, + requireLiquidityCheck: 1, + ledgerAccountTypeId: 1, + autoPositionReset: 1, + adjustPosition: 0, + settlementAccountTypeId: 2 + } + ]) + const sampleParticipantLimitReturnValues = [ + { + participantId: 2, + currencyId: 'USD', + participantLimitTypeId: 1, + value: 1000000 + }, + { + participantId: 3, + currencyId: 'USD', + participantLimitTypeId: 1, + value: 1000000 + } + ] + participantFacade.getParticipantLimitByParticipantCurrencyLimit.returns(sampleParticipantLimitReturnValues.shift()) + + const result = await BinProcessor.processBins(sampleBins, trx) + + // Assert on result.notifyMessages + test.equal(result.notifyMessages.length, 7, 'processBins should return no messages') + + // TODO: What if there are no position changes in a batch? + // Assert on number of function calls for DB update on position value + // test.ok(BatchPositionModel.updateParticipantPosition.notCalled, 'updateParticipantPosition should not be called') + + // TODO: Assert on number of function calls for DB bulk insert of transferStateChanges + // TODO: Assert on number of function calls for DB bulk insert of positionChanges + + // Assert on DB update for position values of all accounts in each function call + test.deepEqual(BatchPositionModel.updateParticipantPosition.getCalls().map(call => call.args), [ + [{}, 7, 8, 0], + [{}, 15, 6, 0] + ], 'updateParticipantPosition should be called with the expected arguments') + + // TODO: Assert on DB bulk insert of transferStateChanges in each function call + // TODO: Assert on DB bulk insert of positionChanges in each function call + + test.end() + }) + + prepareActionTest.test('processBins should handle no prepare binItems', async (test) => { + const sampleParticipantLimitReturnValues = [ + { + participantId: 2, + currencyId: 'USD', + participantLimitTypeId: 1, + value: 1000000 + }, + { + participantId: 3, + currencyId: 'USD', + participantLimitTypeId: 1, + value: 1000000 + } + ] + participantFacade.getParticipantLimitByParticipantCurrencyLimit.returns(sampleParticipantLimitReturnValues.shift()) + const sampleBinsDeepCopy = JSON.parse(JSON.stringify(sampleBins)) + delete sampleBinsDeepCopy[7].prepare + delete sampleBinsDeepCopy[15].prepare + const result = await BinProcessor.processBins(sampleBinsDeepCopy, trx) + + // Assert on result.notifyMessages + test.equal(result.notifyMessages.length, 0, 'processBins should return no messages') + + // TODO: What if there are no position changes in a batch? + // Assert on number of function calls for DB update on position value + // test.ok(BatchPositionModel.updateParticipantPosition.notCalled, 'updateParticipantPosition should not be called') + + // TODO: Assert on number of function calls for DB bulk insert of transferStateChanges + // TODO: Assert on number of function calls for DB bulk insert of positionChanges + + // Assert on DB update for position values of all accounts in each function call + test.deepEqual(BatchPositionModel.updateParticipantPosition.getCalls().map(call => call.args), [ + [{}, 7, 0, 0], + [{}, 15, 0, 0] + ], 'updateParticipantPosition should be called with the expected arguments') + + // TODO: Assert on DB bulk insert of transferStateChanges in each function call + // TODO: Assert on DB bulk insert of positionChanges in each function call + + test.end() + }) + prepareActionTest.end() + }) + binProcessorTest.test('iterateThroughBins should', async (iterateThroughBinsTest) => { + iterateThroughBinsTest.test('iterateThroughBins should call callback function for each message in bins', async (test) => { + const spyCb = sandbox.spy() + await BinProcessor.iterateThroughBins(sampleBins, spyCb) + + test.equal(spyCb.callCount, 10, 'callback should be called 10 times') + test.end() + }) + iterateThroughBinsTest.test('iterateThroughBins should call error callback function if callback function throws error', async (test) => { + const spyCb = sandbox.stub() + const errorCb = sandbox.spy() + spyCb.onFirstCall().throws() + spyCb.onThirdCall().throws() + await BinProcessor.iterateThroughBins(sampleBins, spyCb, errorCb) + + test.equal(spyCb.callCount, 10, 'callback should be called 10 times') + test.equal(errorCb.callCount, 2, 'error callback should be called 2 times') + test.end() + }) + iterateThroughBinsTest.test('iterateThroughBins should not affect if callback function throws error', async (test) => { + const spyCb = sandbox.stub() + spyCb.onFirstCall().throws() + await BinProcessor.iterateThroughBins(sampleBins, spyCb) + + test.equal(spyCb.callCount, 10, 'callback should be called 10 times') + test.end() + }) + iterateThroughBinsTest.end() + }) + binProcessorTest.end() +}) diff --git a/test/unit/domain/position/prepare.test.js b/test/unit/domain/position/prepare.test.js new file mode 100644 index 000000000..dbba431d0 --- /dev/null +++ b/test/unit/domain/position/prepare.test.js @@ -0,0 +1,751 @@ +/***** + License + -------------- + Copyright © 2017 Bill & Melinda Gates Foundation + The Mojaloop files are made available by the Bill & Melinda Gates Foundation under the Apache License, Version 2.0 (the "License") and you may not use these files except in compliance with the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, the Mojaloop files are distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + Contributors + -------------- + This is the official list of the Mojaloop project contributors for this file. + Names of the original copyright holders (individuals or organizations) + should be listed with a '*' in the first column. People who have + contributed from an organization can be listed under the organization + that actually holds the copyright for their contributions (see the + Gates Foundation organization for an example). Those individuals should have + their names indented and be marked with a '-'. Email address can be added + optionally within square brackets . + * Gates Foundation + - Name Surname + + * Kevin Leyow + -------------- + ******/ + +'use strict' + +const Test = require('tapes')(require('tape')) +const { Enum } = require('@mojaloop/central-services-shared') +const Sinon = require('sinon') +const { processPositionPrepareBin } = require('../../../../src/domain/position/prepare') +const Logger = require('@mojaloop/central-services-logger') + +// Each transfer is for $2.00 USD +const transferMessage1 = { + value: { + from: 'perffsp1', + to: 'perffsp2', + id: '1cf6981b-25d8-4bd7-b9d9-b1c0fc8cdeaf', + content: { + uriParams: { + id: '1cf6981b-25d8-4bd7-b9d9-b1c0fc8cdeaf' + }, + headers: { + host: 'ml-api-adapter:3000', + 'content-length': 1314, + accept: 'application/vnd.interoperability.transfers+json;version=1.1', + 'content-type': 'application/vnd.interoperability.transfers+json;version=1.1', + date: '2023-08-17T15:25:08.000Z', + 'fspiop-destination': 'perffsp2', + 'fspiop-source': 'perffsp1', + traceparent: '00-e11ece8cc6ca3dc170a8ab693910d934-25d85755f1bc6898-01', + tracestate: 'tx_end2end_start_ts=1692285908510' + }, + payload: 'data:application/vnd.interoperability.transfers+json;version=1.1;base64,eyJ0cmFuc2ZlcklkIjoiMWNmNjk4MWItMjVkOC00YmQ3LWI5ZDktYjFjMGZjOGNkZWFmIiwicGF5ZXJGc3AiOiJwZXJmZnNwMSIsInBheWVlRnNwIjoicGVyZmZzcDIiLCJhbW91bnQiOnsiYW1vdW50IjoiMiIsImN1cnJlbmN5IjoiVVNEIn0sImV4cGlyYXRpb24iOiIyMDMwLTAxLTAxVDAwOjAwOjAwLjAwMFoiLCJpbHBQYWNrZXQiOiJBWUlER1FBQUFBQUFBQ2NRSVdjdVozSmxaVzVpWVc1clpuTndMbTF6YVhOa2JpNHlOemN4TXpnd016a3hNb0lDNjJWNVNqQmpiVVoxWXpKR2FtUkhiSFppYTJ4clNXcHZhVTFFUlhoYVIxRXlUbGRaZEU1VVFYcE5lVEF3VFZkTk1reFVhekZhUjFsMFQxUkZlRmw2V1RSUFZGRXhXV3BvYlVscGQybGpXRloyWkVkV1NscERTVFpKYlZGM1RYcEpNVTFFVlRKTVZFMHhUbGRGZEU1RVVteE5VekZwVDFSbk1FeFhXWGRaVkZFeFRtcEZNRmt5UlhwUFEwbHpTVzVDYUdWWFZteEphbkEzU1c1Q2FHTnVValZUVjFKS1ltMWFka2xxY0RkSmJrSm9ZMjVTTlZOWFVsVmxXRUpzU1dwdmFWUldUa3BWTUZKUFNXbDNhV05IUm5sa1NHeEtXa2RXZFdSSGJHMWhWMVo1U1dwdmFVMXFZek5OVkUwMFRVUk5OVTFVU1dsTVEwcHRZek5DU2xwRFNUWkpiV1I1V2xkV2RWbHRSblZoTWxwNlkwTktPV1pUZDJsalIwWTFXbGhKYVU5dWMybGpSMFo1WkVoc1NscEZiSFZhYlRocFQyNXphV05IUm5sa1NHeEtXa1pTTldOSFZXbFBhVXBPVlRCc1ZGSkZOR2xNUTBwM1dWaEtNR1ZWYkd0YVZ6VXdZVmRhY0ZwWVNXbFBhVWt3VGtSRmVVMTZVVEZPYW1NMFQxTkpjMGx0V25walJXeHJTV3B2YVdOSGJIVmhNa3BvWW0xMGJXTXpRV2xtVTNkcFkwZFdlV015T1hWWlYzaEtZbTFhZGtscWNEZEpiVTUyWWxoQ2MxcFlhRTlaVnpGc1NXcHdOMGx0V25CamJrNHdWRzFHZEZwVFNUWkphMXB3WTI1T01HSnRSblJhVXpGVldsaE9NRWxwZDJsaVIwWjZaRVUxYUdKWFZXbFBhVXBOV1ZoT01HSnRSblJhVXpGVldsaE9NRWx1TUhOSmJWSm9aRWRXVUZwclNuQmpibEp2U1dwdmFVMVVhelJPUXpCM1RWTXdkMDFUU2psbVUzZHBXVmN4ZG1SWE5UQkphbkEzU1cxT01XTnVTbXhpYlU0MVNXcHZhVlpXVGtWSmFYZHBXVmN4ZG1SWE5UQkphbTlwVFZSQmQwbHVNSE5KYmxKNVdWYzFlbGxYVGpCaFZ6bDFWa2hzZDFwVFNUWmxlVXA2V1RKV2RWbFlTbkJpZVVrMlNXeFNVMUZWTlZSU2ExWlRTV2wzYVdGWE5YQmtSMnhvWkVjNWVVbHFiMmxWUlVaYVVsWkphVXhEU25CaWJXd3dZVmRHTUdJelNsVmxXRUpzU1dwdmFWRXdPVTlWTVZaT1VsWkphV1pZTUFBIiwiY29uZGl0aW9uIjoiNW0wZ3FfNWRMUWxUU1NSS1FtTHBqME1aMU10V0xXZ1N1MW9MR1ZUSnlZcyJ9' + }, + type: 'application/json', + metadata: { + correlationId: '1cf6981b-25d8-4bd7-b9d9-b1c0fc8cdeaf', + event: { + type: 'position', + action: 'prepare', + createdAt: '2023-08-17T15:25:08.511Z', + state: { + status: 'success', + code: 0, + description: 'action successful' + }, + id: '790d84e4-912a-47dc-b1dd-2f2ac2989a9e' + }, + trace: { + service: 'cl_transfer_prepare', + traceId: 'e11ece8cc6ca3dc170a8ab693910d934', + spanId: '1a2c4baf99bdb2c6', + sampled: 1, + flags: '01', + parentSpanId: '3c5863bb3c2b4ecc', + startTimestamp: '2023-08-17T15:25:08.860Z', + tags: { + tracestate: 'acmevendor=eyJzcGFuSWQiOiIxYTJjNGJhZjk5YmRiMmM2IiwidGltZUFwaVByZXBhcmUiOiIxNjkyMjg1OTA4NTEwIn0=,tx_end2end_start_ts=1692285908510', + transactionType: 'transfer', + transactionAction: 'prepare', + transactionId: '1cf6981b-25d8-4bd7-b9d9-b1c0fc8cdeaf', + source: 'perffsp1', + destination: 'perffsp2', + payerFsp: 'perffsp1', + payeeFsp: 'perffsp2' + }, + tracestates: { + acmevendor: { + spanId: '1a2c4baf99bdb2c6', + timeApiPrepare: '1692285908510' + }, + tx_end2end_start_ts: '1692285908510' + } + }, + 'protocol.createdAt': 1692285908866 + } + }, + size: 3489, + key: 51, + topic: 'topic-transfer-position', + offset: 4070, + partition: 0, + timestamp: 1694175690401 +} +const transferMessage2 = { + value: { + from: 'perffsp1', + to: 'perffsp2', + id: '6c2c09c3-19b6-48ba-becc-cbdffcaadd7e', + content: { + uriParams: { + id: '6c2c09c3-19b6-48ba-becc-cbdffcaadd7e' + }, + headers: { + host: 'ml-api-adapter:3000', + 'user-agent': 'k6/0.45.0 (https://k6.io/)', + 'content-length': 1314, + accept: 'application/vnd.interoperability.transfers+json;version=1.1', + 'content-type': 'application/vnd.interoperability.transfers+json;version=1.1', + date: '2023-08-17T15:25:08.000Z', + 'fspiop-destination': 'perffsp2', + 'fspiop-source': 'perffsp1', + traceparent: '00-3353dbcbb8f5a36cf4236873bf71fa00-cae41b1f121ab294-01', + tracestate: 'tx_end2end_start_ts=1692285908887' + }, + payload: 'data:application/vnd.interoperability.transfers+json;version=1.1;base64,eyJ0cmFuc2ZlcklkIjoiNmMyYzA5YzMtMTliNi00OGJhLWJlY2MtY2JkZmZjYWFkZDdlIiwicGF5ZXJGc3AiOiJwZXJmZnNwMSIsInBheWVlRnNwIjoicGVyZmZzcDIiLCJhbW91bnQiOnsiYW1vdW50IjoiMiIsImN1cnJlbmN5IjoiVVNEIn0sImV4cGlyYXRpb24iOiIyMDMwLTAxLTAxVDAwOjAwOjAwLjAwMFoiLCJpbHBQYWNrZXQiOiJBWUlER1FBQUFBQUFBQ2NRSVdjdVozSmxaVzVpWVc1clpuTndMbTF6YVhOa2JpNHlOemN4TXpnd016a3hNb0lDNjJWNVNqQmpiVVoxWXpKR2FtUkhiSFppYTJ4clNXcHZhVTFFUlhoYVIxRXlUbGRaZEU1VVFYcE5lVEF3VFZkTk1reFVhekZhUjFsMFQxUkZlRmw2V1RSUFZGRXhXV3BvYlVscGQybGpXRloyWkVkV1NscERTVFpKYlZGM1RYcEpNVTFFVlRKTVZFMHhUbGRGZEU1RVVteE5VekZwVDFSbk1FeFhXWGRaVkZFeFRtcEZNRmt5UlhwUFEwbHpTVzVDYUdWWFZteEphbkEzU1c1Q2FHTnVValZUVjFKS1ltMWFka2xxY0RkSmJrSm9ZMjVTTlZOWFVsVmxXRUpzU1dwdmFWUldUa3BWTUZKUFNXbDNhV05IUm5sa1NHeEtXa2RXZFdSSGJHMWhWMVo1U1dwdmFVMXFZek5OVkUwMFRVUk5OVTFVU1dsTVEwcHRZek5DU2xwRFNUWkpiV1I1V2xkV2RWbHRSblZoTWxwNlkwTktPV1pUZDJsalIwWTFXbGhKYVU5dWMybGpSMFo1WkVoc1NscEZiSFZhYlRocFQyNXphV05IUm5sa1NHeEtXa1pTTldOSFZXbFBhVXBPVlRCc1ZGSkZOR2xNUTBwM1dWaEtNR1ZWYkd0YVZ6VXdZVmRhY0ZwWVNXbFBhVWt3VGtSRmVVMTZVVEZPYW1NMFQxTkpjMGx0V25walJXeHJTV3B2YVdOSGJIVmhNa3BvWW0xMGJXTXpRV2xtVTNkcFkwZFdlV015T1hWWlYzaEtZbTFhZGtscWNEZEpiVTUyWWxoQ2MxcFlhRTlaVnpGc1NXcHdOMGx0V25CamJrNHdWRzFHZEZwVFNUWkphMXB3WTI1T01HSnRSblJhVXpGVldsaE9NRWxwZDJsaVIwWjZaRVUxYUdKWFZXbFBhVXBOV1ZoT01HSnRSblJhVXpGVldsaE9NRWx1TUhOSmJWSm9aRWRXVUZwclNuQmpibEp2U1dwdmFVMVVhelJPUXpCM1RWTXdkMDFUU2psbVUzZHBXVmN4ZG1SWE5UQkphbkEzU1cxT01XTnVTbXhpYlU0MVNXcHZhVlpXVGtWSmFYZHBXVmN4ZG1SWE5UQkphbTlwVFZSQmQwbHVNSE5KYmxKNVdWYzFlbGxYVGpCaFZ6bDFWa2hzZDFwVFNUWmxlVXA2V1RKV2RWbFlTbkJpZVVrMlNXeFNVMUZWTlZSU2ExWlRTV2wzYVdGWE5YQmtSMnhvWkVjNWVVbHFiMmxWUlVaYVVsWkphVXhEU25CaWJXd3dZVmRHTUdJelNsVmxXRUpzU1dwdmFWRXdPVTlWTVZaT1VsWkphV1pZTUFBIiwiY29uZGl0aW9uIjoiNW0wZ3FfNWRMUWxUU1NSS1FtTHBqME1aMU10V0xXZ1N1MW9MR1ZUSnlZcyJ9' + }, + type: 'application/json', + metadata: { + correlationId: '6c2c09c3-19b6-48ba-becc-cbdffcaadd7e', + event: { + type: 'position', + action: 'prepare', + createdAt: '2023-08-17T15:25:08.888Z', + state: { + status: 'success', + code: 0, + description: 'action successful' + }, + id: 'ed2e00ed-2b65-4f6a-886d-df9591c9df1f' + }, + trace: { + service: 'cl_transfer_prepare', + traceId: '3353dbcbb8f5a36cf4236873bf71fa00', + spanId: '95f51fddcfd3895b', + sampled: 1, + flags: '01', + parentSpanId: '1e916a41afd771c7', + startTimestamp: '2023-08-17T15:25:08.951Z', + tags: { + tracestate: 'acmevendor=eyJzcGFuSWQiOiI5NWY1MWZkZGNmZDM4OTViIiwidGltZUFwaVByZXBhcmUiOiIxNjkyMjg1OTA4ODg4In0=,tx_end2end_start_ts=1692285908887', + transactionType: 'transfer', + transactionAction: 'prepare', + transactionId: '6c2c09c3-19b6-48ba-becc-cbdffcaadd7e', + source: 'perffsp1', + destination: 'perffsp2', + payerFsp: 'perffsp1', + payeeFsp: 'perffsp2' + }, + tracestates: { + acmevendor: { + spanId: '95f51fddcfd3895b', + timeApiPrepare: '1692285908888' + }, + tx_end2end_start_ts: '1692285908887' + } + }, + 'protocol.createdAt': 1692285908957 + } + }, + size: 3489, + key: 51, + topic: 'topic-transfer-position', + offset: 4073, + partition: 0, + timestamp: 1694175690401 +} +const transferMessage3 = { + value: { + from: 'perffsp1', + to: 'perffsp2', + id: '5dff336f-62c0-4619-92c6-9ccd7c8f0369', + content: { + uriParams: { + id: '5dff336f-62c0-4619-92c6-9ccd7c8f0369' + }, + headers: { + host: 'ml-api-adapter:3000', + 'user-agent': 'k6/0.45.0 (https://k6.io/)', + 'content-length': 1314, + accept: 'application/vnd.interoperability.transfers+json;version=1.1', + 'content-type': 'application/vnd.interoperability.transfers+json;version=1.1', + date: '2023-08-17T15:25:08.000Z', + 'fspiop-destination': 'perffsp2', + 'fspiop-source': 'perffsp1', + traceparent: '00-7295d8d6f6aed39e143fcbfcd5bf89b2-ecdb5940d1b8ec45-01', + tracestate: 'tx_end2end_start_ts=1692285908467' + }, + payload: 'data:application/vnd.interoperability.transfers+json;version=1.1;base64,eyJ0cmFuc2ZlcklkIjoiNWRmZjMzNmYtNjJjMC00NjE5LTkyYzYtOWNjZDdjOGYwMzY5IiwicGF5ZXJGc3AiOiJwZXJmZnNwMSIsInBheWVlRnNwIjoicGVyZmZzcDIiLCJhbW91bnQiOnsiYW1vdW50IjoiMiIsImN1cnJlbmN5IjoiVVNEIn0sImV4cGlyYXRpb24iOiIyMDMwLTAxLTAxVDAwOjAwOjAwLjAwMFoiLCJpbHBQYWNrZXQiOiJBWUlER1FBQUFBQUFBQ2NRSVdjdVozSmxaVzVpWVc1clpuTndMbTF6YVhOa2JpNHlOemN4TXpnd016a3hNb0lDNjJWNVNqQmpiVVoxWXpKR2FtUkhiSFppYTJ4clNXcHZhVTFFUlhoYVIxRXlUbGRaZEU1VVFYcE5lVEF3VFZkTk1reFVhekZhUjFsMFQxUkZlRmw2V1RSUFZGRXhXV3BvYlVscGQybGpXRloyWkVkV1NscERTVFpKYlZGM1RYcEpNVTFFVlRKTVZFMHhUbGRGZEU1RVVteE5VekZwVDFSbk1FeFhXWGRaVkZFeFRtcEZNRmt5UlhwUFEwbHpTVzVDYUdWWFZteEphbkEzU1c1Q2FHTnVValZUVjFKS1ltMWFka2xxY0RkSmJrSm9ZMjVTTlZOWFVsVmxXRUpzU1dwdmFWUldUa3BWTUZKUFNXbDNhV05IUm5sa1NHeEtXa2RXZFdSSGJHMWhWMVo1U1dwdmFVMXFZek5OVkUwMFRVUk5OVTFVU1dsTVEwcHRZek5DU2xwRFNUWkpiV1I1V2xkV2RWbHRSblZoTWxwNlkwTktPV1pUZDJsalIwWTFXbGhKYVU5dWMybGpSMFo1WkVoc1NscEZiSFZhYlRocFQyNXphV05IUm5sa1NHeEtXa1pTTldOSFZXbFBhVXBPVlRCc1ZGSkZOR2xNUTBwM1dWaEtNR1ZWYkd0YVZ6VXdZVmRhY0ZwWVNXbFBhVWt3VGtSRmVVMTZVVEZPYW1NMFQxTkpjMGx0V25walJXeHJTV3B2YVdOSGJIVmhNa3BvWW0xMGJXTXpRV2xtVTNkcFkwZFdlV015T1hWWlYzaEtZbTFhZGtscWNEZEpiVTUyWWxoQ2MxcFlhRTlaVnpGc1NXcHdOMGx0V25CamJrNHdWRzFHZEZwVFNUWkphMXB3WTI1T01HSnRSblJhVXpGVldsaE9NRWxwZDJsaVIwWjZaRVUxYUdKWFZXbFBhVXBOV1ZoT01HSnRSblJhVXpGVldsaE9NRWx1TUhOSmJWSm9aRWRXVUZwclNuQmpibEp2U1dwdmFVMVVhelJPUXpCM1RWTXdkMDFUU2psbVUzZHBXVmN4ZG1SWE5UQkphbkEzU1cxT01XTnVTbXhpYlU0MVNXcHZhVlpXVGtWSmFYZHBXVmN4ZG1SWE5UQkphbTlwVFZSQmQwbHVNSE5KYmxKNVdWYzFlbGxYVGpCaFZ6bDFWa2hzZDFwVFNUWmxlVXA2V1RKV2RWbFlTbkJpZVVrMlNXeFNVMUZWTlZSU2ExWlRTV2wzYVdGWE5YQmtSMnhvWkVjNWVVbHFiMmxWUlVaYVVsWkphVXhEU25CaWJXd3dZVmRHTUdJelNsVmxXRUpzU1dwdmFWRXdPVTlWTVZaT1VsWkphV1pZTUFBIiwiY29uZGl0aW9uIjoiNW0wZ3FfNWRMUWxUU1NSS1FtTHBqME1aMU10V0xXZ1N1MW9MR1ZUSnlZcyJ9' + }, + type: 'application/json', + metadata: { + correlationId: '5dff336f-62c0-4619-92c6-9ccd7c8f0369', + event: { + type: 'position', + action: 'prepare', + createdAt: '2023-08-17T15:25:08.468Z', + state: { + status: 'success', + code: 0, + description: 'action successful' + }, + id: '7c9eeb85-4fab-4dbd-8a16-0f870f3efbe3' + }, + trace: { + service: 'cl_transfer_prepare', + traceId: '7295d8d6f6aed39e143fcbfcd5bf89b2', + spanId: '31ac97084059ed78', + sampled: 1, + flags: '01', + parentSpanId: '87647df7326a1b6d', + startTimestamp: '2023-08-17T15:25:08.732Z', + tags: { + tracestate: 'acmevendor=eyJzcGFuSWQiOiIzMWFjOTcwODQwNTllZDc4IiwidGltZUFwaVByZXBhcmUiOiIxNjkyMjg1OTA4NDY4In0=,tx_end2end_start_ts=1692285908467', + transactionType: 'transfer', + transactionAction: 'prepare', + transactionId: '5dff336f-62c0-4619-92c6-9ccd7c8f0369', + source: 'perffsp1', + destination: 'perffsp2', + payerFsp: 'perffsp1', + payeeFsp: 'perffsp2' + }, + tracestates: { + acmevendor: { + spanId: '31ac97084059ed78', + timeApiPrepare: '1692285908468' + }, + tx_end2end_start_ts: '1692285908467' + } + }, + 'protocol.createdAt': 1692285908736 + } + }, + size: 3489, + key: 51, + topic: 'topic-transfer-position', + offset: 4076, + partition: 0, + timestamp: 1694175690401 +} +const span = {} +const binItems = [{ + message: transferMessage1, + span, + decodedPayload: { + amount: { + amount: '2', + currency: 'USD' + }, + condition: '5m0gq_5dLQlTSSRKQmLpj0MZ1MtWLWgSu1oLGVTJyYs', + expiration: '2030-01-01T00:00:00.000Z', + ilpPacket: 'AYIDGQAAAAAAACcQIWcuZ3JlZW5iYW5rZnNwLm1zaXNkbi4yNzcxMzgwMzkxMoIC62V5SjBjbUZ1YzJGamRHbHZia2xrSWpvaU1ERXhaR1EyTldZdE5UQXpNeTAwTVdNMkxUazFaR1l0T1RFeFl6WTRPVFExWWpobUlpd2ljWFZ2ZEdWSlpDSTZJbVF3TXpJMU1EVTJMVE0xTldFdE5EUmxNUzFpT1RnMExXWXdZVFExTmpFMFkyRXpPQ0lzSW5CaGVXVmxJanA3SW5CaGNuUjVTV1JKYm1adklqcDdJbkJoY25SNVNXUlVlWEJsSWpvaVRWTkpVMFJPSWl3aWNHRnlkSGxKWkdWdWRHbG1hV1Z5SWpvaU1qYzNNVE00TURNNU1USWlMQ0ptYzNCSlpDSTZJbWR5WldWdVltRnVhMlp6Y0NKOWZTd2ljR0Y1WlhJaU9uc2ljR0Z5ZEhsSlpFbHVabThpT25zaWNHRnlkSGxKWkZSNWNHVWlPaUpOVTBsVFJFNGlMQ0p3WVhKMGVVbGtaVzUwYVdacFpYSWlPaUkwTkRFeU16UTFOamM0T1NJc0ltWnpjRWxrSWpvaWNHbHVhMkpoYm10bWMzQWlmU3dpY0dWeWMyOXVZV3hKYm1adklqcDdJbU52YlhCc1pYaE9ZVzFsSWpwN0ltWnBjbk4wVG1GdFpTSTZJa1pwY25OMGJtRnRaUzFVWlhOMElpd2liR0Z6ZEU1aGJXVWlPaUpNWVhOMGJtRnRaUzFVWlhOMEluMHNJbVJoZEdWUFprSnBjblJvSWpvaU1UazROQzB3TVMwd01TSjlmU3dpWVcxdmRXNTBJanA3SW1OMWNuSmxibU41SWpvaVZWTkVJaXdpWVcxdmRXNTBJam9pTVRBd0luMHNJblJ5WVc1ellXTjBhVzl1Vkhsd1pTSTZleUp6WTJWdVlYSnBieUk2SWxSU1FVNVRSa1ZTSWl3aWFXNXBkR2xoZEc5eUlqb2lVRUZaUlZJaUxDSnBibWwwYVdGMGIzSlVlWEJsSWpvaVEwOU9VMVZOUlZJaWZYMAA', + payeeFsp: 'perffsp2', + payerFsp: 'perffsp1', + transferId: '1cf6981b-25d8-4bd7-b9d9-b1c0fc8cdeaf' + } +}, +{ + message: transferMessage2, + span, + decodedPayload: { + amount: { + amount: '2', + currency: 'USD' + }, + condition: '5m0gq_5dLQlTSSRKQmLpj0MZ1MtWLWgSu1oLGVTJyYs', + expiration: '2030-01-01T00:00:00.000Z', + ilpPacket: 'AYIDGQAAAAAAACcQIWcuZ3JlZW5iYW5rZnNwLm1zaXNkbi4yNzcxMzgwMzkxMoIC62V5SjBjbUZ1YzJGamRHbHZia2xrSWpvaU1ERXhaR1EyTldZdE5UQXpNeTAwTVdNMkxUazFaR1l0T1RFeFl6WTRPVFExWWpobUlpd2ljWFZ2ZEdWSlpDSTZJbVF3TXpJMU1EVTJMVE0xTldFdE5EUmxNUzFpT1RnMExXWXdZVFExTmpFMFkyRXpPQ0lzSW5CaGVXVmxJanA3SW5CaGNuUjVTV1JKYm1adklqcDdJbkJoY25SNVNXUlVlWEJsSWpvaVRWTkpVMFJPSWl3aWNHRnlkSGxKWkdWdWRHbG1hV1Z5SWpvaU1qYzNNVE00TURNNU1USWlMQ0ptYzNCSlpDSTZJbWR5WldWdVltRnVhMlp6Y0NKOWZTd2ljR0Y1WlhJaU9uc2ljR0Z5ZEhsSlpFbHVabThpT25zaWNHRnlkSGxKWkZSNWNHVWlPaUpOVTBsVFJFNGlMQ0p3WVhKMGVVbGtaVzUwYVdacFpYSWlPaUkwTkRFeU16UTFOamM0T1NJc0ltWnpjRWxrSWpvaWNHbHVhMkpoYm10bWMzQWlmU3dpY0dWeWMyOXVZV3hKYm1adklqcDdJbU52YlhCc1pYaE9ZVzFsSWpwN0ltWnBjbk4wVG1GdFpTSTZJa1pwY25OMGJtRnRaUzFVWlhOMElpd2liR0Z6ZEU1aGJXVWlPaUpNWVhOMGJtRnRaUzFVWlhOMEluMHNJbVJoZEdWUFprSnBjblJvSWpvaU1UazROQzB3TVMwd01TSjlmU3dpWVcxdmRXNTBJanA3SW1OMWNuSmxibU41SWpvaVZWTkVJaXdpWVcxdmRXNTBJam9pTVRBd0luMHNJblJ5WVc1ellXTjBhVzl1Vkhsd1pTSTZleUp6WTJWdVlYSnBieUk2SWxSU1FVNVRSa1ZTSWl3aWFXNXBkR2xoZEc5eUlqb2lVRUZaUlZJaUxDSnBibWwwYVdGMGIzSlVlWEJsSWpvaVEwOU9VMVZOUlZJaWZYMAA', + payeeFsp: 'perffsp2', + payerFsp: 'perffsp1', + transferId: '6c2c09c3-19b6-48ba-becc-cbdffcaadd7e' + } +}, +{ + message: transferMessage3, + span, + decodedPayload: { + amount: { + amount: '2', + currency: 'USD' + }, + condition: '5m0gq_5dLQlTSSRKQmLpj0MZ1MtWLWgSu1oLGVTJyYs', + expiration: '2030-01-01T00:00:00.000Z', + ilpPacket: 'AYIDGQAAAAAAACcQIWcuZ3JlZW5iYW5rZnNwLm1zaXNkbi4yNzcxMzgwMzkxMoIC62V5SjBjbUZ1YzJGamRHbHZia2xrSWpvaU1ERXhaR1EyTldZdE5UQXpNeTAwTVdNMkxUazFaR1l0T1RFeFl6WTRPVFExWWpobUlpd2ljWFZ2ZEdWSlpDSTZJbVF3TXpJMU1EVTJMVE0xTldFdE5EUmxNUzFpT1RnMExXWXdZVFExTmpFMFkyRXpPQ0lzSW5CaGVXVmxJanA3SW5CaGNuUjVTV1JKYm1adklqcDdJbkJoY25SNVNXUlVlWEJsSWpvaVRWTkpVMFJPSWl3aWNHRnlkSGxKWkdWdWRHbG1hV1Z5SWpvaU1qYzNNVE00TURNNU1USWlMQ0ptYzNCSlpDSTZJbWR5WldWdVltRnVhMlp6Y0NKOWZTd2ljR0Y1WlhJaU9uc2ljR0Z5ZEhsSlpFbHVabThpT25zaWNHRnlkSGxKWkZSNWNHVWlPaUpOVTBsVFJFNGlMQ0p3WVhKMGVVbGtaVzUwYVdacFpYSWlPaUkwTkRFeU16UTFOamM0T1NJc0ltWnpjRWxrSWpvaWNHbHVhMkpoYm10bWMzQWlmU3dpY0dWeWMyOXVZV3hKYm1adklqcDdJbU52YlhCc1pYaE9ZVzFsSWpwN0ltWnBjbk4wVG1GdFpTSTZJa1pwY25OMGJtRnRaUzFVWlhOMElpd2liR0Z6ZEU1aGJXVWlPaUpNWVhOMGJtRnRaUzFVWlhOMEluMHNJbVJoZEdWUFprSnBjblJvSWpvaU1UazROQzB3TVMwd01TSjlmU3dpWVcxdmRXNTBJanA3SW1OMWNuSmxibU41SWpvaVZWTkVJaXdpWVcxdmRXNTBJam9pTVRBd0luMHNJblJ5WVc1ellXTjBhVzl1Vkhsd1pTSTZleUp6WTJWdVlYSnBieUk2SWxSU1FVNVRSa1ZTSWl3aWFXNXBkR2xoZEc5eUlqb2lVRUZaUlZJaUxDSnBibWwwYVdGMGIzSlVlWEJsSWpvaVEwOU9VMVZOUlZJaWZYMAA', + payeeFsp: 'perffsp2', + payerFsp: 'perffsp1', + transferId: '5dff336f-62c0-4619-92c6-9ccd7c8f0369' + } +}] + +Test('Prepare domain', positionIndexTest => { + let sandbox + + positionIndexTest.beforeEach(t => { + sandbox = Sinon.createSandbox() + t.end() + }) + + positionIndexTest.afterEach(t => { + sandbox.restore() + t.end() + }) + + positionIndexTest.test('processPositionPrepareBin should', changeParticipantPositionTest => { + changeParticipantPositionTest.test('produce abort message for transfers not in the right transfer state', async (test) => { + const participantLimit = { + participantCurrencyId: 1, + participantLimitTypeId: 1, + value: 900, // Participant limit value + isActive: 1, + createdBy: 'unknown', + participantLimitId: 1, + thresholdAlarmPercentage: 0.5 + } + const settlementModel = { + settlementModelId: 1, + name: 'DEFERREDNET', + isActive: 1, + settlementGranularityId: 2, + settlementInterchangeId: 2, + settlementDelayId: 2, // 1 Immediate, 2 Deferred + currencyId: 'USD', + requireLiquidityCheck: 1, + ledgerAccountTypeId: 1, // 1 Position, 2 Settlement + autoPositionReset: 1, + adjustPosition: 0, + settlementAccountTypeId: 2 + } + const processedMessages = await processPositionPrepareBin( + binItems, + 0, // Accumulated position value + 0, + { + '1cf6981b-25d8-4bd7-b9d9-b1c0fc8cdeaf': Enum.Transfers.TransferInternalState.RECEIVED_PREPARE, + '6c2c09c3-19b6-48ba-becc-cbdffcaadd7e': Enum.Transfers.TransferInternalState.RECEIVED_PREPARE, + '5dff336f-62c0-4619-92c6-9ccd7c8f0369': 'INVALID_STATE' + }, + -1000, // Settlement participant position value + settlementModel, + participantLimit + ) + Logger.isInfoEnabled && Logger.info(processedMessages) + test.equal(processedMessages.notifyMessages.length, 3) + test.equal(processedMessages.notifyMessages[0].message.content.headers.accept, transferMessage1.value.content.headers.accept) + test.equal(processedMessages.notifyMessages[0].message.content.headers['fspiop-destination'], transferMessage1.value.content.headers['fspiop-destination']) + test.equal(processedMessages.notifyMessages[0].message.content.headers['fspiop-source'], transferMessage1.value.content.headers['fspiop-source']) + test.equal(processedMessages.notifyMessages[0].message.content.headers['content-type'], transferMessage1.value.content.headers['content-type']) + test.equal(processedMessages.accumulatedTransferStates[transferMessage1.value.id], Enum.Transfers.TransferState.RESERVED) + + test.equal(processedMessages.notifyMessages[1].message.content.headers.accept, transferMessage2.value.content.headers.accept) + test.equal(processedMessages.notifyMessages[1].message.content.headers['fspiop-destination'], transferMessage2.value.content.headers['fspiop-destination']) + test.equal(processedMessages.notifyMessages[1].message.content.headers['fspiop-source'], transferMessage2.value.content.headers['fspiop-source']) + test.equal(processedMessages.notifyMessages[1].message.content.headers['content-type'], transferMessage2.value.content.headers['content-type']) + test.equal(processedMessages.accumulatedTransferStates[transferMessage2.value.id], Enum.Transfers.TransferState.RESERVED) + + test.equal(processedMessages.notifyMessages[2].message.content.uriParams.id, transferMessage3.value.id) + test.equal(processedMessages.notifyMessages[2].message.content.headers.accept, transferMessage3.value.content.headers.accept) + test.equal(processedMessages.notifyMessages[2].message.content.headers['fspiop-destination'], transferMessage3.value.content.headers['fspiop-source']) + test.equal(processedMessages.notifyMessages[2].message.content.headers['fspiop-source'], Enum.Http.Headers.FSPIOP.SWITCH.value) + test.equal(processedMessages.notifyMessages[2].message.content.headers['content-type'], transferMessage3.value.content.headers['content-type']) + test.equal(processedMessages.notifyMessages[2].message.content.payload.errorInformation.errorCode, '2001') + test.equal(processedMessages.notifyMessages[2].message.content.payload.errorInformation.errorDescription, 'Internal server error') + test.equal(processedMessages.accumulatedTransferStates[transferMessage3.value.id], Enum.Transfers.TransferInternalState.ABORTED_REJECTED) + + test.equal(processedMessages.accumulatedTransferStateChanges[0].transferId, transferMessage1.value.id) + test.equal(processedMessages.accumulatedTransferStateChanges[1].transferId, transferMessage2.value.id) + test.equal(processedMessages.accumulatedTransferStateChanges[2].transferId, transferMessage3.value.id) + + test.equal(processedMessages.accumulatedTransferStateChanges[0].transferStateId, Enum.Transfers.TransferState.RESERVED) + test.equal(processedMessages.accumulatedTransferStateChanges[1].transferStateId, Enum.Transfers.TransferState.RESERVED) + test.equal(processedMessages.accumulatedTransferStateChanges[2].transferStateId, Enum.Transfers.TransferInternalState.ABORTED_REJECTED) + + test.equal(processedMessages.accumulatedPositionValue, 4) + test.end() + }) + + changeParticipantPositionTest.test('produce abort message for when payer does not have enough liquidity', async (test) => { + const participantLimit = { + participantCurrencyId: 1, + participantLimitTypeId: 1, + value: 0, // Set low + isActive: 1, + createdBy: 'unknown', + participantLimitId: 1, + thresholdAlarmPercentage: 0.5 + } + const settlementModel = { + settlementModelId: 1, + name: 'DEFERREDNET', + isActive: 1, + settlementGranularityId: 2, + settlementInterchangeId: 2, + settlementDelayId: 2, // 1 Immediate, 2 Deferred + currencyId: 'USD', + requireLiquidityCheck: 1, + ledgerAccountTypeId: 1, // 1 Position, 2 Settlement + autoPositionReset: 1, + adjustPosition: 0, + settlementAccountTypeId: 2 + } + const processedMessages = await processPositionPrepareBin( + binItems, + 0, // No accumulated position value + 0, + { + '1cf6981b-25d8-4bd7-b9d9-b1c0fc8cdeaf': Enum.Transfers.TransferInternalState.RECEIVED_PREPARE, + '6c2c09c3-19b6-48ba-becc-cbdffcaadd7e': Enum.Transfers.TransferInternalState.RECEIVED_PREPARE, + '5dff336f-62c0-4619-92c6-9ccd7c8f0369': 'INVALID_STATE' + }, + 0, // Settlement participant position value + settlementModel, + participantLimit + ) + Logger.isInfoEnabled && Logger.info(processedMessages) + test.equal(processedMessages.notifyMessages.length, 3) + test.equal(processedMessages.accumulatedPositionChanges.length, 0) + + test.equal(processedMessages.notifyMessages[0].message.content.uriParams.id, transferMessage1.value.id) + test.equal(processedMessages.notifyMessages[0].message.content.headers.accept, transferMessage1.value.content.headers.accept) + test.equal(processedMessages.notifyMessages[0].message.content.headers['fspiop-destination'], transferMessage1.value.content.headers['fspiop-source']) + test.equal(processedMessages.notifyMessages[0].message.content.headers['fspiop-source'], Enum.Http.Headers.FSPIOP.SWITCH.value) + test.equal(processedMessages.notifyMessages[0].message.content.headers['content-type'], transferMessage1.value.content.headers['content-type']) + + test.equal(processedMessages.notifyMessages[0].message.content.payload.errorInformation.errorCode, '4001') + test.equal(processedMessages.notifyMessages[0].message.content.payload.errorInformation.errorDescription, 'Payer FSP insufficient liquidity') + test.equal(processedMessages.accumulatedTransferStates[transferMessage1.value.id], Enum.Transfers.TransferInternalState.ABORTED_REJECTED) + + test.equal(processedMessages.notifyMessages[1].message.content.uriParams.id, transferMessage2.value.id) + test.equal(processedMessages.notifyMessages[1].message.content.headers.accept, transferMessage2.value.content.headers.accept) + test.equal(processedMessages.notifyMessages[1].message.content.headers['fspiop-destination'], transferMessage2.value.content.headers['fspiop-source']) + test.equal(processedMessages.notifyMessages[1].message.content.headers['fspiop-source'], Enum.Http.Headers.FSPIOP.SWITCH.value) + test.equal(processedMessages.notifyMessages[1].message.content.headers['content-type'], transferMessage2.value.content.headers['content-type']) + test.equal(processedMessages.notifyMessages[1].message.content.payload.errorInformation.errorCode, '4001') + test.equal(processedMessages.notifyMessages[1].message.content.payload.errorInformation.errorDescription, 'Payer FSP insufficient liquidity') + test.equal(processedMessages.accumulatedTransferStates[transferMessage2.value.id], Enum.Transfers.TransferInternalState.ABORTED_REJECTED) + + test.equal(processedMessages.notifyMessages[2].message.content.uriParams.id, transferMessage3.value.id) + test.equal(processedMessages.notifyMessages[2].message.content.headers.accept, transferMessage3.value.content.headers.accept) + test.equal(processedMessages.notifyMessages[2].message.content.headers['fspiop-destination'], transferMessage3.value.content.headers['fspiop-source']) + test.equal(processedMessages.notifyMessages[2].message.content.headers['fspiop-source'], Enum.Http.Headers.FSPIOP.SWITCH.value) + test.equal(processedMessages.notifyMessages[2].message.content.headers['content-type'], transferMessage3.value.content.headers['content-type']) + test.equal(processedMessages.notifyMessages[2].message.content.payload.errorInformation.errorCode, '2001') + test.equal(processedMessages.notifyMessages[2].message.content.payload.errorInformation.errorDescription, 'Internal server error') + test.equal(processedMessages.accumulatedTransferStates[transferMessage3.value.id], Enum.Transfers.TransferInternalState.ABORTED_REJECTED) + + test.equal(processedMessages.accumulatedTransferStateChanges[0].transferId, transferMessage1.value.id) + test.equal(processedMessages.accumulatedTransferStateChanges[1].transferId, transferMessage2.value.id) + test.equal(processedMessages.accumulatedTransferStateChanges[2].transferId, transferMessage3.value.id) + + test.equal(processedMessages.accumulatedTransferStateChanges[0].transferStateId, Enum.Transfers.TransferInternalState.ABORTED_REJECTED) + test.equal(processedMessages.accumulatedTransferStateChanges[1].transferStateId, Enum.Transfers.TransferInternalState.ABORTED_REJECTED) + test.equal(processedMessages.accumulatedTransferStateChanges[2].transferStateId, Enum.Transfers.TransferInternalState.ABORTED_REJECTED) + + test.equal(processedMessages.accumulatedPositionValue, 0) + test.end() + }) + + changeParticipantPositionTest.test('produce abort message for when payer has reached their set payer limit', async (test) => { + const participantLimit = { + participantCurrencyId: 1, + participantLimitTypeId: 1, + value: 1000, + isActive: 1, + createdBy: 'unknown', + participantLimitId: 1, + thresholdAlarmPercentage: 0.5 + } + const settlementModel = { + settlementModelId: 1, + name: 'DEFERREDNET', + isActive: 1, + settlementGranularityId: 2, + settlementInterchangeId: 2, + settlementDelayId: 2, // 1 Immediate, 2 Deferred + currencyId: 'USD', + requireLiquidityCheck: 1, + ledgerAccountTypeId: 1, // 1 Position, 2 Settlement + autoPositionReset: 1, + adjustPosition: 0, + settlementAccountTypeId: 2 + } + const processedMessages = await processPositionPrepareBin( + binItems, + 1000, // Position value has reached limit of 1000 + 0, + { + '1cf6981b-25d8-4bd7-b9d9-b1c0fc8cdeaf': Enum.Transfers.TransferInternalState.RECEIVED_PREPARE, + '6c2c09c3-19b6-48ba-becc-cbdffcaadd7e': Enum.Transfers.TransferInternalState.RECEIVED_PREPARE, + '5dff336f-62c0-4619-92c6-9ccd7c8f0369': 'INVALID_STATE' + }, + -2000, // Payer has liquidity + settlementModel, + participantLimit + ) + Logger.isInfoEnabled && Logger.info(processedMessages) + test.equal(processedMessages.notifyMessages.length, 3) + test.equal(processedMessages.accumulatedPositionChanges.length, 0) + + test.equal(processedMessages.notifyMessages[0].message.content.uriParams.id, transferMessage1.value.id) + test.equal(processedMessages.notifyMessages[0].message.content.headers.accept, transferMessage1.value.content.headers.accept) + test.equal(processedMessages.notifyMessages[0].message.content.headers['fspiop-destination'], transferMessage1.value.content.headers['fspiop-source']) + test.equal(processedMessages.notifyMessages[0].message.content.headers['fspiop-source'], Enum.Http.Headers.FSPIOP.SWITCH.value) + test.equal(processedMessages.notifyMessages[0].message.content.headers['content-type'], transferMessage1.value.content.headers['content-type']) + test.equal(processedMessages.notifyMessages[0].message.content.payload.errorInformation.errorCode, '4200') + test.equal(processedMessages.notifyMessages[0].message.content.payload.errorInformation.errorDescription, 'Payer limit error') + test.equal(processedMessages.accumulatedTransferStates[transferMessage1.value.id], Enum.Transfers.TransferInternalState.ABORTED_REJECTED) + + test.equal(processedMessages.notifyMessages[1].message.content.uriParams.id, transferMessage2.value.id) + test.equal(processedMessages.notifyMessages[1].message.content.headers.accept, transferMessage2.value.content.headers.accept) + test.equal(processedMessages.notifyMessages[1].message.content.headers['fspiop-destination'], transferMessage2.value.content.headers['fspiop-source']) + test.equal(processedMessages.notifyMessages[1].message.content.headers['fspiop-source'], Enum.Http.Headers.FSPIOP.SWITCH.value) + test.equal(processedMessages.notifyMessages[1].message.content.headers['content-type'], transferMessage2.value.content.headers['content-type']) + test.equal(processedMessages.notifyMessages[1].message.content.payload.errorInformation.errorCode, '4200') + test.equal(processedMessages.notifyMessages[1].message.content.payload.errorInformation.errorDescription, 'Payer limit error') + test.equal(processedMessages.accumulatedTransferStates[transferMessage2.value.id], Enum.Transfers.TransferInternalState.ABORTED_REJECTED) + + test.equal(processedMessages.notifyMessages[2].message.content.uriParams.id, transferMessage3.value.id) + test.equal(processedMessages.notifyMessages[2].message.content.headers.accept, transferMessage3.value.content.headers.accept) + test.equal(processedMessages.notifyMessages[2].message.content.headers['fspiop-destination'], transferMessage3.value.content.headers['fspiop-source']) + test.equal(processedMessages.notifyMessages[2].message.content.headers['fspiop-source'], Enum.Http.Headers.FSPIOP.SWITCH.value) + test.equal(processedMessages.notifyMessages[2].message.content.headers['content-type'], transferMessage3.value.content.headers['content-type']) + test.equal(processedMessages.notifyMessages[2].message.content.payload.errorInformation.errorCode, '2001') + test.equal(processedMessages.notifyMessages[2].message.content.payload.errorInformation.errorDescription, 'Internal server error') + test.equal(processedMessages.accumulatedTransferStates[transferMessage3.value.id], Enum.Transfers.TransferInternalState.ABORTED_REJECTED) + + test.equal(processedMessages.accumulatedTransferStateChanges[0].transferId, transferMessage1.value.id) + test.equal(processedMessages.accumulatedTransferStateChanges[1].transferId, transferMessage2.value.id) + test.equal(processedMessages.accumulatedTransferStateChanges[2].transferId, transferMessage3.value.id) + + test.equal(processedMessages.accumulatedTransferStateChanges[0].transferStateId, Enum.Transfers.TransferInternalState.ABORTED_REJECTED) + test.equal(processedMessages.accumulatedTransferStateChanges[1].transferStateId, Enum.Transfers.TransferInternalState.ABORTED_REJECTED) + test.equal(processedMessages.accumulatedTransferStateChanges[2].transferStateId, Enum.Transfers.TransferInternalState.ABORTED_REJECTED) + + // Accumulated position value should not change from the input + test.equal(processedMessages.accumulatedPositionValue, 1000) + test.end() + }) + + changeParticipantPositionTest.test('produce reserved messages for valid transfer messages', async (test) => { + const participantLimit = { + participantCurrencyId: 1, + participantLimitTypeId: 1, + value: 10000, + isActive: 1, + createdBy: 'unknown', + participantLimitId: 1, + thresholdAlarmPercentage: 0.5 + } + const settlementModel = { + settlementModelId: 1, + name: 'DEFERREDNET', + isActive: 1, + settlementGranularityId: 2, + settlementInterchangeId: 2, + settlementDelayId: 2, // 1 Immediate, 2 Deferred + currencyId: 'USD', + requireLiquidityCheck: 1, + ledgerAccountTypeId: 1, // 1 Position, 2 Settlement + autoPositionReset: 1, + adjustPosition: 0, + settlementAccountTypeId: 2 + } + const processedMessages = await processPositionPrepareBin( + binItems, + -4, // Accumulated position value + 0, + { + '1cf6981b-25d8-4bd7-b9d9-b1c0fc8cdeaf': Enum.Transfers.TransferInternalState.RECEIVED_PREPARE, + '6c2c09c3-19b6-48ba-becc-cbdffcaadd7e': Enum.Transfers.TransferInternalState.RECEIVED_PREPARE, + '5dff336f-62c0-4619-92c6-9ccd7c8f0369': 'INVALID_STATE' + }, + 0, // Settlement participant position value + settlementModel, + participantLimit + ) + Logger.isInfoEnabled && Logger.info(processedMessages) + test.equal(processedMessages.notifyMessages.length, 3) + + test.equal(processedMessages.accumulatedPositionChanges.length, 2) + + test.equal(processedMessages.notifyMessages[0].message.content.headers.accept, transferMessage1.value.content.headers.accept) + test.equal(processedMessages.notifyMessages[0].message.content.headers['fspiop-destination'], transferMessage1.value.content.headers['fspiop-destination']) + test.equal(processedMessages.notifyMessages[0].message.content.headers['fspiop-source'], transferMessage1.value.content.headers['fspiop-source']) + test.equal(processedMessages.notifyMessages[0].message.content.headers['content-type'], transferMessage1.value.content.headers['content-type']) + test.equal(processedMessages.accumulatedPositionChanges[0].value, -2) + test.equal(processedMessages.accumulatedTransferStates[transferMessage1.value.id], Enum.Transfers.TransferState.RESERVED) + + test.equal(processedMessages.notifyMessages[1].message.content.headers.accept, transferMessage2.value.content.headers.accept) + test.equal(processedMessages.notifyMessages[1].message.content.headers['fspiop-destination'], transferMessage2.value.content.headers['fspiop-destination']) + test.equal(processedMessages.notifyMessages[1].message.content.headers['fspiop-source'], transferMessage2.value.content.headers['fspiop-source']) + test.equal(processedMessages.notifyMessages[1].message.content.headers['content-type'], transferMessage2.value.content.headers['content-type']) + test.equal(processedMessages.accumulatedPositionChanges[1].value, 0) + test.equal(processedMessages.accumulatedTransferStates[transferMessage2.value.id], Enum.Transfers.TransferState.RESERVED) + + test.equal(processedMessages.notifyMessages[2].message.content.uriParams.id, transferMessage3.value.id) + test.equal(processedMessages.notifyMessages[2].message.content.headers.accept, transferMessage3.value.content.headers.accept) + test.equal(processedMessages.notifyMessages[2].message.content.headers['fspiop-destination'], transferMessage3.value.content.headers['fspiop-source']) + test.equal(processedMessages.notifyMessages[2].message.content.headers['fspiop-source'], Enum.Http.Headers.FSPIOP.SWITCH.value) + test.equal(processedMessages.notifyMessages[2].message.content.headers['content-type'], transferMessage3.value.content.headers['content-type']) + test.equal(processedMessages.notifyMessages[2].message.content.payload.errorInformation.errorCode, '2001') + test.equal(processedMessages.notifyMessages[2].message.content.payload.errorInformation.errorDescription, 'Internal server error') + test.equal(processedMessages.accumulatedTransferStates[transferMessage3.value.id], Enum.Transfers.TransferInternalState.ABORTED_REJECTED) + + test.equal(processedMessages.accumulatedTransferStateChanges[0].transferId, transferMessage1.value.id) + test.equal(processedMessages.accumulatedTransferStateChanges[1].transferId, transferMessage2.value.id) + test.equal(processedMessages.accumulatedTransferStateChanges[2].transferId, transferMessage3.value.id) + + test.equal(processedMessages.accumulatedTransferStateChanges[0].transferStateId, Enum.Transfers.TransferState.RESERVED) + test.equal(processedMessages.accumulatedTransferStateChanges[1].transferStateId, Enum.Transfers.TransferState.RESERVED) + test.equal(processedMessages.accumulatedTransferStateChanges[2].transferStateId, Enum.Transfers.TransferInternalState.ABORTED_REJECTED) + + test.equal(processedMessages.accumulatedPositionValue, 0) + test.end() + }) + + changeParticipantPositionTest.test('produce reserved messages for valid transfer messages with default settlement model', async (test) => { + const participantLimit = { + participantCurrencyId: 1, + participantLimitTypeId: 1, + value: 10000, + isActive: 1, + createdBy: 'unknown', + participantLimitId: 1, + thresholdAlarmPercentage: 0.5 + } + const settlementModel = { + settlementModelId: 1, + name: 'DEFERREDNET', + isActive: 1, + settlementGranularityId: 2, + settlementInterchangeId: 2, + settlementDelayId: 2, // 1 Immediate, 2 Deferred + currencyId: null, // Default settlement model is null currencyId + requireLiquidityCheck: 1, + ledgerAccountTypeId: 1, // 1 Position, 2 Settlement + autoPositionReset: 1, + adjustPosition: 0, + settlementAccountTypeId: 2 + } + const processedMessages = await processPositionPrepareBin( + binItems, + -4, + 0, + { + '1cf6981b-25d8-4bd7-b9d9-b1c0fc8cdeaf': Enum.Transfers.TransferInternalState.RECEIVED_PREPARE, + '6c2c09c3-19b6-48ba-becc-cbdffcaadd7e': Enum.Transfers.TransferInternalState.RECEIVED_PREPARE, + '5dff336f-62c0-4619-92c6-9ccd7c8f0369': 'INVALID_STATE' + }, + 0, + settlementModel, + participantLimit + ) + Logger.isInfoEnabled && Logger.info(processedMessages) + test.equal(processedMessages.notifyMessages.length, 3) + test.equal(processedMessages.accumulatedPositionChanges.length, 2) + + test.equal(processedMessages.notifyMessages[0].message.content.headers.accept, transferMessage1.value.content.headers.accept) + test.equal(processedMessages.notifyMessages[0].message.content.headers['fspiop-destination'], transferMessage1.value.content.headers['fspiop-destination']) + test.equal(processedMessages.notifyMessages[0].message.content.headers['fspiop-source'], transferMessage1.value.content.headers['fspiop-source']) + test.equal(processedMessages.notifyMessages[0].message.content.headers['content-type'], transferMessage1.value.content.headers['content-type']) + test.equal(processedMessages.accumulatedPositionChanges[0].value, -2) + test.equal(processedMessages.accumulatedTransferStates[transferMessage1.value.id], Enum.Transfers.TransferState.RESERVED) + + test.equal(processedMessages.notifyMessages[1].message.content.headers.accept, transferMessage2.value.content.headers.accept) + test.equal(processedMessages.notifyMessages[1].message.content.headers['fspiop-destination'], transferMessage2.value.content.headers['fspiop-destination']) + test.equal(processedMessages.notifyMessages[1].message.content.headers['fspiop-source'], transferMessage2.value.content.headers['fspiop-source']) + test.equal(processedMessages.notifyMessages[1].message.content.headers['content-type'], transferMessage2.value.content.headers['content-type']) + test.equal(processedMessages.accumulatedPositionChanges[1].value, 0) + test.equal(processedMessages.accumulatedTransferStates[transferMessage2.value.id], Enum.Transfers.TransferState.RESERVED) + + test.equal(processedMessages.notifyMessages[2].message.content.uriParams.id, transferMessage3.value.id) + test.equal(processedMessages.notifyMessages[2].message.content.headers.accept, transferMessage3.value.content.headers.accept) + test.equal(processedMessages.notifyMessages[2].message.content.headers['fspiop-destination'], transferMessage3.value.content.headers['fspiop-source']) + test.equal(processedMessages.notifyMessages[2].message.content.headers['fspiop-source'], Enum.Http.Headers.FSPIOP.SWITCH.value) + test.equal(processedMessages.notifyMessages[2].message.content.headers['content-type'], transferMessage3.value.content.headers['content-type']) + test.equal(processedMessages.notifyMessages[2].message.content.payload.errorInformation.errorCode, '2001') + test.equal(processedMessages.notifyMessages[2].message.content.payload.errorInformation.errorDescription, 'Internal server error') + test.equal(processedMessages.accumulatedTransferStates[transferMessage3.value.id], Enum.Transfers.TransferInternalState.ABORTED_REJECTED) + + test.equal(processedMessages.accumulatedTransferStateChanges[0].transferId, transferMessage1.value.id) + test.equal(processedMessages.accumulatedTransferStateChanges[1].transferId, transferMessage2.value.id) + test.equal(processedMessages.accumulatedTransferStateChanges[2].transferId, transferMessage3.value.id) + + test.equal(processedMessages.accumulatedTransferStateChanges[0].transferStateId, Enum.Transfers.TransferState.RESERVED) + test.equal(processedMessages.accumulatedTransferStateChanges[1].transferStateId, Enum.Transfers.TransferState.RESERVED) + test.equal(processedMessages.accumulatedTransferStateChanges[2].transferStateId, Enum.Transfers.TransferInternalState.ABORTED_REJECTED) + + test.equal(processedMessages.accumulatedPositionValue, 0) + test.end() + }) + + changeParticipantPositionTest.test('produce proper limit alarms', async (test) => { + const participantLimit = { + participantCurrencyId: 1, + participantLimitTypeId: 1, + value: 4, + isActive: 1, + createdBy: 'unknown', + participantLimitId: 1, + thresholdAlarmPercentage: 0.5 + } + const settlementModel = { + settlementModelId: 1, + name: 'DEFERREDNET', + isActive: 1, + settlementGranularityId: 2, + settlementInterchangeId: 2, + settlementDelayId: 2, // 1 Immediate, 2 Deferred + currencyId: null, // Default settlement model is null currencyId + requireLiquidityCheck: 1, + ledgerAccountTypeId: 1, // 1 Position, 2 Settlement + autoPositionReset: 1, + adjustPosition: 0, + settlementAccountTypeId: 2 + } + const processedMessages = await processPositionPrepareBin( + binItems, + 0, + 0, + { + '1cf6981b-25d8-4bd7-b9d9-b1c0fc8cdeaf': Enum.Transfers.TransferInternalState.RECEIVED_PREPARE, + '6c2c09c3-19b6-48ba-becc-cbdffcaadd7e': Enum.Transfers.TransferInternalState.RECEIVED_PREPARE, + '5dff336f-62c0-4619-92c6-9ccd7c8f0369': 'INVALID_STATE' + }, + -4, + settlementModel, + participantLimit + ) + Logger.isInfoEnabled && Logger.info(processedMessages) + test.equal(processedMessages.notifyMessages.length, 3) + test.equal(processedMessages.limitAlarms.length, 2) + test.equal(processedMessages.accumulatedPositionValue, 4) + test.end() + }) + + changeParticipantPositionTest.end() + }) + + positionIndexTest.end() +}) diff --git a/test/unit/domain/position/sampleBins.js b/test/unit/domain/position/sampleBins.js new file mode 100644 index 000000000..797b6d499 --- /dev/null +++ b/test/unit/domain/position/sampleBins.js @@ -0,0 +1,863 @@ +module.exports = { + 7: { + prepare: [ + { + message: { + value: { + from: 'perffsp1', + to: 'perffsp2', + id: '1cf6981b-25d8-4bd7-b9d9-b1c0fc8cdeaf', + content: { + uriParams: { + id: '1cf6981b-25d8-4bd7-b9d9-b1c0fc8cdeaf' + }, + headers: { + host: 'ml-api-adapter:3000', + 'content-length': 1314, + accept: 'application/vnd.interoperability.transfers+json;version=1.1', + 'content-type': 'application/vnd.interoperability.transfers+json;version=1.1', + date: '2023-08-17T15:25:08.000Z', + 'fspiop-destination': 'perffsp2', + 'fspiop-source': 'perffsp1', + traceparent: '00-e11ece8cc6ca3dc170a8ab693910d934-25d85755f1bc6898-01', + tracestate: 'tx_end2end_start_ts=1692285908510' + }, + payload: 'data:application/vnd.interoperability.transfers+json;version=1.1;base64,eyJ0cmFuc2ZlcklkIjoiMWNmNjk4MWItMjVkOC00YmQ3LWI5ZDktYjFjMGZjOGNkZWFmIiwicGF5ZXJGc3AiOiJwZXJmZnNwMSIsInBheWVlRnNwIjoicGVyZmZzcDIiLCJhbW91bnQiOnsiYW1vdW50IjoiMiIsImN1cnJlbmN5IjoiVVNEIn0sImV4cGlyYXRpb24iOiIyMDMwLTAxLTAxVDAwOjAwOjAwLjAwMFoiLCJpbHBQYWNrZXQiOiJBWUlER1FBQUFBQUFBQ2NRSVdjdVozSmxaVzVpWVc1clpuTndMbTF6YVhOa2JpNHlOemN4TXpnd016a3hNb0lDNjJWNVNqQmpiVVoxWXpKR2FtUkhiSFppYTJ4clNXcHZhVTFFUlhoYVIxRXlUbGRaZEU1VVFYcE5lVEF3VFZkTk1reFVhekZhUjFsMFQxUkZlRmw2V1RSUFZGRXhXV3BvYlVscGQybGpXRloyWkVkV1NscERTVFpKYlZGM1RYcEpNVTFFVlRKTVZFMHhUbGRGZEU1RVVteE5VekZwVDFSbk1FeFhXWGRaVkZFeFRtcEZNRmt5UlhwUFEwbHpTVzVDYUdWWFZteEphbkEzU1c1Q2FHTnVValZUVjFKS1ltMWFka2xxY0RkSmJrSm9ZMjVTTlZOWFVsVmxXRUpzU1dwdmFWUldUa3BWTUZKUFNXbDNhV05IUm5sa1NHeEtXa2RXZFdSSGJHMWhWMVo1U1dwdmFVMXFZek5OVkUwMFRVUk5OVTFVU1dsTVEwcHRZek5DU2xwRFNUWkpiV1I1V2xkV2RWbHRSblZoTWxwNlkwTktPV1pUZDJsalIwWTFXbGhKYVU5dWMybGpSMFo1WkVoc1NscEZiSFZhYlRocFQyNXphV05IUm5sa1NHeEtXa1pTTldOSFZXbFBhVXBPVlRCc1ZGSkZOR2xNUTBwM1dWaEtNR1ZWYkd0YVZ6VXdZVmRhY0ZwWVNXbFBhVWt3VGtSRmVVMTZVVEZPYW1NMFQxTkpjMGx0V25walJXeHJTV3B2YVdOSGJIVmhNa3BvWW0xMGJXTXpRV2xtVTNkcFkwZFdlV015T1hWWlYzaEtZbTFhZGtscWNEZEpiVTUyWWxoQ2MxcFlhRTlaVnpGc1NXcHdOMGx0V25CamJrNHdWRzFHZEZwVFNUWkphMXB3WTI1T01HSnRSblJhVXpGVldsaE9NRWxwZDJsaVIwWjZaRVUxYUdKWFZXbFBhVXBOV1ZoT01HSnRSblJhVXpGVldsaE9NRWx1TUhOSmJWSm9aRWRXVUZwclNuQmpibEp2U1dwdmFVMVVhelJPUXpCM1RWTXdkMDFUU2psbVUzZHBXVmN4ZG1SWE5UQkphbkEzU1cxT01XTnVTbXhpYlU0MVNXcHZhVlpXVGtWSmFYZHBXVmN4ZG1SWE5UQkphbTlwVFZSQmQwbHVNSE5KYmxKNVdWYzFlbGxYVGpCaFZ6bDFWa2hzZDFwVFNUWmxlVXA2V1RKV2RWbFlTbkJpZVVrMlNXeFNVMUZWTlZSU2ExWlRTV2wzYVdGWE5YQmtSMnhvWkVjNWVVbHFiMmxWUlVaYVVsWkphVXhEU25CaWJXd3dZVmRHTUdJelNsVmxXRUpzU1dwdmFWRXdPVTlWTVZaT1VsWkphV1pZTUFBIiwiY29uZGl0aW9uIjoiNW0wZ3FfNWRMUWxUU1NSS1FtTHBqME1aMU10V0xXZ1N1MW9MR1ZUSnlZcyJ9' + }, + type: 'application/json', + metadata: { + correlationId: '1cf6981b-25d8-4bd7-b9d9-b1c0fc8cdeaf', + event: { + type: 'position', + action: 'prepare', + createdAt: '2023-08-17T15:25:08.511Z', + state: { + status: 'success', + code: 0, + description: 'action successful' + }, + id: '790d84e4-912a-47dc-b1dd-2f2ac2989a9e' + }, + trace: { + service: 'cl_transfer_prepare', + traceId: 'e11ece8cc6ca3dc170a8ab693910d934', + spanId: '1a2c4baf99bdb2c6', + sampled: 1, + flags: '01', + parentSpanId: '3c5863bb3c2b4ecc', + startTimestamp: '2023-08-17T15:25:08.860Z', + tags: { + tracestate: 'acmevendor=eyJzcGFuSWQiOiIxYTJjNGJhZjk5YmRiMmM2IiwidGltZUFwaVByZXBhcmUiOiIxNjkyMjg1OTA4NTEwIn0=,tx_end2end_start_ts=1692285908510', + transactionType: 'transfer', + transactionAction: 'prepare', + transactionId: '1cf6981b-25d8-4bd7-b9d9-b1c0fc8cdeaf', + source: 'perffsp1', + destination: 'perffsp2', + payerFsp: 'perffsp1', + payeeFsp: 'perffsp2' + }, + tracestates: { + acmevendor: { + spanId: '1a2c4baf99bdb2c6', + timeApiPrepare: '1692285908510' + }, + tx_end2end_start_ts: '1692285908510' + } + }, + 'protocol.createdAt': 1692285908866 + } + }, + size: 3489, + key: 51, + topic: 'topic-transfer-position', + offset: 4070, + partition: 0, + timestamp: 1694175690401 + }, + span: {}, + decodedPayload: { + amount: { + amount: '2', + currency: 'USD' + }, + condition: '5m0gq_5dLQlTSSRKQmLpj0MZ1MtWLWgSu1oLGVTJyYs', + expiration: '2030-01-01T00:00:00.000Z', + ilpPacket: 'AYIDGQAAAAAAACcQIWcuZ3JlZW5iYW5rZnNwLm1zaXNkbi4yNzcxMzgwMzkxMoIC62V5SjBjbUZ1YzJGamRHbHZia2xrSWpvaU1ERXhaR1EyTldZdE5UQXpNeTAwTVdNMkxUazFaR1l0T1RFeFl6WTRPVFExWWpobUlpd2ljWFZ2ZEdWSlpDSTZJbVF3TXpJMU1EVTJMVE0xTldFdE5EUmxNUzFpT1RnMExXWXdZVFExTmpFMFkyRXpPQ0lzSW5CaGVXVmxJanA3SW5CaGNuUjVTV1JKYm1adklqcDdJbkJoY25SNVNXUlVlWEJsSWpvaVRWTkpVMFJPSWl3aWNHRnlkSGxKWkdWdWRHbG1hV1Z5SWpvaU1qYzNNVE00TURNNU1USWlMQ0ptYzNCSlpDSTZJbWR5WldWdVltRnVhMlp6Y0NKOWZTd2ljR0Y1WlhJaU9uc2ljR0Z5ZEhsSlpFbHVabThpT25zaWNHRnlkSGxKWkZSNWNHVWlPaUpOVTBsVFJFNGlMQ0p3WVhKMGVVbGtaVzUwYVdacFpYSWlPaUkwTkRFeU16UTFOamM0T1NJc0ltWnpjRWxrSWpvaWNHbHVhMkpoYm10bWMzQWlmU3dpY0dWeWMyOXVZV3hKYm1adklqcDdJbU52YlhCc1pYaE9ZVzFsSWpwN0ltWnBjbk4wVG1GdFpTSTZJa1pwY25OMGJtRnRaUzFVWlhOMElpd2liR0Z6ZEU1aGJXVWlPaUpNWVhOMGJtRnRaUzFVWlhOMEluMHNJbVJoZEdWUFprSnBjblJvSWpvaU1UazROQzB3TVMwd01TSjlmU3dpWVcxdmRXNTBJanA3SW1OMWNuSmxibU41SWpvaVZWTkVJaXdpWVcxdmRXNTBJam9pTVRBd0luMHNJblJ5WVc1ellXTjBhVzl1Vkhsd1pTSTZleUp6WTJWdVlYSnBieUk2SWxSU1FVNVRSa1ZTSWl3aWFXNXBkR2xoZEc5eUlqb2lVRUZaUlZJaUxDSnBibWwwYVdGMGIzSlVlWEJsSWpvaVEwOU9VMVZOUlZJaWZYMAA', + payeeFsp: 'perffsp2', + payerFsp: 'perffsp1', + transferId: '1cf6981b-25d8-4bd7-b9d9-b1c0fc8cdeaf' + } + }, + { + message: { + value: { + from: 'perffsp1', + to: 'perffsp2', + id: '6c2c09c3-19b6-48ba-becc-cbdffcaadd7e', + content: { + uriParams: { + id: '6c2c09c3-19b6-48ba-becc-cbdffcaadd7e' + }, + headers: { + host: 'ml-api-adapter:3000', + 'user-agent': 'k6/0.45.0 (https://k6.io/)', + 'content-length': 1314, + accept: 'application/vnd.interoperability.transfers+json;version=1.1', + 'content-type': 'application/vnd.interoperability.transfers+json;version=1.1', + date: '2023-08-17T15:25:08.000Z', + 'fspiop-destination': 'perffsp2', + 'fspiop-source': 'perffsp1', + traceparent: '00-3353dbcbb8f5a36cf4236873bf71fa00-cae41b1f121ab294-01', + tracestate: 'tx_end2end_start_ts=1692285908887' + }, + payload: 'data:application/vnd.interoperability.transfers+json;version=1.1;base64,eyJ0cmFuc2ZlcklkIjoiNmMyYzA5YzMtMTliNi00OGJhLWJlY2MtY2JkZmZjYWFkZDdlIiwicGF5ZXJGc3AiOiJwZXJmZnNwMSIsInBheWVlRnNwIjoicGVyZmZzcDIiLCJhbW91bnQiOnsiYW1vdW50IjoiMiIsImN1cnJlbmN5IjoiVVNEIn0sImV4cGlyYXRpb24iOiIyMDMwLTAxLTAxVDAwOjAwOjAwLjAwMFoiLCJpbHBQYWNrZXQiOiJBWUlER1FBQUFBQUFBQ2NRSVdjdVozSmxaVzVpWVc1clpuTndMbTF6YVhOa2JpNHlOemN4TXpnd016a3hNb0lDNjJWNVNqQmpiVVoxWXpKR2FtUkhiSFppYTJ4clNXcHZhVTFFUlhoYVIxRXlUbGRaZEU1VVFYcE5lVEF3VFZkTk1reFVhekZhUjFsMFQxUkZlRmw2V1RSUFZGRXhXV3BvYlVscGQybGpXRloyWkVkV1NscERTVFpKYlZGM1RYcEpNVTFFVlRKTVZFMHhUbGRGZEU1RVVteE5VekZwVDFSbk1FeFhXWGRaVkZFeFRtcEZNRmt5UlhwUFEwbHpTVzVDYUdWWFZteEphbkEzU1c1Q2FHTnVValZUVjFKS1ltMWFka2xxY0RkSmJrSm9ZMjVTTlZOWFVsVmxXRUpzU1dwdmFWUldUa3BWTUZKUFNXbDNhV05IUm5sa1NHeEtXa2RXZFdSSGJHMWhWMVo1U1dwdmFVMXFZek5OVkUwMFRVUk5OVTFVU1dsTVEwcHRZek5DU2xwRFNUWkpiV1I1V2xkV2RWbHRSblZoTWxwNlkwTktPV1pUZDJsalIwWTFXbGhKYVU5dWMybGpSMFo1WkVoc1NscEZiSFZhYlRocFQyNXphV05IUm5sa1NHeEtXa1pTTldOSFZXbFBhVXBPVlRCc1ZGSkZOR2xNUTBwM1dWaEtNR1ZWYkd0YVZ6VXdZVmRhY0ZwWVNXbFBhVWt3VGtSRmVVMTZVVEZPYW1NMFQxTkpjMGx0V25walJXeHJTV3B2YVdOSGJIVmhNa3BvWW0xMGJXTXpRV2xtVTNkcFkwZFdlV015T1hWWlYzaEtZbTFhZGtscWNEZEpiVTUyWWxoQ2MxcFlhRTlaVnpGc1NXcHdOMGx0V25CamJrNHdWRzFHZEZwVFNUWkphMXB3WTI1T01HSnRSblJhVXpGVldsaE9NRWxwZDJsaVIwWjZaRVUxYUdKWFZXbFBhVXBOV1ZoT01HSnRSblJhVXpGVldsaE9NRWx1TUhOSmJWSm9aRWRXVUZwclNuQmpibEp2U1dwdmFVMVVhelJPUXpCM1RWTXdkMDFUU2psbVUzZHBXVmN4ZG1SWE5UQkphbkEzU1cxT01XTnVTbXhpYlU0MVNXcHZhVlpXVGtWSmFYZHBXVmN4ZG1SWE5UQkphbTlwVFZSQmQwbHVNSE5KYmxKNVdWYzFlbGxYVGpCaFZ6bDFWa2hzZDFwVFNUWmxlVXA2V1RKV2RWbFlTbkJpZVVrMlNXeFNVMUZWTlZSU2ExWlRTV2wzYVdGWE5YQmtSMnhvWkVjNWVVbHFiMmxWUlVaYVVsWkphVXhEU25CaWJXd3dZVmRHTUdJelNsVmxXRUpzU1dwdmFWRXdPVTlWTVZaT1VsWkphV1pZTUFBIiwiY29uZGl0aW9uIjoiNW0wZ3FfNWRMUWxUU1NSS1FtTHBqME1aMU10V0xXZ1N1MW9MR1ZUSnlZcyJ9' + }, + type: 'application/json', + metadata: { + correlationId: '6c2c09c3-19b6-48ba-becc-cbdffcaadd7e', + event: { + type: 'position', + action: 'prepare', + createdAt: '2023-08-17T15:25:08.888Z', + state: { + status: 'success', + code: 0, + description: 'action successful' + }, + id: 'ed2e00ed-2b65-4f6a-886d-df9591c9df1f' + }, + trace: { + service: 'cl_transfer_prepare', + traceId: '3353dbcbb8f5a36cf4236873bf71fa00', + spanId: '95f51fddcfd3895b', + sampled: 1, + flags: '01', + parentSpanId: '1e916a41afd771c7', + startTimestamp: '2023-08-17T15:25:08.951Z', + tags: { + tracestate: 'acmevendor=eyJzcGFuSWQiOiI5NWY1MWZkZGNmZDM4OTViIiwidGltZUFwaVByZXBhcmUiOiIxNjkyMjg1OTA4ODg4In0=,tx_end2end_start_ts=1692285908887', + transactionType: 'transfer', + transactionAction: 'prepare', + transactionId: '6c2c09c3-19b6-48ba-becc-cbdffcaadd7e', + source: 'perffsp1', + destination: 'perffsp2', + payerFsp: 'perffsp1', + payeeFsp: 'perffsp2' + }, + tracestates: { + acmevendor: { + spanId: '95f51fddcfd3895b', + timeApiPrepare: '1692285908888' + }, + tx_end2end_start_ts: '1692285908887' + } + }, + 'protocol.createdAt': 1692285908957 + } + }, + size: 3489, + key: 51, + topic: 'topic-transfer-position', + offset: 4073, + partition: 0, + timestamp: 1694175690401 + }, + span: {}, + decodedPayload: { + amount: { + amount: '2', + currency: 'USD' + }, + condition: '5m0gq_5dLQlTSSRKQmLpj0MZ1MtWLWgSu1oLGVTJyYs', + expiration: '2030-01-01T00:00:00.000Z', + ilpPacket: 'AYIDGQAAAAAAACcQIWcuZ3JlZW5iYW5rZnNwLm1zaXNkbi4yNzcxMzgwMzkxMoIC62V5SjBjbUZ1YzJGamRHbHZia2xrSWpvaU1ERXhaR1EyTldZdE5UQXpNeTAwTVdNMkxUazFaR1l0T1RFeFl6WTRPVFExWWpobUlpd2ljWFZ2ZEdWSlpDSTZJbVF3TXpJMU1EVTJMVE0xTldFdE5EUmxNUzFpT1RnMExXWXdZVFExTmpFMFkyRXpPQ0lzSW5CaGVXVmxJanA3SW5CaGNuUjVTV1JKYm1adklqcDdJbkJoY25SNVNXUlVlWEJsSWpvaVRWTkpVMFJPSWl3aWNHRnlkSGxKWkdWdWRHbG1hV1Z5SWpvaU1qYzNNVE00TURNNU1USWlMQ0ptYzNCSlpDSTZJbWR5WldWdVltRnVhMlp6Y0NKOWZTd2ljR0Y1WlhJaU9uc2ljR0Z5ZEhsSlpFbHVabThpT25zaWNHRnlkSGxKWkZSNWNHVWlPaUpOVTBsVFJFNGlMQ0p3WVhKMGVVbGtaVzUwYVdacFpYSWlPaUkwTkRFeU16UTFOamM0T1NJc0ltWnpjRWxrSWpvaWNHbHVhMkpoYm10bWMzQWlmU3dpY0dWeWMyOXVZV3hKYm1adklqcDdJbU52YlhCc1pYaE9ZVzFsSWpwN0ltWnBjbk4wVG1GdFpTSTZJa1pwY25OMGJtRnRaUzFVWlhOMElpd2liR0Z6ZEU1aGJXVWlPaUpNWVhOMGJtRnRaUzFVWlhOMEluMHNJbVJoZEdWUFprSnBjblJvSWpvaU1UazROQzB3TVMwd01TSjlmU3dpWVcxdmRXNTBJanA3SW1OMWNuSmxibU41SWpvaVZWTkVJaXdpWVcxdmRXNTBJam9pTVRBd0luMHNJblJ5WVc1ellXTjBhVzl1Vkhsd1pTSTZleUp6WTJWdVlYSnBieUk2SWxSU1FVNVRSa1ZTSWl3aWFXNXBkR2xoZEc5eUlqb2lVRUZaUlZJaUxDSnBibWwwYVdGMGIzSlVlWEJsSWpvaVEwOU9VMVZOUlZJaWZYMAA', + payeeFsp: 'perffsp2', + payerFsp: 'perffsp1', + transferId: '6c2c09c3-19b6-48ba-becc-cbdffcaadd7e' + } + }, + { + message: { + value: { + from: 'perffsp1', + to: 'perffsp2', + id: '5dff336f-62c0-4619-92c6-9ccd7c8f0369', + content: { + uriParams: { + id: '5dff336f-62c0-4619-92c6-9ccd7c8f0369' + }, + headers: { + host: 'ml-api-adapter:3000', + 'user-agent': 'k6/0.45.0 (https://k6.io/)', + 'content-length': 1314, + accept: 'application/vnd.interoperability.transfers+json;version=1.1', + 'content-type': 'application/vnd.interoperability.transfers+json;version=1.1', + date: '2023-08-17T15:25:08.000Z', + 'fspiop-destination': 'perffsp2', + 'fspiop-source': 'perffsp1', + traceparent: '00-7295d8d6f6aed39e143fcbfcd5bf89b2-ecdb5940d1b8ec45-01', + tracestate: 'tx_end2end_start_ts=1692285908467' + }, + payload: 'data:application/vnd.interoperability.transfers+json;version=1.1;base64,eyJ0cmFuc2ZlcklkIjoiNWRmZjMzNmYtNjJjMC00NjE5LTkyYzYtOWNjZDdjOGYwMzY5IiwicGF5ZXJGc3AiOiJwZXJmZnNwMSIsInBheWVlRnNwIjoicGVyZmZzcDIiLCJhbW91bnQiOnsiYW1vdW50IjoiMiIsImN1cnJlbmN5IjoiVVNEIn0sImV4cGlyYXRpb24iOiIyMDMwLTAxLTAxVDAwOjAwOjAwLjAwMFoiLCJpbHBQYWNrZXQiOiJBWUlER1FBQUFBQUFBQ2NRSVdjdVozSmxaVzVpWVc1clpuTndMbTF6YVhOa2JpNHlOemN4TXpnd016a3hNb0lDNjJWNVNqQmpiVVoxWXpKR2FtUkhiSFppYTJ4clNXcHZhVTFFUlhoYVIxRXlUbGRaZEU1VVFYcE5lVEF3VFZkTk1reFVhekZhUjFsMFQxUkZlRmw2V1RSUFZGRXhXV3BvYlVscGQybGpXRloyWkVkV1NscERTVFpKYlZGM1RYcEpNVTFFVlRKTVZFMHhUbGRGZEU1RVVteE5VekZwVDFSbk1FeFhXWGRaVkZFeFRtcEZNRmt5UlhwUFEwbHpTVzVDYUdWWFZteEphbkEzU1c1Q2FHTnVValZUVjFKS1ltMWFka2xxY0RkSmJrSm9ZMjVTTlZOWFVsVmxXRUpzU1dwdmFWUldUa3BWTUZKUFNXbDNhV05IUm5sa1NHeEtXa2RXZFdSSGJHMWhWMVo1U1dwdmFVMXFZek5OVkUwMFRVUk5OVTFVU1dsTVEwcHRZek5DU2xwRFNUWkpiV1I1V2xkV2RWbHRSblZoTWxwNlkwTktPV1pUZDJsalIwWTFXbGhKYVU5dWMybGpSMFo1WkVoc1NscEZiSFZhYlRocFQyNXphV05IUm5sa1NHeEtXa1pTTldOSFZXbFBhVXBPVlRCc1ZGSkZOR2xNUTBwM1dWaEtNR1ZWYkd0YVZ6VXdZVmRhY0ZwWVNXbFBhVWt3VGtSRmVVMTZVVEZPYW1NMFQxTkpjMGx0V25walJXeHJTV3B2YVdOSGJIVmhNa3BvWW0xMGJXTXpRV2xtVTNkcFkwZFdlV015T1hWWlYzaEtZbTFhZGtscWNEZEpiVTUyWWxoQ2MxcFlhRTlaVnpGc1NXcHdOMGx0V25CamJrNHdWRzFHZEZwVFNUWkphMXB3WTI1T01HSnRSblJhVXpGVldsaE9NRWxwZDJsaVIwWjZaRVUxYUdKWFZXbFBhVXBOV1ZoT01HSnRSblJhVXpGVldsaE9NRWx1TUhOSmJWSm9aRWRXVUZwclNuQmpibEp2U1dwdmFVMVVhelJPUXpCM1RWTXdkMDFUU2psbVUzZHBXVmN4ZG1SWE5UQkphbkEzU1cxT01XTnVTbXhpYlU0MVNXcHZhVlpXVGtWSmFYZHBXVmN4ZG1SWE5UQkphbTlwVFZSQmQwbHVNSE5KYmxKNVdWYzFlbGxYVGpCaFZ6bDFWa2hzZDFwVFNUWmxlVXA2V1RKV2RWbFlTbkJpZVVrMlNXeFNVMUZWTlZSU2ExWlRTV2wzYVdGWE5YQmtSMnhvWkVjNWVVbHFiMmxWUlVaYVVsWkphVXhEU25CaWJXd3dZVmRHTUdJelNsVmxXRUpzU1dwdmFWRXdPVTlWTVZaT1VsWkphV1pZTUFBIiwiY29uZGl0aW9uIjoiNW0wZ3FfNWRMUWxUU1NSS1FtTHBqME1aMU10V0xXZ1N1MW9MR1ZUSnlZcyJ9' + }, + type: 'application/json', + metadata: { + correlationId: '5dff336f-62c0-4619-92c6-9ccd7c8f0369', + event: { + type: 'position', + action: 'prepare', + createdAt: '2023-08-17T15:25:08.468Z', + state: { + status: 'success', + code: 0, + description: 'action successful' + }, + id: '7c9eeb85-4fab-4dbd-8a16-0f870f3efbe3' + }, + trace: { + service: 'cl_transfer_prepare', + traceId: '7295d8d6f6aed39e143fcbfcd5bf89b2', + spanId: '31ac97084059ed78', + sampled: 1, + flags: '01', + parentSpanId: '87647df7326a1b6d', + startTimestamp: '2023-08-17T15:25:08.732Z', + tags: { + tracestate: 'acmevendor=eyJzcGFuSWQiOiIzMWFjOTcwODQwNTllZDc4IiwidGltZUFwaVByZXBhcmUiOiIxNjkyMjg1OTA4NDY4In0=,tx_end2end_start_ts=1692285908467', + transactionType: 'transfer', + transactionAction: 'prepare', + transactionId: '5dff336f-62c0-4619-92c6-9ccd7c8f0369', + source: 'perffsp1', + destination: 'perffsp2', + payerFsp: 'perffsp1', + payeeFsp: 'perffsp2' + }, + tracestates: { + acmevendor: { + spanId: '31ac97084059ed78', + timeApiPrepare: '1692285908468' + }, + tx_end2end_start_ts: '1692285908467' + } + }, + 'protocol.createdAt': 1692285908736 + } + }, + size: 3489, + key: 51, + topic: 'topic-transfer-position', + offset: 4076, + partition: 0, + timestamp: 1694175690401 + }, + span: {}, + decodedPayload: { + amount: { + amount: '2', + currency: 'USD' + }, + condition: '5m0gq_5dLQlTSSRKQmLpj0MZ1MtWLWgSu1oLGVTJyYs', + expiration: '2030-01-01T00:00:00.000Z', + ilpPacket: 'AYIDGQAAAAAAACcQIWcuZ3JlZW5iYW5rZnNwLm1zaXNkbi4yNzcxMzgwMzkxMoIC62V5SjBjbUZ1YzJGamRHbHZia2xrSWpvaU1ERXhaR1EyTldZdE5UQXpNeTAwTVdNMkxUazFaR1l0T1RFeFl6WTRPVFExWWpobUlpd2ljWFZ2ZEdWSlpDSTZJbVF3TXpJMU1EVTJMVE0xTldFdE5EUmxNUzFpT1RnMExXWXdZVFExTmpFMFkyRXpPQ0lzSW5CaGVXVmxJanA3SW5CaGNuUjVTV1JKYm1adklqcDdJbkJoY25SNVNXUlVlWEJsSWpvaVRWTkpVMFJPSWl3aWNHRnlkSGxKWkdWdWRHbG1hV1Z5SWpvaU1qYzNNVE00TURNNU1USWlMQ0ptYzNCSlpDSTZJbWR5WldWdVltRnVhMlp6Y0NKOWZTd2ljR0Y1WlhJaU9uc2ljR0Z5ZEhsSlpFbHVabThpT25zaWNHRnlkSGxKWkZSNWNHVWlPaUpOVTBsVFJFNGlMQ0p3WVhKMGVVbGtaVzUwYVdacFpYSWlPaUkwTkRFeU16UTFOamM0T1NJc0ltWnpjRWxrSWpvaWNHbHVhMkpoYm10bWMzQWlmU3dpY0dWeWMyOXVZV3hKYm1adklqcDdJbU52YlhCc1pYaE9ZVzFsSWpwN0ltWnBjbk4wVG1GdFpTSTZJa1pwY25OMGJtRnRaUzFVWlhOMElpd2liR0Z6ZEU1aGJXVWlPaUpNWVhOMGJtRnRaUzFVWlhOMEluMHNJbVJoZEdWUFprSnBjblJvSWpvaU1UazROQzB3TVMwd01TSjlmU3dpWVcxdmRXNTBJanA3SW1OMWNuSmxibU41SWpvaVZWTkVJaXdpWVcxdmRXNTBJam9pTVRBd0luMHNJblJ5WVc1ellXTjBhVzl1Vkhsd1pTSTZleUp6WTJWdVlYSnBieUk2SWxSU1FVNVRSa1ZTSWl3aWFXNXBkR2xoZEc5eUlqb2lVRUZaUlZJaUxDSnBibWwwYVdGMGIzSlVlWEJsSWpvaVEwOU9VMVZOUlZJaWZYMAA', + payeeFsp: 'perffsp2', + payerFsp: 'perffsp1', + transferId: '5dff336f-62c0-4619-92c6-9ccd7c8f0369' + } + }, + { + message: { + value: { + from: 'perffsp1', + to: 'perffsp2', + id: 'ccf68c01-fe1e-494e-8736-42a9107d3ba0', + content: { + uriParams: { + id: 'ccf68c01-fe1e-494e-8736-42a9107d3ba0' + }, + headers: { + host: 'ml-api-adapter:3000', + 'user-agent': 'k6/0.45.0 (https://k6.io/)', + 'content-length': 1314, + accept: 'application/vnd.interoperability.transfers+json;version=1.1', + 'content-type': 'application/vnd.interoperability.transfers+json;version=1.1', + date: '2023-08-17T15:25:08.000Z', + 'fspiop-destination': 'perffsp2', + 'fspiop-source': 'perffsp1', + traceparent: '00-ccab5527f6642103dbc6f6b06619b1c6-332e529af2f2c89e-01', + tracestate: 'tx_end2end_start_ts=1692285908096' + }, + payload: 'data:application/vnd.interoperability.transfers+json;version=1.1;base64,eyJ0cmFuc2ZlcklkIjoiY2NmNjhjMDEtZmUxZS00OTRlLTg3MzYtNDJhOTEwN2QzYmEwIiwicGF5ZXJGc3AiOiJwZXJmZnNwMSIsInBheWVlRnNwIjoicGVyZmZzcDIiLCJhbW91bnQiOnsiYW1vdW50IjoiMiIsImN1cnJlbmN5IjoiVVNEIn0sImV4cGlyYXRpb24iOiIyMDMwLTAxLTAxVDAwOjAwOjAwLjAwMFoiLCJpbHBQYWNrZXQiOiJBWUlER1FBQUFBQUFBQ2NRSVdjdVozSmxaVzVpWVc1clpuTndMbTF6YVhOa2JpNHlOemN4TXpnd016a3hNb0lDNjJWNVNqQmpiVVoxWXpKR2FtUkhiSFppYTJ4clNXcHZhVTFFUlhoYVIxRXlUbGRaZEU1VVFYcE5lVEF3VFZkTk1reFVhekZhUjFsMFQxUkZlRmw2V1RSUFZGRXhXV3BvYlVscGQybGpXRloyWkVkV1NscERTVFpKYlZGM1RYcEpNVTFFVlRKTVZFMHhUbGRGZEU1RVVteE5VekZwVDFSbk1FeFhXWGRaVkZFeFRtcEZNRmt5UlhwUFEwbHpTVzVDYUdWWFZteEphbkEzU1c1Q2FHTnVValZUVjFKS1ltMWFka2xxY0RkSmJrSm9ZMjVTTlZOWFVsVmxXRUpzU1dwdmFWUldUa3BWTUZKUFNXbDNhV05IUm5sa1NHeEtXa2RXZFdSSGJHMWhWMVo1U1dwdmFVMXFZek5OVkUwMFRVUk5OVTFVU1dsTVEwcHRZek5DU2xwRFNUWkpiV1I1V2xkV2RWbHRSblZoTWxwNlkwTktPV1pUZDJsalIwWTFXbGhKYVU5dWMybGpSMFo1WkVoc1NscEZiSFZhYlRocFQyNXphV05IUm5sa1NHeEtXa1pTTldOSFZXbFBhVXBPVlRCc1ZGSkZOR2xNUTBwM1dWaEtNR1ZWYkd0YVZ6VXdZVmRhY0ZwWVNXbFBhVWt3VGtSRmVVMTZVVEZPYW1NMFQxTkpjMGx0V25walJXeHJTV3B2YVdOSGJIVmhNa3BvWW0xMGJXTXpRV2xtVTNkcFkwZFdlV015T1hWWlYzaEtZbTFhZGtscWNEZEpiVTUyWWxoQ2MxcFlhRTlaVnpGc1NXcHdOMGx0V25CamJrNHdWRzFHZEZwVFNUWkphMXB3WTI1T01HSnRSblJhVXpGVldsaE9NRWxwZDJsaVIwWjZaRVUxYUdKWFZXbFBhVXBOV1ZoT01HSnRSblJhVXpGVldsaE9NRWx1TUhOSmJWSm9aRWRXVUZwclNuQmpibEp2U1dwdmFVMVVhelJPUXpCM1RWTXdkMDFUU2psbVUzZHBXVmN4ZG1SWE5UQkphbkEzU1cxT01XTnVTbXhpYlU0MVNXcHZhVlpXVGtWSmFYZHBXVmN4ZG1SWE5UQkphbTlwVFZSQmQwbHVNSE5KYmxKNVdWYzFlbGxYVGpCaFZ6bDFWa2hzZDFwVFNUWmxlVXA2V1RKV2RWbFlTbkJpZVVrMlNXeFNVMUZWTlZSU2ExWlRTV2wzYVdGWE5YQmtSMnhvWkVjNWVVbHFiMmxWUlVaYVVsWkphVXhEU25CaWJXd3dZVmRHTUdJelNsVmxXRUpzU1dwdmFWRXdPVTlWTVZaT1VsWkphV1pZTUFBIiwiY29uZGl0aW9uIjoiNW0wZ3FfNWRMUWxUU1NSS1FtTHBqME1aMU10V0xXZ1N1MW9MR1ZUSnlZcyJ9' + }, + type: 'application/json', + metadata: { + correlationId: 'ccf68c01-fe1e-494e-8736-42a9107d3ba0', + event: { + type: 'position', + action: 'prepare', + createdAt: '2023-08-17T15:25:08.097Z', + state: { + status: 'success', + code: 0, + description: 'action successful' + }, + id: 'dda11f80-5fbd-4bc2-845d-e3943735600a' + }, + trace: { + service: 'cl_transfer_prepare', + traceId: 'ccab5527f6642103dbc6f6b06619b1c6', + spanId: 'c27da09d2c77c9e9', + sampled: 1, + flags: '01', + parentSpanId: 'd8fc4341a76a2671', + startTimestamp: '2023-08-17T15:25:08.126Z', + tags: { + tracestate: 'acmevendor=eyJzcGFuSWQiOiJjMjdkYTA5ZDJjNzdjOWU5IiwidGltZUFwaVByZXBhcmUiOiIxNjkyMjg1OTA4MDk3In0=,tx_end2end_start_ts=1692285908096', + transactionType: 'transfer', + transactionAction: 'prepare', + transactionId: 'ccf68c01-fe1e-494e-8736-42a9107d3ba0', + source: 'perffsp1', + destination: 'perffsp2', + payerFsp: 'perffsp1', + payeeFsp: 'perffsp2' + }, + tracestates: { + acmevendor: { + spanId: 'c27da09d2c77c9e9', + timeApiPrepare: '1692285908097' + }, + tx_end2end_start_ts: '1692285908096' + } + }, + 'protocol.createdAt': 1692285908131 + } + }, + size: 3489, + key: 51, + topic: 'topic-transfer-position', + offset: 4079, + partition: 0, + timestamp: 1694175690401 + }, + span: {}, + decodedPayload: { + amount: { + amount: '2', + currency: 'USD' + }, + condition: '5m0gq_5dLQlTSSRKQmLpj0MZ1MtWLWgSu1oLGVTJyYs', + expiration: '2030-01-01T00:00:00.000Z', + ilpPacket: 'AYIDGQAAAAAAACcQIWcuZ3JlZW5iYW5rZnNwLm1zaXNkbi4yNzcxMzgwMzkxMoIC62V5SjBjbUZ1YzJGamRHbHZia2xrSWpvaU1ERXhaR1EyTldZdE5UQXpNeTAwTVdNMkxUazFaR1l0T1RFeFl6WTRPVFExWWpobUlpd2ljWFZ2ZEdWSlpDSTZJbVF3TXpJMU1EVTJMVE0xTldFdE5EUmxNUzFpT1RnMExXWXdZVFExTmpFMFkyRXpPQ0lzSW5CaGVXVmxJanA3SW5CaGNuUjVTV1JKYm1adklqcDdJbkJoY25SNVNXUlVlWEJsSWpvaVRWTkpVMFJPSWl3aWNHRnlkSGxKWkdWdWRHbG1hV1Z5SWpvaU1qYzNNVE00TURNNU1USWlMQ0ptYzNCSlpDSTZJbWR5WldWdVltRnVhMlp6Y0NKOWZTd2ljR0Y1WlhJaU9uc2ljR0Z5ZEhsSlpFbHVabThpT25zaWNHRnlkSGxKWkZSNWNHVWlPaUpOVTBsVFJFNGlMQ0p3WVhKMGVVbGtaVzUwYVdacFpYSWlPaUkwTkRFeU16UTFOamM0T1NJc0ltWnpjRWxrSWpvaWNHbHVhMkpoYm10bWMzQWlmU3dpY0dWeWMyOXVZV3hKYm1adklqcDdJbU52YlhCc1pYaE9ZVzFsSWpwN0ltWnBjbk4wVG1GdFpTSTZJa1pwY25OMGJtRnRaUzFVWlhOMElpd2liR0Z6ZEU1aGJXVWlPaUpNWVhOMGJtRnRaUzFVWlhOMEluMHNJbVJoZEdWUFprSnBjblJvSWpvaU1UazROQzB3TVMwd01TSjlmU3dpWVcxdmRXNTBJanA3SW1OMWNuSmxibU41SWpvaVZWTkVJaXdpWVcxdmRXNTBJam9pTVRBd0luMHNJblJ5WVc1ellXTjBhVzl1Vkhsd1pTSTZleUp6WTJWdVlYSnBieUk2SWxSU1FVNVRSa1ZTSWl3aWFXNXBkR2xoZEc5eUlqb2lVRUZaUlZJaUxDSnBibWwwYVdGMGIzSlVlWEJsSWpvaVEwOU9VMVZOUlZJaWZYMAA', + payeeFsp: 'perffsp2', + payerFsp: 'perffsp1', + transferId: 'ccf68c01-fe1e-494e-8736-42a9107d3ba0' + } + } + ], + commit: [ + { + message: { + value: { + from: 'perffsp2', + to: 'perffsp1', + id: '4830fa00-0c2a-4de1-9640-5ad4e68f5f62', + content: { + uriParams: { + id: '4830fa00-0c2a-4de1-9640-5ad4e68f5f62' + }, + headers: { + accept: 'application/vnd.interoperability.transfers+json;version=1.1', + 'content-type': 'application/vnd.interoperability.transfers+json;version=1.1', + date: '2023-08-21T10:22:27.000Z', + 'fspiop-source': 'perffsp2', + 'fspiop-destination': 'perffsp1', + traceparent: '00-1fcd3843697316bd4dea096eb8b0f20d-242262bdec0c9c76-01', + tracestate: 'acmevendor=eyJzcGFuSWQiOiIyNDIyNjJiZGVjMGM5Yzc2IiwidGltZUFwaVByZXBhcmUiOiIxNjkyMjg1OTEyMDI3In0=,tx_end2end_start_ts=1692285912027,tx_callback_start_ts=1692613347073', + 'user-agent': 'axios/1.4.0', + 'content-length': '136', + 'accept-encoding': 'gzip, compress, deflate, br', + host: 'ml-api-adapter:3000', + connection: 'keep-alive' + }, + payload: 'data:application/vnd.interoperability.transfers+json;version=1.1;base64,eyJ0cmFuc2ZlclN0YXRlIjoiQ09NTUlUVEVEIiwiZnVsZmlsbWVudCI6ImxuWWU0cll3THRoV2J6aFZ5WDVjQXVEZkwxVWx3NFdkYVRneUdEUkV5c3ciLCJjb21wbGV0ZWRUaW1lc3RhbXAiOiIyMDIzLTA4LTIxVDEwOjIyOjI3LjA3M1oifQ==' + }, + type: 'application/json', + metadata: { + correlationId: '4830fa00-0c2a-4de1-9640-5ad4e68f5f62', + event: { + type: 'position', + action: 'commit', + createdAt: '2023-08-21T10:22:27.074Z', + state: { + status: 'success', + code: 0, + description: 'action successful' + }, + id: 'c16155a3-1807-470d-9386-ce46603ed875' + }, + trace: { + service: 'cl_transfer_fulfil', + traceId: '1fcd3843697316bd4dea096eb8b0f20d', + spanId: '5690c3dbd5bb1ee5', + sampled: 1, + flags: '01', + parentSpanId: '66055f3f76497fc9', + startTimestamp: '2023-08-21T10:23:45.332Z', + tags: { + tracestate: 'acmevendor=eyJzcGFuSWQiOiI1NjkwYzNkYmQ1YmIxZWU1IiwidGltZUFwaVByZXBhcmUiOiIxNjkyMjg1OTEyMDI3IiwidGltZUFwaUZ1bGZpbCI6IjE2OTI2MTMzNDcwNzQifQ==,tx_end2end_start_ts=1692285912027,tx_callback_start_ts=1692613347073', + transactionType: 'transfer', + transactionAction: 'fulfil', + transactionId: '4830fa00-0c2a-4de1-9640-5ad4e68f5f62', + source: 'perffsp2', + destination: 'perffsp1' + }, + tracestates: { + acmevendor: { + spanId: '5690c3dbd5bb1ee5', + timeApiPrepare: '1692285912027', + timeApiFulfil: '1692613347074' + }, + tx_end2end_start_ts: '1692285912027', + tx_callback_start_ts: '1692613347073' + } + }, + 'protocol.createdAt': 1692613425335 + } + }, + size: 2215, + key: 51, + topic: 'topic-transfer-position', + offset: 4075, + partition: 0, + timestamp: 1694175690401 + }, + span: {} + }, + { + message: { + value: { + from: 'perffsp2', + to: 'perffsp1', + id: '33d42717-1dc9-4224-8c9b-45aab4fe6457', + content: { + uriParams: { + id: '33d42717-1dc9-4224-8c9b-45aab4fe6457' + }, + headers: { + accept: 'application/vnd.interoperability.transfers+json;version=1.1', + 'content-type': 'application/vnd.interoperability.transfers+json;version=1.1', + date: '2023-08-21T10:22:18.000Z', + 'fspiop-source': 'perffsp2', + 'fspiop-destination': 'perffsp1', + traceparent: '00-b48fd06da5c0cdfec57d73d05bb72d2c-d2f88469e471feca-01', + tracestate: 'acmevendor=eyJzcGFuSWQiOiJkMmY4ODQ2OWU0NzFmZWNhIiwidGltZUFwaVByZXBhcmUiOiIxNjkyMjg1OTA5OTcyIn0=,tx_end2end_start_ts=1692285909971,tx_callback_start_ts=1692613338590', + 'user-agent': 'axios/1.4.0', + 'content-length': '136', + 'accept-encoding': 'gzip, compress, deflate, br', + host: 'ml-api-adapter:3000', + connection: 'keep-alive' + }, + payload: 'data:application/vnd.interoperability.transfers+json;version=1.1;base64,eyJ0cmFuc2ZlclN0YXRlIjoiQ09NTUlUVEVEIiwiZnVsZmlsbWVudCI6ImxuWWU0cll3THRoV2J6aFZ5WDVjQXVEZkwxVWx3NFdkYVRneUdEUkV5c3ciLCJjb21wbGV0ZWRUaW1lc3RhbXAiOiIyMDIzLTA4LTIxVDEwOjIyOjE4LjU5MFoifQ==' + }, + type: 'application/json', + metadata: { + correlationId: '33d42717-1dc9-4224-8c9b-45aab4fe6457', + event: { + type: 'position', + action: 'commit', + createdAt: '2023-08-21T10:22:18.592Z', + state: { + status: 'success', + code: 0, + description: 'action successful' + }, + id: '4226d8a2-8a11-49bb-b04b-58d49c7761a9' + }, + trace: { + service: 'cl_transfer_fulfil', + traceId: 'b48fd06da5c0cdfec57d73d05bb72d2c', + spanId: 'f4119c69bb70a9a2', + sampled: 1, + flags: '01', + parentSpanId: '82513b418895d853', + startTimestamp: '2023-08-21T10:23:43.155Z', + tags: { + tracestate: 'acmevendor=eyJzcGFuSWQiOiJmNDExOWM2OWJiNzBhOWEyIiwidGltZUFwaVByZXBhcmUiOiIxNjkyMjg1OTA5OTcyIiwidGltZUFwaUZ1bGZpbCI6IjE2OTI2MTMzMzg1OTEifQ==,tx_end2end_start_ts=1692285909971,tx_callback_start_ts=1692613338590', + transactionType: 'transfer', + transactionAction: 'fulfil', + transactionId: '33d42717-1dc9-4224-8c9b-45aab4fe6457', + source: 'perffsp2', + destination: 'perffsp1' + }, + tracestates: { + acmevendor: { + spanId: 'f4119c69bb70a9a2', + timeApiPrepare: '1692285909972', + timeApiFulfil: '1692613338591' + }, + tx_end2end_start_ts: '1692285909971', + tx_callback_start_ts: '1692613338590' + } + }, + 'protocol.createdAt': 1692613423158 + } + }, + size: 2215, + key: 51, + topic: 'topic-transfer-position', + offset: 4078, + partition: 0, + timestamp: 1694175690401 + }, + span: {} + } + ] + }, + 15: { + prepare: [ + { + message: { + value: { + from: 'perffsp2', + to: 'perffsp1', + id: '42da6ac4-6c42-469d-930a-1213149f41fe', + content: { + uriParams: { + id: '42da6ac4-6c42-469d-930a-1213149f41fe' + }, + headers: { + host: 'ml-api-adapter:3000', + 'user-agent': 'k6/0.45.0 (https://k6.io/)', + 'content-length': 1314, + accept: 'application/vnd.interoperability.transfers+json;version=1.1', + 'content-type': 'application/vnd.interoperability.transfers+json;version=1.1', + date: '2023-08-17T15:25:07.000Z', + 'fspiop-destination': 'perffsp1', + 'fspiop-source': 'perffsp2', + traceparent: '00-b49069e4bbbcc4322e8ac55cf0d418f1-aae26e308413a8e3-01', + tracestate: 'tx_end2end_start_ts=1692285907912' + }, + payload: 'data:application/vnd.interoperability.transfers+json;version=1.1;base64,eyJ0cmFuc2ZlcklkIjoiNDJkYTZhYzQtNmM0Mi00NjlkLTkzMGEtMTIxMzE0OWY0MWZlIiwicGF5ZXJGc3AiOiJwZXJmZnNwMiIsInBheWVlRnNwIjoicGVyZmZzcDEiLCJhbW91bnQiOnsiYW1vdW50IjoiMiIsImN1cnJlbmN5IjoiVVNEIn0sImV4cGlyYXRpb24iOiIyMDMwLTAxLTAxVDAwOjAwOjAwLjAwMFoiLCJpbHBQYWNrZXQiOiJBWUlER1FBQUFBQUFBQ2NRSVdjdVozSmxaVzVpWVc1clpuTndMbTF6YVhOa2JpNHlOemN4TXpnd016a3hNb0lDNjJWNVNqQmpiVVoxWXpKR2FtUkhiSFppYTJ4clNXcHZhVTFFUlhoYVIxRXlUbGRaZEU1VVFYcE5lVEF3VFZkTk1reFVhekZhUjFsMFQxUkZlRmw2V1RSUFZGRXhXV3BvYlVscGQybGpXRloyWkVkV1NscERTVFpKYlZGM1RYcEpNVTFFVlRKTVZFMHhUbGRGZEU1RVVteE5VekZwVDFSbk1FeFhXWGRaVkZFeFRtcEZNRmt5UlhwUFEwbHpTVzVDYUdWWFZteEphbkEzU1c1Q2FHTnVValZUVjFKS1ltMWFka2xxY0RkSmJrSm9ZMjVTTlZOWFVsVmxXRUpzU1dwdmFWUldUa3BWTUZKUFNXbDNhV05IUm5sa1NHeEtXa2RXZFdSSGJHMWhWMVo1U1dwdmFVMXFZek5OVkUwMFRVUk5OVTFVU1dsTVEwcHRZek5DU2xwRFNUWkpiV1I1V2xkV2RWbHRSblZoTWxwNlkwTktPV1pUZDJsalIwWTFXbGhKYVU5dWMybGpSMFo1WkVoc1NscEZiSFZhYlRocFQyNXphV05IUm5sa1NHeEtXa1pTTldOSFZXbFBhVXBPVlRCc1ZGSkZOR2xNUTBwM1dWaEtNR1ZWYkd0YVZ6VXdZVmRhY0ZwWVNXbFBhVWt3VGtSRmVVMTZVVEZPYW1NMFQxTkpjMGx0V25walJXeHJTV3B2YVdOSGJIVmhNa3BvWW0xMGJXTXpRV2xtVTNkcFkwZFdlV015T1hWWlYzaEtZbTFhZGtscWNEZEpiVTUyWWxoQ2MxcFlhRTlaVnpGc1NXcHdOMGx0V25CamJrNHdWRzFHZEZwVFNUWkphMXB3WTI1T01HSnRSblJhVXpGVldsaE9NRWxwZDJsaVIwWjZaRVUxYUdKWFZXbFBhVXBOV1ZoT01HSnRSblJhVXpGVldsaE9NRWx1TUhOSmJWSm9aRWRXVUZwclNuQmpibEp2U1dwdmFVMVVhelJPUXpCM1RWTXdkMDFUU2psbVUzZHBXVmN4ZG1SWE5UQkphbkEzU1cxT01XTnVTbXhpYlU0MVNXcHZhVlpXVGtWSmFYZHBXVmN4ZG1SWE5UQkphbTlwVFZSQmQwbHVNSE5KYmxKNVdWYzFlbGxYVGpCaFZ6bDFWa2hzZDFwVFNUWmxlVXA2V1RKV2RWbFlTbkJpZVVrMlNXeFNVMUZWTlZSU2ExWlRTV2wzYVdGWE5YQmtSMnhvWkVjNWVVbHFiMmxWUlVaYVVsWkphVXhEU25CaWJXd3dZVmRHTUdJelNsVmxXRUpzU1dwdmFWRXdPVTlWTVZaT1VsWkphV1pZTUFBIiwiY29uZGl0aW9uIjoiNW0wZ3FfNWRMUWxUU1NSS1FtTHBqME1aMU10V0xXZ1N1MW9MR1ZUSnlZcyJ9' + }, + type: 'application/json', + metadata: { + correlationId: '42da6ac4-6c42-469d-930a-1213149f41fe', + event: { + type: 'position', + action: 'prepare', + createdAt: '2023-08-17T15:25:07.913Z', + state: { + status: 'success', + code: 0, + description: 'action successful' + }, + id: 'dbb1af2d-759a-4e86-98b1-82baa48a51ac' + }, + trace: { + service: 'cl_transfer_prepare', + traceId: 'b49069e4bbbcc4322e8ac55cf0d418f1', + spanId: '425d92515f6508bc', + sampled: 1, + flags: '01', + parentSpanId: '3dfa31e3cf005305', + startTimestamp: '2023-08-17T15:25:08.015Z', + tags: { + tracestate: 'acmevendor=eyJzcGFuSWQiOiI0MjVkOTI1MTVmNjUwOGJjIiwidGltZUFwaVByZXBhcmUiOiIxNjkyMjg1OTA3OTEzIn0=,tx_end2end_start_ts=1692285907912', + transactionType: 'transfer', + transactionAction: 'prepare', + transactionId: '42da6ac4-6c42-469d-930a-1213149f41fe', + source: 'perffsp2', + destination: 'perffsp1', + payerFsp: 'perffsp2', + payeeFsp: 'perffsp1' + }, + tracestates: { + acmevendor: { + spanId: '425d92515f6508bc', + timeApiPrepare: '1692285907913' + }, + tx_end2end_start_ts: '1692285907912' + } + }, + 'protocol.createdAt': 1692285908020 + } + }, + size: 3489, + key: 52, + topic: 'topic-transfer-position', + offset: 4071, + partition: 0, + timestamp: 1694175690401 + }, + span: {}, + decodedPayload: { + amount: { + amount: '2', + currency: 'USD' + }, + condition: '5m0gq_5dLQlTSSRKQmLpj0MZ1MtWLWgSu1oLGVTJyYs', + expiration: '2030-01-01T00:00:00.000Z', + ilpPacket: 'AYIDGQAAAAAAACcQIWcuZ3JlZW5iYW5rZnNwLm1zaXNkbi4yNzcxMzgwMzkxMoIC62V5SjBjbUZ1YzJGamRHbHZia2xrSWpvaU1ERXhaR1EyTldZdE5UQXpNeTAwTVdNMkxUazFaR1l0T1RFeFl6WTRPVFExWWpobUlpd2ljWFZ2ZEdWSlpDSTZJbVF3TXpJMU1EVTJMVE0xTldFdE5EUmxNUzFpT1RnMExXWXdZVFExTmpFMFkyRXpPQ0lzSW5CaGVXVmxJanA3SW5CaGNuUjVTV1JKYm1adklqcDdJbkJoY25SNVNXUlVlWEJsSWpvaVRWTkpVMFJPSWl3aWNHRnlkSGxKWkdWdWRHbG1hV1Z5SWpvaU1qYzNNVE00TURNNU1USWlMQ0ptYzNCSlpDSTZJbWR5WldWdVltRnVhMlp6Y0NKOWZTd2ljR0Y1WlhJaU9uc2ljR0Z5ZEhsSlpFbHVabThpT25zaWNHRnlkSGxKWkZSNWNHVWlPaUpOVTBsVFJFNGlMQ0p3WVhKMGVVbGtaVzUwYVdacFpYSWlPaUkwTkRFeU16UTFOamM0T1NJc0ltWnpjRWxrSWpvaWNHbHVhMkpoYm10bWMzQWlmU3dpY0dWeWMyOXVZV3hKYm1adklqcDdJbU52YlhCc1pYaE9ZVzFsSWpwN0ltWnBjbk4wVG1GdFpTSTZJa1pwY25OMGJtRnRaUzFVWlhOMElpd2liR0Z6ZEU1aGJXVWlPaUpNWVhOMGJtRnRaUzFVWlhOMEluMHNJbVJoZEdWUFprSnBjblJvSWpvaU1UazROQzB3TVMwd01TSjlmU3dpWVcxdmRXNTBJanA3SW1OMWNuSmxibU41SWpvaVZWTkVJaXdpWVcxdmRXNTBJam9pTVRBd0luMHNJblJ5WVc1ellXTjBhVzl1Vkhsd1pTSTZleUp6WTJWdVlYSnBieUk2SWxSU1FVNVRSa1ZTSWl3aWFXNXBkR2xoZEc5eUlqb2lVRUZaUlZJaUxDSnBibWwwYVdGMGIzSlVlWEJsSWpvaVEwOU9VMVZOUlZJaWZYMAA', + payeeFsp: 'perffsp1', + payerFsp: 'perffsp2', + transferId: '42da6ac4-6c42-469d-930a-1213149f41fe' + } + }, + { + message: { + value: { + from: 'perffsp2', + to: 'perffsp1', + id: '605ce9e6-a320-4a25-a4c4-397ac6d2544b', + content: { + uriParams: { + id: '605ce9e6-a320-4a25-a4c4-397ac6d2544b' + }, + headers: { + host: 'ml-api-adapter:3000', + 'user-agent': 'k6/0.45.0 (https://k6.io/)', + 'content-length': 1314, + accept: 'application/vnd.interoperability.transfers+json;version=1.1', + 'content-type': 'application/vnd.interoperability.transfers+json;version=1.1', + date: '2023-08-17T15:25:08.000Z', + 'fspiop-destination': 'perffsp1', + 'fspiop-source': 'perffsp2', + traceparent: '00-162bd630efca4e7314f25fc66519f8fb-429aa670755ad8da-01', + tracestate: 'tx_end2end_start_ts=1692285908078' + }, + payload: 'data:application/vnd.interoperability.transfers+json;version=1.1;base64,eyJ0cmFuc2ZlcklkIjoiNjA1Y2U5ZTYtYTMyMC00YTI1LWE0YzQtMzk3YWM2ZDI1NDRiIiwicGF5ZXJGc3AiOiJwZXJmZnNwMiIsInBheWVlRnNwIjoicGVyZmZzcDEiLCJhbW91bnQiOnsiYW1vdW50IjoiMiIsImN1cnJlbmN5IjoiVVNEIn0sImV4cGlyYXRpb24iOiIyMDMwLTAxLTAxVDAwOjAwOjAwLjAwMFoiLCJpbHBQYWNrZXQiOiJBWUlER1FBQUFBQUFBQ2NRSVdjdVozSmxaVzVpWVc1clpuTndMbTF6YVhOa2JpNHlOemN4TXpnd016a3hNb0lDNjJWNVNqQmpiVVoxWXpKR2FtUkhiSFppYTJ4clNXcHZhVTFFUlhoYVIxRXlUbGRaZEU1VVFYcE5lVEF3VFZkTk1reFVhekZhUjFsMFQxUkZlRmw2V1RSUFZGRXhXV3BvYlVscGQybGpXRloyWkVkV1NscERTVFpKYlZGM1RYcEpNVTFFVlRKTVZFMHhUbGRGZEU1RVVteE5VekZwVDFSbk1FeFhXWGRaVkZFeFRtcEZNRmt5UlhwUFEwbHpTVzVDYUdWWFZteEphbkEzU1c1Q2FHTnVValZUVjFKS1ltMWFka2xxY0RkSmJrSm9ZMjVTTlZOWFVsVmxXRUpzU1dwdmFWUldUa3BWTUZKUFNXbDNhV05IUm5sa1NHeEtXa2RXZFdSSGJHMWhWMVo1U1dwdmFVMXFZek5OVkUwMFRVUk5OVTFVU1dsTVEwcHRZek5DU2xwRFNUWkpiV1I1V2xkV2RWbHRSblZoTWxwNlkwTktPV1pUZDJsalIwWTFXbGhKYVU5dWMybGpSMFo1WkVoc1NscEZiSFZhYlRocFQyNXphV05IUm5sa1NHeEtXa1pTTldOSFZXbFBhVXBPVlRCc1ZGSkZOR2xNUTBwM1dWaEtNR1ZWYkd0YVZ6VXdZVmRhY0ZwWVNXbFBhVWt3VGtSRmVVMTZVVEZPYW1NMFQxTkpjMGx0V25walJXeHJTV3B2YVdOSGJIVmhNa3BvWW0xMGJXTXpRV2xtVTNkcFkwZFdlV015T1hWWlYzaEtZbTFhZGtscWNEZEpiVTUyWWxoQ2MxcFlhRTlaVnpGc1NXcHdOMGx0V25CamJrNHdWRzFHZEZwVFNUWkphMXB3WTI1T01HSnRSblJhVXpGVldsaE9NRWxwZDJsaVIwWjZaRVUxYUdKWFZXbFBhVXBOV1ZoT01HSnRSblJhVXpGVldsaE9NRWx1TUhOSmJWSm9aRWRXVUZwclNuQmpibEp2U1dwdmFVMVVhelJPUXpCM1RWTXdkMDFUU2psbVUzZHBXVmN4ZG1SWE5UQkphbkEzU1cxT01XTnVTbXhpYlU0MVNXcHZhVlpXVGtWSmFYZHBXVmN4ZG1SWE5UQkphbTlwVFZSQmQwbHVNSE5KYmxKNVdWYzFlbGxYVGpCaFZ6bDFWa2hzZDFwVFNUWmxlVXA2V1RKV2RWbFlTbkJpZVVrMlNXeFNVMUZWTlZSU2ExWlRTV2wzYVdGWE5YQmtSMnhvWkVjNWVVbHFiMmxWUlVaYVVsWkphVXhEU25CaWJXd3dZVmRHTUdJelNsVmxXRUpzU1dwdmFWRXdPVTlWTVZaT1VsWkphV1pZTUFBIiwiY29uZGl0aW9uIjoiNW0wZ3FfNWRMUWxUU1NSS1FtTHBqME1aMU10V0xXZ1N1MW9MR1ZUSnlZcyJ9' + }, + type: 'application/json', + metadata: { + correlationId: '605ce9e6-a320-4a25-a4c4-397ac6d2544b', + event: { + type: 'position', + action: 'prepare', + createdAt: '2023-08-17T15:25:08.079Z', + state: { + status: 'success', + code: 0, + description: 'action successful' + }, + id: 'a5d86735-2f7a-4dd5-b342-af4a0258f452' + }, + trace: { + service: 'cl_transfer_prepare', + traceId: '162bd630efca4e7314f25fc66519f8fb', + spanId: '86e19151bf6c32f5', + sampled: 1, + flags: '01', + parentSpanId: '96293206811ab4cd', + startTimestamp: '2023-08-17T15:25:08.099Z', + tags: { + tracestate: 'acmevendor=eyJzcGFuSWQiOiI4NmUxOTE1MWJmNmMzMmY1IiwidGltZUFwaVByZXBhcmUiOiIxNjkyMjg1OTA4MDc5In0=,tx_end2end_start_ts=1692285908078', + transactionType: 'transfer', + transactionAction: 'prepare', + transactionId: '605ce9e6-a320-4a25-a4c4-397ac6d2544b', + source: 'perffsp2', + destination: 'perffsp1', + payerFsp: 'perffsp2', + payeeFsp: 'perffsp1' + }, + tracestates: { + acmevendor: { + spanId: '86e19151bf6c32f5', + timeApiPrepare: '1692285908079' + }, + tx_end2end_start_ts: '1692285908078' + } + }, + 'protocol.createdAt': 1692285908106 + } + }, + size: 3489, + key: 52, + topic: 'topic-transfer-position', + offset: 4072, + partition: 0, + timestamp: 1694175690401 + }, + span: {}, + decodedPayload: { + amount: { + amount: '2', + currency: 'USD' + }, + condition: '5m0gq_5dLQlTSSRKQmLpj0MZ1MtWLWgSu1oLGVTJyYs', + expiration: '2030-01-01T00:00:00.000Z', + ilpPacket: 'AYIDGQAAAAAAACcQIWcuZ3JlZW5iYW5rZnNwLm1zaXNkbi4yNzcxMzgwMzkxMoIC62V5SjBjbUZ1YzJGamRHbHZia2xrSWpvaU1ERXhaR1EyTldZdE5UQXpNeTAwTVdNMkxUazFaR1l0T1RFeFl6WTRPVFExWWpobUlpd2ljWFZ2ZEdWSlpDSTZJbVF3TXpJMU1EVTJMVE0xTldFdE5EUmxNUzFpT1RnMExXWXdZVFExTmpFMFkyRXpPQ0lzSW5CaGVXVmxJanA3SW5CaGNuUjVTV1JKYm1adklqcDdJbkJoY25SNVNXUlVlWEJsSWpvaVRWTkpVMFJPSWl3aWNHRnlkSGxKWkdWdWRHbG1hV1Z5SWpvaU1qYzNNVE00TURNNU1USWlMQ0ptYzNCSlpDSTZJbWR5WldWdVltRnVhMlp6Y0NKOWZTd2ljR0Y1WlhJaU9uc2ljR0Z5ZEhsSlpFbHVabThpT25zaWNHRnlkSGxKWkZSNWNHVWlPaUpOVTBsVFJFNGlMQ0p3WVhKMGVVbGtaVzUwYVdacFpYSWlPaUkwTkRFeU16UTFOamM0T1NJc0ltWnpjRWxrSWpvaWNHbHVhMkpoYm10bWMzQWlmU3dpY0dWeWMyOXVZV3hKYm1adklqcDdJbU52YlhCc1pYaE9ZVzFsSWpwN0ltWnBjbk4wVG1GdFpTSTZJa1pwY25OMGJtRnRaUzFVWlhOMElpd2liR0Z6ZEU1aGJXVWlPaUpNWVhOMGJtRnRaUzFVWlhOMEluMHNJbVJoZEdWUFprSnBjblJvSWpvaU1UazROQzB3TVMwd01TSjlmU3dpWVcxdmRXNTBJanA3SW1OMWNuSmxibU41SWpvaVZWTkVJaXdpWVcxdmRXNTBJam9pTVRBd0luMHNJblJ5WVc1ellXTjBhVzl1Vkhsd1pTSTZleUp6WTJWdVlYSnBieUk2SWxSU1FVNVRSa1ZTSWl3aWFXNXBkR2xoZEc5eUlqb2lVRUZaUlZJaUxDSnBibWwwYVdGMGIzSlVlWEJsSWpvaVEwOU9VMVZOUlZJaWZYMAA', + payeeFsp: 'perffsp1', + payerFsp: 'perffsp2', + transferId: '605ce9e6-a320-4a25-a4c4-397ac6d2544b' + } + }, + { + message: { + value: { + from: 'perffsp2', + to: 'perffsp1', + id: '64b91a07-99a7-4c68-b7dd-d6b3211ad09c', + content: { + uriParams: { + id: '64b91a07-99a7-4c68-b7dd-d6b3211ad09c' + }, + headers: { + host: 'ml-api-adapter:3000', + 'user-agent': 'k6/0.45.0 (https://k6.io/)', + 'content-length': 1314, + accept: 'application/vnd.interoperability.transfers+json;version=1.1', + 'content-type': 'application/vnd.interoperability.transfers+json;version=1.1', + date: '2023-08-17T15:25:07.000Z', + 'fspiop-destination': 'perffsp1', + 'fspiop-source': 'perffsp2', + traceparent: '00-9617167dcbe1a8079599c0d4b086f574-1816a3a9f4c899de-01', + tracestate: 'tx_end2end_start_ts=1692285907706' + }, + payload: 'data:application/vnd.interoperability.transfers+json;version=1.1;base64,eyJ0cmFuc2ZlcklkIjoiNjRiOTFhMDctOTlhNy00YzY4LWI3ZGQtZDZiMzIxMWFkMDljIiwicGF5ZXJGc3AiOiJwZXJmZnNwMiIsInBheWVlRnNwIjoicGVyZmZzcDEiLCJhbW91bnQiOnsiYW1vdW50IjoiMiIsImN1cnJlbmN5IjoiVVNEIn0sImV4cGlyYXRpb24iOiIyMDMwLTAxLTAxVDAwOjAwOjAwLjAwMFoiLCJpbHBQYWNrZXQiOiJBWUlER1FBQUFBQUFBQ2NRSVdjdVozSmxaVzVpWVc1clpuTndMbTF6YVhOa2JpNHlOemN4TXpnd016a3hNb0lDNjJWNVNqQmpiVVoxWXpKR2FtUkhiSFppYTJ4clNXcHZhVTFFUlhoYVIxRXlUbGRaZEU1VVFYcE5lVEF3VFZkTk1reFVhekZhUjFsMFQxUkZlRmw2V1RSUFZGRXhXV3BvYlVscGQybGpXRloyWkVkV1NscERTVFpKYlZGM1RYcEpNVTFFVlRKTVZFMHhUbGRGZEU1RVVteE5VekZwVDFSbk1FeFhXWGRaVkZFeFRtcEZNRmt5UlhwUFEwbHpTVzVDYUdWWFZteEphbkEzU1c1Q2FHTnVValZUVjFKS1ltMWFka2xxY0RkSmJrSm9ZMjVTTlZOWFVsVmxXRUpzU1dwdmFWUldUa3BWTUZKUFNXbDNhV05IUm5sa1NHeEtXa2RXZFdSSGJHMWhWMVo1U1dwdmFVMXFZek5OVkUwMFRVUk5OVTFVU1dsTVEwcHRZek5DU2xwRFNUWkpiV1I1V2xkV2RWbHRSblZoTWxwNlkwTktPV1pUZDJsalIwWTFXbGhKYVU5dWMybGpSMFo1WkVoc1NscEZiSFZhYlRocFQyNXphV05IUm5sa1NHeEtXa1pTTldOSFZXbFBhVXBPVlRCc1ZGSkZOR2xNUTBwM1dWaEtNR1ZWYkd0YVZ6VXdZVmRhY0ZwWVNXbFBhVWt3VGtSRmVVMTZVVEZPYW1NMFQxTkpjMGx0V25walJXeHJTV3B2YVdOSGJIVmhNa3BvWW0xMGJXTXpRV2xtVTNkcFkwZFdlV015T1hWWlYzaEtZbTFhZGtscWNEZEpiVTUyWWxoQ2MxcFlhRTlaVnpGc1NXcHdOMGx0V25CamJrNHdWRzFHZEZwVFNUWkphMXB3WTI1T01HSnRSblJhVXpGVldsaE9NRWxwZDJsaVIwWjZaRVUxYUdKWFZXbFBhVXBOV1ZoT01HSnRSblJhVXpGVldsaE9NRWx1TUhOSmJWSm9aRWRXVUZwclNuQmpibEp2U1dwdmFVMVVhelJPUXpCM1RWTXdkMDFUU2psbVUzZHBXVmN4ZG1SWE5UQkphbkEzU1cxT01XTnVTbXhpYlU0MVNXcHZhVlpXVGtWSmFYZHBXVmN4ZG1SWE5UQkphbTlwVFZSQmQwbHVNSE5KYmxKNVdWYzFlbGxYVGpCaFZ6bDFWa2hzZDFwVFNUWmxlVXA2V1RKV2RWbFlTbkJpZVVrMlNXeFNVMUZWTlZSU2ExWlRTV2wzYVdGWE5YQmtSMnhvWkVjNWVVbHFiMmxWUlVaYVVsWkphVXhEU25CaWJXd3dZVmRHTUdJelNsVmxXRUpzU1dwdmFWRXdPVTlWTVZaT1VsWkphV1pZTUFBIiwiY29uZGl0aW9uIjoiNW0wZ3FfNWRMUWxUU1NSS1FtTHBqME1aMU10V0xXZ1N1MW9MR1ZUSnlZcyJ9' + }, + type: 'application/json', + metadata: { + correlationId: '64b91a07-99a7-4c68-b7dd-d6b3211ad09c', + event: { + type: 'position', + action: 'prepare', + createdAt: '2023-08-17T15:25:07.708Z', + state: { + status: 'success', + code: 0, + description: 'action successful' + }, + id: '75fcdd1e-fd08-4ed7-8fb4-dc5a3f20bc8d' + }, + trace: { + service: 'cl_transfer_prepare', + traceId: '9617167dcbe1a8079599c0d4b086f574', + spanId: '2072472a40dc4b13', + sampled: 1, + flags: '01', + parentSpanId: '883dff1f906f3339', + startTimestamp: '2023-08-17T15:25:07.847Z', + tags: { + tracestate: 'acmevendor=eyJzcGFuSWQiOiIyMDcyNDcyYTQwZGM0YjEzIiwidGltZUFwaVByZXBhcmUiOiIxNjkyMjg1OTA3NzA3In0=,tx_end2end_start_ts=1692285907706', + transactionType: 'transfer', + transactionAction: 'prepare', + transactionId: '64b91a07-99a7-4c68-b7dd-d6b3211ad09c', + source: 'perffsp2', + destination: 'perffsp1', + payerFsp: 'perffsp2', + payeeFsp: 'perffsp1' + }, + tracestates: { + acmevendor: { + spanId: '2072472a40dc4b13', + timeApiPrepare: '1692285907707' + }, + tx_end2end_start_ts: '1692285907706' + } + }, + 'protocol.createdAt': 1692285907853 + } + }, + size: 3489, + key: 52, + topic: 'topic-transfer-position', + offset: 4074, + partition: 0, + timestamp: 1694175690401 + }, + span: {}, + decodedPayload: { + amount: { + amount: '2', + currency: 'USD' + }, + condition: '5m0gq_5dLQlTSSRKQmLpj0MZ1MtWLWgSu1oLGVTJyYs', + expiration: '2030-01-01T00:00:00.000Z', + ilpPacket: 'AYIDGQAAAAAAACcQIWcuZ3JlZW5iYW5rZnNwLm1zaXNkbi4yNzcxMzgwMzkxMoIC62V5SjBjbUZ1YzJGamRHbHZia2xrSWpvaU1ERXhaR1EyTldZdE5UQXpNeTAwTVdNMkxUazFaR1l0T1RFeFl6WTRPVFExWWpobUlpd2ljWFZ2ZEdWSlpDSTZJbVF3TXpJMU1EVTJMVE0xTldFdE5EUmxNUzFpT1RnMExXWXdZVFExTmpFMFkyRXpPQ0lzSW5CaGVXVmxJanA3SW5CaGNuUjVTV1JKYm1adklqcDdJbkJoY25SNVNXUlVlWEJsSWpvaVRWTkpVMFJPSWl3aWNHRnlkSGxKWkdWdWRHbG1hV1Z5SWpvaU1qYzNNVE00TURNNU1USWlMQ0ptYzNCSlpDSTZJbWR5WldWdVltRnVhMlp6Y0NKOWZTd2ljR0Y1WlhJaU9uc2ljR0Z5ZEhsSlpFbHVabThpT25zaWNHRnlkSGxKWkZSNWNHVWlPaUpOVTBsVFJFNGlMQ0p3WVhKMGVVbGtaVzUwYVdacFpYSWlPaUkwTkRFeU16UTFOamM0T1NJc0ltWnpjRWxrSWpvaWNHbHVhMkpoYm10bWMzQWlmU3dpY0dWeWMyOXVZV3hKYm1adklqcDdJbU52YlhCc1pYaE9ZVzFsSWpwN0ltWnBjbk4wVG1GdFpTSTZJa1pwY25OMGJtRnRaUzFVWlhOMElpd2liR0Z6ZEU1aGJXVWlPaUpNWVhOMGJtRnRaUzFVWlhOMEluMHNJbVJoZEdWUFprSnBjblJvSWpvaU1UazROQzB3TVMwd01TSjlmU3dpWVcxdmRXNTBJanA3SW1OMWNuSmxibU41SWpvaVZWTkVJaXdpWVcxdmRXNTBJam9pTVRBd0luMHNJblJ5WVc1ellXTjBhVzl1Vkhsd1pTSTZleUp6WTJWdVlYSnBieUk2SWxSU1FVNVRSa1ZTSWl3aWFXNXBkR2xoZEc5eUlqb2lVRUZaUlZJaUxDSnBibWwwYVdGMGIzSlVlWEJsSWpvaVEwOU9VMVZOUlZJaWZYMAA', + payeeFsp: 'perffsp1', + payerFsp: 'perffsp2', + transferId: '64b91a07-99a7-4c68-b7dd-d6b3211ad09c' + } + } + ], + commit: [ + { + message: { + value: { + from: 'perffsp1', + to: 'perffsp2', + id: 'f33add51-38b1-4715-9876-83d8a08c485d', + content: { + uriParams: { + id: 'f33add51-38b1-4715-9876-83d8a08c485d' + }, + headers: { + accept: 'application/vnd.interoperability.transfers+json;version=1.1', + 'content-type': 'application/vnd.interoperability.transfers+json;version=1.1', + date: '2023-08-21T10:22:11.000Z', + 'fspiop-source': 'perffsp1', + 'fspiop-destination': 'perffsp2', + traceparent: '00-278414be0ce56adab6c6461b1196f7ec-c2639bb302a327f2-01', + tracestate: 'acmevendor=eyJzcGFuSWQiOiJjMjYzOWJiMzAyYTMyN2YyIiwidGltZUFwaVByZXBhcmUiOiIxNjkyMjg1OTA4MTc4In0=,tx_end2end_start_ts=1692285908177,tx_callback_start_ts=1692613331481', + 'user-agent': 'axios/1.4.0', + 'content-length': '136', + 'accept-encoding': 'gzip, compress, deflate, br', + host: 'ml-api-adapter:3000', + connection: 'keep-alive' + }, + payload: 'data:application/vnd.interoperability.transfers+json;version=1.1;base64,eyJ0cmFuc2ZlclN0YXRlIjoiQ09NTUlUVEVEIiwiZnVsZmlsbWVudCI6ImxuWWU0cll3THRoV2J6aFZ5WDVjQXVEZkwxVWx3NFdkYVRneUdEUkV5c3ciLCJjb21wbGV0ZWRUaW1lc3RhbXAiOiIyMDIzLTA4LTIxVDEwOjIyOjExLjQ4MVoifQ==' + }, + type: 'application/json', + metadata: { + correlationId: 'f33add51-38b1-4715-9876-83d8a08c485d', + event: { + type: 'position', + action: 'commit', + createdAt: '2023-08-21T10:22:11.481Z', + state: { + status: 'success', + code: 0, + description: 'action successful' + }, + id: 'ffa2969c-8b90-4fa7-97b3-6013b5937553' + }, + trace: { + service: 'cl_transfer_fulfil', + traceId: '278414be0ce56adab6c6461b1196f7ec', + spanId: '29dcf2b250cd22d1', + sampled: 1, + flags: '01', + parentSpanId: 'e038bfd263a0b4c0', + startTimestamp: '2023-08-21T10:23:31.357Z', + tags: { + tracestate: 'acmevendor=eyJzcGFuSWQiOiIyOWRjZjJiMjUwY2QyMmQxIiwidGltZUFwaVByZXBhcmUiOiIxNjkyMjg1OTA4MTc4IiwidGltZUFwaUZ1bGZpbCI6IjE2OTI2MTMzMzE0ODEifQ==,tx_end2end_start_ts=1692285908177,tx_callback_start_ts=1692613331481', + transactionType: 'transfer', + transactionAction: 'fulfil', + transactionId: 'f33add51-38b1-4715-9876-83d8a08c485d', + source: 'perffsp1', + destination: 'perffsp2' + }, + tracestates: { + acmevendor: { + spanId: '29dcf2b250cd22d1', + timeApiPrepare: '1692285908178', + timeApiFulfil: '1692613331481' + }, + tx_end2end_start_ts: '1692285908177', + tx_callback_start_ts: '1692613331481' + } + }, + 'protocol.createdAt': 1692613411360 + } + }, + size: 2215, + key: 52, + topic: 'topic-transfer-position', + offset: 4077, + partition: 0, + timestamp: 1694175690401 + }, + span: {} + } + ] + } +} diff --git a/test/unit/handlers/index.test.js b/test/unit/handlers/index.test.js index 63fecc3d9..684803972 100644 --- a/test/unit/handlers/index.test.js +++ b/test/unit/handlers/index.test.js @@ -66,7 +66,8 @@ Test('cli', async (cliTest) => { '--admin', '--bulkprepare', '--bulkfulfil', - '--bulkprocessing' + '--bulkprocessing', + '--positionbatch' ] process.argv = argv @@ -120,9 +121,15 @@ Test('cli', async (cliTest) => { enabled: true } + const positionBatchHandler = { + type: 'positionbatch', + enabled: true + } + const modulesList = [ prepareHandler, positionHandler, + positionBatchHandler, getHandler, fulfilHandler, timeoutHandler, @@ -228,6 +235,43 @@ Test('cli', async (cliTest) => { test.end() }) + commanderTest.test('start all position batch Handlers up with no FSPList provided', async test => { + const argv = [ + 'node', + 'index.js', + 'handler', + '--positionbatch' + ] + + process.argv = argv + + const Index = Proxyquire('../../../src/handlers/index', { + '../shared/setup': SetupStub + }) + + const positionHandler = { + type: 'positionbatch', + enabled: true + } + + const modulesList = [ + positionHandler + ] + + const initOptions = { + service: 'handler', + port: Config.PORT, + modules: [Plugin, MetricsPlugin], + runMigrations: false, + handlers: modulesList, + runHandlers: true + } + + test.ok(Index) + test.ok(SetupStub.initialize.calledWith(initOptions)) + test.end() + }) + commanderTest.test('start all prepare Handlers up with an invalid FSPList provided', async test => { const argv = [ 'node', diff --git a/test/unit/handlers/positions/handlerBatch.test.js b/test/unit/handlers/positions/handlerBatch.test.js new file mode 100644 index 000000000..84e480b07 --- /dev/null +++ b/test/unit/handlers/positions/handlerBatch.test.js @@ -0,0 +1,481 @@ +/***** + License + -------------- + Copyright © 2017 Bill & Melinda Gates Foundation + The Mojaloop files are made available by the Bill & Melinda Gates Foundation under the Apache License, Version 2.0 (the "License") and you may not use these files except in compliance with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, the Mojaloop files are distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + + Contributors + -------------- + This is the official list of the Mojaloop project contributors for this file. + Names of the original copyright holders (individuals or organizations) + should be listed with a '*' in the first column. People who have + contributed from an organization can be listed under the organization + that actually holds the copyright for their contributions (see the + Gates Foundation organization for an example). Those individuals should have + their names indented and be marked with a '-'. Email address can be added + optionally within square brackets . + + * Gates Foundation + - Name Surname + + * INFITX + - Vijay Kumar Guthi + + -------------- + ******/ +'use strict' + +const Sinon = require('sinon') +const Test = require('tapes')(require('tape')) +const Kafka = require('@mojaloop/central-services-shared').Util.Kafka +const BinProcessor = require('../../../../src/domain/position/binProcessor') +const Consumer = require('@mojaloop/central-services-stream').Util.Consumer +const KafkaConsumer = Consumer.Consumer +const BatchPositionModel = require('../../../../src/models/position/batch') +const SettlementModelCached = require('../../../../src/models/settlement/settlementModelCached') +const Enum = require('@mojaloop/central-services-shared').Enum +const Proxyquire = require('proxyquire') +const Logger = require('@mojaloop/central-services-logger') + +const topicName = 'topic-transfer-position-batch' + +const prepareMessageValue = { + metadata: { + event: { + action: Enum.Events.Event.Action.PREPARE + } + }, + content: { + payload: {} + } +} +const commitMessageValue = { + metadata: { + event: { + action: Enum.Events.Event.Action.COMMIT + } + }, + content: { + payload: {} + } +} + +const samplePrepareMessages = [ + { + key: '1001', + value: prepareMessageValue, + topic: topicName + }, + { + key: '1001', + value: prepareMessageValue, + topic: topicName + }, + { + key: '1002', + value: prepareMessageValue, + topic: topicName + } +] + +const sampleCommitMessages = [ + { + key: '1001', + value: commitMessageValue, + topic: topicName + }, + { + key: '1002', + value: commitMessageValue, + topic: topicName + } +] + +const config = { + options: { + mode: 2, + batchSize: 1, + pollFrequency: 10, + recursiveTimeout: 100, + messageCharset: 'utf8', + messageAsJSON: true, + sync: true, + consumeTimeout: 1000 + }, + rdkafkaConf: { + 'client.id': 'kafka-test', + debug: 'all', + 'group.id': 'central-ledger-kafka', + 'metadata.broker.list': 'localhost:9092', + 'enable.auto.commit': false + } +} + +const command = () => {} + +let SpanStub +let allTransferHandlers +let trxStub +let messages +let expectedBins + +Test('Position handler', positionBatchHandlerTest => { + let sandbox + + positionBatchHandlerTest.beforeEach(test => { + sandbox = Sinon.createSandbox() + SpanStub = { + audit: sandbox.stub().callsFake(), + error: sandbox.stub().callsFake(), + finish: sandbox.stub().callsFake(), + setTags: sandbox.stub().callsFake() + } + + // Mixed messages + messages = [ + samplePrepareMessages[0], + samplePrepareMessages[1], + sampleCommitMessages[0], + samplePrepareMessages[2], + sampleCommitMessages[1] + ] + + expectedBins = { + 1001: { + prepare: [ + { + message: samplePrepareMessages[0], + span: SpanStub, + result: {}, + decodedPayload: {} + }, + { + message: samplePrepareMessages[1], + span: SpanStub, + result: {}, + decodedPayload: {} + } + ], + commit: [ + { + message: sampleCommitMessages[0], + span: SpanStub, + result: {}, + decodedPayload: {} + } + ] + }, + 1002: { + prepare: [ + { + message: samplePrepareMessages[2], + span: SpanStub, + result: {}, + decodedPayload: {} + } + ], + commit: [ + { + message: sampleCommitMessages[1], + span: SpanStub, + result: {}, + decodedPayload: {} + } + ] + } + } + + const TracerStub = { + extractContextFromMessage: sandbox.stub().callsFake(() => { + return {} + }), + createChildSpanFromContext: sandbox.stub().callsFake(() => { + return SpanStub + }) + } + + const EventSdkStub = { + Tracer: TracerStub + } + + allTransferHandlers = Proxyquire('../../../../src/handlers/positions/handlerBatch', { + '@mojaloop/event-sdk': EventSdkStub + }) + + sandbox.stub(SettlementModelCached) + sandbox.stub(Kafka) + sandbox.stub(KafkaConsumer.prototype, 'constructor').resolves() + sandbox.stub(KafkaConsumer.prototype, 'connect').resolves() + sandbox.stub(KafkaConsumer.prototype, 'consume').resolves() + sandbox.stub(KafkaConsumer.prototype, 'commitMessageSync').resolves() + sandbox.stub(Consumer, 'getConsumer').returns({ + commitMessageSync: async function () { return true } + }) + sandbox.stub(BatchPositionModel) + trxStub = { + commit: sandbox.stub().returns(true), + rollback: sandbox.stub().returns(true) + } + BatchPositionModel.startDbTransaction.returns(trxStub) + sandbox.stub(BinProcessor) + BinProcessor.processBins.resolves({ + notifyMessages: messages.map((i) => ({ binItem: { message: i, span: SpanStub }, message: { metadata: { event: { state: { status: 'success' } } } } })) + }) + BinProcessor.iterateThroughBins.restore() + + Kafka.transformAccountToTopicName.returns(topicName) + Kafka.produceGeneralMessage.resolves() + test.end() + }) + + positionBatchHandlerTest.afterEach(test => { + sandbox.restore() + test.end() + }) + + positionBatchHandlerTest.test('registerAllHandlers should', registerAllHandlersTest => { + registerAllHandlersTest.test('register all consumers on Kafka', async test => { + try { + await Consumer.createHandler(topicName, config, command) + Kafka.transformGeneralTopicName.returns(topicName) + Kafka.getKafkaConfig.returns(config) + const result = await allTransferHandlers.registerAllHandlers() + test.equal(result, true) + test.pass('Error not thrown') + test.end() + } catch (e) { + test.fail('Error not thrown') + test.end() + } + }) + + registerAllHandlersTest.test('register a consumer on Kafka', async test => { + await Consumer.createHandler(topicName, config, command) + Kafka.transformGeneralTopicName.returns(topicName) + Kafka.getKafkaConfig.returns(config) + const result = await allTransferHandlers.registerAllHandlers() + test.equal(result, true) + test.end() + }) + + registerAllHandlersTest.test('throw error when there is an error getting KafkaConfig', async test => { + try { + Consumer.createHandler(topicName, config, command) + Kafka.transformGeneralTopicName.returns(topicName) + Kafka.getKafkaConfig.throws(new Error()) + + await allTransferHandlers.registerAllHandlers() + test.fail('Error not thrown') + test.end() + } catch (e) { + test.pass('Error thrown') + test.end() + } + }) + + registerAllHandlersTest.end() + }) + + positionBatchHandlerTest.test('registerAllHandlers should', registerPositionHandlerTest => { + registerPositionHandlerTest.test('registerPositionHandler throw error when there is an error getting KafkaConfig', async test => { + try { + await Consumer.createHandler(topicName, config, command) + Kafka.transformGeneralTopicName.returns(topicName) + Kafka.getKafkaConfig.throws(new Error()) + + await allTransferHandlers.registerPositionHandler() + test.fail('Error not thrown') + test.end() + } catch (e) { + test.pass('Error thrown') + test.end() + } + }) + + registerPositionHandlerTest.test('registerPositionHandler registers handler and returns true ', async test => { + try { + await Consumer.createHandler(topicName, config, command) + Kafka.transformGeneralTopicName.returns(topicName) + Kafka.getKafkaConfig.returns(config) + + const result = await allTransferHandlers.registerPositionHandler() + test.equal(result, true, 'Result should be true') + test.end() + } catch (e) { + test.fail('Error should not be thrown') + test.end() + } + }) + + registerPositionHandlerTest.end() + }) + + positionBatchHandlerTest.test('positions should', positionsTest => { + positionsTest.test('process messages and commit Kafka offset and DB transaction', async test => { + // Arrange + await Consumer.createHandler(topicName, config, command) + Kafka.transformGeneralTopicName.returns(topicName) + Kafka.getKafkaConfig.returns(config) + Kafka.proceed.returns(true) + + // Act + try { + await allTransferHandlers.positions(null, messages) + test.ok(BatchPositionModel.startDbTransaction.calledOnce, 'startDbTransaction should be called once') + // Need an easier way to do partial matching... + delete BinProcessor.processBins.getCall(0).args[0][1001].commit[0].histTimerMsgEnd + delete BinProcessor.processBins.getCall(0).args[0][1001].prepare[0].histTimerMsgEnd + delete BinProcessor.processBins.getCall(0).args[0][1001].prepare[1].histTimerMsgEnd + delete BinProcessor.processBins.getCall(0).args[0][1002].commit[0].histTimerMsgEnd + delete BinProcessor.processBins.getCall(0).args[0][1002].prepare[0].histTimerMsgEnd + test.deepEqual(BinProcessor.processBins.getCall(0).args[0][1001].commit, expectedBins[1001].commit) + test.deepEqual(BinProcessor.processBins.getCall(0).args[0][1001].prepare, expectedBins[1001].prepare) + test.deepEqual(BinProcessor.processBins.getCall(0).args[0][1002].commit, expectedBins[1002].commit) + test.deepEqual(BinProcessor.processBins.getCall(0).args[0][1002].prepare, expectedBins[1002].prepare) + test.equal(BinProcessor.processBins.getCall(0).args[1], trxStub) + const expectedLastMessageToCommit = messages[messages.length - 1] + test.equal(Kafka.proceed.getCall(0).args[1].message.offset, expectedLastMessageToCommit.offset, 'kafkaProceed should be called with the correct offset') + test.equal(SpanStub.audit.callCount, 5, 'span.audit should be called five times') + test.equal(SpanStub.finish.callCount, 5, 'span.finish should be called five times') + test.ok(trxStub.commit.calledOnce, 'trx.commit should be called once') + test.ok(trxStub.rollback.notCalled, 'trx.rollback should not be called') + test.equal(Kafka.produceGeneralMessage.callCount, 5, 'produceGeneralMessage should be five times to produce kafka notification events') + test.end() + } catch (err) { + test.fail('Error should not be thrown') + test.end() + } + }) + + positionsTest.test('rollback DB transaction and audit error if BinProcessor fails', async test => { + // Arrange + BinProcessor.processBins.rejects(new Error('BinProcessor failed')) + await Consumer.createHandler(topicName, config, command) + Kafka.transformGeneralTopicName.returns(topicName) + Kafka.getKafkaConfig.returns(config) + Kafka.proceed.returns(true) + + // Act + try { + await allTransferHandlers.positions(null, messages) + test.ok(BatchPositionModel.startDbTransaction.calledOnce, 'startDbTransaction should be called once') + // Need an easier way to do partial matching... + delete BinProcessor.processBins.getCall(0).args[0][1001].commit[0].histTimerMsgEnd + delete BinProcessor.processBins.getCall(0).args[0][1001].prepare[0].histTimerMsgEnd + delete BinProcessor.processBins.getCall(0).args[0][1001].prepare[1].histTimerMsgEnd + delete BinProcessor.processBins.getCall(0).args[0][1002].commit[0].histTimerMsgEnd + delete BinProcessor.processBins.getCall(0).args[0][1002].prepare[0].histTimerMsgEnd + test.deepEqual(BinProcessor.processBins.getCall(0).args[0][1001].commit, expectedBins[1001].commit) + test.deepEqual(BinProcessor.processBins.getCall(0).args[0][1001].prepare, expectedBins[1001].prepare) + test.deepEqual(BinProcessor.processBins.getCall(0).args[0][1002].commit, expectedBins[1002].commit) + test.deepEqual(BinProcessor.processBins.getCall(0).args[0][1002].prepare, expectedBins[1002].prepare) + test.equal(BinProcessor.processBins.getCall(0).args[1], trxStub) + test.ok(Kafka.proceed.notCalled, 'kafkaProceed should not be called') + test.equal(SpanStub.audit.callCount, 5, 'span.audit should be called five times') + test.equal(SpanStub.error.callCount, 5, 'span.error should be called five times') + test.equal(SpanStub.finish.callCount, 5, 'span.finish should be called five times') + test.ok(Kafka.produceGeneralMessage.notCalled, 'produceGeneralMessage should not be called') + test.ok(trxStub.rollback.calledOnce, 'trx.rollback should be called once') + test.ok(trxStub.commit.notCalled, 'trx.commit should not be called') + test.end() + } catch (err) { + test.fail('Error should not be thrown') + test.end() + } + }) + + positionsTest.test('throw error if error pass in as input', async test => { + // Arrange + await Consumer.createHandler(topicName, config, command) + Kafka.transformGeneralTopicName.returns(topicName) + Kafka.getKafkaConfig.returns(config) + Kafka.proceed.returns(true) + + // Act + try { + await allTransferHandlers.positions(new Error(), messages) + test.fail('Error should be thrown') + } catch (err) { + test.pass('Error was thrown') + test.end() + } + }) + + positionsTest.test('process messages if input is not an array but a message', async test => { + // Arrange + await Consumer.createHandler(topicName, config, command) + Kafka.transformGeneralTopicName.returns(topicName) + Kafka.getKafkaConfig.returns(config) + Kafka.proceed.returns(true) + + BinProcessor.processBins.resolves({ + notifyMessages: [{ binItem: { message: messages[0], span: SpanStub }, message: { metadata: { event: { state: 'success' } } } }] + }) + + // Act + try { + await allTransferHandlers.positions(null, messages[0]) + test.ok(BatchPositionModel.startDbTransaction.calledOnce, 'startDbTransaction should be called once') + // Need an easier way to do partial matching... + delete BinProcessor.processBins.getCall(0).args[0][1001].prepare[0].histTimerMsgEnd + test.deepEqual(BinProcessor.processBins.getCall(0).args[0][1001].prepare[0], expectedBins[1001].prepare[0]) + test.equal(BinProcessor.processBins.getCall(0).args[1], trxStub) + const expectedLastMessageToCommit = messages[messages.length - 1] + test.equal(Kafka.proceed.getCall(0).args[1].message.offset, expectedLastMessageToCommit.offset, 'kafkaProceed should be called with the correct offset') + test.equal(SpanStub.audit.callCount, 1, 'span.audit should be called one time') + test.equal(SpanStub.finish.callCount, 1, 'span.finish should be called one time') + test.ok(trxStub.commit.calledOnce, 'trx.commit should be called once') + test.ok(trxStub.rollback.notCalled, 'trx.rollback should not be called') + test.equal(Kafka.produceGeneralMessage.callCount, 1, 'produceGeneralMessage should be one time to produce kafka notification events') + test.end() + } catch (err) { + Logger.info(err) + test.fail('Error should not be thrown') + test.end() + } + }) + + positionsTest.test('calls Kafka.produceGeneralMessage with correct eventStatus if event is a failure event', async test => { + // Arrange + await Consumer.createHandler(topicName, config, command) + Kafka.transformGeneralTopicName.returns(topicName) + Kafka.getKafkaConfig.returns(config) + Kafka.proceed.returns(true) + + BinProcessor.processBins.resolves({ + notifyMessages: [{ binItem: { message: messages[0], span: SpanStub }, message: { metadata: { event: { state: { status: 'error' } } } } }] + }) + + // Act + try { + await allTransferHandlers.positions(null, messages[0]) + test.ok(BatchPositionModel.startDbTransaction.calledOnce, 'startDbTransaction should be called once') + // Need an easier way to do partial matching... + delete BinProcessor.processBins.getCall(0).args[0][1001].prepare[0].histTimerMsgEnd + test.deepEqual(BinProcessor.processBins.getCall(0).args[0][1001].prepare[0], expectedBins[1001].prepare[0]) + test.equal(BinProcessor.processBins.getCall(0).args[1], trxStub) + const expectedLastMessageToCommit = messages[messages.length - 1] + test.equal(Kafka.proceed.getCall(0).args[1].message.offset, expectedLastMessageToCommit.offset, 'kafkaProceed should be called with the correct offset') + test.equal(SpanStub.audit.callCount, 1, 'span.audit should be called one time') + test.equal(SpanStub.finish.callCount, 1, 'span.finish should be called one time') + test.ok(trxStub.commit.calledOnce, 'trx.commit should be called once') + test.ok(trxStub.rollback.notCalled, 'trx.rollback should not be called') + test.equal(Kafka.produceGeneralMessage.callCount, 1, 'produceGeneralMessage should be one time to produce kafka notification events') + test.equal(Kafka.produceGeneralMessage.getCall(0).args[5], Enum.Events.EventStatus.FAILURE, 'produceGeneralMessage should be called with eventStatus as Enum.Events.EventStatus.FAILURE') + test.end() + } catch (err) { + Logger.info(err) + test.fail('Error should not be thrown') + test.end() + } + }) + + positionsTest.end() + }) + + positionBatchHandlerTest.end() +}) diff --git a/test/unit/models/position/batch.test.js b/test/unit/models/position/batch.test.js new file mode 100644 index 000000000..e5168a502 --- /dev/null +++ b/test/unit/models/position/batch.test.js @@ -0,0 +1,362 @@ +/***** + License + -------------- + Copyright © 2017 Bill & Melinda Gates Foundation + The Mojaloop files are made available by the Bill & Melinda Gates Foundation under the Apache License, Version 2.0 (the "License") and you may not use these files except in compliance with the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, the Mojaloop files are distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + Contributors + -------------- + This is the official list of the Mojaloop project contributors for this file. + Names of the original copyright holders (individuals or organizations) + should be listed with a '*' in the first column. People who have + contributed from an organization can be listed under the organization + that actually holds the copyright for their contributions (see the + Gates Foundation organization for an example). Those individuals should have + their names indented and be marked with a '-'. Email address can be added + optionally within square brackets . + * Gates Foundation + - Name Surname + + * Kevin Leyow + -------------- + ******/ + +'use strict' + +const Test = require('tapes')(require('tape')) +const Sinon = require('sinon') +const Db = require('../../../../src/lib/db') +const Model = require('../../../../src/models/position/batch') +const Logger = require('@mojaloop/central-services-logger') + +Test('Batch model', async (positionBatchTest) => { + let sandbox + + positionBatchTest.beforeEach(t => { + sandbox = Sinon.createSandbox() + Db.participantCurrency = {} + Db.participantPosition = {} + Db.transferStateChange = {} + Db.participantPositionChange = {} + + Db.from = (table) => { + return Db[table] + } + + t.end() + }) + + positionBatchTest.afterEach(t => { + sandbox.restore() + t.end() + }) + + await positionBatchTest.test('startDbTransaction returns transaction', async (test) => { + try { + sandbox.stub(Db, 'getKnex') + const knexStub = sandbox.stub() + const trxStub = sandbox.stub() + trxStub.commit = sandbox.stub() + knexStub.transaction = sandbox.stub().returns(trxStub) + Db.getKnex.returns(knexStub) + + const trx = await Model.startDbTransaction() + test.pass('completed successfully') + test.ok(trx) + test.end() + } catch (err) { + Logger.error(`getAllParticipantCurrency failed with error - ${err}`) + test.fail() + test.end() + } + }) + + await positionBatchTest.test('getAllParticipantCurrency with trx', async (test) => { + try { + sandbox.stub(Db, 'getKnex') + const knexStub = sandbox.stub() + const trxStub = sandbox.stub() + trxStub.commit = sandbox.stub() + knexStub.transaction = sandbox.stub().callsArgWith(0, trxStub) + Db.getKnex.returns(knexStub) + + knexStub.returns({ + transacting: sandbox.stub().returns({ + select: sandbox.stub().returns({}) + }) + }) + + await Model.getAllParticipantCurrency(trxStub) + test.pass('completed successfully') + test.ok(knexStub.withArgs('participantCurrency').calledOnce, 'knex called with participantCurrency once') + test.end() + } catch (err) { + Logger.error(`getAllParticipantCurrency failed with error - ${err}`) + test.fail() + test.end() + } + }) + + await positionBatchTest.test('getAllParticipantCurrency without trx', async (test) => { + try { + sandbox.stub(Db, 'getKnex') + const knexStub = sandbox.stub() + Db.getKnex.returns(knexStub) + + knexStub.returns({ + select: sandbox.stub().returns({}) + }) + + await Model.getAllParticipantCurrency() + test.pass('completed successfully') + test.ok(knexStub.withArgs('participantCurrency').calledOnce, 'knex called with participantCurrency once') + test.end() + } catch (err) { + Logger.error(`getAllParticipantCurrency failed with error - ${err}`) + test.fail() + test.end() + } + }) + + await positionBatchTest.test('getLatestTransferStateChangesByTransferIdList', async (test) => { + try { + sandbox.stub(Db, 'getKnex') + + const knexStub = sandbox.stub() + const trxStub = sandbox.stub() + trxStub.commit = sandbox.stub() + knexStub.transaction = sandbox.stub().callsArgWith(0, trxStub) + Db.getKnex.returns(knexStub) + + knexStub.returns({ + transacting: sandbox.stub().returns({ + whereIn: sandbox.stub().returns({ + orderBy: sandbox.stub().returns({ + select: sandbox.stub().returns([{ transferId: 1 }, { transferId: 2 }, { transferId: 2 }]) + }) + }) + }) + }) + + await Model.getLatestTransferStateChangesByTransferIdList(trxStub, [1, 2]) + test.pass('completed successfully') + test.ok(knexStub.withArgs('transferStateChange').calledOnce, 'knex called with transferStateChange once') + test.end() + } catch (err) { + Logger.error(`getLatestTransferStateChangesByTransferIdList failed with error - ${err}`) + test.fail() + test.end() + } + }) + + await positionBatchTest.test('getLatestTransferStateChangesByTransferIdList should re throw db error', async (test) => { + try { + sandbox.stub(Db, 'getKnex') + + const knexStub = sandbox.stub() + const trxStub = sandbox.stub() + trxStub.commit = sandbox.stub() + knexStub.transaction = sandbox.stub().callsArgWith(0, trxStub) + Db.getKnex.returns(knexStub) + + knexStub.returns({ + transacting: sandbox.stub().returns({ + whereIn: sandbox.stub().returns({ + orderBy: sandbox.stub().returns({ + select: sandbox.stub().throws(new Error()) + }) + }) + }) + }) + + try { + await Model.getLatestTransferStateChangesByTransferIdList(trxStub, [1, 2]) + test.fail('should throw error') + } catch (err) { + test.pass('completed successfully') + } + test.end() + } catch (err) { + Logger.error(`getLatestTransferStateChangesByTransferIdList failed with error - ${err}`) + test.fail() + test.end() + } + }) + + await positionBatchTest.test('getPositionsByAccountIdsForUpdate', async (test) => { + try { + sandbox.stub(Db, 'getKnex') + + const knexStub = sandbox.stub() + const trxStub = sandbox.stub() + trxStub.commit = sandbox.stub() + knexStub.transaction = sandbox.stub().callsArgWith(0, trxStub) + Db.getKnex.returns(knexStub) + + knexStub.returns({ + transacting: sandbox.stub().returns({ + whereIn: sandbox.stub().returns({ + forUpdate: sandbox.stub().returns({ + select: sandbox.stub().returns([{ participantCurrencyId: 1 }, { participantCurrencyId: 2 }]) + }) + }) + }) + }) + await Model.getPositionsByAccountIdsForUpdate(trxStub, [1, 2]) + test.pass('completed successfully') + test.ok(knexStub.withArgs('participantPosition').calledOnce, 'knex called with transferStateChange once') + test.end() + } catch (err) { + Logger.error(`getPositionsByAccountIdsForUpdate failed with error - ${err}`) + test.fail() + test.end() + } + }) + + await positionBatchTest.test('updateParticipantPosition should run when participantPositionReservedValue is not passed', async (test) => { + try { + sandbox.stub(Db, 'getKnex') + + const knexStub = sandbox.stub() + const trxStub = sandbox.stub() + trxStub.commit = sandbox.stub() + knexStub.transaction = sandbox.stub().callsArgWith(0, trxStub) + Db.getKnex.returns(knexStub) + + knexStub.returns({ + transacting: sandbox.stub().returns({ + where: sandbox.stub().returns({ + update: sandbox.stub().returns({ + }) + }) + }) + }) + + await Model.updateParticipantPosition(trxStub, 1, 1) + test.pass('completed successfully') + test.ok(knexStub.withArgs('participantPosition').calledOnce, 'knex called with transferStateChange once') + test.end() + } catch (err) { + Logger.error(`updateParticipantPosition failed with error - ${err}`) + test.fail() + test.end() + } + }) + + await positionBatchTest.test('updateParticipantPosition should run when participantPositionReservedValue is null', async (test) => { + try { + sandbox.stub(Db, 'getKnex') + + const knexStub = sandbox.stub() + const trxStub = sandbox.stub() + trxStub.commit = sandbox.stub() + knexStub.transaction = sandbox.stub().callsArgWith(0, trxStub) + Db.getKnex.returns(knexStub) + + knexStub.returns({ + transacting: sandbox.stub().returns({ + where: sandbox.stub().returns({ + update: sandbox.stub().returns({ + }) + }) + }) + }) + + await Model.updateParticipantPosition(trxStub, 1, 1, null) + test.pass('completed successfully') + test.ok(knexStub.withArgs('participantPosition').calledOnce, 'knex called with transferStateChange once') + test.end() + } catch (err) { + Logger.error(`updateParticipantPosition failed with error - ${err}`) + test.fail() + test.end() + } + }) + + await positionBatchTest.test('updateParticipantPosition participantPositionReservedValue set', async (test) => { + try { + sandbox.stub(Db, 'getKnex') + + const knexStub = sandbox.stub() + const trxStub = sandbox.stub() + trxStub.commit = sandbox.stub() + knexStub.transaction = sandbox.stub().callsArgWith(0, trxStub) + Db.getKnex.returns(knexStub) + + knexStub.returns({ + transacting: sandbox.stub().returns({ + where: sandbox.stub().returns({ + update: sandbox.stub().returns({ + }) + }) + }) + }) + + await Model.updateParticipantPosition(trxStub, 1, 1, 1) + test.pass('completed successfully') + test.ok(knexStub.withArgs('participantPosition').calledOnce, 'knex called with transferStateChange once') + test.end() + } catch (err) { + Logger.error(`updateParticipantPosition failed with error - ${err}`) + test.fail() + test.end() + } + }) + + await positionBatchTest.test('bulkInsertTransferStateChanges', async (test) => { + try { + sandbox.stub(Db, 'getKnex') + + const knexStub = sandbox.stub() + const trxStub = sandbox.stub() + trxStub.commit = sandbox.stub() + knexStub.batchInsert = sandbox.stub().returns({ + transacting: sandbox.stub().returns({ + where: sandbox.stub().returns({ + update: sandbox.stub().returns({}) + }) + }) + }) + Db.getKnex.returns(knexStub) + + await Model.bulkInsertTransferStateChanges(trxStub, 1, 1, null) + test.pass('completed successfully') + test.ok(knexStub.batchInsert.withArgs('transferStateChange').calledOnce, 'knex called with transferStateChange once') + test.end() + } catch (err) { + Logger.error(`bulkInsertTransferStateChanges failed with error - ${err}`) + test.fail() + test.end() + } + }) + + await positionBatchTest.test('bulkInsertParticipantPositionChanges', async (test) => { + try { + sandbox.stub(Db, 'getKnex') + + const knexStub = sandbox.stub() + const trxStub = sandbox.stub() + trxStub.commit = sandbox.stub() + knexStub.batchInsert = sandbox.stub().returns({ + transacting: sandbox.stub().returns({ + where: sandbox.stub().returns({ + update: sandbox.stub().returns({}) + }) + }) + }) + Db.getKnex.returns(knexStub) + + await Model.bulkInsertParticipantPositionChanges(trxStub, 1, 1, null) + test.pass('completed successfully') + test.ok(knexStub.batchInsert.withArgs('participantPositionChange').calledOnce, 'knex called with transferStateChange once') + test.end() + } catch (err) { + Logger.error(`bulkInsertParticipantPositionChanges failed with error - ${err}`) + test.fail() + test.end() + } + }) + + positionBatchTest.end() +}) diff --git a/test/unit/models/position/batchCached.test.js b/test/unit/models/position/batchCached.test.js new file mode 100644 index 000000000..14e820faf --- /dev/null +++ b/test/unit/models/position/batchCached.test.js @@ -0,0 +1,145 @@ +/***** + License + -------------- + Copyright © 2017 Bill & Melinda Gates Foundation + The Mojaloop files are made available by the Bill & Melinda Gates Foundation under the Apache License, Version 2.0 (the "License") and you may not use these files except in compliance with the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, the Mojaloop files are distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + Contributors + -------------- + This is the official list of the Mojaloop project contributors for this file. + Names of the original copyright holders (individuals or organizations) + should be listed with a '*' in the first column. People who have + contributed from an organization can be listed under the organization + that actually holds the copyright for their contributions (see the + Gates Foundation organization for an example). Those individuals should have + their names indented and be marked with a '-'. Email address can be added + optionally within square brackets . + * Gates Foundation + - Name Surname + + * Kevin Leyow + -------------- + ******/ + +'use strict' + +const Test = require('tapes')(require('tape')) +const Sinon = require('sinon') +const BatchUncached = require('../../../../src/models/position/batch') +const Cache = require('../../../../src/lib/cache') +const Model = require('../../../../src/models/position/batchCached') + +Test('Batch cached model', async (batchCachedTest) => { + let sandbox + + const participantCurrencyFixtures = [ + { + participantCurrencyId: 7, + participantId: 2, + currencyId: 'USD', + ledgerAccountTypeId: 1, + isActive: 1, + createdDate: '2023-08-17T09:36:27.000Z', + createdBy: 'unknown' + }, + { + participantCurrencyId: 15, + participantId: 3, + currencyId: 'USD', + ledgerAccountTypeId: 1, + isActive: 1, + createdDate: '2023-08-17T09:36:37.000Z', + createdBy: 'unknown' + }, + { + participantCurrencyId: 16, + participantId: 3, + currencyId: 'USD', + ledgerAccountTypeId: 1, + isActive: 1, + createdDate: '2023-08-17T09:36:37.000Z', + createdBy: 'unknown' + } + ] + + batchCachedTest.beforeEach(t => { + sandbox = Sinon.createSandbox() + sandbox.stub(Cache) + sandbox.stub(BatchUncached) + BatchUncached.getAllParticipantCurrency.returns(participantCurrencyFixtures) + t.end() + }) + + batchCachedTest.afterEach(t => { + sandbox.restore() + t.end() + }) + + await batchCachedTest.test('initializes cache correctly', async (test) => { + const cacheClient = { + createKey: sandbox.stub().returns({}) + } + Cache.registerCacheClient.returns(cacheClient) + + // initialize calls registerCacheClient and createKey + test.notOk(Cache.registerCacheClient.calledOnce) + test.notOk(cacheClient.createKey.calledOnce) + await Model.initialize() + test.ok(Cache.registerCacheClient.calledOnce) + test.ok(cacheClient.createKey.calledOnce) + + test.end() + }) + + await batchCachedTest.test('getParticipantCurrencyByIds(), getParticipantCurrencyByParticipantIds() work', async (test) => { + let cache = null + const cacheClient = { + createKey: sandbox.stub().returns({}), + get: () => cache, + set: (key, x) => { + cache = { item: x } // the cache retuns {item: } structure + } + } + Cache.registerCacheClient.returns(cacheClient) + await Model.initialize() + + // check getParticipantCurrencyByIds() + const participantCurrencyById = await Model.getParticipantCurrencyByIds(null, [7, 15]) + test.equal(JSON.stringify(participantCurrencyById), JSON.stringify([participantCurrencyFixtures[0], participantCurrencyFixtures[1]]), 'getParticipantCurrencyByIds() works') + + // check getParticipantCurrencyByParticipantIds() + const participantByName = await Model.getParticipantCurrencyByParticipantIds(null, [participantCurrencyFixtures[1].participantId]) + test.equal(JSON.stringify(participantByName), JSON.stringify([participantCurrencyFixtures[1], participantCurrencyFixtures[2]]), 'getParticipantCurrencyByParticipantIds() works') + + test.end() + }) + + await batchCachedTest.test('getParticipantCurrencyByIds(), getParticipantCurrencyByParticipantIds() fail when error thrown', async (test) => { + const cacheClient = { + createKey: sandbox.stub().returns({}), + get: () => null + } + Cache.registerCacheClient.returns(cacheClient) + await Model.initialize() + BatchUncached.getAllParticipantCurrency.throws(new Error()) + + try { + await Model.getParticipantCurrencyByIds(null, [7]) + test.fail('Error not thrown') + } catch (err) { + test.pass('Error thrown') + } + + try { + await Model.getParticipantCurrencyByParticipantIds(null, [participantCurrencyFixtures[1].participantId]) + test.fail('Error not thrown') + } catch (err) { + test.pass('Error thrown') + } + + test.end() + }) + + await batchCachedTest.end() +}) diff --git a/test/unit/shared/setup.test.js b/test/unit/shared/setup.test.js index 41243d2f0..81e646356 100644 --- a/test/unit/shared/setup.test.js +++ b/test/unit/shared/setup.test.js @@ -103,6 +103,9 @@ Test('setup', setupTest => { positions: { registerPositionHandler: sandbox.stub().returns(Promise.resolve()) }, + positionsBatch: { + registerPositionHandler: sandbox.stub().returns(Promise.resolve()) + }, timeouts: { registerAllHandlers: sandbox.stub().returns(Promise.resolve()), registerTimeoutHandler: sandbox.stub().returns(Promise.resolve()) @@ -503,6 +506,12 @@ Test('setup', setupTest => { fspList } + const positionBatchHandler = { + type: 'positionbatch', + enabled: true, + fspList + } + const fulfilHandler = { type: 'fulfil', enabled: true @@ -546,6 +555,7 @@ Test('setup', setupTest => { const modulesList = [ prepareHandler, positionHandler, + positionBatchHandler, fulfilHandler, timeoutHandler, adminHandler, @@ -561,6 +571,7 @@ Test('setup', setupTest => { test.ok(RegisterHandlersStub.transfers.registerPrepareHandler.called) test.ok(RegisterHandlersStub.transfers.registerFulfilHandler.called) test.ok(RegisterHandlersStub.positions.registerPositionHandler.called) + test.ok(RegisterHandlersStub.positionsBatch.registerPositionHandler.called) test.ok(RegisterHandlersStub.timeouts.registerTimeoutHandler.called) test.ok(RegisterHandlersStub.admin.registerAdminHandlers.called) test.ok(RegisterHandlersStub.transfers.registerGetHandler.called)