diff --git a/.circleci/config.yml b/.circleci/config.yml index 6ebb6411..4ed3bc9a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -366,12 +366,8 @@ jobs: # command: | # docker-compose pull - run: - name: Start docker-compose - command: | - docker-compose up -d - - # Check straight away to see if any containers have exited - docker-compose ps + name: Start docker compose + command: npm run dc:up - run: name: Wait for Health Services command: | @@ -973,4 +969,4 @@ workflows: only: /v[0-9]+(\.[0-9]+)*\-snapshot+((\.[0-9]+)?)/ branches: ignore: - - /.*/ \ No newline at end of file + - /.*/ diff --git a/.dockerignore b/.dockerignore index 54daf0f5..09228099 100644 --- a/.dockerignore +++ b/.dockerignore @@ -9,4 +9,4 @@ admin.Dockerfile Dockerfile LICENSE README.md -.circleci/ \ No newline at end of file +.circleci/ diff --git a/.gitignore b/.gitignore index 185bbb4b..2fe32979 100644 --- a/.gitignore +++ b/.gitignore @@ -58,7 +58,7 @@ typings/ .yarn-integrity # dotenv environment variables file -.env +#.env # next.js build output .next @@ -79,3 +79,6 @@ devspace* # General Ignore *IGNORE* + +# macOS +.DS_Store diff --git a/.ncurc.yaml b/.ncurc.yaml index 47d8499e..9cc95c53 100644 --- a/.ncurc.yaml +++ b/.ncurc.yaml @@ -2,5 +2,6 @@ reject: [ # TODO: v6+ (ref: https://github.com/sindresorhus/get-port/releases/tag/v6.0.0) is an ESM library and thus not compatible with CommonJS. Future story needed to resolve. "get-port", - "@mojaloop/sdk-standard-components" + "@mojaloop/sdk-standard-components", + "@mojaloop/central-services-shared" ## need to remove before merging proxyCache functionality to main ] diff --git a/.nvmrc b/.nvmrc index 561a1e9a..a9d08739 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -18.20.3 +18.19.0 diff --git a/Dockerfile b/Dockerfile index 8be2eeb9..4d25c963 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,19 +6,19 @@ ARG NODE_VERSION=lts-alpine # export NODE_VERSION="$(cat .nvmrc)-alpine" \ # docker build \ # --build-arg NODE_VERSION=$NODE_VERSION \ -# -t mojaloop/sdk-scheme-adapter:local \ +# -t mojaloop/account-lookup-service:local \ # . \ # # Build Image -FROM node:${NODE_VERSION} as builder +FROM node:${NODE_VERSION} AS builder WORKDIR /opt/app RUN apk --no-cache add git -RUN apk add --no-cache -t build-dependencies make gcc g++ python3 libtool openssl-dev autoconf automake bash \ +RUN apk add --no-cache -t build-dependencies make gcc g++ py3-setuptools python3 libtool openssl-dev autoconf automake bash \ && cd $(npm root -g)/npm -COPY package.json package-lock.json* /opt/app/ +COPY package.json package-lock.json /opt/app/ RUN npm ci @@ -26,7 +26,6 @@ COPY src /opt/app/src COPY config /opt/app/config COPY migrations /opt/app/migrations COPY seeds /opt/app/seeds -COPY test /opt/app/test FROM node:${NODE_VERSION} WORKDIR /opt/app @@ -36,7 +35,7 @@ RUN mkdir ./logs && touch ./logs/combined.log RUN ln -sf /dev/stdout ./logs/combined.log # Create a non-root user: ml-user -RUN adduser -D ml-user +RUN adduser -D ml-user USER ml-user COPY --chown=ml-user --from=builder /opt/app . diff --git a/LICENSE.md b/LICENSE.md index 0142ad08..7515cc5d 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -2,9 +2,8 @@ Copyright © 2020-2024 Mojaloop Foundation -The Mojaloop files are made available by the Mojaloop Foundation under the Apache License, Version 2.0 -(the "License") and you may not use these files except in compliance with the [License](http://www.apache.org/licenses/LICENSE-2.0). +The Mojaloop files are made available by the Mojaloop 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](http://www.apache.org/licenses/LICENSE-2.0) +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](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. diff --git a/README.md b/README.md index 59d50a98..9134c036 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,8 @@ - [Documentation](http://docs.mojaloop.io/documentation/mojaloop-technical-overview/account-lookup-service/) - [API Swagger Reference](/src/interface/api-swagger.yaml) - [Admin Swagger Referemce](/src/interface/admin-swagger.yaml) +- [Discovery flow diagram using proxy](./docs/Proxy/Discovery.md) +- [Proxy implementation details](./docs/Proxy/Readme.md) ## Database initialisation @@ -149,7 +151,7 @@ If you want to run integration tests in a repetitive manner, you can startup the Start containers required for Integration Tests ```bash - docker-compose -f docker-compose.yml up -d + npm run dc:up ``` Run wait script which will report once all required containers are up and running diff --git a/audit-ci.jsonc b/audit-ci.jsonc index d504e4d6..2a750a20 100644 --- a/audit-ci.jsonc +++ b/audit-ci.jsonc @@ -26,7 +26,8 @@ "GHSA-f9xv-q969-pqx4", "GHSA-7fh5-64p2-3v2j", "GHSA-rm97-x556-q36h", // https://github.com/advisories/GHSA-rm97-x556-q36h - "GHSA-rv95-896h-c2vc" // https://github.com/advisories/GHSA-rv95-896h-c2vc - + "GHSA-rv95-896h-c2vc", // https://github.com/advisories/GHSA-rv95-896h-c2vc + "GHSA-3xgq-45jj-v275", // https://github.com/advisories/GHSA-3xgq-45jj-v275 + "GHSA-rhx6-c78j-4q9w" // https://github.com/advisories/GHSA-rhx6-c78j-4q9w ] } diff --git a/config/default.json b/config/default.json index 2ff792e6..fc86e04e 100644 --- a/config/default.json +++ b/config/default.json @@ -1,4 +1,8 @@ { + "HUB_PARTICIPANT": { + "ID": 1, + "NAME": "Hub" + }, "ADMIN_PORT": 4001, "API_PORT": 4002, "DATABASE": { @@ -51,10 +55,29 @@ "MAX_BYTE_SIZE": 10000000, "EXPIRES_IN_MS": 61000 }, + "PROXY_CACHE": { + "enabled": true, + "type": "redis-cluster", + "proxyConfig": { + "cluster": [ + { "host": "localhost", "port": 6379 } + ] + } + }, "ERROR_HANDLING": { "includeCauseExtension": false, "truncateExtensions": true }, + "HANDLERS": { + "DISABLED": false, + "MONITORING_PORT": 4003, + "TIMEOUT": { + "DISABLED": false, + "TIMEXP": "*/30 * * * * *", + "TIMEZONE": "UTC", + "BATCH_SIZE": 100 + } + }, "SWITCH_ENDPOINT": "http://localhost:3001", "INSTRUMENTATION": { "METRICS": { @@ -74,7 +97,6 @@ "ENDPOINT_SECURITY":{ "JWS": { "JWS_SIGN": false, - "FSPIOP_SOURCE_TO_SIGN": "switch", "JWS_SIGNING_KEY_PATH": "secrets/jwsSigningKey.key" } }, diff --git a/docker-compose.yml b/docker-compose.yml index 479c2466..f5af4084 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,42 +1,81 @@ -version: "3.7" - networks: als-mojaloop-net: name: als-mojaloop-net + +# @see https://uninterrupted.tech/blog/hassle-free-redis-cluster-deployment-using-docker/ +x-redis-node: &REDIS_NODE + image: docker.io/bitnami/redis-cluster:6.2.14 + environment: &REDIS_ENVS + ALLOW_EMPTY_PASSWORD: yes + REDIS_CLUSTER_DYNAMIC_IPS: no + REDIS_CLUSTER_ANNOUNCE_IP: ${REDIS_CLUSTER_ANNOUNCE_IP} + REDIS_NODES: localhost:6379 localhost:6380 localhost:6381 localhost:6382 localhost:6383 localhost:6384 + healthcheck: + test: [ "CMD", "redis-cli", "ping" ] + timeout: 2s + network_mode: host -services: - account-lookup-service: +x-account-lookup-service: &ACCOUNT_LOOKUP_SERVICE image: mojaloop/account-lookup-service:local build: context: . target: builder - container_name: als_account-lookup-service user: root - command: - - "sh" - - "-c" - - "sh /opt/wait-for/wait-for-account-lookup-service.sh && node src/index.js server" - ports: - - "4001:4001" - - "4002:4002" + environment: + LOG_LEVEL: debug networks: - als-mojaloop-net depends_on: - - mysql-als - central-ledger + - proxy + - mysql-als + - redis-node-0 + # - redis volumes: - ./secrets:/opt/app/secrets - ./src:/opt/app/src - ./docker/account-lookup-service/default.json:/opt/app/config/default.json - ./docker/wait-for:/opt/wait-for + extra_hosts: + - "redis-node-0:host-gateway" + +services: + account-lookup-service: + <<: *ACCOUNT_LOOKUP_SERVICE + container_name: als_account-lookup-service + command: + - "sh" + - "-c" + - "sh /opt/wait-for/wait-for-account-lookup-service.sh && node src/index.js server" + ports: + - "4001:4001" + - "4002:4002" healthcheck: test: ["CMD", "sh", "-c" ,"apk --no-cache add curl", ";", "curl", "http://localhost:4001/health"] timeout: 20s retries: 10 interval: 30s - + + account-lookup-service-handlers: + <<: *ACCOUNT_LOOKUP_SERVICE + container_name: als_account-lookup-service-handlers + command: + - "sh" + - "-c" + - "node src/handlers/index.js h --timeout" + depends_on: + - redis-node-0 + ports: + - "4003:4003" + healthcheck: + test: ["CMD", "sh", "-c" ,"apk --no-cache add curl", ";", "curl", "http://localhost:4003/health"] + timeout: 20s + retries: 10 + interval: 30s + central-ledger: - image: mojaloop/central-ledger:latest +# image: mojaloop/central-ledger:latest + image: mojaloop/central-ledger:v17.8.0.160-snapshot.4 container_name: als_central-ledger user: root command: @@ -44,12 +83,12 @@ services: - "-c" - "sh /opt/wait-for/wait-for-central-ledger.sh && node src/api/index.js" ports: - - "3001:3001" + - "${CL_PORT:-3001}:3001" volumes: - ./docker/central-ledger/default.json:/opt/app/config/default.json - ./docker/wait-for:/opt/wait-for environment: - - LOG_LEVEL=info + - LOG_LEVEL=debug - CLEDG_DATABASE_URI=mysql://central_ledger:password@mysql-cl:3306/central_ledger - CLEDG_SIDECAR__DISABLED=true - CLEDG_MONGODB__DISABLED=true @@ -64,6 +103,17 @@ services: retries: 10 interval: 30s + proxy: + build: + context: ./docker/mock-proxy + dockerfile: Dockerfile + restart: always + env_file: ./test/integration/.env + ports: + - "${PROXY_PORT}:${PROXY_PORT}" + networks: + - als-mojaloop-net + kafka: image: johnnypark/kafka-zookeeper:2.3.0 container_name: als_kafka @@ -124,3 +174,67 @@ services: retries: 10 start_period: 40s interval: 30s + + redis-node-0: + <<: *REDIS_NODE + environment: + <<: *REDIS_ENVS + REDIS_CLUSTER_CREATOR: yes + REDIS_PORT_NUMBER: 6379 + depends_on: + - redis-node-1 + - redis-node-2 + - redis-node-3 + - redis-node-4 + - redis-node-5 + redis-node-1: + <<: *REDIS_NODE + environment: + <<: *REDIS_ENVS + REDIS_PORT_NUMBER: 6380 + ports: + - "16380:16380" + redis-node-2: + <<: *REDIS_NODE + environment: + <<: *REDIS_ENVS + REDIS_PORT_NUMBER: 6381 + ports: + - "16381:16381" + redis-node-3: + <<: *REDIS_NODE + environment: + <<: *REDIS_ENVS + REDIS_PORT_NUMBER: 6382 + ports: + - "16382:16382" + redis-node-4: + <<: *REDIS_NODE + environment: + <<: *REDIS_ENVS + REDIS_PORT_NUMBER: 6383 + ports: + - "16383:16383" + redis-node-5: + <<: *REDIS_NODE + environment: + <<: *REDIS_ENVS + REDIS_PORT_NUMBER: 6384 + ports: + - "16384:16384" + +## To be used with proxyCache.type === 'redis' +# redis: +# image: redis:6.2.4-alpine +# restart: "unless-stopped" +# environment: +# <<: *REDIS_ENVS +# REDIS_CLUSTER_CREATOR: yes +# depends_on: +# - redis-node-1 +# - redis-node-2 +# - redis-node-3 +# - redis-node-4 +# - redis-node-5 +# ports: +# - "6379:6379" diff --git a/docker/account-lookup-service/default.json b/docker/account-lookup-service/default.json index b424b93d..b8014087 100644 --- a/docker/account-lookup-service/default.json +++ b/docker/account-lookup-service/default.json @@ -1,4 +1,9 @@ { + "HUB_PARTICIPANT": { + "ID": 1, + "NAME": "Hub" + }, + "LOG_LEVEL": "debug", "ADMIN_PORT": 4001, "API_PORT": 4002, "DATABASE": { @@ -15,16 +20,66 @@ "DESTROY_TIMEOUT_MILLIS": 5000, "IDLE_TIMEOUT_MILLIS": 30000, "REAP_INTERVAL_MILLIS": 1000, - "CREATE_RETRY_INTERVAL_MILLIS": 200, - "DEBUG": false + "CREATE_RETRY_INTERVAL_MILLIS": 200 + }, + "PROTOCOL_VERSIONS": { + "CONTENT": { + "DEFAULT": "1.1", + "VALIDATELIST": [ + "1.1", + "1.0" + ] + }, + "ACCEPT": { + "DEFAULT": "1", + "VALIDATELIST": [ + "1", + "1.0", + "1.1" + ] + } }, "DISPLAY_ROUTES": true, "RUN_MIGRATIONS": true, "CENTRAL_SHARED_ENDPOINT_CACHE_CONFIG": { "expiresIn": 180000, - "generateTimeout": 30000 + "generateTimeout": 30000, + "getDecoratedValue": true + }, + "CENTRAL_SHARED_PARTICIPANT_CACHE_CONFIG": { + "expiresIn": 61000, + "generateTimeout": 30000, + "getDecoratedValue": true + }, + "GENERAL_CACHE_CONFIG": { + "CACHE_ENABLED": false, + "MAX_BYTE_SIZE": 10000000, + "EXPIRES_IN_MS": 61000 + }, + "PROXY_CACHE": { + "enabled": true, + "type": "redis-cluster", + "proxyConfig": { + "cluster": [ + { "host": "redis-node-0", "port": 6379 } + ] + } + }, + "ERROR_HANDLING": { + "includeCauseExtension": false, + "truncateExtensions": true + }, + "HANDLERS": { + "DISABLED": false, + "MONITORING_PORT": 4003, + "TIMEOUT": { + "DISABLED": false, + "TIMEXP": "*/30 * * * * *", + "TIMEZONE": "UTC", + "BATCH_SIZE": 100 + } }, - "SWITCH_ENDPOINT": "http://localhost:3001", + "SWITCH_ENDPOINT": "http://central-ledger:3001", "INSTRUMENTATION": { "METRICS": { "DISABLED": false, @@ -33,19 +88,19 @@ }, "config": { "timeout": 5000, - "prefix": "moja_", + "prefix": "moja_als_", "defaultLabels": { "serviceName": "account-lookup-service" } } } }, - "ENDPOINT_SECURITY":{ + "ENDPOINT_SECURITY": { "JWS": { "JWS_SIGN": false, - "FSPIOP_SOURCE_TO_SIGN": "switch", "JWS_SIGNING_KEY_PATH": "secrets/jwsSigningKey.key" } }, - "API_DOC_ENDPOINTS_ENABLED": true + "API_DOC_ENDPOINTS_ENABLED": true, + "FEATURE_ENABLE_EXTENDED_PARTY_ID_TYPE": false } diff --git a/docker/account-lookup-service/make-default-json.sh b/docker/account-lookup-service/make-default-json.sh new file mode 100755 index 00000000..51345786 --- /dev/null +++ b/docker/account-lookup-service/make-default-json.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +# script to merge local config/default.json and override.json into one file default.json + +jq -s 'reduce .[] as $item ({}; . * $item)' ../../config/default.json ./override.json > ./default.json diff --git a/docker/account-lookup-service/override.json b/docker/account-lookup-service/override.json new file mode 100644 index 00000000..5c1477d6 --- /dev/null +++ b/docker/account-lookup-service/override.json @@ -0,0 +1,15 @@ +{ + "DATABASE": { + "HOST": "mysql-als" + }, + "PROXY_CACHE": { + "type": "redis-cluster", + "proxyConfig": { + "cluster": [ + { "host": "redis-node-0", "port": 6379 } + ] + } + }, + "SWITCH_ENDPOINT": "http://central-ledger:3001", + "LOG_LEVEL": "debug" +} diff --git a/docker/config-modifier/account-lookup-service.js b/docker/config-modifier/account-lookup-service.js index d7430c5e..13ad70c3 100644 --- a/docker/config-modifier/account-lookup-service.js +++ b/docker/config-modifier/account-lookup-service.js @@ -18,5 +18,12 @@ module.exports = { expiresIn: 180000, generateTimeout: 30000, getDecoratedValue: true + }, + PROXY_CACHE: { + type: 'redis', + proxyConfig: { + host: 'redis', + port: 6379 + } } } diff --git a/docker/mock-proxy/Dockerfile b/docker/mock-proxy/Dockerfile new file mode 100644 index 00000000..df5129c0 --- /dev/null +++ b/docker/mock-proxy/Dockerfile @@ -0,0 +1,15 @@ +ARG NODE_VERSION=lts-alpine +ARG USERNAME=ml-user + +FROM node:${NODE_VERSION} +ARG USERNAME + +WORKDIR /opt/app +COPY tsconfig.json package*.json ./ +COPY src ./src +RUN npm ci + +RUN adduser -D ${USERNAME} +USER ${USERNAME} + +CMD [ "npm" , "start" ] diff --git a/docker/mock-proxy/README.md b/docker/mock-proxy/README.md new file mode 100644 index 00000000..4840bf06 --- /dev/null +++ b/docker/mock-proxy/README.md @@ -0,0 +1,21 @@ +## mock-proxy + +This is a simple mock proxy server that can be used to emulate +`inter-scheme-proxy-adatper` functionality in integration tests + +#### Routes + - `GET /history` - returns and array of requests made to the proxy. +It's used to assert some tests scenarios + - `DELETE /history` - cleans up history + - `ALL /echo` - returns request method, body, headers for testing purposes. Inject also some additional headers + - `GET /health` - Returns a JSON object indicating the health status of the server. + - `GET /parties/:type/:id` - Handles requests for party information. Responds with a 202 status and the request headers modified for the hub callback. + - `PUT /parties/:type/:id/error` - Handles error responses for party information. Responds with a 200 status and the request headers modified for the DFSP callback. + - `GET /oracle*` - Returns a JSON object with an empty party list. + +#### Usage in integration tests + 1. In _docker-compose.yml_ file there's a service `proxy` defined. Some env vars are passed using [./test/integration/.env](../../test/integration/.env) file. + 2. Before running tests, we onboard some participants, including `proxy`, using [./test/integration/prepareTestParticipants.js](../../test/integration/prepareTestParticipants.js) script. + 3. Run tests and validate `proxy` calls (using _/history_ endpoint) according test scenarios. + + diff --git a/docker/mock-proxy/package-lock.json b/docker/mock-proxy/package-lock.json new file mode 100644 index 00000000..a5e7f5fa --- /dev/null +++ b/docker/mock-proxy/package-lock.json @@ -0,0 +1,4986 @@ +{ + "name": "mock-proxy", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "mock-proxy", + "version": "0.1.0", + "license": "Apache-2.0", + "dependencies": { + "@mojaloop/central-services-shared": "^18.6.0-snapshot.4", + "@types/node": "^20.14.2", + "axios": "^1.7.2", + "body-parser": "^1.20.2", + "express": "^4.19.2", + "ts-node": "^10.9.2", + "typescript": "^5.4.5" + }, + "devDependencies": { + "@types/body-parser": "^1.19.5", + "@types/express": "^4.17.21" + } + }, + "node_modules/@apidevtools/json-schema-ref-parser": { + "version": "11.6.4", + "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-11.6.4.tgz", + "integrity": "sha512-9K6xOqeevacvweLGik6LnZCb1fBtCOSIWQs8d096XGeqoLKC33UVMGz9+77Gw44KvbH4pKcQPWo4ZpxkXYj05w==", + "dependencies": { + "@jsdevtools/ono": "^7.1.3", + "@types/json-schema": "^7.0.15", + "js-yaml": "^4.1.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/philsturgeon" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", + "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", + "dependencies": { + "@babel/highlight": "^7.24.7", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/code-frame/node_modules/picocolors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", + "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", + "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", + "dependencies": { + "@babel/helper-validator-identifier": "^7.24.7", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/picocolors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/runtime": { + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.8.tgz", + "integrity": "sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA==", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "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==", + "peer": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "peer": true, + "dependencies": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, + "node_modules/@exodus/schemasafe": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@exodus/schemasafe/-/schemasafe-1.3.0.tgz", + "integrity": "sha512-5Aap/GaRupgNx/feGBwLLTVv8OQFfv3pq2lPRzPg9R+IOBnDgghTGW7l7EuVXOvg5cc/xSAlRW8rBrjIC3Nvqw==" + }, + "node_modules/@grpc/grpc-js": { + "version": "1.10.11", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.10.11.tgz", + "integrity": "sha512-3RaoxOqkHHN2c05bwtBNVJmOf/UwMam0rZYtdl7dsRpsvDwcNpv6LkGgzltQ7xVf822LzBoKEPRvf4D7+xeIDw==", + "peer": true, + "dependencies": { + "@grpc/proto-loader": "^0.7.13", + "@js-sdsl/ordered-map": "^4.4.2" + }, + "engines": { + "node": ">=12.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.13", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz", + "integrity": "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==", + "peer": true, + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@hapi/boom": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@hapi/boom/-/boom-10.0.1.tgz", + "integrity": "sha512-ERcCZaEjdH3OgSJlyjVk8pHIFeus91CjKP3v+MpgBNp5IvGzP2l/bRiD78nqYcKPaZdbKkK5vDBVPd2ohHBlsA==", + "dependencies": { + "@hapi/hoek": "^11.0.2" + } + }, + "node_modules/@hapi/catbox": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/@hapi/catbox/-/catbox-12.1.1.tgz", + "integrity": "sha512-hDqYB1J+R0HtZg4iPH3LEnldoaBsar6bYp0EonBmNQ9t5CO+1CqgCul2ZtFveW1ReA5SQuze9GPSU7/aecERhw==", + "dependencies": { + "@hapi/boom": "^10.0.1", + "@hapi/hoek": "^11.0.2", + "@hapi/podium": "^5.0.0", + "@hapi/validate": "^2.0.1" + } + }, + "node_modules/@hapi/catbox-memory": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@hapi/catbox-memory/-/catbox-memory-5.0.1.tgz", + "integrity": "sha512-QWw9nOYJq5PlvChLWV8i6hQHJYfvdqiXdvTupJFh0eqLZ64Xir7mKNi96d5/ZMUAqXPursfNDIDxjFgoEDUqeQ==", + "dependencies": { + "@hapi/boom": "9.x.x", + "@hapi/hoek": "9.x.x" + } + }, + "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/@hapi/catbox-memory/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==" + }, + "node_modules/@hapi/hoek": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-11.0.4.tgz", + "integrity": "sha512-PnsP5d4q7289pS2T2EgGz147BFJ2Jpb4yrEdkpz2IhgEUzos1S7HTl7ezWh1yfYzYlj89KzLdCRkqsP6SIryeQ==" + }, + "node_modules/@hapi/podium": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@hapi/podium/-/podium-5.0.1.tgz", + "integrity": "sha512-eznFTw6rdBhAijXFIlBOMJJd+lXTvqbrBIS4Iu80r2KTVIo4g+7fLy4NKp/8+UnSt5Ox6mJtAlKBU/Sf5080TQ==", + "dependencies": { + "@hapi/hoek": "^11.0.2", + "@hapi/teamwork": "^6.0.0", + "@hapi/validate": "^2.0.1" + } + }, + "node_modules/@hapi/teamwork": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@hapi/teamwork/-/teamwork-6.0.0.tgz", + "integrity": "sha512-05HumSy3LWfXpmJ9cr6HzwhAavrHkJ1ZRCmNE2qJMihdM5YcWreWPfyN0yKT2ZjCM92au3ZkuodjBxOibxM67A==", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@hapi/topo": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-6.0.2.tgz", + "integrity": "sha512-KR3rD5inZbGMrHmgPxsJ9dbi6zEK+C3ZwUwTa+eMwWLz7oijWUTWD2pMSNNYJAU6Qq+65NkxXjqHr/7LM2Xkqg==", + "dependencies": { + "@hapi/hoek": "^11.0.2" + } + }, + "node_modules/@hapi/validate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@hapi/validate/-/validate-2.0.1.tgz", + "integrity": "sha512-NZmXRnrSLK8MQ9y/CMqE9WSspgB9xA41/LlYR0k967aSZebWr4yNrpxIbov12ICwKy4APSlWXZga9jN5p6puPA==", + "dependencies": { + "@hapi/hoek": "^11.0.2", + "@hapi/topo": "^6.0.1" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@js-sdsl/ordered-map": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/@jsdevtools/ono": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", + "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==" + }, + "node_modules/@mojaloop/central-services-error-handling": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/@mojaloop/central-services-error-handling/-/central-services-error-handling-13.0.1.tgz", + "integrity": "sha512-Hl0KBHX30LbF127tgqNK/fdo0hwa6Bt23tb8DesLstYawKtCesJtk9lPuo6jE+dafNeG2QusUwVQyI+7kwAUHQ==", + "peer": true, + "dependencies": { + "lodash": "4.17.21" + }, + "peerDependencies": { + "@mojaloop/sdk-standard-components": ">=18.x.x" + }, + "peerDependenciesMeta": { + "@mojaloop/sdk-standard-components": { + "optional": false + } + } + }, + "node_modules/@mojaloop/central-services-logger": { + "version": "11.3.1", + "resolved": "https://registry.npmjs.org/@mojaloop/central-services-logger/-/central-services-logger-11.3.1.tgz", + "integrity": "sha512-XVU2K5grE1ZcIyxUXeMlvoVkeIcs9y1/0EKxa2Bk5sEbqXUtHuR8jqbAGlwaUIi9T9YWZRJyVC77nOQe/X1teA==", + "peer": true, + "dependencies": { + "@types/node": "^20.12.7", + "parse-strings-in-object": "2.0.0", + "rc": "1.2.8", + "safe-stable-stringify": "^2.4.3", + "winston": "3.13.0" + } + }, + "node_modules/@mojaloop/central-services-metrics": { + "version": "12.0.8", + "resolved": "https://registry.npmjs.org/@mojaloop/central-services-metrics/-/central-services-metrics-12.0.8.tgz", + "integrity": "sha512-eYWX56zMlj0M0bE6qBLzhwDjo0C4LUQLcQW8du3xJ3mhxH0fSmw+Y5wsmuPmUVQZ90EU4S8l39VcXwh6ludLVg==", + "peer": true, + "dependencies": { + "prom-client": "14.2.0" + } + }, + "node_modules/@mojaloop/central-services-shared": { + "version": "18.6.0-snapshot.4", + "resolved": "https://registry.npmjs.org/@mojaloop/central-services-shared/-/central-services-shared-18.6.0-snapshot.4.tgz", + "integrity": "sha512-BsJlv6xWE/9O+3H6MAsrbJhnq1i0r6LffJgEuz0vIQ6IBSpCjQihaYCeM5y96BdyITuQTAfI/sW/oFeSCLbEzQ==", + "dependencies": { + "@hapi/catbox": "12.1.1", + "@hapi/catbox-memory": "5.0.1", + "axios": "1.7.2", + "clone": "2.1.2", + "dotenv": "16.4.5", + "env-var": "7.5.0", + "event-stream": "4.0.1", + "immutable": "4.3.6", + "lodash": "4.17.21", + "mustache": "4.2.0", + "openapi-backend": "5.10.6", + "raw-body": "2.5.2", + "rc": "1.2.8", + "shins": "2.6.0", + "uuid4": "2.0.3", + "widdershins": "^4.0.1", + "yaml": "2.4.5" + }, + "peerDependencies": { + "@mojaloop/central-services-error-handling": ">=13.x.x", + "@mojaloop/central-services-logger": ">=11.x.x", + "@mojaloop/central-services-metrics": ">=12.x.x", + "@mojaloop/event-sdk": ">=14.1.1", + "ajv": "8.x.x", + "ajv-keywords": "5.x.x" + }, + "peerDependenciesMeta": { + "@mojaloop/central-services-error-handling": { + "optional": false + }, + "@mojaloop/central-services-logger": { + "optional": false + }, + "@mojaloop/central-services-metrics": { + "optional": false + }, + "@mojaloop/event-sdk": { + "optional": false + }, + "ajv": { + "optional": false + }, + "ajv-keyboards": { + "optional": false + } + } + }, + "node_modules/@mojaloop/event-sdk": { + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/@mojaloop/event-sdk/-/event-sdk-14.1.1.tgz", + "integrity": "sha512-Cw0aFUNa7dzxrgTkfco/5AMiHuGxguYC4XAbYoX+kPdOW2MmVa9aT6tRpURlYdGhtmfQFVn8c1VViJjBogR/WA==", + "peer": true, + "dependencies": { + "@grpc/grpc-js": "^1.10.9", + "@grpc/proto-loader": "0.7.13", + "brototype": "0.0.6", + "error-callsites": "2.0.4", + "lodash": "4.17.21", + "moment": "2.30.1", + "parse-strings-in-object": "2.0.0", + "protobufjs": "7.3.0", + "rc": "1.2.8", + "serialize-error": "8.1.0", + "traceparent": "1.0.0", + "tslib": "2.6.3", + "uuid4": "2.0.3", + "winston": "3.13.0" + }, + "peerDependencies": { + "@mojaloop/central-services-logger": ">=11.x.x", + "@mojaloop/central-services-stream": ">=11.2.4" + }, + "peerDependenciesMeta": { + "@mojaloop/central-services-logger": { + "optional": false + }, + "@mojaloop/central-services-stream": { + "optional": true + } + } + }, + "node_modules/@mojaloop/sdk-standard-components": { + "version": "18.3.0", + "resolved": "https://registry.npmjs.org/@mojaloop/sdk-standard-components/-/sdk-standard-components-18.3.0.tgz", + "integrity": "sha512-JO4Z3yNXqUCvovqli+vgzzo3ekevd/IDbU5+njXUpkRs8K7w6FwkGfeud8FGS8Yt+Rzp0zSMuEAGDFlGGPMr8A==", + "peer": true, + "dependencies": { + "base64url": "3.0.1", + "fast-safe-stringify": "^2.1.1", + "ilp-packet": "2.2.0", + "jsonwebtoken": "9.0.2", + "jws": "4.0.0" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "peer": true + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "peer": true + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "peer": true + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "peer": true + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "peer": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "peer": true + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", + "peer": true + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "peer": true + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "peer": true + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "peer": true + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==" + }, + "node_modules/@types/body-parser": { + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "dev": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.19.5", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.5.tgz", + "integrity": "sha512-y6W03tvrACO72aijJ5uF02FRq5cgDR9lUxddQ8vyF+GvmjJQqbzDcJngEjURc+ZsG31VI3hODNZJ2URj86pzmg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", + "dev": true + }, + "node_modules/@types/json-schema": { + "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/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.14.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.10.tgz", + "integrity": "sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/qs": { + "version": "6.9.15", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz", + "integrity": "sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==", + "dev": true + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true + }, + "node_modules/@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "dev": true, + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.7", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", + "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", + "dev": true, + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" + } + }, + "node_modules/@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", + "peer": true + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.3", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.3.tgz", + "integrity": "sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ajv": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.16.0.tgz", + "integrity": "sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw==", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.4.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/align-text": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "integrity": "sha512-GrTZLRpmp6wIC2ztrWW9MjjTgSKccffgFagbNDOX95/dcjEcYZibYTeaOntySQLcdw1ztBoFkviiUvTMbb9MYg==", + "dependencies": { + "kind-of": "^3.0.2", + "longest": "^1.0.1", + "repeat-string": "^1.5.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" + }, + "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-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", + "peer": true + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", + "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/base64url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", + "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==", + "peer": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/bath-es5": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/bath-es5/-/bath-es5-3.0.3.tgz", + "integrity": "sha512-PdCioDToH3t84lP40kUFCKWCOCH389Dl1kbC8FGoqOwamxsmqxxnJSXdkTOsPoNHXjem4+sJ+bbNoQm5zeCqxg==" + }, + "node_modules/bignumber.js": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-5.0.0.tgz", + "integrity": "sha512-KWTu6ZMVk9sxlDJQh2YH1UOnfDP8O8TpxUxgQG/vKASoSnEjK9aVuOueFaPcQEYQ5fyNXNTOYwYw3099RYebWg==", + "peer": true, + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bintrees": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.2.tgz", + "integrity": "sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw==", + "peer": true + }, + "node_modules/body-parser": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/brototype": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/brototype/-/brototype-0.0.6.tgz", + "integrity": "sha512-UcQusNAX7nnuXf9tvvLRC6DtZ8/YkDJRtTIbiA5ayb8MehwtSwtkvd5ZTXNLUTTtU6J/yJsi+1LJXqgRz1obwg==", + "peer": true + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "peer": true + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-me-maybe": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz", + "integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==" + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/center-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "integrity": "sha512-Baz3aNe2gd2LP2qk5U+sDk/m4oSuwSDcBfayTCTBoWpfIGO5XFxPmjILQII4NGiZjD6DoDI6kf7gKaxkf7s3VQ==", + "dependencies": { + "align-text": "^0.1.3", + "lazy-cache": "^1.0.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cheerio": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", + "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "htmlparser2": "^8.0.1", + "parse5": "^7.0.0", + "parse5-htmlparser2-tree-adapter": "^7.0.0" + }, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "dependencies": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "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/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "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/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/code-error-fragment": { + "version": "0.0.230", + "resolved": "https://registry.npmjs.org/code-error-fragment/-/code-error-fragment-0.0.230.tgz", + "integrity": "sha512-cadkfKp6932H8UkhzE/gcUqhRMNf8jHzkAN7+5Myabswaghu4xABTgPHDCjW+dBAJxj/SpkTYokpzDqY4pCzQw==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "peer": true, + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "peer": true, + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/colorspace": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", + "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", + "peer": true, + "dependencies": { + "color": "^3.1.3", + "text-hex": "1.0.x" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dependencies": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/compression/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/core-js": { + "version": "3.37.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.37.1.tgz", + "integrity": "sha512-Xn6qmxrQZyB0FFY8E3bgRXei3lWDJHhvI+u0q9TKIYM49G8pAr0FgnnrFRAmsbptZL1yxRADVXn+x5AGsbBfyw==", + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" + }, + "node_modules/cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/cross-spawn/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dereference-json-schema": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/dereference-json-schema/-/dereference-json-schema-0.2.1.tgz", + "integrity": "sha512-uzJsrg225owJyRQ8FNTPHIuBOdSzIZlHhss9u6W8mp7jJldHqGuLv9cULagP/E26QVJDnjtG8U7Dw139mM1ydA==" + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dot": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dot/-/dot-1.1.3.tgz", + "integrity": "sha512-/nt74Rm+PcfnirXGEdhZleTwGC2LMnuKTeeTIlI82xb5loBBoXNYzr2ezCroPSMtilK8EZIfcNZwOcHN+ib1Lg==", + "engines": [ + "node >=0.2.6" + ], + "bin": { + "dottojs": "bin/dot-packer" + } + }, + "node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "peer": true, + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/ejs": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.7.4.tgz", + "integrity": "sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA==", + "hasInstallScript": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", + "peer": true + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/env-var": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/env-var/-/env-var-7.5.0.tgz", + "integrity": "sha512-mKZOzLRN0ETzau2W2QXefbFjo5EF4yWq28OyKb9ICdeNhHJlOE/pHHnz4hdYJ9cNZXcJHo5xN4OT4pzuSHSNvA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/error-callsites": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/error-callsites/-/error-callsites-2.0.4.tgz", + "integrity": "sha512-V877Ch4FC4FN178fDK1fsrHN4I1YQIBdtjKrHh3BUHMnh3SMvwUVrqkaOgDpUuevgSNna0RBq6Ox9SGlxYrigA==", + "peer": true, + "engines": { + "node": ">=6.x" + } + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es6-promise": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", + "integrity": "sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==" + }, + "node_modules/escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-4.0.1.tgz", + "integrity": "sha512-qACXdu/9VHPBzcyhdOWR5/IahhGMf0roTeZJfzz077GwylcDd90yOHLouhmv7GJ5XzPi6ekaQWd8AvPP2nOvpA==", + "dependencies": { + "duplexer": "^0.1.1", + "from": "^0.1.7", + "map-stream": "0.0.7", + "pause-stream": "^0.0.11", + "split": "^1.0.1", + "stream-combiner": "^0.2.2", + "through": "^2.3.8" + } + }, + "node_modules/execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dependencies": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/execa/node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/express": { + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", + "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.2", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.6.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/extensible-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/extensible-error/-/extensible-error-1.0.2.tgz", + "integrity": "sha512-kXU1FiTsGT8PyMKtFM074RK/VBpzwuQJicAHqBpsPDeTXBQiSALPjkjKXlyKdG/GP6lR7bBaEkq8qdoO2geu9g==", + "peer": true + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" + }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", + "peer": true + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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==", + "peer": true + }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/foreach": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.6.tgz", + "integrity": "sha512-k6GAGDyqLe9JaebCsFCoudPPWfihKu8pylYXRlqP1J7ms39iPoTtk2fviNglIeQEwdh0bQeKJ01ZPyuyQvKzwg==" + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/from": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", + "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==" + }, + "node_modules/fs-readfile-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fs-readfile-promise/-/fs-readfile-promise-2.0.1.tgz", + "integrity": "sha512-7+P9eOOMnkIOmtxrBWTzWOBQlE7Nz/cBx9EYTX5hm8DzmZ/Fj9YWeUY2O9G+Q8YblScd1hyEkcmNcZMDj5U8Ug==", + "dependencies": { + "graceful-fs": "^4.1.2" + } + }, + "node_modules/fs-writefile-promise": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fs-writefile-promise/-/fs-writefile-promise-1.0.3.tgz", + "integrity": "sha512-yI+wDwj0FsgX7tyIQJR+EP60R64evMSixtGb9AzGWjJVKlF5tCet95KomfqGBg/aIAG1Dhd6wjCOQe5HbX/qLA==", + "dependencies": { + "mkdirp-promise": "^1.0.0", + "pinkie-promise": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/fs-writefile-promise/node_modules/pinkie": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-1.0.0.tgz", + "integrity": "sha512-VFVaU1ysKakao68ktZm76PIdOhvEfoNNRaGkyLln9Os7r0/MCxqHjHyBM7dT3pgTiBybqiPtpqKfpENwdBp50Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fs-writefile-promise/node_modules/pinkie-promise": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-1.0.0.tgz", + "integrity": "sha512-5mvtVNse2Ml9zpFKkWBpGsTPwm3DKhs+c95prO/F6E7d6DN0FPqxs6LONpLNpyD7Iheb7QN4BbUoKJgo+DnkQA==", + "dependencies": { + "pinkie": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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.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/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-own-enumerable-property-symbols": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", + "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==" + }, + "node_modules/get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "node_modules/grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==" + }, + "node_modules/har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", + "engines": { + "node": ">=4" + } + }, + "node_modules/har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "deprecated": "this library is no longer supported", + "dependencies": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/har-validator/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/har-validator/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "node_modules/has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "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", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "engines": { + "node": "*" + } + }, + "node_modules/highlightjs": { + "version": "9.16.2", + "resolved": "https://registry.npmjs.org/highlightjs/-/highlightjs-9.16.2.tgz", + "integrity": "sha512-FK1vmMj8BbEipEy8DLIvp71t5UsC7n2D6En/UfM/91PCwmOpj6f2iu0Y0coRC62KSRHHC+dquM2xMULV/X7NFg==", + "deprecated": "Use the 'highlight.js' package instead https://npm.im/highlight.js" + }, + "node_modules/htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http2-client": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/http2-client/-/http2-client-1.3.5.tgz", + "integrity": "sha512-EC2utToWl4RKfs5zd36Mxq7nzHHBuomZboI0yYL6Y0RmBgT7Sgkq4rQ0ezFTYoIsSs7Tm9SJe+o2FcAg6GBhGA==" + }, + "node_modules/httpsnippet": { + "version": "1.25.0", + "resolved": "https://registry.npmjs.org/httpsnippet/-/httpsnippet-1.25.0.tgz", + "integrity": "sha512-jobE6S923cLuf5BPG6Jf+oLBRkPzv2RPp0dwOHcWwj/t9FwV/t9hyZ46kpT3Q5DHn9iFNmGhrcmmFUBqyjoTQg==", + "dependencies": { + "chalk": "^1.1.1", + "commander": "^2.9.0", + "debug": "^2.2.0", + "event-stream": "3.3.4", + "form-data": "3.0.0", + "fs-readfile-promise": "^2.0.1", + "fs-writefile-promise": "^1.0.3", + "har-validator": "^5.0.0", + "pinkie-promise": "^2.0.0", + "stringify-object": "^3.3.0" + }, + "bin": { + "httpsnippet": "bin/httpsnippet" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/httpsnippet/node_modules/event-stream": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", + "integrity": "sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==", + "dependencies": { + "duplexer": "~0.1.1", + "from": "~0", + "map-stream": "~0.1.0", + "pause-stream": "0.0.11", + "split": "0.3", + "stream-combiner": "~0.0.4", + "through": "~2.3.1" + } + }, + "node_modules/httpsnippet/node_modules/form-data": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz", + "integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/httpsnippet/node_modules/map-stream": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", + "integrity": "sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==" + }, + "node_modules/httpsnippet/node_modules/split": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", + "integrity": "sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==", + "dependencies": { + "through": "2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/httpsnippet/node_modules/stream-combiner": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", + "integrity": "sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==", + "dependencies": { + "duplexer": "~0.1.1" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ilp-packet": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ilp-packet/-/ilp-packet-2.2.0.tgz", + "integrity": "sha512-QEGqY0HzGrue4r+4GWWe7lB7Xvjij4cyc2XeOTHYmwkO0BjgwzJW85mZJzR9q5HmK8zdFkN6C0CfedAaYiUv9w==", + "peer": true, + "dependencies": { + "bignumber.js": "^5.0.0", + "extensible-error": "^1.0.2", + "long": "^3.2.0", + "oer-utils": "^1.3.2" + } + }, + "node_modules/ilp-packet/node_modules/long": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz", + "integrity": "sha512-ZYvPPOMqUwPoDsbJaR10iQJYnMuZhRTvHYl62ErLIEX7RgFlziSBUUvrt3OVfc47QlHHpzPZYP17g3Fv7oeJkg==", + "peer": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/immutable": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.6.tgz", + "integrity": "sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + }, + "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/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "peer": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "peer": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "node_modules/jgexml": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/jgexml/-/jgexml-0.4.4.tgz", + "integrity": "sha512-j0AzSWT7LXy3s3i1cdv5NZxUtscocwiBxgOLiEBfitCehm8STdXVrcOlbAWsJFLCq1elZYpQlGqA9k8Z+n9iJA==", + "bin": { + "json2xml": "cli/json2xml.js", + "xml2json": "cli/xml2json.js", + "xsd2json": "cli/xsd2json.js" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "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/json-pointer": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/json-pointer/-/json-pointer-0.6.2.tgz", + "integrity": "sha512-vLWcKbOaXlO+jvRy4qNd+TI1QUPZzfJj1tpJ3vAXDych5XJf93ftpUKe5pKCrzyIIwgBJcOcCVRUfqQP25afBw==", + "dependencies": { + "foreach": "^2.0.4" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/json-to-ast": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/json-to-ast/-/json-to-ast-2.1.0.tgz", + "integrity": "sha512-W9Lq347r8tA1DfMvAGn9QNcgYm4Wm7Yc+k8e6vezpMnRT+NHbtlxgNBXRVjXe9YM6eTn6+p/MKOlV/aABJcSnQ==", + "dependencies": { + "code-error-fragment": "0.0.230", + "grapheme-splitter": "^1.0.4" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/jsonpointer": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.1.0.tgz", + "integrity": "sha512-CXcRvMyTlnR53xMcKnuMzfCA5i/nfblTnnr74CZb6C4vG39eu6w51t7nKmU5MfLfbTgGItliNyjO/ciNPDqClg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "peer": true, + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "peer": true, + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jsonwebtoken/node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "peer": true, + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jsonwebtoken/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==", + "peer": true + }, + "node_modules/jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "peer": true, + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "peer": true, + "dependencies": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", + "peer": true + }, + "node_modules/lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha512-RE2g0b5VGZsOCFOCgP7omTRYFqydmZkBwl5oNnQ1lDYC57uyO9KqNnNVxT7COSHTxrRCWVcAVOcbjk+tvh/rgQ==", + "engines": { + "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", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/linkify-it": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz", + "integrity": "sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==", + "dependencies": { + "uc.micro": "^1.0.1" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "peer": true + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "peer": true + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "peer": true + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "peer": true + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "peer": true + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "peer": true + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "peer": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "peer": true + }, + "node_modules/logform": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.6.1.tgz", + "integrity": "sha512-CdaO738xRapbKIMVn2m4F6KTj4j7ooJ8POVnebSgKo3KBz5axNXRAL7ZdRjIV6NOr2Uf4vjtRkxrFETOioCqSA==", + "peer": true, + "dependencies": { + "@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/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "peer": true + }, + "node_modules/long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==", + "peer": true + }, + "node_modules/longest": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "integrity": "sha512-k+yt5n3l48JU4k8ftnKG6V7u32wyH2NfKzeMto9F/QRE0amxy/LayxwlvjjkZEIzqR+19IrtFO8p5kB9QaYUFg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" + }, + "node_modules/map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "dependencies": { + "p-defer": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/map-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", + "integrity": "sha512-C0X0KQmGm3N2ftbTGBhSyuydQ+vV1LC3f3zPvT3RXHXNZrvfPZcoXp/N5DOa8vedX/rTMm2CjTtivFg2STJMRQ==" + }, + "node_modules/markdown-it": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-10.0.0.tgz", + "integrity": "sha512-YWOP1j7UbDNz+TumYP1kpwnP0aEa711cJjrAQrzd0UXlbJfc5aAq0F/PZHjiioqDC1NKgvIMX+o+9Bk7yuM2dg==", + "dependencies": { + "argparse": "^1.0.7", + "entities": "~2.0.0", + "linkify-it": "^2.0.0", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + }, + "bin": { + "markdown-it": "bin/markdown-it.js" + } + }, + "node_modules/markdown-it-attrs": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/markdown-it-attrs/-/markdown-it-attrs-1.2.1.tgz", + "integrity": "sha512-EYYKLF9RvQJx1Etsb6EsBGWL7qNQLpg9BRej5f06+UdX75T5gvldEn7ts6bkLIQqugE15SGn4lw1CXDS1A+XUA==", + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "markdown-it": ">=7.0.1" + } + }, + "node_modules/markdown-it-emoji": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/markdown-it-emoji/-/markdown-it-emoji-1.4.0.tgz", + "integrity": "sha512-QCz3Hkd+r5gDYtS2xsFXmBYrgw6KuWcJZLCEkdfAuwzZbShCmCfta+hwAMq4NX/4xPzkSHduMKgMkkPUJxSXNg==" + }, + "node_modules/markdown-it-lazy-headers": { + "version": "0.1.3", + "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": "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/markdown-it/node_modules/entities": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz", + "integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==" + }, + "node_modules/mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==" + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mem": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", + "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", + "dependencies": { + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^2.0.0", + "p-is-promise": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "peer": true, + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mkdirp-promise": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mkdirp-promise/-/mkdirp-promise-1.1.0.tgz", + "integrity": "sha512-xzB0UZFcW1UGS2xkXeDh39jzTP282lb3Vwp4QzCQYmkTn4ysaV5dBdbkOXmhkcE1TQlZebQlgTceaWvDr3oFgw==", + "deprecated": "This package is broken and no longer maintained. 'mkdirp' itself supports promises now, please switch to that.", + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "mkdirp": ">=0.5.0" + } + }, + "node_modules/mock-json-schema": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/mock-json-schema/-/mock-json-schema-1.1.1.tgz", + "integrity": "sha512-YV23vlsLP1EEOy0EviUvZTluXjLR+rhMzeayP2rcDiezj3RW01MhOSQkbQskdtg0K2fnGas5LKbSXgNjAOSX4A==", + "dependencies": { + "lodash": "^4.17.21" + } + }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "peer": true, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/mustache": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", + "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", + "bin": { + "mustache": "bin/mustache" + } + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-fetch-h2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/node-fetch-h2/-/node-fetch-h2-2.3.0.tgz", + "integrity": "sha512-ofRW94Ab0T4AOh5Fk8t0h8OBWrmjb0SSB20xh1H8YnPV9EJ+f5AMoYSUQ2zgJ4Iq2HAK0I2l5/Nequ8YzFS3Hg==", + "dependencies": { + "http2-client": "^1.2.5" + }, + "engines": { + "node": "4.x || >=6.0.0" + } + }, + "node_modules/node-readfiles": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/node-readfiles/-/node-readfiles-0.2.0.tgz", + "integrity": "sha512-SU00ZarexNlE4Rjdm83vglt5Y9yiQ+XI1XpflWlb7q7UTN1JUItm69xMeiQCTxtTfnzt+83T8Cx+vI2ED++VDA==", + "dependencies": { + "es6-promise": "^3.2.1" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", + "dependencies": { + "path-key": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/oas-kit-common": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/oas-kit-common/-/oas-kit-common-1.0.8.tgz", + "integrity": "sha512-pJTS2+T0oGIwgjGpw7sIRU8RQMcUoKCDWFLdBqKB2BNmGpbBMH2sdqAaOXUg8OzonZHU0L7vfJu1mJFEiYDWOQ==", + "dependencies": { + "fast-safe-stringify": "^2.0.7" + } + }, + "node_modules/oas-linter": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/oas-linter/-/oas-linter-3.2.2.tgz", + "integrity": "sha512-KEGjPDVoU5K6swgo9hJVA/qYGlwfbFx+Kg2QB/kd7rzV5N8N5Mg6PlsoCMohVnQmo+pzJap/F610qTodKzecGQ==", + "dependencies": { + "@exodus/schemasafe": "^1.0.0-rc.2", + "should": "^13.2.1", + "yaml": "^1.10.0" + }, + "funding": { + "url": "https://github.com/Mermade/oas-kit?sponsor=1" + } + }, + "node_modules/oas-linter/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/oas-resolver": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/oas-resolver/-/oas-resolver-2.5.6.tgz", + "integrity": "sha512-Yx5PWQNZomfEhPPOphFbZKi9W93CocQj18NlD2Pa4GWZzdZpSJvYwoiuurRI7m3SpcChrnO08hkuQDL3FGsVFQ==", + "dependencies": { + "node-fetch-h2": "^2.3.0", + "oas-kit-common": "^1.0.8", + "reftools": "^1.1.9", + "yaml": "^1.10.0", + "yargs": "^17.0.1" + }, + "bin": { + "resolve": "resolve.js" + }, + "funding": { + "url": "https://github.com/Mermade/oas-kit?sponsor=1" + } + }, + "node_modules/oas-resolver/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/oas-schema-walker": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/oas-schema-walker/-/oas-schema-walker-1.1.5.tgz", + "integrity": "sha512-2yucenq1a9YPmeNExoUa9Qwrt9RFkjqaMAA1X+U7sbb0AqBeTIdMHky9SQQ6iN94bO5NW0W4TRYXerG+BdAvAQ==", + "funding": { + "url": "https://github.com/Mermade/oas-kit?sponsor=1" + } + }, + "node_modules/oas-validator": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/oas-validator/-/oas-validator-4.0.8.tgz", + "integrity": "sha512-bIt8erTyclF7bkaySTtQ9sppqyVc+mAlPi7vPzCLVHJsL9nrivQjc/jHLX/o+eGbxHd6a6YBwuY/Vxa6wGsiuw==", + "dependencies": { + "ajv": "^5.5.2", + "better-ajv-errors": "^0.6.7", + "call-me-maybe": "^1.0.1", + "oas-kit-common": "^1.0.8", + "oas-linter": "^3.1.3", + "oas-resolver": "^2.4.3", + "oas-schema-walker": "^1.1.5", + "reftools": "^1.1.5", + "should": "^13.2.1", + "yaml": "^1.8.3" + }, + "funding": { + "url": "https://github.com/Mermade/oas-kit?sponsor=1" + } + }, + "node_modules/oas-validator/node_modules/ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha512-Ajr4IcMXq/2QmMkEmSvxqfLN5zGmJ92gHXAeOXq1OekoH2rfDNsgdDoL2f7QaRCy7G/E6TpxBVdRuNraMztGHw==", + "dependencies": { + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" + } + }, + "node_modules/oas-validator/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/oas-validator/node_modules/better-ajv-errors": { + "version": "0.6.7", + "resolved": "https://registry.npmjs.org/better-ajv-errors/-/better-ajv-errors-0.6.7.tgz", + "integrity": "sha512-PYgt/sCzR4aGpyNy5+ViSQ77ognMnWq7745zM+/flYO4/Yisdtp9wDQW2IKCyVYPUxQt3E/b5GBSwfhd1LPdlg==", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "@babel/runtime": "^7.0.0", + "chalk": "^2.4.1", + "core-js": "^3.2.1", + "json-to-ast": "^2.0.3", + "jsonpointer": "^4.0.1", + "leven": "^3.1.0" + }, + "peerDependencies": { + "ajv": "4.11.8 - 6" + } + }, + "node_modules/oas-validator/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/oas-validator/node_modules/fast-deep-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha512-fueX787WZKCV0Is4/T2cyAdM4+x1S3MXXOAhavE1ys/W42SHAPacLTQhucja22QBYrfGw50M2sRiXPtTGv9Ymw==" + }, + "node_modules/oas-validator/node_modules/json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha512-4JD/Ivzg7PoW8NzdrBSr3UFwC9mHgvI7Z6z3QGBsSHgKaRTUDmyZAAKJo2UbG1kUVfS9WS8bi36N49U1xw43DA==" + }, + "node_modules/oas-validator/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/oas-validator/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", + "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/oer-utils": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/oer-utils/-/oer-utils-1.3.4.tgz", + "integrity": "sha512-JTRqe1iQuB0weu1Mppu0YUApL6CU0CxtmB8pJIhTyTm4X7rmps6p18GVRzwHRfvSP7YUGakzgA+xPqZseF1FOA==", + "peer": true + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "peer": true, + "dependencies": { + "fn.name": "1.x.x" + } + }, + "node_modules/openapi-backend": { + "version": "5.10.6", + "resolved": "https://registry.npmjs.org/openapi-backend/-/openapi-backend-5.10.6.tgz", + "integrity": "sha512-vTjBRys/O4JIHdlRHUKZ7pxS+gwIJreAAU9dvYRFrImtPzQ5qxm5a6B8BTVT9m6I8RGGsShJv35MAc3Tu2/y/A==", + "dependencies": { + "@apidevtools/json-schema-ref-parser": "^11.1.0", + "ajv": "^8.6.2", + "bath-es5": "^3.0.3", + "cookie": "^0.5.0", + "dereference-json-schema": "^0.2.1", + "lodash": "^4.17.15", + "mock-json-schema": "^1.0.7", + "openapi-schema-validator": "^12.0.0", + "openapi-types": "^12.0.2", + "qs": "^6.9.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/anttiviljami" + } + }, + "node_modules/openapi-backend/node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/openapi-sampler": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/openapi-sampler/-/openapi-sampler-1.5.1.tgz", + "integrity": "sha512-tIWIrZUKNAsbqf3bd9U1oH6JEXo8LNYuDlXw26By67EygpjT+ArFnsxxyTMjFWRfbqo5ozkvgSQDK69Gd8CddA==", + "dependencies": { + "@types/json-schema": "^7.0.7", + "json-pointer": "0.6.2" + } + }, + "node_modules/openapi-schema-validator": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-schema-validator/-/openapi-schema-validator-12.1.3.tgz", + "integrity": "sha512-xTHOmxU/VQGUgo7Cm0jhwbklOKobXby+/237EG967+3TQEYJztMgX9Q5UE2taZKwyKPUq0j11dngpGjUuxz1hQ==", + "dependencies": { + "ajv": "^8.1.0", + "ajv-formats": "^2.0.2", + "lodash.merge": "^4.6.1", + "openapi-types": "^12.1.3" + } + }, + "node_modules/openapi-types": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz", + "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==" + }, + "node_modules/opn": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", + "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", + "dependencies": { + "is-wsl": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "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/p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha512-wB3wfAxZpk2AzOfUMJNL+d36xothRSyj8EXOa4f6GMqYDN9BJaaSISbsk+wS9abmnebVw95C2Kb5t85UmpCxuw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "engines": { + "node": ">=4" + } + }, + "node_modules/p-is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", + "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-srcset": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz", + "integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==" + }, + "node_modules/parse-strings-in-object": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parse-strings-in-object/-/parse-strings-in-object-2.0.0.tgz", + "integrity": "sha512-hb50xDyEo8boMtyzB1IdVE4KcTNVbIirk/ZqC8na1irOf/70DyZS30y1FIIAUe9jyHJk9s2QoZ4aBNHR9NXHsg==", + "peer": true + }, + "node_modules/parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "dependencies": { + "entities": "^4.4.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", + "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", + "dependencies": { + "domhandler": "^5.0.2", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, + "node_modules/pause-stream": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==", + "dependencies": { + "through": "~2.3" + } + }, + "node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", + "dependencies": { + "pinkie": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/prom-client": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/prom-client/-/prom-client-14.2.0.tgz", + "integrity": "sha512-sF308EhTenb/pDRPakm+WgiN+VdM/T1RaHj1x+MvAuT8UiQP8JmOEbxVqtkbfR4LrvOg5n7ic01kRBDGXjYikA==", + "peer": true, + "dependencies": { + "tdigest": "^0.1.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/protobufjs": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.3.0.tgz", + "integrity": "sha512-YWD03n3shzV9ImZRX3ccbjqLxj7NokGN0V/ESiBV5xWqrommYHYiihuIyavq03pWSGqlyvYUFmfoMKd+1rPA/g==", + "hasInstallScript": true, + "peer": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/random-poly-fill": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/random-poly-fill/-/random-poly-fill-1.0.1.tgz", + "integrity": "sha512-bMOL0hLfrNs52+EHtIPIXxn2PxYwXb0qjnKruTjXiM/sKfYqj506aB2plFwWW1HN+ri724bAVVGparh4AtlJKw==", + "peer": true + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "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==", + "peer": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/reftools": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/reftools/-/reftools-1.1.9.tgz", + "integrity": "sha512-OVede/NQE13xBQ+ob5CKd5KyeJYU2YInb1bmV4nRoOfquZPkAkxuOXicSe1PvqIuZZ4kD13sPKBbR7UFDmli6w==", + "funding": { + "url": "https://github.com/Mermade/oas-kit?sponsor=1" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" + }, + "node_modules/right-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "integrity": "sha512-yqINtL/G7vs2v+dFIZmFUDbnVyFUJFKd6gK22Kgo6R4jfJGFtisKyncWDDULgjfqf4ASQuIQyjJ7XZ+3aWpsAg==", + "dependencies": { + "align-text": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-stable-stringify": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", + "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/sanitize-html": { + "version": "1.27.5", + "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-1.27.5.tgz", + "integrity": "sha512-M4M5iXDAUEcZKLXkmk90zSYWEtk5NH3JmojQxKxV371fnMh+x9t1rqdmXaGoyEHw3z/X/8vnFhKjGL5xFGOJ3A==", + "dependencies": { + "htmlparser2": "^4.1.0", + "lodash": "^4.17.15", + "parse-srcset": "^1.0.2", + "postcss": "^7.0.27" + } + }, + "node_modules/sanitize-html/node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/sanitize-html/node_modules/dom-serializer/node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/sanitize-html/node_modules/domhandler": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-3.3.0.tgz", + "integrity": "sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA==", + "dependencies": { + "domelementtype": "^2.0.1" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/sanitize-html/node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/sanitize-html/node_modules/domutils/node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/sanitize-html/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/sanitize-html/node_modules/htmlparser2": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-4.1.0.tgz", + "integrity": "sha512-4zDq1a1zhE4gQso/c5LP1OtrhYTncXNSpvJYtWJBtXAETPlMfi3IFNjGuQbYLuVY4ZR0QMqRVvo4Pdy9KLyP8Q==", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^3.0.0", + "domutils": "^2.0.0", + "entities": "^2.0.0" + } + }, + "node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "peer": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "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", + "integrity": "sha512-3NnuWfM6vBYoy5gZFvHiYsVbafvI9vZv/+jlIigFn4oP4zjNPK3LhcY0xSCgeb1a5L8jO71Mit9LlNoi2UfDDQ==", + "peer": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "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.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shins": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/shins/-/shins-2.6.0.tgz", + "integrity": "sha512-Y9asd1r6GshJDskRgwLZmd9xX+5DU+T2mZ+wpPT0nee0AbSLdiiSZahbcuXE5EIljsEmWSwHhduAmkbbQrAdlw==", + "dependencies": { + "call-me-maybe": "^1.0.1", + "cheerio": "^1.0.0-rc.2", + "chokidar": "^3.0.2", + "compression": "^1.6.2", + "ejs": "^2.5.1", + "express": "^4.15.5", + "highlight.js": "^10.0.2", + "markdown-it": "^10.0.0", + "markdown-it-attrs": "^1.2.1", + "markdown-it-emoji": "^1.4.0", + "markdown-it-lazy-headers": "^0.1.3", + "opn": "^5.2.0", + "sanitize-html": "^1.15.0", + "tiny-opts-parser": "0.0.3", + "uglify-js": "^2.7.4", + "yaml": "^1.9.2" + }, + "bin": { + "shins": "shins.js" + } + }, + "node_modules/shins/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/should": { + "version": "13.2.3", + "resolved": "https://registry.npmjs.org/should/-/should-13.2.3.tgz", + "integrity": "sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ==", + "dependencies": { + "should-equal": "^2.0.0", + "should-format": "^3.0.3", + "should-type": "^1.4.0", + "should-type-adaptors": "^1.0.1", + "should-util": "^1.0.0" + } + }, + "node_modules/should-equal": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/should-equal/-/should-equal-2.0.0.tgz", + "integrity": "sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA==", + "dependencies": { + "should-type": "^1.4.0" + } + }, + "node_modules/should-format": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/should-format/-/should-format-3.0.3.tgz", + "integrity": "sha512-hZ58adtulAk0gKtua7QxevgUaXTTXxIi8t41L3zo9AHvjXO1/7sdLECuHeIN2SRtYXpNkmhoUP2pdeWgricQ+Q==", + "dependencies": { + "should-type": "^1.3.0", + "should-type-adaptors": "^1.0.1" + } + }, + "node_modules/should-type": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/should-type/-/should-type-1.4.0.tgz", + "integrity": "sha512-MdAsTu3n25yDbIe1NeN69G4n6mUnJGtSJHygX3+oN0ZbO3DTiATnf7XnYJdGT42JCXurTb1JI0qOBR65shvhPQ==" + }, + "node_modules/should-type-adaptors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/should-type-adaptors/-/should-type-adaptors-1.1.0.tgz", + "integrity": "sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA==", + "dependencies": { + "should-type": "^1.3.0", + "should-util": "^1.0.0" + } + }, + "node_modules/should-util": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/should-util/-/should-util-1.0.1.tgz", + "integrity": "sha512-oXF8tfxx5cDk8r2kYqlkUJzZpDBqVY/II2WhvU0n9Y3XYvAYRmeaf1PvvIvTgPnv4KJ+ES5M0PyDq5Jp+Ygy2g==" + }, + "node_modules/side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "peer": true, + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", + "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", + "dependencies": { + "through": "2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "peer": true, + "engines": { + "node": "*" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/stream-combiner": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz", + "integrity": "sha512-6yHMqgLYDzQDcAkL+tjJDC5nSNuNIx0vZtRZeiPh7Saef7VHX9H5Ijn9l2VIol2zaNYlYEX6KyuT/237A58qEQ==", + "dependencies": { + "duplexer": "~0.1.1", + "through": "~2.3.4" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "peer": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "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/string-width/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/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/stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "dependencies": { + "get-own-enumerable-property-symbols": "^3.0.0", + "is-obj": "^1.0.1", + "is-regexp": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/swagger2openapi": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/swagger2openapi/-/swagger2openapi-6.2.3.tgz", + "integrity": "sha512-cUUktzLpK69UwpMbcTzjMw2ns9RZChfxh56AHv6+hTx3StPOX2foZjPgds3HlJcINbxosYYBn/D3cG8nwcCWwQ==", + "dependencies": { + "better-ajv-errors": "^0.6.1", + "call-me-maybe": "^1.0.1", + "node-fetch-h2": "^2.3.0", + "node-readfiles": "^0.2.0", + "oas-kit-common": "^1.0.8", + "oas-resolver": "^2.4.3", + "oas-schema-walker": "^1.1.5", + "oas-validator": "^4.0.8", + "reftools": "^1.1.5", + "yaml": "^1.8.3", + "yargs": "^15.3.1" + }, + "bin": { + "boast": "boast.js", + "oas-validate": "oas-validate.js", + "swagger2openapi": "swagger2openapi.js" + }, + "funding": { + "url": "https://github.com/Mermade/oas-kit?sponsor=1" + } + }, + "node_modules/swagger2openapi/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/swagger2openapi/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/swagger2openapi/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/swagger2openapi/node_modules/better-ajv-errors": { + "version": "0.6.7", + "resolved": "https://registry.npmjs.org/better-ajv-errors/-/better-ajv-errors-0.6.7.tgz", + "integrity": "sha512-PYgt/sCzR4aGpyNy5+ViSQ77ognMnWq7745zM+/flYO4/Yisdtp9wDQW2IKCyVYPUxQt3E/b5GBSwfhd1LPdlg==", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "@babel/runtime": "^7.0.0", + "chalk": "^2.4.1", + "core-js": "^3.2.1", + "json-to-ast": "^2.0.3", + "jsonpointer": "^4.0.1", + "leven": "^3.1.0" + }, + "peerDependencies": { + "ajv": "4.11.8 - 6" + } + }, + "node_modules/swagger2openapi/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/swagger2openapi/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/swagger2openapi/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==" + }, + "node_modules/swagger2openapi/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "peer": true + }, + "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", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/swagger2openapi/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/swagger2openapi/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==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/swagger2openapi/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==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/swagger2openapi/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" + }, + "node_modules/swagger2openapi/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/swagger2openapi/node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/swagger2openapi/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tdigest": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/tdigest/-/tdigest-0.1.2.tgz", + "integrity": "sha512-+G0LLgjjo9BZX2MfdvPfH+MKLCrxlXSYec5DaPYP1fe6Iyhf0/fSmJ0bFiZ1F8BT6cGXl2LpltQptzjXKWEkKA==", + "peer": true, + "dependencies": { + "bintrees": "1.0.2" + } + }, + "node_modules/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", + "peer": true + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" + }, + "node_modules/tiny-opts-parser": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tiny-opts-parser/-/tiny-opts-parser-0.0.3.tgz", + "integrity": "sha512-JSb93x8gZWbfJ9GJSEcRj9Y/GsfcECgeUWX98vsBsIxo2khv3u+rtf3tySVn0QRBL5hzHrm41cnem7/vgMb53A==" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/traceparent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/traceparent/-/traceparent-1.0.0.tgz", + "integrity": "sha512-b/hAbgx57pANQ6cg2eBguY3oxD6FGVLI1CC2qoi01RmHR7AYpQHPXTig9FkzbWohEsVuHENZHP09aXuw3/LM+w==", + "peer": true, + "dependencies": { + "random-poly-fill": "^1.0.1" + } + }, + "node_modules/triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "peer": true, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", + "peer": true + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typescript": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", + "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uc.micro": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==" + }, + "node_modules/uglify-js": { + "version": "2.8.29", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "integrity": "sha512-qLq/4y2pjcU3vhlhseXGGJ7VbFO4pBANu0kwl8VCa9KEI0V8VfZIx2Fy3w01iSTA/pGwKZSmu/+I4etLNDdt5w==", + "dependencies": { + "source-map": "~0.5.1", + "yargs": "~3.10.0" + }, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + }, + "optionalDependencies": { + "uglify-to-browserify": "~1.0.0" + } + }, + "node_modules/uglify-js/node_modules/camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha512-wzLkDa4K/mzI1OSITC+DUyjgIl/ETNHE9QvYgy6J6Jvqyyz4C0Xfd+lQhb19sX2jMpZV4IssUn0VDVmglV+s4g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/uglify-js/node_modules/cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha512-GIOYRizG+TGoc7Wgc1LiOTLare95R3mzKgoln+Q/lE4ceiYH19gUpl0l0Ffq4lJDEf3FxujMe6IBfOCs7pfqNA==", + "dependencies": { + "center-align": "^0.1.1", + "right-align": "^0.1.1", + "wordwrap": "0.0.2" + } + }, + "node_modules/uglify-js/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/uglify-js/node_modules/yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha512-QFzUah88GAGy9lyDKGBqZdkYApt63rCXYBGYnEP4xDJPXNqXXnBDACnbrXnViV6jRSqAePwrATi2i8mfYm4L1A==", + "dependencies": { + "camelcase": "^1.0.2", + "cliui": "^2.1.0", + "decamelize": "^1.0.0", + "window-size": "0.1.0" + } + }, + "node_modules/uglify-to-browserify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "integrity": "sha512-vb2s1lYx2xBtUgy+ta+b2J/GLVUR+wmpINwHePmPRhOsIVCG2wDzKJ0n14GslH1BifsqVzSOwQhRaCAsZ/nI4Q==", + "optional": 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/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/urijs": { + "version": "1.19.11", + "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.11.tgz", + "integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==" + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "peer": true + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid4": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/uuid4/-/uuid4-2.0.3.tgz", + "integrity": "sha512-CTpAkEVXMNJl2ojgtpLXHgz23dh8z81u6/HEPiQFOvBc/c2pde6TVHmH4uwY0d/GLF3tb7+VDAj4+2eJaQSdZQ==" + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==" + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==" + }, + "node_modules/widdershins": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/widdershins/-/widdershins-4.0.1.tgz", + "integrity": "sha512-y7TGynno+J/EqRPtUrpEuEjJUc1N2ajfP7R4sHU7Qg8I/VFHGavBxL7ZTeOAVmd1fhmY2wJIbpX2LMDWf37vVA==", + "dependencies": { + "dot": "^1.1.3", + "fast-safe-stringify": "^2.0.7", + "highlightjs": "^9.12.0", + "httpsnippet": "^1.19.0", + "jgexml": "latest", + "markdown-it": "^10.0.0", + "markdown-it-emoji": "^1.4.0", + "node-fetch": "^2.0.0", + "oas-resolver": "^2.3.1", + "oas-schema-walker": "^1.1.3", + "openapi-sampler": "^1.0.0-beta.15", + "reftools": "^1.1.0", + "swagger2openapi": "^6.0.1", + "urijs": "^1.19.0", + "yaml": "^1.8.3", + "yargs": "^12.0.5" + }, + "bin": { + "testRunner": "testRunner.js", + "widdershins": "widdershins.js" + } + }, + "node_modules/widdershins/node_modules/ansi-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/widdershins/node_modules/cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "dependencies": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + } + }, + "node_modules/widdershins/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/widdershins/node_modules/get-caller-file": { + "version": "1.0.3", + "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/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "engines": { + "node": ">=4" + } + }, + "node_modules/widdershins/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/widdershins/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/widdershins/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/widdershins/node_modules/require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha512-IqSUtOVP4ksd1C/ej5zeEh/BIP2ajqpn8c5x+q99gvcIG/Qf0cud5raVnE/Dwd0ua9TXYDoDc0RE5hBSdz22Ug==" + }, + "node_modules/widdershins/node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/widdershins/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/widdershins/node_modules/wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha512-vAaEaDM946gbNpH5pLVNR+vX2ht6n0Bt3GXwVB1AuAqZosOvHNF3P7wDnh8KLkSqgUh0uh77le7Owgoz+Z9XBw==", + "dependencies": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/widdershins/node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/widdershins/node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==", + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/widdershins/node_modules/wrap-ansi/node_modules/string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==", + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/widdershins/node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/widdershins/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" + }, + "node_modules/widdershins/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/widdershins/node_modules/yargs": { + "version": "12.0.5", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", + "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", + "dependencies": { + "cliui": "^4.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^1.0.1", + "os-locale": "^3.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^11.1.1" + } + }, + "node_modules/widdershins/node_modules/yargs-parser": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", + "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "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/winston": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.13.0.tgz", + "integrity": "sha512-rwidmA1w3SE4j0E5MuIufFhyJPBDG7Nu71RkZor1p2+qHvJSZ9GYDA81AyleQcZbh/+V6HjeBdfnTZJm9rSeQQ==", + "peer": true, + "dependencies": { + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.2", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.4.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.7.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.7.1.tgz", + "integrity": "sha512-wQCXXVgfv/wUPOfb2x0ruxzwkcZfxcktz6JIMUaPLmcNhO4bZTwA/WtDWK74xV3F2dKu8YadrFv0qhwYjVEwhA==", + "peer": true, + "dependencies": { + "logform": "^2.6.1", + "readable-stream": "^3.6.2", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha512-xSBsCeh+g+dinoBv3GAOWM4LcVVO68wLXRanibtBSdUvkGWQRGeE9P7IwU9EmDDi4jA6L44lz15CGMwdw9N5+Q==", + "engines": { + "node": ">=0.4.0" + } + }, + "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/wrap-ansi/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "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==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "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==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "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==" + }, + "node_modules/wrap-ansi/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/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yaml": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.5.tgz", + "integrity": "sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, + "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/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/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "engines": { + "node": ">=6" + } + } + } +} diff --git a/docker/mock-proxy/package.json b/docker/mock-proxy/package.json new file mode 100644 index 00000000..627f7f35 --- /dev/null +++ b/docker/mock-proxy/package.json @@ -0,0 +1,24 @@ +{ + "name": "mock-proxy", + "version": "0.1.0", + "description": "Mock proxy adapter", + "author": "Eugen Klymniuk (geka-evk)", + "license": "Apache-2.0", + "main": "src/server.ts", + "scripts": { + "start": "ts-node src/server.ts" + }, + "dependencies": { + "@mojaloop/central-services-shared": "^18.6.0-snapshot.4", + "@types/node": "^20.14.2", + "axios": "^1.7.2", + "body-parser": "^1.20.2", + "express": "^4.19.2", + "ts-node": "^10.9.2", + "typescript": "^5.4.5" + }, + "devDependencies": { + "@types/body-parser": "^1.19.5", + "@types/express": "^4.17.21" + } +} diff --git a/docker/mock-proxy/src/config.ts b/docker/mock-proxy/src/config.ts new file mode 100644 index 00000000..7ea928bb --- /dev/null +++ b/docker/mock-proxy/src/config.ts @@ -0,0 +1,19 @@ +import process from 'node:process'; + +export const { + PROXY_PORT = 14200, + PROXY_NAME = 'proxyAB', + CL_HOST = 'localhost', + CL_PORT = 3001, +} = process.env; + +console.log({ + PROXY_PORT, + PROXY_NAME, + CL_HOST, + CL_PORT +}) + +export const ROUTES = { + history: '/history', +} as const; diff --git a/docker/mock-proxy/src/server.ts b/docker/mock-proxy/src/server.ts new file mode 100644 index 00000000..fb65c9f4 --- /dev/null +++ b/docker/mock-proxy/src/server.ts @@ -0,0 +1,94 @@ +import http from 'node:http'; +import * as console from 'node:console'; +import express, { Request, Response, NextFunction } from 'express'; +import bodyParser from 'body-parser'; + +import { PROXY_PORT, PROXY_NAME, ROUTES, CL_HOST, CL_PORT } from './config'; +import { hubCbHeaders, dfspCbHeaders } from './utils'; + +const app = express(); +app.use(bodyParser.json()); +app.use(bodyParser.urlencoded({ extended: true })); + +const history: Record[] = []; + +app.all('*', (req: Request, res: Response, next: NextFunction) => { + const { path, method, headers, body, params, query } = req; + const reqDetails = { path, method, headers, body, params, query } + console.log(`[==>] [${new Date().toISOString()}] incoming request...`, reqDetails); + + if (path !== ROUTES.history) { + history.push(reqDetails); + } + next(); +}); + +app.get(ROUTES.history, (req: Request, res: Response) => { + res.json({ history }); +}); +app.delete(ROUTES.history, (req: Request, res: Response) => { + history.length = 0 + res.json({ history }); +}); + +app.get('/health', (req: Request, res: Response) => { + res.json({ success: true }); +}); + +app.get('/parties/:type/:id', (req: Request, res: Response) => { + const { type, id } = req.params; + const headers = hubCbHeaders(req.headers); + console.log('parties request details:', { type, id, headers, CL_HOST, CL_PORT }); + + // todo: reply to CL with party info + + res + .set(headers) + .status(202) + .json({ success: true }); +}); + +app.put('/parties/:type/:id/error', (req: Request, res: Response) => { + const { type, id } = req.params; + const headers = dfspCbHeaders(req.headers); + console.log('parties put error request details:', { type, id, headers, CL_HOST, CL_PORT }); + + res + .set(headers) + .status(200) + .json({ success: true }); +}); + +app.get('/oracle*', (req: Request, res: Response) => { + // console.log('oracle request details:', { type, id, headers }); + const data = { + partyList: [] + } + res.json({ data }); +}); + +app.all('/echo', async (req: Request, res: Response) => { + const input = { + method: req.method, + path: req.path, + headers: req.headers, + query: req.query, + body: req.body, + }; + console.log('incoming request...', input); + + res.set(hubCbHeaders).json(input); +}); + +app.use((err: Error, req: Request, res: Response, next: NextFunction) => { + const { message, stack, cause } = err; + const response = { message, stack, cause }; + console.error('error response:', response); + res.status(500).json(response); +}); + +const httpsServer = http.createServer(app); + +httpsServer.listen(PROXY_PORT, () => { + console.log(`Mock proxyAdapter "${PROXY_NAME}" is running on port ${PROXY_PORT}...`); +}); diff --git a/docker/mock-proxy/src/utils.ts b/docker/mock-proxy/src/utils.ts new file mode 100644 index 00000000..45ae9484 --- /dev/null +++ b/docker/mock-proxy/src/utils.ts @@ -0,0 +1,29 @@ +import { IncomingHttpHeaders } from 'node:http'; +import { Enum } from '@mojaloop/central-services-shared'; +import { PROXY_NAME } from './config'; + +const { Headers} = Enum.Http + +export const hubCbHeaders = (headers: IncomingHttpHeaders) => { + const { + [Headers.FSPIOP.SOURCE]: source, + [Headers.FSPIOP.DESTINATION]: destination, + host, + 'content-length': _, + ...restHeaders + } = headers; + + return { + ...restHeaders, + [Headers.FSPIOP.SOURCE]: destination, + [Headers.FSPIOP.DESTINATION]: source, + [Headers.FSPIOP.PROXY]: PROXY_NAME, + }; +}; + +export const dfspCbHeaders = (headers: IncomingHttpHeaders) => { + const h = hubCbHeaders(headers); + delete h[Headers.FSPIOP.PROXY]; + + return h; +} diff --git a/docker/mock-proxy/tsconfig.json b/docker/mock-proxy/tsconfig.json new file mode 100644 index 00000000..56986955 --- /dev/null +++ b/docker/mock-proxy/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "baseUrl": "./", + "rootDir": "./src", + "outDir": "./dist", + "strict": true, + "noUncheckedIndexedAccess": true, + "noImplicitAny": true, + "lib": ["esNext"], + "target": "esNext", + "module": "nodeNext", + "moduleResolution": "nodeNext", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "allowSyntheticDefaultImports": true, + "skipLibCheck": true, + "resolveJsonModule": true, + "sourceMap": true, + "declaration": true, + "declarationMap": true + }, + "include": ["./src"], + "exclude": ["node_modules"] +} diff --git a/docs/Proxy/Discovery.md b/docs/Proxy/Discovery.md new file mode 100644 index 00000000..d166332c --- /dev/null +++ b/docs/Proxy/Discovery.md @@ -0,0 +1,20 @@ +# On Demand Discovery + +The discovery flows are summarized as follows: +1. On Demand loading of cross network identifiers - using Oracles for identifier lookups in local scheme +2. On Demand loading for all identifiers + +## On Demand Discovery using local oracles +- Scheme uses Oracles to map local identifiers to participants of the scheme +- Identifiers for other schemes are discovered via a depth first search, but asking all participants. Proxy participant then forward the request to the connected scheme +- This diagram shows two connected schemes, but this design work for any number of connected schemes. + +![Proxy pattern - On Demand Discovery with Oracles](./Proxy%20pattern%20-%20Lazy%20Discovery%20-%20Oracles.png) + + +## On Demand Discover with incorrectly cached results +- When an identifier moved to another dfsp provider, then the store cache for that participant will route to an unsuccessful get \parties call. + +Here is a sequence diagram show how that gets updated. +### Sequence Diagram +![Invalid Cache](Proxy%20pattern%20-%20Lazy%20Discovery%20Identifier%20Cache%20Invalid.png) diff --git a/docs/Proxy/FXAPI_POC_payer_conversion_RECEIVE.plantuml b/docs/Proxy/FXAPI_POC_payer_conversion_RECEIVE.plantuml new file mode 100644 index 00000000..c0fc2cda --- /dev/null +++ b/docs/Proxy/FXAPI_POC_payer_conversion_RECEIVE.plantuml @@ -0,0 +1,577 @@ +@startuml + +!$simplified = false +!$shortCutSingleFXP = true +!$hideSwitchDetail = false +!$senderName = "Keeya" +!$receiverName = "Yaro" +!$payerCurrency = "BWP" +!$payeeCurrency = "TZS" +!$payerFSPID = "PayerFSP" +!$payeeFSPID = "PayeeFSP" +!$payerMSISDN = "26787654321" +!$payeeMSISDN = "2551234567890" +!$payeeReceiveAmount = "50000" +!$payeeFee = "4000" +!$targetAmount = "54000" +!$fxpChargesSource = "33" +!$fxpChargesTarget = "6000" +!$fxpSourceAmount = "330" +!$fxpTargetAmount = "54000" +!$totalChargesSourceCurrency = "55" + + +title Remittance Transfer using Mojaloop FX APIs POC\nPayer DFSP requests conversion with RECEIVE amount +actor "$senderName" as A1 +box "Payer DFSP" #LightBlue + participant "Payer CBS" as PayerCBS + participant "Payer\nMojaloop\nConnector" as D1 +end box + +participant "Mojaloop Switch" as S1 + +box "Discovery Service" #LightYellow + participant "ALS Oracle" as ALS +end box + +box "FX provider" + participant "FXP\nConnector" as FXP + participant "Backend FX API" as FXPBackend +end box + +box "Payee DFSP" #LightBlue + participant "Payee\nMojaloop\nConnector" as D2 + participant "Payee CBS" as PayeeCBS +end box + +actor "$receiverName" as A2 +autonumber + +A1->PayerCBS:I'd like to pay $receiverName\n$payeeReceiveAmount $payeeCurrency for his latest book, please +PayerCBS->D1: Initiate remittance transfer +== Discovery Phase == +activate D1 +D1->>S1:I want to send to MSISDN $payeeMSISDN\n**GET /parties/MSISDN/$payeeMSISDN** +activate S1 +!if ($simplified != true) +S1-->>D1:202 I'll get back to you +!endif +deactivate D1 +S1->ALS:Who owns MSISDN $payeeMSISDN? +activate ALS +ALS-->S1:It's $payeeFSPID +deactivate ALS +S1->>D2:Do you own MSISDN $payeeMSISDN? +activate D2 +!if ($simplified != true) +D2-->>S1:202 I'll get back to you +!endif +D2->D2: Check Sanction list status & trigger a refresh of the status +D2->PayeeCBS: Check account and get currency type +!if ($simplified != true) +PayeeCBS-->D2: Result +!endif +deactivate S1 +D2->>S1:Yes, it's $receiverName. He can receive in $payeeCurrency\n**PUT /parties/MSISDN/$payeeMSISDN** +!if ($simplified != true) +note over D2 + PUT /parties + + "party": { + "partyIdInfo": { + "partyIdType": "MSISDN", + "partyIdentifier": "$payeeMSISDN" + }, + "name": "$receiverName", + "supportedCurrencies":[ + "$payeeCurrency" + ], + "kycInformation": "" + } +end note +!else +note over D2 + Payee Info with Encrypted KYC Data +end note +!endif +activate S1 +!if ($simplified != true) +S1-->>D2:200 Gotcha +!endif +deactivate D2 +S1->>D1:Yes, it's $receiverName. He can receive in $payeeCurrency\n**PUT /parties/MSISDN/$payeeMSISDN** +activate D1 +!if ($simplified != true) +D1-->>S1:200 Gotcha +!endif +deactivate S1 + +!if ($shortCutSingleFXP != true) +== Get FX providers == + +D1->D1:Hmmm. I can only send in $payerCurrency.\nI need to get some currency conversion + +D1->>S1:What FXPs do you know about?\n**GET /services/FXP** +activate S1 +!if ($simplified != true) +S1-->>D1:202 I'll get back to you +!endif +deactivate D1 +S1->ALS:What FXPs do you know about? +activate ALS +ALS-->S1:FDH FX +deactivate ALS +S1->>D1:Here are the available FXPs:FDH FX +note over S1 + PUT /services/FXP + + "fxpProviders": [ + "FDH_FX" + ] +end note +activate D1 +!if ($simplified != true) +D1-->>S1:200 Gotcha +!endif + +!endif + +D1->D1: I need to find out if payee charges any fee so that I can include that in fxQuote + +== Agreement Phase == +D1->>S1:Please quote for a payment of $payeeReceiveAmount $payeeCurrency.\n**POST /quotes** +note left + This is a standard Mojaloop quote. + No development required +end note +!if ($simplified != true) +note over D1 + POST /quotes + + { + "quoteId": "382987a8-75ce-4037-b500-c475e08c1727" + ,"transactionId": "d9ce59d4-3598-4396-8630-581bb0551451" + , "payee": { + "partyIdInfo": { + "partyIdType": "MSISDN" + , "partyIdentifier": "$payeeMSISDN" + } + } + , "payer": { + "partyIdInfo": { + "partyIdType": "MSISDN" + , "partyIdentifier": "$payerMSISDN" + } + }, + "amountType": "RECEIVE" + , "amount": { + "currency": "$payeeCurrency" + , "amount": "$payeeReceiveAmount" + } + , "validity": "2021-08-25T14:17:09.663+01:00" + } +end note +!endif +!if ($simplified != true) +S1-->>D1:202 I'll get back to you +!endif +S1->>D2:**POST /quotes** +activate D2 +!if ($simplified != true) +D2-->>S1:202 I'll get back to you +!endif +deactivate S1 +D2->D2:OK, so I will charge $payeeFee $payeeCurrency for this.\nNow I create terms of the transfer and sign the transaction object +D2->>S1:Here's the signed quote +note over D2 + **put /quotes/382987a8-75ce-4037-b500-c475e08c1727** + + { + "transferAmount": { + "currency": "$payeeCurrency" + , "amount": "$targetAmount" + } + , "payeeReceiveAmount": { + "currency": "$payeeCurrency" + , "amount": "$payeeReceiveAmount" + }, + "payeeFspFee": { + "currency": "$payeeCurrency" + , "amount": "$payeeFee" + } + , "expiration": "2021-08-25T14:17:09.663+01:00 + , "transaction": { + , "transactionId": "d9ce59d4-3598-4396-8630-581bb0551451" + , "quoteId": "382987a8-75ce-4037-b500-c475e08c1727" + , "payee": { + "fspId": "$payeeFSPID" + , "partyIdInfo": { + "partyIdType": "MSISDN" + , "partyIdentifier": "$payeeMSISDN" + } + } + , "payer": { + "fspId": "$payerFSPID" + , "partyIdInfo": { + "partyIdType": "MSISDN" + , "partyIdentifier": "$payerMSISDN" + } + } + , "amount": { + "currency": "$payeeCurrency" + "amount": "$payeeReceiveAmount" + } + , "payeeReceiveAmount": { + "currency": "$payeeCurrency" + , "amount": "$payeeReceiveAmount" + } + , "converter": "PAYER" + } + , "condition": "BfNFPRgfKF8Ke9kpoNAagmcI4/Hya5o/rq9/fq97ZiA=" + } + +end note +activate S1 +!if ($simplified != true) +S1-->>D2:200 Gotcha +!endif +deactivate D2 +S1->>D1:Here's the signed quote\n**PUT /quotes/382987a8-75ce-4037-b500-c475e08c1727** +activate D1 +!if ($simplified != true) +D1-->>S1:200 Gotcha +!endif +deactivate S1 +D1->D1:OK, I can see that there are going to be $payeeFee $payeeCurrency in charges and I need send $targetAmount $payeeCurrency to make this transfer + +group Currency Conversion +D1->D1:Now I need to find out what the exchange rate is +deactivate S1 +D1->D1:I'll ask FDH FX to perform my conversion + +!if ($shortCutSingleFXP != true) +D1->>S1:Here is the initial version of the transfer.\nPlease quote me for the currency conversion. +!else +D1->>FXP:Here is the initial version of the transfer.\nPlease quote me for the currency conversion. +!endif +note over D1 + **post /fxQuotes** + { + "conversionRequestId": "828cc75f-1654-415e-8fcd-df76cc9329b9" + , "conversionTerms": { + "conversionId": "581f68ef-b54f-416f-9161-ac34e889a84b", + , "counterPartyFsp": "FDH_FX" + , "amountType": "RECEIVE" + , "sourceAmount": { + "currency": "$payerCurrency" + } + , "targetAmount": { + "currency": "$payeeCurrency" + , "amount": "$targetAmount" + } + , "validity": "2021-08-25T14:17:09.663+01:00" + } + } + +end note +!if ($shortCutSingleFXP != true) +activate S1 +!if ($simplified != true) +S1-->>D1:202 I'll get back to you +!endif +deactivate D1 +S1->>FXP:Here is the initial version of the transfer.\nPlease quote me for the currency conversion.\n**POST /fxQuote** +activate FXP +!if ($simplified != true) +FXP-->>S1:202 I'll get back to you +!endif +deactivate S1 +!else +!endif +FXP->FXPBackend:Lookup FX rate +FXPBackend-->FXP:Return FX rate +' !if ($shortCutSingleFXP != true) + +note over FXP + I will add a $fxpChargesSource $payerCurrency fee for undertaking the conversion. + Now I'll set an expiry time, sign the quotation object, + create an ILP prepare packet and return it in the intermediary object. + + NOTE: the ILP prepare packet contains the following items, all encoded: + - The amount being sent (i.e. in the source currency) + - An expiry time + - The condition + - The name of the FXP + - The content of the conversion terms + + ** PUT /fxQuotes/828cc75f-1654-415e-8fcd-df76cc9329b9** + { + "condition": "bdbcf517cfc7e474392935781cc14043602e53dc2e8e8452826c5241dfd5e7ab" + , "conversionTerms": { + "conversionId": "581f68ef-b54f-416f-9161-ac34e889a84b" + , "initiatingFsp": "$payerFSPID" + "sourceAmount": { + "currency": "$payerCurrency", + "amount": "$fxpSourceAmount" + } + , "targetAmount": { + "currency": "$payeeCurrency"", + "amount": "$fxpTargetAmount" + } + , "charges": [ + { + "chargeType": "Conversion fee" + , "sourceAmount": { + "currency": "$payerCurrency" + , "amount": "$fxpChargesSource" + } + , "targetAmount": { + "currency": "$payeeCurrency" + , "amount": "$fxpChargesTarget" + } + } + ] + , "validity": "2021-08-25T14:17:09.663+01:00" + } + } +end note +!if ($shortCutSingleFXP != true) +FXP->>S1:Here's the signed conversion object +activate S1 +!if ($simplified != true) +S1-->>FXP:200 Gotcha +!endif +deactivate FXP +S1->>D1:Here's the signed conversion object\n**PUT /fxQuotes/828cc75f-1654-415e-8fcd-df76cc9329b9** +activate D1 +!if ($simplified != true) +D1-->>S1:Gotcha +!endif +deactivate S1 +!else +FXP-->>D1:Here's the signed conversion object\n**PUT /fxQuotes/828cc75f-1654-415e-8fcd-df76cc9329b9** +activate D1 +!endif + +end group + +== Sender Confirmation == + +D1->PayerCBS:Here's the quote for the transfer\nIt expires at 2021-08-25T14:17:09.663+01:00 +PayerCBS->A1:Hi, $senderName: I can do the transfer.\nIt'll cost you $totalChargesSourceCurrency $payerCurrency in fees\nand $receiverName will receive\n$payeeReceiveAmount $payeeCurrency.\nLet me know if you want to go ahead +A1-->PayerCBS:Great! Yes please, go ahead + +PayerCBS-->D1: Payer has accepted the terms please proceed + +== Transfer Phase == +D1->D1:First, activate the conversion +D1->>S1:Please confirm your part of the transfer +note over D1 +**POST /fxTransfers** +{ + "commitRequestId": "77c9d78d-c26a-4474-8b3c-99b96a814bfc" + , "determiningTransactionId": "d9ce59d4-3598-4396-8630-581bb0551451" + , "requestingFsp": "$payerFSPID" + , "respondingFxp": "FDH_FX" + , "sourceAmount": { + "currency": "$payerCurrency", + "amount": "$fxpSourceAmount" + } + , "targetAmount": { + "currency": "$payeeCurrency", + "amount": "$fxpTargetAmount" + } + , "condition": "bdbcf517cfc7e474392935781cc14043602e53dc2e8e8452826c5241dfd5e7ab" +} +end note +activate S1 +!if ($simplified != true) +S1-->>D1:202 I'll get back to you +!endif +deactivate D2 +!if ($hideSwitchDetail != true) +S1->S1:OK, so this is an FX confirmation. +S1->S1: Does the sender have an account in this currency?\nYes, it does. +!endif +S1->S1: Liquidity check and reserve on Payer DFSP's account +!if ($hideSwitchDetail != true) +note over S1 +Reservations: + +**$payerFSPID has a reservation of $fxpSourceAmount $payerCurrency** +end note +!endif +S1->>FXP:Please confirm the currency conversion part of the transfer\n** POST /fxTransfers** +activate FXP +!if ($simplified != true) +FXP-->>S1:202 I'll get back to you +!endif +deactivate S1 +FXP->FXPBackend:Reserve funds for FX conversion +FXPBackend->FXP:Success +FXP->>S1:Confirmed. Here's the fulfilment +note over FXP +**PUT /fxTransfers/77c9d78d-c26a-4474-8b3c-99b96a814bfc** +{ + "fulfilment": "188909ceb6cd5c35d5c6b394f0a9e5a0571199c332fbd013dc1e6b8a2d5fff42" + , "completedTimeStamp": "2021-08-25T14:17:08.175+01:00" + , "conversionState": "RESERVED" +} +end note +activate S1 +!if ($simplified != true) +S1-->>FXP:200 Gotcha +!endif +deactivate FXP +!if ($simplified != true) +S1->S1:Check fulfilment matches and cancel if not. +alt Conversion failed +S1->FXP:Sorry. Conversion failed +note over FXP +**PATCH /fxTransfers/77c9d78d-c26a-4474-8b3c-99b96a814bfc** +{ + "fulfilment": "188909ceb6cd5c35d5c6b394f0a9e5a0571199c332fbd013dc1e6b8a2d5fff42" + , "completedTimeStamp": "2021-08-25T14:17:08.175+01:00" + , "conversionState": "ABORTED" +} +end note +activate FXP +FXP-->S1:Acknowledged +FXP->FXP:Remove any reservations\nor obligations +deactivate FXP + +S1->>D1:Sorry. Conversion failed +note over S1 +**PUT /fxTransfers/77c9d78d-c26a-4474-8b3c-99b96a814bfc/error** +{ + "errorCode": "9999" + , "errorDescription": "Whatever the error was" +} +end note +else Conversion succeeded +S1->D1:Conversion succeeded subject to transfer success\n**PUT /fxTransfers/77c9d78d-c26a-4474-8b3c-99b96a814bfc** + +end +!else +S1->D1:Conversion succeeded subject to transfer success\n**PUT /fxTransfers/77c9d78d-c26a-4474-8b3c-99b96a814bfc** +!endif +activate D1 +!if ($simplified != true) +D1-->S1:200 Gotcha +!endif +deactivate S1 +D1->D1:OK, so that's all right\nNow I can send the transfer itself + +D1->S1:Please do the transfer **POST /transfers** +!if ($simplified != true) +note over D1 +POST /transfers +{ + "transferId": "c720ae14-fc72-4acd-9113-8b601b34ba4d" + , "payeeFsp": "$payeeFSPID" + , "payerFsp": "$payerFSPID" + , "amount": { + "currency": "$payeeCurrency" + , "amount": "$targetAmount" + } + , "transaction": { + , "transactionId": "d9ce59d4-3598-4396-8630-581bb0551451" + , "quoteId": "382987a8-75ce-4037-b500-c475e08c1727" + , "payee": { + "fspId": "$payeeFSPID" + , "partyIdInfo": { + "partyIdType": "MSISDN" + , "partyIdentifier": "$payeeMSISDN" + } + } + , "payer": { + "fspId": "$payerFSPID" + , "partyIdInfo": { + "partyIdType": "MSISDN" + , "partyIdentifier": "$payerMSISDN" + } + } + } +} +end note +!endif +activate S1 +!if ($simplified != true) +S1-->D1:202 I'll get back to you +!endif +deactivate D1 +!if ($hideSwitchDetail != true) +S1->S1:Is there a dependent transfer? Yes +!endif +S1->S1:Perform liquidity check and reserve funds\nagainst creditor party to dependent transfer +note over S1 +Reservations: + +$payerFSPID has a reservation of $fxpSourceAmount $payerCurrency +**FDH_FX has a reservation of $targetAmount $payeeCurrency** +end note + +S1->D2:Please do the transfer\n**POST /transfers** +activate D2 +!if ($simplified != true) +D2-->S1:202 I'll get back to you +!endif +deactivate S1 +D2->D2:Let me check that the terms of the dependent transfer\nare the same as the ones I agreed to\nand that the fulfilment and condition match +D2->D2:Yes, they do. I approve the transfer +D2->PayeeCBS:Please credit $receiverName's account with $payeeReceiveAmount $payeeCurrency +D2->S1:Transfer is confirmed, here's the fulfilment +note over D2 +**PUT /transfers/c720ae14-fc72-4acd-9113-8b601b34ba4d** +{ + "fulfilment": "mhPUT9ZAwd-BXLfeSd7-YPh46rBWRNBiTCSWjpku90s" + , "completedTimestamp": "2021-08-25T14:17:08.227+01:00" + , "transferState": "COMMITTED" +} +end note +activate S1 +!if ($simplified != true) +S1-->D2:200 Gotcha +!endif +deactivate D2 +!if ($hideSwitchDetail != true) +S1->S1:Is there a dependent transfer?\nYes, there is. +S1->S1:Is this dependency against the debtor party to the transfer?\nYes, it is. +S1->S1:Create an obligation from the debtor party to the party named in the dependency (the FXP) +S1->S1:Is the transfer denominated in the currency of the payee receive amount?\nYes, it is. +S1->S1:Create an obligation from the party named in the dependency\nto the creditor party for the transfer +!else +S1->S1:Create obligations from the payer to the FXP and from FXP to the payee +!endif +S1->FXP:The transfer succeeded.\nYou can clear it in your ledgers +note over S1 +**PATCH /fxTransfers/77c9d78d-c26a-4474-8b3c-99b96a814bfc** +{ + "fulfilment": "2e6870fb4eda9c2a29ecf376ceb5b05c" + , "completedTimeStamp": "2021-08-25T14:17:08.175+01:00" + , "conversionState": "COMMITTED" +} +end note +activate FXP +FXP->FXP:Let's just check: does this match the stuff I sent? +FXP->FXP:It does. Great. I'll clear the conversion +FXP-->S1:200 Gotcha +deactivate FXP +note over S1 + Ledger positions: + $payerFSPID has a debit of $fxpSourceAmount $payerCurrency + FDH_FX has a credit of $fxpSourceAmount $payerCurrency + FDH_FX has a debit of $fxpTargetAmount $payeeCurrency + $payeeFSPID has a credit of $targetAmount $payeeCurrency +end note +S1->D1:Transfer is complete\n**PUT /transfers/c720ae14-fc72-4acd-9113-8b601b34ba4d** +activate D1 +!if ($simplified != true) +D1-->S1:200 Gotcha +!endif +deactivate S1 +D1->D1:Commit the funds in my ledgers +D1->A1:Transfer was completed successfully +deactivate D1 + +@enduml diff --git a/docs/Proxy/FXAPI_POC_payer_conversion_SEND.plantuml b/docs/Proxy/FXAPI_POC_payer_conversion_SEND.plantuml new file mode 100644 index 00000000..7421e5fe --- /dev/null +++ b/docs/Proxy/FXAPI_POC_payer_conversion_SEND.plantuml @@ -0,0 +1,1423 @@ +@startuml + +!$simplified = false +!$shortCutSingleFXP = false +!$hideSwitchDetail = false +!$advancedCoreConnectorFlow = true +!$senderLastName = "" +!$senderDOB = "1966-06-16" +!$receiverName = "Yaro" +!$receiverFirstName = "Yaro" +!$receiverMiddleName = "" +!$receiverLastName = "" +!$receiverDOB = "1966-06-16" +!$payerCurrency = "BWP" +!$payeeCurrency = "TZS" +!$payerFSPID = "PayerFSP" +!$payeeFSPID = "PayeeFSP" +!$fxpID = "FDH_FX" +!$payerMSISDN = "26787654321" +!$payeeMSISDN = "2551234567890" +!$payeeReceiveAmount = "44000" +!$payerSendAmount = "300" +!$payeeFee = "4000" +!$targetAmount = "48000" +!$fxpChargesSource = "33" +!$fxpChargesTarget = "6000" +!$fxpSourceAmount = "300" +!$fxpTargetAmount = "48000" +!$totalChargesSourceCurrency = "55" +!$totalChargesTargetCurrency = "10000" +!$conversionRequestId = "828cc75f-1654-415e-8fcd-df76cc9329b9" +!$conversionId = "581f68ef-b54f-416f-9161-ac34e889a84b" +!$homeTransactionId = "string" +!$quoteId = "382987a8-75ce-4037-b500-c475e08c1727" +!$transactionId = "d9ce59d4-3598-4396-8630-581bb0551451" +!$quotePayerExpiration = "2021-08-25T14:17:09.663+01:00" +!$quotePayeeExpiration = "2021-08-25T14:17:09.663+01:00" +!$commitRequestId = "77c9d78d-c26a-4474-8b3c-99b96a814bfc" +!$determiningTransferId = "d9ce59d4-3598-4396-8630-581bb0551451" +!$transferId = "d9ce59d4-3598-4396-8630-581bb0551451" +!$fxCondition = "GRzLaTP7DJ9t4P-a_BA0WA9wzzlsugf00-Tn6kESAfM" +!$condition = "HOr22-H3AfTDHrSkPjJtVPRdKouuMkDXTR4ejlQa8Ks" + +title Remittance Transfer using Mojaloop FX APIs POC\nPayer DFSP requests conversion with SEND amount +actor "$senderName" as A1 +box "Payer DFSP" #LightBlue + participant "Payer CBS" as PayerCBS + participant "Core Connector" as PayerCC + participant "Payer\nMojaloop\nConnector" as D1 +end box + +participant "Mojaloop Switch" as S1 + +box "Discovery Service" #LightYellow + participant "ALS Oracle" as ALS +end box + +box "FX provider" + participant "FXP\nConnector" as FXP + participant "Backend FX API" as FXPBackend +end box + +box "Payee DFSP" #LightBlue + participant "Payee\nMojaloop\nConnector" as D2 + participant "Core Connector" as PayeeCC +end box + +actor "$receiverName" as A2 +autonumber + +A1->PayerCBS:I'd like to pay $receiverName\n$payerSendAmount $payerCurrency, please +PayerCBS->PayerCC: Initiate remittance transfer +!if ($advancedCoreConnectorFlow != true) + PayerCC->D1: **POST /transfers** + !if ($simplified != true) + note right of PayerCC + { + "homeTransactionId": "$homeTransactionId", + "from": { + "dateOfBirth": "$senderDOB", + "displayName": "$senderName", + "firstName": "$senderFirstName", + "middleName": "$senderMiddleName", + "lastName": "$senderLastName" + "fspId": "$payerFSPID", + "idType": "MSISDN", + "idValue": "$payerMSISDN" + }, + "to": { + "idType": "MSISDN", + "idValue": "$payeeMSISDN" + }, + "amountType": "SEND", + "currency": "$payerCurrency", + "amount": "$payerSendAmount" + } + end note + !endif +!else +PayerCC->D1: **GET /parties/MSISDN/$payeeMSISDN** +!endif + +== Discovery Phase == +activate D1 +D1->>S1:I want to send to MSISDN $payeeMSISDN\n**GET /parties/MSISDN/$payeeMSISDN** +activate S1 +!if ($simplified != true) +S1-->>D1:202 I'll get back to you +!endif +deactivate D1 +S1->ALS:Who owns MSISDN $payeeMSISDN? +activate ALS +ALS-->S1:It's $payeeFSPID +deactivate ALS +S1->>D2:Do you own MSISDN $payeeMSISDN? +activate D2 +!if ($simplified != true) +D2-->>S1:202 I'll get back to you +!endif +D2->D2: Check Sanction list status & trigger a refresh of the status +D2->PayeeCC: Check account and get currency type +!if ($simplified != true) +PayeeCC-->D2: Result +!endif +deactivate S1 +D2->>S1:Yes, it's $receiverName. He can receive in $payeeCurrency\n**PUT /parties/MSISDN/$payeeMSISDN** +!if ($simplified != true) +note left of D2 + PUT /parties + { + "partyIdInfo": { + "partyIdType": "MSISDN", + "partyIdentifier": "$payeeMSISDN", + "fspId": "$payeeFSPID" + }, + "merchantClassificationCode": "string", + "name": "$receiverName", + "personalInfo": { + "complexName": { + "firstName": "$receiverFirstName", + "middleName": "$receiverMiddleName", + "lastName": "$receiverLastName" + }, + "dateOfBirth": "$receiverDOB", + "kycInformation": "" + }, + "supportedCurrencies": [ "$payeeCurrency" ] + } +end note +!else +note over D2 + Payee Info with Encrypted KYC Data +end note +!endif +activate S1 +!if ($simplified != true) +S1-->>D2:200 Gotcha +!endif +deactivate D2 +S1->>D1:Yes, it's $receiverName. He can receive in $payeeCurrency\n**PUT /parties/MSISDN/$payeeMSISDN** +activate D1 +!if ($simplified != true) +D1-->>S1:200 Gotcha +!endif +deactivate S1 + +!if ($advancedCoreConnectorFlow != true) + D1->PayerCC: Here is the party information\nand supported currencies + note right of PayerCC + { + "transferId": "$transferId", + "homeTransactionId": "$homeTransactionId", + "from": { + "dateOfBirth": "$senderDOB", + "displayName": "$senderName", + "firstName": "$senderFirstName", + "middleName": "$senderMiddleName", + "lastName": "$senderLastName" + "fspId": "$payerFSPID", + "idType": "MSISDN", + "idValue": "$payerMSISDN" + }, + "to": { + "type": "CONSUMER", + "idType": "MSISDN", + "idValue": "$payeeMSISDN", + "displayName": "$receiverName", + "firstName": "$receiverFirstName", + "middleName": "$receiverMiddleName", + "lastName": "$receiverLastName" + "dateOfBirth": "$receiverDOB", + + "fspId": "$payeeFSPID" + "supportedCurrencies": [ "$payeeCurrency" ] + "kycInformation": "" + }, + "amountType": "SEND", + "currency": "$payerCurrency", + "amount": "$payerSendAmount" + "currentState": "**WAITING_FOR_PARTY_ACCEPTANCE**", + "getPartiesResponse": { + "body": { + "partyIdInfo": { + "partyIdType": "MSISDN", + "partyIdentifier": "$payeeMSISDN", + "fspId": "$payeeFSPID" + }, + "merchantClassificationCode": "string", + "name": "$receiverName", + "personalInfo": { + "complexName": { + "firstName": "$receiverFirstName", + "middleName": "$receiverMiddleName", + "lastName": "$receiverLastName" + }, + "dateOfBirth": "$receiverDOB", + "kycInformation": "" + }, + "supportedCurrencies": [ "$payeeCurrency" ] + }, + "headers": {} + } + } + end note +!else + D1->PayerCC: Here is the party information\nand supported currencies + !if ($simplified != true) + note right of PayerCC + { + "party": { + "body": { + "partyIdInfo": { + "partyIdType": "MSISDN", + "partyIdentifier": "$payeeMSISDN", + "fspId": "$payeeFSPID" + }, + "merchantClassificationCode": "string", + "name": "$receiverName", + "personalInfo": { + "complexName": { + "firstName": "$receiverFirstName", + "middleName": "$receiverMiddleName", + "lastName": "$receiverLastName" + }, + "dateOfBirth": "$receiverDOB", + "kycInformation": "" + }, + "supportedCurrencies": [ "$payeeCurrency" ] + }, + "headers": {} + }, + "currentState": "COMPLETED" + } + end note + !endif +!endif + +PayerCC->PayerCBS:Here's are the receiver details +PayerCBS->A1:Hi, $senderName: The number belongs to $receiverName \nLet me know if you want to go ahead +A1->PayerCBS:Yes please, go ahead + +PayerCBS->PayerCC: Payer has accepted the party information + +!if ($shortCutSingleFXP != true) +== Currency conversion == + +!if ($advancedCoreConnectorFlow != true) +PayerCC->>D1:Get quotation\n**PUT /transfers/$transferId** +note right of PayerCC +{ + "acceptParty": true +} +end note +D1->D1:Hmmm. I can only send in $payerCurrency.\nI need to get some currency conversion +!else +PayerCC->PayerCC:Hmmm. I can only send in $payerCurrency.\nI need to get some currency conversion +PayerCC->>D1:What FXPs do you know about?\n**GET /services/FXP** +!endif + +D1->>S1:What FXPs do you know about?\n**GET /services/FXP** +activate S1 +!if ($simplified != true) +S1-->>D1:202 I'll get back to you +!endif +deactivate D1 +S1->ALS:What FXPs do you know about? +activate ALS +ALS-->S1:FDH FX +deactivate ALS +S1->>D1:Here are the available FXPs:FDH FX +note left of S1 + PUT /services/FXP + { + "providers": [ + "$fxpID" + ] + } +end note +activate D1 +!if ($simplified != true) +D1-->>S1:200 Gotcha +!endif + +!if ($advancedCoreConnectorFlow != true) +' TODO: We can pause the execution here if required to allow the core connector to select the FXP +D1->D1:I'll ask FDH FX to perform my conversion +!else +D1->>PayerCC:Here are the available FXPs +note right of PayerCC + { + "providers": [ + "$fxpID" + ] + } +end note + +PayerCC->PayerCC:I'll ask FDH FX to perform my conversion +PayerCC->D1: I want to get a quote from this FXP\n**POST /fxQuotes** + !if ($simplified != true) + note right of PayerCC + { + "homeTransactionId": "$homeTransactionId", + "conversionRequestId": "$conversionRequestId", + "conversionTerms": { + "conversionId": "$conversionId", + "initiatingFsp": "$payerFSPID", + "counterPartyFsp": "$fxpID", + "amountType": "SEND", + "sourceAmount": { + "currency": "$payerCurrency", + "amount": "$payerSendAmount" + }, + "targetAmount": { + "currency": "$payeeCurrency" + }, + "expiration": "2021-08-25T14:17:09.663+01:00" + } + } + end note + !endif +!endif + + +!endif + + + +deactivate S1 + + +!if ($shortCutSingleFXP != true) +D1->>S1:Here is the initial version of the transfer.\nPlease quote me for the currency conversion. +!else +D1->>FXP:Here is the initial version of the transfer.\nPlease quote me for the currency conversion. +!endif +note right of D1 + **post /fxQuotes** + { + "conversionRequestId": "$conversionRequestId", + "conversionTerms": { + "conversionId": "$conversionId", + "initiatingFsp": "$payerFSPID", + "counterPartyFsp": "$fxpID", + "amountType": "SEND", + "sourceAmount": { + "currency": "$payerCurrency", + "amount": "$payerSendAmount" + }, + "targetAmount": { + "currency": "$payeeCurrency" + }, + "expiration": "2021-08-25T14:17:09.663+01:00" + } + } +end note +!if ($shortCutSingleFXP != true) +activate S1 +!if ($simplified != true) +S1-->>D1:202 I'll get back to you +!endif +deactivate D1 +S1->>FXP:Here is the initial version of the transfer.\nPlease quote me for the currency conversion.\n**POST /fxQuote** +activate FXP +!if ($simplified != true) +FXP-->>S1:202 I'll get back to you +!endif +deactivate S1 +!else +!if ($simplified != true) +FXP-->>D1:202 I'll get back to you +!endif +!endif +FXP->FXPBackend:Lookup FX rate +!if ($simplified != true) +note right of FXP + **post /fxQuotes** + { + "conversionRequestId": "$conversionRequestId", + "conversionTerms": { + "conversionId": "$conversionId", + "initiatingFsp": "$payerFSPID", + "counterPartyFsp": "$fxpID", + "amountType": "SEND", + "sourceAmount": { + "currency": "$payerCurrency", + "amount": "$payerSendAmount" + }, + "targetAmount": { + "currency": "$payeeCurrency" + }, + "expiration": "2021-08-25T14:17:09.663+01:00" + } + } +end note +!endif +FXPBackend-->FXP:Return FX rate +note over FXPBackend + I will add a $fxpChargesSource $payerCurrency fee for undertaking the conversion. + Now I'll set an expiry time, sign the quotation object, +end note +!if ($simplified != true) +note right of FXP + { + "conversionTerms": { + "conversionId": "$conversionId", + "initiatingFsp": "$payerFSPID", + "counterPartyFsp": "$fxpID", + "amountType": "SEND", + "sourceAmount": { + "currency": "$payerCurrency", + "amount": "$fxpSourceAmount" + }, + "targetAmount": { + "currency": "$payeeCurrency", + "amount": "$fxpTargetAmount" + }, + "expiration": "2021-08-25T14:17:09.663+01:00" + "charges": [ + { + "chargeType": "string", + "sourceAmount": { + "currency": "$payerCurrency", + "amount": "$fxpChargesSource" + }, + "targetAmount": { + "currency": "$payeeCurrency", + "amount": "$fxpChargesTarget" + } + } + ] + } + } +end note +!endif + +note over FXP + Now I'll sign the quotation object, + create an ILP prepare packet and return it in the intermediary object. + + NOTE: the ILP prepare packet contains the following items, all encoded: + - The amount being sent (i.e. in the source currency) + - An expiry time + - The condition + - The name of the FXP + - The content of the conversion terms +end note + +note left of FXP + **PUT /fxQuotes/$conversionRequestId** + { + "condition": "$fxCondition", + "conversionTerms": { + "conversionId": "$conversionId", + "initiatingFsp": "$payerFSPID", + "counterPartyFsp": "$fxpID", + "amountType": "SEND", + "sourceAmount": { + "currency": "$payerCurrency", + "amount": "$fxpSourceAmount" + }, + "targetAmount": { + "currency": "$payeeCurrency", + "amount": "$fxpTargetAmount" + }, + "expiration": "2021-08-25T14:17:09.663+01:00" + "charges": [ + { + "chargeType": "string", + "sourceAmount": { + "currency": "$payerCurrency", + "amount": "$fxpChargesSource" + }, + "targetAmount": { + "currency": "$payeeCurrency", + "amount": "$fxpChargesTarget" + } + } + ] + } + } +end note +!if ($shortCutSingleFXP != true) +FXP->>S1:Here's the signed conversion object +activate S1 +!if ($simplified != true) +S1-->>FXP:200 Gotcha +!endif +deactivate FXP +S1->>D1:Here's the signed conversion object\n**PUT /fxQuotes/$conversionRequestId** +activate D1 +!if ($simplified != true) +D1-->>S1:Gotcha +!endif +deactivate S1 +!else +FXP-->>D1:Here's the signed conversion object\n**PUT /fxQuotes/$conversionRequestId** +!if ($simplified != true) +D1-->>FXP:202 I'll get back to you +!endif +activate D1 +!endif + + +!if ($advancedCoreConnectorFlow != true) + D1-->PayerCC: Here are the conversion terms + note right of PayerCC + { + "transferId": "$transferId", + "homeTransactionId": "$homeTransactionId", + "from": { + "dateOfBirth": "$senderDOB", + "displayName": "$senderName", + "firstName": "$senderFirstName", + "middleName": "$senderMiddleName", + "lastName": "$senderLastName" + "fspId": "$payerFSPID", + "idType": "MSISDN", + "idValue": "$payerMSISDN" + }, + "to": { + "type": "CONSUMER", + "idType": "MSISDN", + "idValue": "$payeeMSISDN", + "displayName": "$receiverName", + "firstName": "$receiverFirstName", + "middleName": "$receiverMiddleName", + "lastName": "$receiverLastName" + "dateOfBirth": "$receiverDOB", + + "fspId": "$payeeFSPID" + "supportedCurrencies": [ "$payeeCurrency" ] + "kycInformation": "" + }, + "amountType": "SEND", + "currency": "$payerCurrency", + "amount": "$payerSendAmount" + "currentState": "**WAITING_FOR_CONVERSION_ACCEPTANCE**", + "getPartiesResponse": { + + }, + "conversionRequestId": "$conversionRequestId", + "fxQuotesResponse": { + "body": { + "condition": "$fxCondition", + "conversionTerms": { + "conversionId": "$conversionId", + "initiatingFsp": "$payerFSPID", + "counterPartyFsp": "$fxpID", + "amountType": "SEND", + "sourceAmount": { + "currency": "$payerCurrency", + "amount": "$fxpSourceAmount" + }, + "targetAmount": { + "currency": "$payeeCurrency", + "amount": "$fxpTargetAmount" + }, + "expiration": "2021-08-25T14:17:09.663+01:00" + "charges": [ + { + "chargeType": "string", + "sourceAmount": { + "currency": "$payerCurrency", + "amount": "$fxpChargesSource" + }, + "targetAmount": { + "currency": "$payeeCurrency", + "amount": "$fxpChargesTarget" + } + } + ] + } + }, + "headers": {} + }, + "fxQuotesResponseSource": "$payeeFSPID", + } + end note +!else + D1->PayerCC: Here are the conversion terms + !if ($simplified != true) + note right of PayerCC + { + "homeTransactionId": "$homeTransactionId", + "condition": "$fxCondition", + "conversionTerms": { + "conversionId": "$conversionId", + "initiatingFsp": "$payerFSPID", + "counterPartyFsp": "$fxpID", + "amountType": "SEND", + "sourceAmount": { + "currency": "$payerCurrency", + "amount": "$fxpSourceAmount" + }, + "targetAmount": { + "currency": "$payeeCurrency", + "amount": "$fxpTargetAmount" + }, + "expiration": "2021-08-25T14:17:09.663+01:00" + "charges": [ + { + "chargeType": "string", + "sourceAmount": { + "currency": "$payerCurrency", + "amount": "$fxpChargesSource" + }, + "targetAmount": { + "currency": "$payeeCurrency", + "amount": "$fxpChargesTarget" + } + } + ] + } + } + end note + !endif +!endif + + +== Agreement Phase == + + +!if ($advancedCoreConnectorFlow != true) +PayerCC->D1: I want to get a quote from the FSP\nPUT /transfers +note right of PayerCC +{ + "acceptConversion": true +} +end note +!else +PayerCC->D1: I want to get a quote from the FSP\n**POST /quotes** + !if ($simplified != true) + note right of PayerCC + { + "fspId": "$payeeFSPID", + "quotesPostRequest": { + "quoteId": "$quoteId", + "transactionId": "$transactionId", + "payee": { + "partyIdInfo": { + "partyIdType": "MSISDN", + "partyIdentifier": "$payeeMSISDN", + "fspId": "$payeeFSPID" + }, + "name": "$receiverName", + "personalInfo": { + "complexName": { + "firstName": "$receiverFirstName", + "middleName": "$receiverMiddleName", + "lastName": "$receiverLastName" + }, + "dateOfBirth": "$receiverDOB", + "kycInformation": "" + }, + "supportedCurrencies": [ "$payeeCurrency" ] + }, + "payer": { + "partyIdInfo": { + "partyIdType": "MSISDN", + "partyIdentifier": "$payerMSISDN", + "fspId": "$payerFSPID" + }, + "name": "$senderName", + "personalInfo": { + "complexName": { + "firstName": "$senderFirstName", + "middleName": "$senderMiddleName", + "lastName": "$senderLastName" + }, + "dateOfBirth": "$senderDOB" + } + }, + "amountType": "SEND", + "amount": { + "currency": "$payeeCurrency", + "amount": "$fxpTargetAmount" + }, + "converter": "PAYER", + "expiration": "$quotePayerExpiration" + } + } + end note + !endif +!endif + + +D1->>S1:Please quote for a transfer which sends $fxpTargetAmount $payeeCurrency.\n**POST /quotes** +!if ($simplified != true) +note right of D1 +POST /quotes + + { + "quoteId": "$quoteId", + "transactionId": "$transactionId", + "payee": { + "partyIdInfo": { + "partyIdType": "MSISDN", + "partyIdentifier": "$payeeMSISDN", + "fspId": "$payeeFSPID" + }, + "name": "$receiverName", + "personalInfo": { + "complexName": { + "firstName": "$receiverFirstName", + "middleName": "$receiverMiddleName", + "lastName": "$receiverLastName" + }, + "dateOfBirth": "$receiverDOB", + "kycInformation": "" + }, + "supportedCurrencies": [ "$payeeCurrency" ] + }, + "payer": { + "partyIdInfo": { + "partyIdType": "MSISDN", + "partyIdentifier": "$payerMSISDN", + "fspId": "$payerFSPID" + }, + "name": "$senderName", + "personalInfo": { + "complexName": { + "firstName": "$senderFirstName", + "middleName": "$senderMiddleName", + "lastName": "$senderLastName" + }, + "dateOfBirth": "$senderDOB" + } + }, + "amountType": "SEND", + "amount": { + "currency": "$payeeCurrency", + "amount": "$fxpTargetAmount" + }, + "converter": "PAYER", + "expiration": "$quotePayerExpiration" + } +end note +!endif +activate S1 +!if ($simplified != true) +S1-->>D1:202 I'll get back to you +!endif + +S1->>D2:**POST /quotes** +activate D2 +!if ($simplified != true) +D2-->>S1:202 I'll get back to you +!endif +deactivate S1 +D2->PayeeCC:**POST /quoterequests** +!if ($simplified != true) +note right of D2 +{ + "quoteId": "$quoteId", + "transactionId": "$transactionId", + "payee": { + "partyIdInfo": { + "partyIdType": "MSISDN", + "partyIdentifier": "$payeeMSISDN", + "fspId": "$payeeFSPID" + }, + "name": "$receiverName", + "personalInfo": { + "complexName": { + "firstName": "$receiverFirstName", + "middleName": "$receiverMiddleName", + "lastName": "$receiverLastName" + }, + "dateOfBirth": "$receiverDOB", + "kycInformation": "" + }, + "supportedCurrencies": [ "$payeeCurrency" ] + }, + "payer": { + "partyIdInfo": { + "partyIdType": "MSISDN", + "partyIdentifier": "$payerMSISDN", + "fspId": "$payerFSPID" + }, + "name": "$senderName", + "personalInfo": { + "complexName": { + "firstName": "$senderFirstName", + "middleName": "$senderMiddleName", + "lastName": "$senderLastName" + }, + "dateOfBirth": "$senderDOB" + } + }, + "amountType": "SEND", + "amount": { + "currency": "$payeeCurrency", + "amount": "$fxpTargetAmount" + }, + "converter": "PAYER", + "expiration": "$quotePayerExpiration" +} +end note +!endif +PayeeCC->PayeeCC:OK, so I will charge $payeeFee $payeeCurrency for this.\nNow I create terms of the transfer +PayeeCC-->D2:Here are the terms +!if ($simplified != true) +note right of D2 +{ + "quoteId": "$quoteId", + "transactionId": "$transactionId", + "payeeFspFeeAmount": "$payeeFee", + "payeeFspFeeAmountCurrency": "$payeeCurrency", + "payeeReceiveAmount": "$payeeReceiveAmount", + "payeeReceiveAmountCurrency": "$payeeCurrency", + "transferAmount": "$targetAmount", + "transferAmountCurrency": "$payeeCurrency" + "expiration": "$quotePayerExpiration" +} +end note +!endif +D2->D2:Now I will sign the transaction object +D2->>S1:Here's the signed quote +note left of D2 + **put /quotes/$quoteId** + + { + "transferAmount": { + "currency": "$payeeCurrency", + "amount": "$targetAmount" + }, + "payeeReceiveAmount": { + "currency": "$payeeCurrency", + "amount": "$payeeReceiveAmount" + }, + "payeeFspFee": { + "currency": "$payeeCurrency", + "amount": "$payeeFee" + }, + "expiration": "$payeeQuoteExpiration", + "ilpPacket": " + + ", + "condition": "$condition" + } + +end note +activate S1 +!if ($simplified != true) +S1-->>D2:200 Gotcha +!endif +deactivate D2 +S1->>D1:Here's the signed quote\n**PUT /quotes/382987a8-75ce-4037-b500-c475e08c1727** +activate D1 +!if ($simplified != true) +D1-->>S1:200 Gotcha +!endif +deactivate S1 +D1->D1:OK, I can see that there are going to be $payeeFee $payeeCurrency in charges. + +== Sender Confirmation == + +!if ($advancedCoreConnectorFlow != true) + D1-->PayerCC:Here's the quote for the transfer\nIt expires at $quotePayeeExpiration + note right of PayerCC + { + "transferId": "$transferId", + "homeTransactionId": "$homeTransactionId", + "from": { + "dateOfBirth": "$senderDOB", + "displayName": "$senderName", + "firstName": "$senderFirstName", + "middleName": "$senderMiddleName", + "lastName": "$senderLastName" + "fspId": "$payerFSPID", + "idType": "MSISDN", + "idValue": "$payerMSISDN" + }, + "to": { + "type": "CONSUMER", + "idType": "MSISDN", + "idValue": "$payeeMSISDN", + "displayName": "$receiverName", + "firstName": "$receiverFirstName", + "middleName": "$receiverMiddleName", + "lastName": "$receiverLastName" + "dateOfBirth": "$receiverDOB", + + "fspId": "$payeeFSPID" + "supportedCurrencies": [ "$payeeCurrency" ] + "kycInformation": "" + }, + "amountType": "SEND", + "currency": "$payerCurrency", + "amount": "$payerSendAmount" + "currentState": "**WAITING_FOR_QUOTE_ACCEPTANCE**", + "getPartiesResponse": { + + }, + "conversionRequestId": "$conversionRequestId", + "fxQuotesResponse": { + + }, + "fxQuotesResponseSource": "$payeeFSPID", + "quoteId": "$quoteId", + "quoteResponse": { + "body": { + "transferAmount": { + "currency": "$payeeCurrency", + "amount": "$targetAmount" + }, + "payeeReceiveAmount": { + "currency": "$payeeCurrency", + "amount": "$payeeReceiveAmount" + }, + "payeeFspFee": { + "currency": "$payeeCurrency", + "amount": "$payeeFee" + }, + "expiration": "$payeeQuoteExpiration", + "ilpPacket": " + + ", + "condition": "$condition" + }, + "headers": {} + }, + "quoteResponseSource": "$payeeFSPID", + } + end note +!else + D1-->PayerCC:Here's the quote for the transfer\nIt expires at $quotePayeeExpiration + !if ($simplified != true) + note right of PayerCC + { + "quotes": { + "body": { + "transferAmount": { + "currency": "$payeeCurrency", + "amount": "$targetAmount" + }, + "payeeReceiveAmount": { + "currency": "$payeeCurrency", + "amount": "$payeeReceiveAmount" + }, + "payeeFspFee": { + "currency": "$payeeCurrency", + "amount": "$payeeFee" + }, + "expiration": "$payeeQuoteExpiration", + "ilpPacket": " + + ", + "condition": "$condition" + }, + "headers": {} + }, + "currentState": "COMPLETED" + } + end note + !endif +!endif +PayerCC->PayerCBS:Here's the quote +PayerCBS->A1:Hi, $senderName: I can do the transfer.\nIt'll cost you $totalChargesSourceCurrency $payerCurrency($totalChargesTargetCurrency $payeeCurrency) in fees\nand $receiverName will receive\n$payeeReceiveAmount $payeeCurrency.\nLet me know if you want to go ahead +A1->PayerCBS:Great! Yes please, go ahead + +PayerCBS->PayerCC: Payer has accepted the terms please proceed + +== Transfer Phase == + +!if ($advancedCoreConnectorFlow != true) +PayerCC->D1: Proceed with the transfer\nPUT /transfers +note right of PayerCC +{ + "acceptQuote": true +} +end note +!else +PayerCC->D1: Proceed with the transfer\n**POST /fxTransfers** + !if ($simplified != true) + note right of PayerCC + { + "homeTransactionId": "$homeTransactionId", + "commitRequestId": "$commitRequestId", + "determiningTransferId": "$determiningTransferId", + "initiatingFsp": "$payerFSPID", + "counterPartyFsp": "$fxpID", + "amountType": "SEND", + "sourceAmount": { + "currency": "$payerCurrency", + "amount": "$fxpSourceAmount" + }, + "targetAmount": { + "currency": "$payeeCurrency", + "amount": "$fxpTargetAmount" + }, + "condition": "$fxCondition" + } + end note + !endif +!endif + +!if ($advancedCoreConnectorFlow != true) +D1->D1:First, activate the conversion +!endif +D1->>S1:Please confirm your part of the transfer +note right of D1 +**POST /fxTransfers** +{ + "commitRequestId": "$commitRequestId", + "determiningTransferId": "$determiningTransferId", + "initiatingFsp": "$payerFSPID", + "counterPartyFsp": "$fxpID", + "amountType": "SEND", + "sourceAmount": { + "currency": "$payerCurrency", + "amount": "$fxpSourceAmount" + }, + "targetAmount": { + "currency": "$payeeCurrency", + "amount": "$fxpTargetAmount" + }, + "condition": "$fxCondition" +} +end note +activate S1 +!if ($simplified != true) +S1-->>D1:202 I'll get back to you +!endif +deactivate D2 +!if ($hideSwitchDetail != true) +S1->S1:OK, so this is an FX confirmation. +S1->S1: Is there any transfer with determiningTransactionId?\nNo, it does'nt. +!endif +S1->S1: Liquidity check and reserve on Payer DFSP's account +!if ($hideSwitchDetail != true) +note over S1 +Reservations: + +**$payerFSPID has a reservation of $fxpSourceAmount $payerCurrency** +end note +!endif +S1->>FXP:Please confirm the currency conversion part of the transfer\n** POST /fxTransfers** +activate FXP +!if ($simplified != true) +FXP-->>S1:202 I'll get back to you +!endif +deactivate S1 +FXP->FXPBackend:Reserve funds for FX conversion +note right of FXP +**POST /fxTransfers** +{ + "homeTransactionId": "$homeTransactionId", + "commitRequestId": "$commitRequestId", + "determiningTransferId": "$determiningTransferId", + "initiatingFsp": "$payerFSPID", + "counterPartyFsp": "$fxpID", + "amountType": "SEND", + "sourceAmount": { + "currency": "$payerCurrency", + "amount": "$fxpSourceAmount" + }, + "targetAmount": { + "currency": "$payeeCurrency", + "amount": "$fxpTargetAmount" + }, + "condition": "$fxCondition" +} +end note +FXPBackend-->FXP:Success +note right of FXP +{ + "homeTransactionId": "$homeTransactionId", + "completedTimestamp": "2021-08-25T14:17:08.175+01:00", + "conversionState": "RESERVED" +} +end note +FXP->>S1:Confirmed. Here's the fulfilment +note left of FXP +**PUT /fxTransfers/$commitRequestId** +{ + "fulfilment": "188909ceb6cd5c35d5c6b394f0a9e5a0571199c332fbd013dc1e6b8a2d5fff42", + "completedTimestamp": "2021-08-25T14:17:08.175+01:00", + "conversionState": "RESERVED" +} +end note +activate S1 +!if ($simplified != true) +S1-->>FXP:200 Gotcha +!endif +deactivate FXP +!if ($simplified != true) +S1->S1:Check fulfilment matches and cancel if not. +alt Conversion failed +S1->FXP:Sorry. Conversion failed +note over FXP +**PATCH /fxTransfers/$commitRequestId** +{ + "completedTimestamp": "2021-08-25T14:17:08.175+01:00", + "conversionState": "ABORTED" +} +end note +activate FXP +FXP-->S1:Acknowledged +FXP->FXPBackend:Remove any reservations or obligations +note right of FXP +**PUT /fxTransfers/$commitRequestId** +{ + "completedTimestamp": "2021-08-25T14:17:08.175+01:00", + "conversionState": "ABORTED" +} +end note +FXPBackend-->FXP:Ok +deactivate FXP + +S1->>D1:Sorry. Conversion failed +note over S1 +**PUT /fxTransfers/$commitRequestId/error** +{ + "errorCode": "9999" + , "errorDescription": "Whatever the error was" +} +end note +else Conversion succeeded +S1->D1:Conversion succeeded subject to transfer success\n**PUT /fxTransfers/77c9d78d-c26a-4474-8b3c-99b96a814bfc** + +end +!else +S1->D1:Conversion succeeded subject to transfer success\n**PUT /fxTransfers/77c9d78d-c26a-4474-8b3c-99b96a814bfc** +!endif +activate D1 +!if ($simplified != true) +D1-->S1:200 Gotcha +!endif +deactivate S1 + +!if ($advancedCoreConnectorFlow != true) + D1->D1:OK, so that's all right\nNow I can send the transfer itself + ' TODO: Need to add PUT /transfers response here +!else + D1-->PayerCC:Confirmed. You can proceed with the transfer. + note right of PayerCC + **PUT /fxTransfers/$commitRequestId** + { + "fulfilment": "188909ceb6cd5c35d5c6b394f0a9e5a0571199c332fbd013dc1e6b8a2d5fff42", + "completedTimestamp": "2021-08-25T14:17:08.175+01:00", + "conversionState": "RESERVED" + } + end note + + PayerCC-->D1:Please do the transfer **POST /simpleTransfers** + !if ($simplified != true) + note right of PayerCC + { + "fspId": "$payeeFSPID", + "transfersPostRequest": { + "transferId": "$transferId", + "payeeFsp": "$payeeFSPID", + "payerFsp": "$payerFSPID", + "amount": { + "currency": "$payeeCurrency", + "amount": "$targetAmount" + }, + "ilpPacket": "", + "condition": "$condition", + "expiration": "2016-05-24T08:38:08.699-04:00" + } + } + end note + !endif +!endif + +D1->S1:Please do the transfer **POST /transfers** +!if ($simplified != true) +note over D1 +POST /transfers +{ + "transferId": "$transferId", + "payeeFsp": "$payeeFSPID", + "payerFsp": "$payerFSPID", + "amount": { + "currency": "$payeeCurrency", + "amount": "$targetAmount" + }, + "ilpPacket": "", + "condition": "$condition", + "expiration": "2016-05-24T08:38:08.699-04:00" +} +end note +!endif +activate S1 +!if ($simplified != true) +S1-->D1:202 I'll get back to you +!endif +deactivate D1 +!if ($hideSwitchDetail != true) +S1->S1:Is there a dependent transfer? Yes +!endif +S1->S1:Perform liquidity check and reserve funds\nagainst creditor party to dependent transfer +note over S1 +Reservations: + +$payerFSPID has a reservation of $fxpSourceAmount $payerCurrency +**$fxpID has a reservation of $targetAmount $payeeCurrency** +end note + +S1->D2:Please do the transfer\n**POST /transfers** +activate D2 +!if ($simplified != true) +D2-->S1:202 I'll get back to you +!endif +deactivate S1 +D2->D2:Let me check that the terms of the dependent transfer\nare the same as the ones I agreed to\nand that the fulfilment and condition match + +D2->PayeeCC:Please credit $receiverName's account with $payeeReceiveAmount $payeeCurrency +!if ($simplified != true) +note right of D2 +**POST /transfers** +{ + "transferId": "$transferId", + "amount": "$targetAmount", + "currency": "$payeeCurrency", + "amountType": "SEND", + "from": { + "dateOfBirth": "$senderDOB", + "displayName": "$senderName", + "firstName": "$senderFirstName", + "middleName": "$senderMiddleName", + "lastName": "$senderLastName" + "fspId": "$payerFSPID", + "idType": "MSISDN", + "idValue": "$payerMSISDN" + }, + "to": { + "dateOfBirth": "$receiverDOB", + "displayName": "$receiverName", + "firstName": "$receiverFirstName", + "middleName": "$receiverMiddleName", + "lastName": "$receiverLastName", + "fspId": "$payeeFSPID", + "idType": "MSISDN", + "idValue": "$payeeMSISDN" + }, + "note": "string", + "quote": { + "quoteId": "$quoteId", + "transactionId": "$transactionId", + "payeeFspFeeAmount": "$payeeFee", + "payeeFspFeeAmountCurrency": "$payeeCurrency", + "payeeReceiveAmount": "$payeeReceiveAmount", + "payeeReceiveAmountCurrency": "$payeeCurrency", + "transferAmount": "$targetAmount", + "transferAmountCurrency": "$payeeCurrency" + "expiration": "$quotePayeeExpiration" + }, + "transactionType": "TRANSFER", + "ilpPacket": { + "data": + } +} +end note +!endif + +PayeeCC-->D2:Done +!if ($simplified != true) +note right of D2 +{ + "homeTransactionId": "string", + "completedTimestamp": "2021-08-25T14:17:08.227+01:00", + "fulfilment": "mhPUT9ZAwd-BXLfeSd7-YPh46rBWRNBiTCSWjpku90s", + **Note: fulfilment is optional: SDK will create if not found** + "transferState": "COMMITTED" +} +end note +!endif + +D2->>S1:Transfer is confirmed, here's the fulfilment +note over D2 +**PUT /transfers/$commitRequestId** +{ + "completedTimestamp": "2021-08-25T14:17:08.227+01:00", + "fulfilment": "mhPUT9ZAwd-BXLfeSd7-YPh46rBWRNBiTCSWjpku90s", + "transferState": "COMMITTED" +} +end note +activate S1 +!if ($simplified != true) +S1-->>D2:200 Gotcha +!endif +deactivate D2 +!if ($hideSwitchDetail != true) +S1->S1:Is there a dependent transfer?\nYes, there is. +S1->S1:Is this dependency against the debtor party to the transfer?\nYes, it is. +S1->S1:Create an obligation from the debtor party to the party named in the dependency (the FXP) +S1->S1:Is the transfer denominated in the currency of the payee receive amount?\nYes, it is. +S1->S1:Create an obligation from the party named in the dependency\nto the creditor party for the transfer +!else +S1->S1:Create obligations from the payer to the FXP and from FXP to the payee +!endif +S1->>FXP:The transfer succeeded.\nYou can clear it in your ledgers +note over S1 +**PATCH /fxTransfers/$commitRequestId** +{ + "completedTimestamp": "2021-08-25T14:17:08.227+01:00", + "fulfilment": "mhPUT9ZAwd-BXLfeSd7-YPh46rBWRNBiTCSWjpku90s", + "transferState": "COMMITTED" +} +end note +activate FXP +FXP->FXP:Let's just check: does this match the stuff I sent? +FXP->FXP:It does. Great. I'll clear the conversion +FXP-->>S1:200 Gotcha +deactivate FXP +note over S1 + Ledger positions: + $payerFSPID has a debit of $fxpSourceAmount $payerCurrency + $fxpID has a credit of $fxpSourceAmount $payerCurrency + $fxpID has a debit of $fxpTargetAmount $payeeCurrency + $payeeFSPID has a credit of $targetAmount $payeeCurrency +end note +S1->>D1:Transfer is complete\n**PUT /transfers/$commitRequestId** +activate D1 +!if ($simplified != true) +D1-->S1:200 Gotcha +!endif +deactivate S1 +!if ($advancedCoreConnectorFlow != true) + D1-->PayerCC:Transfer was completed successfully + note right of PayerCC + { + "transferId": "$transferId", + "homeTransactionId": "$homeTransactionId", + "from": { + "dateOfBirth": "$senderDOB", + "displayName": "$senderName", + "firstName": "$senderFirstName", + "middleName": "$senderMiddleName", + "lastName": "$senderLastName" + "fspId": "$payerFSPID", + "idType": "MSISDN", + "idValue": "$payerMSISDN" + }, + "to": { + "type": "CONSUMER", + "idType": "MSISDN", + "idValue": "$payeeMSISDN", + "displayName": "$receiverName", + "firstName": "$receiverFirstName", + "middleName": "$receiverMiddleName", + "lastName": "$receiverLastName" + "dateOfBirth": "$receiverDOB", + + "fspId": "$payeeFSPID" + "supportedCurrencies": [ "$payeeCurrency" ] + "kycInformation": "" + }, + "amountType": "SEND", + "currency": "$payerCurrency", + "amount": "$payerSendAmount" + "currentState": "**COMPLETED**", + "getPartiesResponse": { + + }, + "conversionRequestId": "$conversionRequestId", + "fxQuotesResponse": { + + }, + "fxQuotesResponseSource": "$payeeFSPID", + "quoteId": "$quoteId", + "quoteResponse": { + + }, + "quoteResponseSource": "$payeeFSPID", + "fulfil": { + "body": { + "completedTimestamp": "2021-08-25T14:17:08.227+01:00", + "fulfilment": "mhPUT9ZAwd-BXLfeSd7-YPh46rBWRNBiTCSWjpku90s", + "transferState": "COMMITTED" + }, + "headers": {} + }, + } + end note +!else + D1-->PayerCC:Transfer was completed successfully + !if ($simplified != true) + note right of PayerCC + { + "transfer": { + "body": { + "completedTimestamp": "2021-08-25T14:17:08.227+01:00", + "fulfilment": "mhPUT9ZAwd-BXLfeSd7-YPh46rBWRNBiTCSWjpku90s", + "transferState": "COMMITTED" + }, + "headers": {} + }, + "currentState": "COMPLETED" + } + end note + !endif +!endif + +PayerCC->PayerCBS:Transfer was completed successfully +PayerCBS->PayerCBS:Commit the funds in my ledgers +PayerCBS->A1:Your transfer is successful +deactivate D1 + +@enduml diff --git a/docs/Proxy/P2P.md b/docs/Proxy/P2P.md new file mode 100644 index 00000000..0fc9e1ed --- /dev/null +++ b/docs/Proxy/P2P.md @@ -0,0 +1,11 @@ +# P2P flow across network using Proxy +This design make the following assumptions +1. No two connected participant have the same identifier +1. No limit checks are done against proxy participants +1. Get /transactionRequests are resolved at the payee scheme +1. Timeouts in non-payee schemes are disabled (maybe enlarged) + +## Sequence Diagram +Here is a sequence diagram show the Agreement and Transfer stages of a transaction, and how the Get Transfer is resolved. + +![P2P flow](./Proxy%20pattern%20-%20P2P.png) \ No newline at end of file diff --git a/docs/Proxy/Proxy pattern - Happy path.plantuml b/docs/Proxy/Proxy pattern - Happy path.plantuml new file mode 100644 index 00000000..a4ffa2db --- /dev/null +++ b/docs/Proxy/Proxy pattern - Happy path.plantuml @@ -0,0 +1,98 @@ +@startuml + +title Proxy Patterns - Happy Path + +participant "Payer DFSP" as payerDFSP +box "Scheme A" + participant "Handler\nScheme A" as schemeA + participant "Proxy Cache\nScheme A" as pc_A +end box +participant "Proxy AB" as xnp +box "Scheme B" + participant "Handler\nScheme B" as schemeB + participant "Proxy Cache\nScheme B" as pc_B +end box +participant "Payee DFS" as payeeDFSP + +autonumber 1 "[0]" + +== POST == +payerDFSP ->> schemeA: POST /xxx +note left +header + source: payerDFSP + destination: payeeDFSP +body + {ID: 1234} +end note +schemeA -> pc_A: Destination not in scheme:\n payeeDFSP has a proxy mapped\n need to send to 'Proxy AB' +schemeA ->> xnp: POST /xxx +xnp->xnp: Add header + note left + fxpiop-proxy = "Proxy AB" + end note +xnp ->> schemeB: POST /xxx +note left +Message if forwarded to schemeB unmodified +end note + +schemeB -->> xnp: 202 OK +xnp -->> schemeA: 202 OK + +schemeB ->> payeeDFSP: POST /xxx + +== GET == +payerDFSP -> schemeA: GET /xxx/{ID} \nwithout destination +note left + source: payerDFSP +end note + +schemeA->schemeA: lookup if ID result and triggers put + +payerDFSP -> schemeA: GET /xxx/{ID} \nwith destination +note left + source: payerDFSP + destination: payeeDFSP +end note + +schemeA -> pc_A: Destination not in scheme:\n payeeDFSP has a proxy mapped\n need to send to 'Proxy AB' +schemeA ->> xnp: GET /xxx +xnp->xnp: Add header + note left + fxpiop-proxy = "Proxy AB" + end note +xnp ->> schemeB: GET /xxx +note left +Message if forwarded to schemeB unmodified +end note +schemeB -->> xnp: 202 OK +xnp -->> schemeA: 202 OK + +schemeB ->> payeeDFSP: GET /xxx + + + +== PUT == + + +payeeDFSP -> schemeB: PUT /xxx +note right + source: payeeDFSP + destination: payerDFSP +end note +schemeB -> pc_B: Destination not in scheme:\n payerDFSP has a proxy mapped\n need to send to 'Proxy AB' +schemeB -> xnp: PUT /xxx +xnp->xnp: Add header + note left + fxpiop-proxy = "Proxy AB" + end note +xnp -> schemeA: PUT /xxx +note right +Message if forwarded to schemeA unmodified +end note +schemeA -->> xnp: 200 OK +xnp -->> schemeB: 200 OK + +schemeA -> payerDFSP: PUT /xxx + +@enduml \ No newline at end of file diff --git a/docs/Proxy/Proxy pattern - Lazy Discovery - No Oracles.plantuml b/docs/Proxy/Proxy pattern - Lazy Discovery - No Oracles.plantuml new file mode 100644 index 00000000..0b927c94 --- /dev/null +++ b/docs/Proxy/Proxy pattern - Lazy Discovery - No Oracles.plantuml @@ -0,0 +1,105 @@ +@startuml + +title Lazy Discovery - No Oracles Registered + +participant "Payer DFSP" as payerDFSP +box "Scheme A" + participant "ALS\nScheme A" as ALS_A + participant "Proxy Cache\nScheme A" as pc_A +end box +participant "XN Proxy" as xnp +participant "other DFSPs" as dfspsA +box "Scheme B" + participant "ALS\nScheme B" as ALS_B + participant "Proxy Cache\nScheme B" as pc_B +end box +participant "Payee DFS" as payeeDFSP +participant "other DFSPs" as dfspsB + +autonumber 1 "[0]" + +== First time Party Identifier is used == +payerDFSP ->> ALS_A: GET /parties/{Type}/{ID} + +note left + header source = payerDFSP +end note +ALS_A ->> ALS_A: Lookup if {Type} and {ID} is mapped to dfsp +alt if not cached + loop for all DFSPs in Scheme A and not source + alt if XN Proxy + ALS_A ->> xnp: GET /parties/{Type}/{ID} + xnp ->> ALS_B: GET /parties/{Type}/{ID} + ALS_B ->> pc_B: Source not in Scheme: \nAdd 'Payer DFSP' to 'XN Proxy' mapping + pc_B -> pc_B: New mapping + pc_B -> pc_B: Check JWS signature\n & Add to cache +note left +Payer DFSP : XN Proxy +end note + ALS_B -> ALS_B: Lookup if {Type} and {ID} is mapped to dfsp + loop for all DFSPs in Scheme B and not source + alt if Payee DFSP + ALS_B ->> payeeDFSP: GET /parties/{Type}/{ID} + payeeDFSP ->> ALS_B: PUT /parties/{ID} + note right + header desitination = payerDFSP + end note + ALS_B -> ALS_B: Update cache + note left + cache [{Type}][{ID}] = payeeDFSP + end note + ALS_B -> pc_B: Lookup payerDFSP proxy + ALS_B ->> xnp: PUT /parties/{ID} + xnp ->> ALS_A: PUT /parties/{ID} + ALS_A -> pc_A: Source not in Scheme: \nAdd 'Payee DFSP' to 'XN Proxy' mapping + pc_A -> pc_A: New mapping\nCheck JWS signature\n & Add to cache + note left + Payee DFSP : XN Proxy + end note + ALS_A -> ALS_A: Update cache + note left + cache [{Type}][{ID}] = XN Proxy + end note + ALS_A ->> payerDFSP: PUT /parties/{ID} + else if other DFSPs in Scheme B + ALS_B ->> dfspsB: GET /parties/{Type}/{ID} + dfspsB ->> ALS_B: PUT /parties/{ID}/error + note right + header desitination = payerDFSP + end note + ALS_B ->> ALS_B: increment Scheme B Failure Count + alt if Failure Count == TotalCalls + ALS_B -> pc_B: Lookup payerDFSP proxy + ALS_B ->> xnp: PUT /parties/{ID}/error + xnp ->> ALS_A: PUT /parties/{ID}/error + end + end + end loop + else if other DFSPs in Scheme A + ALS_A ->> dfspsA: GET /parties/{Type}/{ID} + dfspsA ->> ALS_A: PUT /parties/{ID}/error + ALS_A ->> ALS_A: increment Scheme A Failure Count + alt if Failure Count == TotalCalls + ALS_A ->> payerDFSP: PUT /parties/{ID}/error + end + end + end loop +else + ALS_A->ALS_A: Yes, it is mapped to XN Proxy + ALS_A ->> xnp: GET /parties/{Type}/{ID} + xnp ->> ALS_B: GET /parties/{Type}/{ID} + ALS_B ->> pc_B: Source not in Scheme: \nAdd 'Payer DFSP' to 'XN Proxy' mapping + pc_B -> pc_B: Got Mapping + ALS_B -> ALS_B: Lookup if {Type} and {ID} is mapped to proxy + ALS_B ->> payeeDFSP: GET /parties/{Type}/{ID} + payeeDFSP ->> ALS_B: PUT /parties/{ID} + note right + header desitination = payerDFSP + end note + ALS_B -> ALS_B: Lookup payerDFSP proxy + ALS_B->ALS_B: Got it; it is mapped to XN Proxy + ALS_B ->> xnp: PUT /parties/{ID} + xnp ->> ALS_A: PUT /parties/{ID} + ALS_A ->> payerDFSP: PUT /parties/{ID} +end +@enduml \ No newline at end of file diff --git a/docs/Proxy/Proxy pattern - Lazy Discovery - No Oracles.png b/docs/Proxy/Proxy pattern - Lazy Discovery - No Oracles.png new file mode 100644 index 00000000..8e5cff50 Binary files /dev/null and b/docs/Proxy/Proxy pattern - Lazy Discovery - No Oracles.png differ diff --git a/docs/Proxy/Proxy pattern - Lazy Discovery - Oracles.plantuml b/docs/Proxy/Proxy pattern - Lazy Discovery - Oracles.plantuml new file mode 100644 index 00000000..844a9d97 --- /dev/null +++ b/docs/Proxy/Proxy pattern - Lazy Discovery - Oracles.plantuml @@ -0,0 +1,130 @@ +@startuml + +title On Demand Discovery - Oracles + +participant "Payer DFSP" as payerDFSP +box "Scheme A" + participant "ALS\nScheme A" as ALS_A + participant "Oracle\nScheme A" as Oracle_A + participant "Proxy Cache\nScheme A" as pc_A +end box +participant "other Proxies" as dfspsA +participant "Proxy AB" as xnp +box "Scheme B" + participant "ALS\nScheme B" as ALS_B + participant "Oracle\nScheme A" as Oracle_B + participant "Proxy Cache\nScheme B" as pc_B +end box +participant "Payee DFS" as payeeDFSP + +autonumber 1 "[0]" + +payerDFSP ->> ALS_A: GET /parties/{Type}/{ID} +note left + header source = payerDFSP +end note +ALS_A-> Oracle_A: GET /participant/{ID} +alt if not found in Oracle + +Oracle_A--> ALS_A: no dfsp found +ALS_A ->> ALS_A: Are there any proxies in Scheme A? + ALS_A ->> ALS_A: Cache Proxy that will receive messages + note left + SentToProxies[{ID}] = {['Proxy AB', 'Proxy CD', 'Proxy EF']} + end note + + loop for all Proxys in Scheme A and not source + alt if Proxy AB + ALS_A ->> xnp: GET /parties/{Type}/{ID} + xnp->xnp: Add header + note left + fxpiop-proxy = "Proxy AB" + end note + + xnp ->> ALS_B: GET /parties/{Type}/{ID} + ALS_B -->> xnp: 202 OK + xnp -->> ALS_A: 202 OK + ALS_B ->> pc_B: Source not in Scheme: \nfxpiop-proxy = "Proxy AB"\nAdd 'Payer DFSP' to 'Proxy AB' mapping + alt not MVP + pc_B -> pc_B: Check JWS signature + end + pc_B -> pc_B: Add new mapping to cache +note left +Payer DFSP : Proxy AB +end note + + ALS_B-> Oracle_B: GET /participant/{ID} + Oracle_B--> ALS_B: dfps = payeeDFSP + ALS_B ->> payeeDFSP: GET /parties/{Type}/{ID} + payeeDFSP ->> ALS_B: PUT /parties/{ID} + note right + header desitination = payerDFSP + source = payeeDFSP + end note + ALS_B -> pc_B: Lookup payerDFSP proxy + ALS_B ->> xnp: PUT /parties/{ID} + xnp->xnp: Add header + note left + fxpiop-proxy = "Proxy AB" + end note + + xnp ->> ALS_A: PUT /parties/{ID} + ALS_A -->> xnp: 200 OK + xnp -->> ALS_B: 200 OK + ALS_A -> pc_A: Source not in Scheme: \nfxpiop-proxy = "Proxy AB"\nAdd 'Payee DFSP' to 'Proxy AB' mapping + pc_A -> pc_A: New mapping\nCheck JWS signature\n & Add to cache +note left +Payee DFSP : Proxy AB +end note + ALS_A -> Oracle_A: Update Oracle with mapping\nPOST /participants/{Type}/{ID} {{"fspId": "Payee DFSP"}} + Oracle_A--> ALS_A: + ALS_A ->> payerDFSP: PUT /parties/{ID} + else if other Proxy in Scheme A + ALS_A ->> dfspsA: GET /parties/{Type}/{ID} + dfspsA ->> ALS_A: PUT /parties/{ID}/error + ALS_A ->> ALS_A: increment Scheme A Failure Count in cache + note left + remove other Proxy from list SentToProxies[{ID}] + end note + + alt if SentToProxies[{ID}] is empty + ALS_A ->> payerDFSP: PUT /parties/{ID}/error + note right + SentToProxies[{ID}] is empty + end note + end + end +end loop +else if found in Oracle + Oracle_A--> ALS_A: dfsp = payeeDFSP + + ALS_A->ALS_A: Payee DFSP is not in scheme A + ALS_A-> pc_A: Lookup proxy for Payee DFSP + ALS_A->ALS_A: Add desitation to header + note left + destination dfsp: Payee DFSP + end note + + ALS_A ->> xnp: GET /parties/{Type}/{ID} + xnp ->> ALS_B: GET /parties/{Type}/{ID} + ALS_B -->> xnp: 202 OK + xnp -->> ALS_A: 202 OK + ALS_B->ALS_B: Forward to desitination + ALS_B ->> payeeDFSP: GET /parties/{Type}/{ID} + + payeeDFSP ->> ALS_B: PUT /parties/{ID} + note right + header desitination = payerDFSP + end note + ALS_B -> pc_B: Lookup payerDFSP proxy + ALS_B ->> xnp: PUT /parties/{ID} + xnp ->> ALS_A: PUT /parties/{ID} + ALS_A -->> xnp: 200 OK + xnp -->> ALS_B: 200 OK + ALS_A -> pc_A: Source not in Scheme: \nAdd 'Payee DFSP' to 'Proxy AB' mapping + pc_A -> pc_A: Got Mapping + ALS_A ->> payerDFSP: PUT /parties/{ID} +end + + +@enduml \ No newline at end of file diff --git a/docs/Proxy/Proxy pattern - Lazy Discovery - Oracles.png b/docs/Proxy/Proxy pattern - Lazy Discovery - Oracles.png new file mode 100644 index 00000000..ebeec486 Binary files /dev/null and b/docs/Proxy/Proxy pattern - Lazy Discovery - Oracles.png differ diff --git a/docs/Proxy/Proxy pattern - Lazy Discovery Identifier Cache Invalid.plantuml b/docs/Proxy/Proxy pattern - Lazy Discovery Identifier Cache Invalid.plantuml new file mode 100644 index 00000000..d8671303 --- /dev/null +++ b/docs/Proxy/Proxy pattern - Lazy Discovery Identifier Cache Invalid.plantuml @@ -0,0 +1,72 @@ +@startuml + +title Lazy Discovery - Party Identifier cache no-longer valid + +participant "Payer DFSP" as payerDFSP +box "Scheme A" + participant "ALS\nScheme A" as ALS_A + participant "Oracle\nScheme A" as Oracle_A + participant "Proxy Cache\nScheme A" as pc_A +end box +participant "Proxy AB" as xnp +box "Scheme B" + participant "ALS\nScheme B" as ALS_B + participant "Oracle\nScheme A" as Oracle_B + participant "Proxy Cache\nScheme B" as pc_B +end box +participant "Payee DFS" as payeeDFSP + +autonumber 1 "[0]" + +payerDFSP ->> ALS_A: **GET** /parties/{Type}/{ID} +note left + header source = payerDFSP +end note + + ALS_A-> Oracle_A: **GET** /participant/{ID} + Oracle_A--> ALS_A: found DFSP = payeeDFSP + ALS_A ->> ALS_A: DFSP not in scheme + ALS_A -> pc_A: Who is payee DFSP's proxy? + pc_A --> ALS_A: forward to Proxy AB + ALS_A ->> xnp: **GET** /parties/{Type}/{ID} + xnp->xnp: Add header + note left + fxpiop-proxy = "Proxy AB" + end note + + xnp ->> ALS_B: **GET** /parties/{Type}/{ID} + ALS_B ->> pc_B: Source not in Scheme: \nfxpiop-proxy = "Proxy AB"\nAdd 'Payer DFSP' to 'Proxy AB' mapping +alt not MVP + pc_B -> pc_B: Check JWS signature +end + pc_B -> pc_B: Add new mapping to cache +note left +Payer DFSP : Proxy AB +end note +ALS_B-> Oracle_B: **GET** /participant/{ID} + Oracle_B--> ALS_B: dfps = payeeDFSP + ALS_B ->> payeeDFSP: **GET** /parties/{Type}/{ID} + payeeDFSP ->> ALS_B: **PUT** /parties/{ID}/error + note right + header desitination = payerDFSP + end note + ALS_B -> pc_B: Lookup payerDFSP proxy + ALS_B ->> xnp: **PUT** /parties/{ID}/error + xnp->xnp: Add header + note left + fxpiop-proxy = "Proxy AB" + end note + xnp ->> ALS_A: **PUT** /parties/{ID}/error + alt message from proxy & error & desitination in scheme + note left ALS_A + fxpiop-proxy = "Proxy AB" + Error Code = 5107 + Destination = Payer DFSP in Scheme A + end note + ALS_A -> Oracle_A: Remove mapping in Oracle\n **DELETE** \participants\{Type}\{ID} + ALS_A ->> ALS_A: Restart the ALS get parties process + else + ALS_A->>payerDFSP: **PUT** /parties/{ID}/error +end + +@enduml \ No newline at end of file diff --git a/docs/Proxy/Proxy pattern - Lazy Discovery Identifier Cache Invalid.png b/docs/Proxy/Proxy pattern - Lazy Discovery Identifier Cache Invalid.png new file mode 100644 index 00000000..1731e191 Binary files /dev/null and b/docs/Proxy/Proxy pattern - Lazy Discovery Identifier Cache Invalid.png differ diff --git a/docs/Proxy/Proxy pattern - P2P.plantuml b/docs/Proxy/Proxy pattern - P2P.plantuml new file mode 100644 index 00000000..2c192277 --- /dev/null +++ b/docs/Proxy/Proxy pattern - P2P.plantuml @@ -0,0 +1,186 @@ +@startuml + +title Proxy Patterns - P2P + +participant "Payer DFSP" as payerDFSP +box Scheme A + participant "Mojaloop\nScheme A" as schemeA + participant "Proxy Cache\nScheme A" as pc_A +end box +participant "Proxy AB" as xnp +box Scheme B + participant "Mojaloop\nScheme B" as schemeB + participant "Proxy Cache\nScheme B" as pc_B +end box +participant "Payee DFS" as payeeDFSP + +autonumber 1 "[0]" + +== Agreement == +payerDFSP ->> schemeA: POST /quotes +note left +header + source: payerDFSP + destination: payeeDFSP +JWS signed by payerDFSP +end note +schemeA -> pc_A: Destination not in Scheme\n Lookup proxy for payeeDFSP = Proxy AB +schemeA ->> xnp: POST /quotes +xnp ->> schemeB: POST /quotes\nmessage unmodified +note left +header + source: payerDFSP + destination: payeeDFSP + fxpiop-proxy: proxyAB +JWS signed by payerDFSP +end note +schemeB -->> xnp: 202 OK +xnp -->> schemeA: 202 OK +schemeB->>payeeDFSP: POST /quotes +note right +Checks JWS signed by payerDFSP +end note +payeeDFSP->>schemeB: PUT /quotes +note right +header + source: payeeDFSP + destination: payerDFSP +JWS signed by payeeDFSP +end note +schemeB -> pc_B: Destination not in Scheme\n Lookup proxy for payerDFSP = Proxy AB +schemeB->>xnp: PUT /quotes +xnp->>schemeA: PUT /quotes\nmessage unmodified +note right +header + source: payeeDFSP + destination: payerDFSP + fxpiop-proxy: proxyAB +JWS signed by payeeDFSP +end note +schemeA-->>xnp: 202 OK +xnp-->>schemeB: 202 OK +schemeA->>payerDFSP: PUT /quotes +note left +Checks JWS signed by payeeDFSP +end note + +== Transfer == + +payerDFSP ->> schemeA: POST /transfers +note left +header + source: payerDFSP + destination: payeeDFSP +JWS signed by payerDFSP +body + transferId: 1234 +end note +schemeA -> schemeA: Payer DFSP\n - Checks limits\n - Updates position +schemeA -> pc_A: Destination not in Scheme\nLookup proxy for payeeDFSP = Proxy AB +schemeA ->> xnp: POST /transfers +xnp ->> schemeB: POST /transfers +note left +header + source: payerDFSP + destination: payeeDFSP + fxpiop-proxy: proxyAB +JWS signed by payerDFSP +body + transferId: 1234 +end note +schemeB -->> xnp: 202 OK +xnp -->> schemeA: 202 OK +schemeA -> schemeA: Disable timeout + +schemeB -> schemeB: Proxy AB\n **- No limit check**\n - Updates position +schemeB->>payeeDFSP: POST /transfers +note right +Checks JWS signed by payerDFSP +end note +payeeDFSP->>schemeB: PUT /transfers \n{fulfilment: "xyz", transferState: "RESERVED"} +note right +header + source: payeeDFSP + destination: payerDFSP +JWS signed by payeeDFSP +end note +schemeB -> schemeB: Payer DFSP\n - Updates position +schemeB -> pc_B: Lookup proxy for payerDFSP = Proxy AB +schemeB->>xnp: PUT /transfers +xnp->>schemeA: PUT /transfers +note right +header + source: payeeDFSP + destination: payerDFSP + fxpiop-proxy: proxyAB +JWS signed by payeeDFSP +end note +schemeA-->>xnp: 200 OK +xnp-->>schemeB: 200 OK +schemeB->>payeeDFSP: PATCH /transfers \n{transferState: "COMMITTED"} +schemeA -> schemeA: NX Proxy\n - Updates position +schemeA->>payerDFSP: PUT /transfers +note left +Checks JWS signed by payeeDFSP +end note + +== Get Transfers == + +payerDFSP ->> schemeA: GET /transfers/1234 +note left +header + source: payerDFSP + destination: payeeDFSP +JWS signed by payerDFSP +end note +schemeA -> schemeA: Load transfer informtaion\nand check if payeeDFSP is in scheme +schemeA -> pc_A: Destination not in Scheme\nLookup proxy for payeeDFSP = Proxy AB +schemeA ->> xnp: GET /transfers/1234 +xnp ->> schemeB: GET /transfers/1234 +note left +header + source: payerDFSP + destination: payeeDFSP + fxpiop-proxy: proxyAB +JWS signed by payerDFSP +end note +schemeB -->> xnp: 202 OK +xnp -->> schemeA: 202 OK +schemeB->schemeB: lookup transfer information +schemeB->>xnp: PUT /transfers/1234 +note right +header + source: schemeB + destination: payerDFSP +JWS signed by schemeB +end note +xnp->>schemeA: PUT /transfers/1234 +note right +header + source: schemeB + destination: payerDFSP + fxpiop-proxy: proxyAB +JWS signed by schemeB +end note +schemeA-->>xnp: 200 OK +xnp-->>schemeB: 200 OK +schemeA->>payerDFSP: PUT /transfers/1234 + +payeeDFSP->>schemeB: GET /transfers/1234 +note right +header + source: payeeDFSP +JWS signed by payeeDFSP +end note +schemeB -> schemeB: Load transfer informtaion\nand check if payeeDFSP is in scheme +schemeB->>payeeDFSP: Yes; return information\nPUT /transfers/1234 +note left +header + source: schemeB + destination: payeeDFSP +JWS signed by schemeB +end note + + + +@enduml \ No newline at end of file diff --git a/docs/Proxy/Proxy pattern - P2P.png b/docs/Proxy/Proxy pattern - P2P.png new file mode 100644 index 00000000..04949248 Binary files /dev/null and b/docs/Proxy/Proxy pattern - P2P.png differ diff --git a/docs/Proxy/Proxy pattern - Unhappy path.plantuml b/docs/Proxy/Proxy pattern - Unhappy path.plantuml new file mode 100644 index 00000000..2b08ea29 --- /dev/null +++ b/docs/Proxy/Proxy pattern - Unhappy path.plantuml @@ -0,0 +1,103 @@ +@startuml + +title Proxy Patterns - Unhappy Path + +participant "Payer DFSP" as payerDFSP +box "Scheme A" + participant "ALS\nScheme A" as schemeA + participant "Proxy Cache\nScheme A" as pc_A +end box +participant "Proxy AB" as xnp +box "Scheme B" + participant "ALS\nScheme B" as schemeB + participant "Proxy Cache\nScheme B" as pc_B +end box +participant "Payee DFS" as payeeDFSP + +autonumber 1 "[0]" + +== POST == +payerDFSP ->> schemeA: POST/GET/PATCH/PUT /xxx +note left +header + source: payerDFSP + destination: payeeDFSP +end note +alt if OpenAPI Error + schemeA -->> payerDFSP: 400 Bad Request +end + + alt if error in schemeA + schemeA ->> payerDFSP: PUT /xxx/{ID}/error +note right +Error Codes: 2xxx, 3xxx, 4xxx, 5xxx +end note + end + + schemeA -> pc_A: lookup proxy for payeeDFSP = Proxy AB + alt if not in proxy cache + schemeA ->> payerDFSP: PUT /xxx/{ID}/error +note right +Error Code: 3201 +end note + end + schemeA ->> xnp: POST/GET/PATCH/PUT /xxx + + alt if error in xnp + xnp ->> schemeA: PUT /xxx/{ID}/error +note right +header + source: Proxy AB + destination: payerDFSP +JWS Signed by Proxy AB +Error Codes: 3100 +end note + schemeA -->> xnp: 200 OK + schemeA ->> payerDFSP: PUT /xxx/{ID}/error + end + + xnp->xnp: Add header + note left + fxpiop-proxy = "Proxy AB" + end note + + xnp ->> schemeB: POST/GET/PATCH/PUT /xxx + schemeB -->> xnp: 202 OK + xnp -->> schemeA: 202 OK + + alt if error in schemeB + schemeB ->> xnp: PUT /xxx/{ID}/error +note right +Error Codes: 2xxx, 3xxx, 4xxx, 5xxx +end note + xnp->xnp: Add header + note left + fxpiop-proxy = "Proxy AB" + end note + xnp ->> schemeA: PUT /xxx/{ID}/error + schemeA -->> xnp: 200 OK + xnp -->> schemeB: 200 OK + schemeA ->> payerDFSP: PUT /xxx/{ID}/error + end + + schemeB ->> payeeDFSP: POST/GET/PATCH/PUT /xxx + + alt if error in payeeDFSP + payeeDFSP->> schemeB: PUT /xxx/{ID}/error +note right +header destination: PayerDFSP +Error Codes: 5xxx +end note + schemeB -> schemeB: Lookup proxy for payerDFSP = Proxy AB + schemeB ->> xnp: PUT /xxx/{ID}/error + xnp->xnp: Add header + note left + fxpiop-proxy = "Proxy AB" + end note + xnp ->> schemeA: PUT /xxx/{ID}/error + schemeA -->> xnp: 200 OK + xnp -->> schemeB: 200 OK + schemeA ->> payerDFSP: PUT /xxx/{ID}/error + end + +@enduml \ No newline at end of file diff --git a/docs/Proxy/Proxy pattern - Unhappy path.png b/docs/Proxy/Proxy pattern - Unhappy path.png new file mode 100644 index 00000000..f87f0e34 Binary files /dev/null and b/docs/Proxy/Proxy pattern - Unhappy path.png differ diff --git a/docs/Proxy/Proxy pattern - happy path.png b/docs/Proxy/Proxy pattern - happy path.png new file mode 100644 index 00000000..c8122e69 Binary files /dev/null and b/docs/Proxy/Proxy pattern - happy path.png differ diff --git a/docs/Proxy/Readme.md b/docs/Proxy/Readme.md new file mode 100644 index 00000000..5e288c3e --- /dev/null +++ b/docs/Proxy/Readme.md @@ -0,0 +1,39 @@ +# Proxy Implementation +The proxy implementation method to connect schemes does the following. +1. Leverages the trust relationship between scheme so that a transaction only has a single pre-funding requirement at the Payer's scheme. +2. Ensures non-repudiation across schemes; removing the requirement for the cross-network proxy to take on responsibility for clearing; which removes costs + +The schemes are connected via a proxy participant, that is registered to act as a proxy in the scheme for adjacent but connected dfsps in other schemes. +Essentially, the two connected schemes behave as if they where a single scheme. + +This design make the following assumptions +1. No two connected participant have the same identifier +1. No limit checks are done against proxy participants +1. Get \transaction request are resolved at the payee scheme +1. Timeouts in non-payee schemes are disabled (maybe enlarged) + +## General Patterns +There are certain general patterns that emerge +### Happy Path Patterns +![Happy Path Patterns](Proxy%20pattern%20-%20happy%20path.png) + +### Error Patterns +![Error Patterns](Proxy%20pattern%20-%20Unhappy%20path.png) + +## Detailed Designs +1. [Discovery - On Demand Implementation](./Discovery.md) +2. [P2P](./P2P.md) + +## Admin API - defining Proxy Participants +![Admin API](SettingUpProxys.png) + +### ALS Proxy Timeout flow +1. If no party info returned from Oracle (see [diagram](./Discovery.md)), and there's some proxies in the hub, we need to send + GET /parties/... requests to each proxy. And wait for the response. +2. At this moment we add 2 records in cache (redis) using `inter-scheme-proxy-cache-lib`: + - key: `als:${sourceId}:${type}:${partyId}` value: `[proxy_1, proxy_2, ...]` (no TTL) + - key: `als:${sourceId}:${type}:${partyId}:expiresAt` value: `expiresAt` (TTL: default 40 seconds) +3. When we get success/failure callbacks from all proxies within TTL period, we process it, and remove `...:expiresAt` key from cache. +4. If not all callbacks received within TTL period, redis will expire `...:expiresAt` key, emit appropriate event, and pass _expired cacheKey_. +5. We subscribe to these events, and provide a [callback](https://github.com/mojaloop/account-lookup-service/blob/feat/fx-impl/src/domain/timeout/index.js#L53) to be executed in case of timeout. +6. From _expired cacheKey_, we get `destination`, `partyType` and `partyId`, which is used to send error callback to appropriate participant. diff --git a/docs/Proxy/SettingUpProxys.plantuml b/docs/Proxy/SettingUpProxys.plantuml new file mode 100644 index 00000000..5721baf4 --- /dev/null +++ b/docs/Proxy/SettingUpProxys.plantuml @@ -0,0 +1,31 @@ +@startuml +title Central Ledger API / Admin API + +participant "Onboarding Script" as script +participant "Mojaloop\nScheme A" as schemeA +participant "XN Proxy" as xnp +participant "Mojaloop\nScheme B" as schemeB + +autonumber 1 "[0]" + +== Create Participant Types == + +script -> schemeA: POST /participants +note left +{ + "name": "XN Proxy", + "currency": "USD", + "isProxy": true +} +end note + +script -> schemeB: POST /participants +note left +{ + "name": "XN Proxy", + "currency": "USD", + "isProxy": true +} +end note + +@enduml \ No newline at end of file diff --git a/docs/Proxy/SettingUpProxys.png b/docs/Proxy/SettingUpProxys.png new file mode 100644 index 00000000..50188956 Binary files /dev/null and b/docs/Proxy/SettingUpProxys.png differ diff --git a/jest-int.config.js b/jest-int.config.js new file mode 100644 index 00000000..d7c61958 --- /dev/null +++ b/jest-int.config.js @@ -0,0 +1,8 @@ +const baseConfig = require('./jest.config.js') + +module.exports = { + ...baseConfig, + setupFiles: ['/test/integration/setup.js'], + testMatch: ['/test/integration/**/*.test.js'], + reporters: ['default', 'jest-junit'] +} diff --git a/jest.config.js b/jest.config.js index 32637336..00269cde 100644 --- a/jest.config.js +++ b/jest.config.js @@ -10,5 +10,7 @@ module.exports = { branches: 90, lines: 90 } - } + }, + setupFiles: ['/test/unit/setup.js'], + testMatch: ['/test/unit/**/*.test.js'] } diff --git a/package-lock.json b/package-lock.json index 74e67322..6fdfc6d5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,65 +1,68 @@ { "name": "account-lookup-service", - "version": "15.3.4", + "version": "15.4.0-snapshot.35", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "account-lookup-service", - "version": "15.3.4", + "version": "15.4.0-snapshot.35", "license": "Apache-2.0", "dependencies": { "@hapi/basic": "7.0.2", "@hapi/boom": "10.0.1", - "@hapi/catbox-memory": "6.0.1", + "@hapi/catbox-memory": "6.0.2", "@hapi/good": "9.0.1", - "@hapi/hapi": "21.3.9", + "@hapi/hapi": "21.3.12", "@hapi/inert": "7.1.0", "@hapi/joi": "17.1.1", "@hapi/vision": "7.0.3", - "@mojaloop/central-services-error-handling": "13.0.1", + "@mojaloop/central-services-error-handling": "13.0.2", "@mojaloop/central-services-health": "15.0.0", - "@mojaloop/central-services-logger": "11.3.1", - "@mojaloop/central-services-metrics": "12.0.8", - "@mojaloop/central-services-shared": "18.3.8", - "@mojaloop/central-services-stream": "11.3.0", - "@mojaloop/database-lib": "11.0.5", + "@mojaloop/central-services-logger": "11.5.1", + "@mojaloop/central-services-metrics": "12.4.1", + "@mojaloop/central-services-shared": "18.7.1", + "@mojaloop/central-services-stream": "11.3.1", + "@mojaloop/database-lib": "11.0.6", "@mojaloop/event-sdk": "14.1.1", - "@mojaloop/sdk-standard-components": "18.1.0", + "@mojaloop/inter-scheme-proxy-cache-lib": "2.3.1", + "@mojaloop/sdk-standard-components": "18.4.0", "@now-ims/hapi-now-auth": "2.1.0", - "ajv": "8.16.0", + "ajv": "8.17.1", "ajv-keywords": "5.1.0", "blipp": "4.0.2", "commander": "12.1.0", + "cron": "3.3.0", "fast-safe-stringify": "^2.1.1", "hapi-auth-bearer-token": "8.0.0", - "joi": "17.13.1", + "joi": "17.13.3", "knex": "3.1.0", "mustache": "4.2.0", "mysql": "2.18.1", "npm-run-all": "4.1.5", "parse-strings-in-object": "2.0.0", - "rc": "1.2.8", - "uuid4": "2.0.3" + "rc": "1.2.8" }, "devDependencies": { - "@types/jest": "29.5.12", - "audit-ci": "^7.0.1", - "axios": "1.7.2", + "@types/jest": "29.5.14", + "audit-ci": "^7.1.0", + "axios": "1.7.9", + "axios-retry": "^4.5.0", "docdash": "2.0.2", + "dotenv": "^16.4.7", "get-port": "5.1.1", + "ioredis-mock": "^8.9.0", "jest": "29.7.0", "jest-junit": "16.0.0", - "jsdoc": "4.0.3", - "nodemon": "3.1.3", - "npm-audit-resolver": "3.0.0-RC.0", - "npm-check-updates": "16.14.20", - "nyc": "17.0.0", + "jsdoc": "4.0.4", + "nodemon": "3.1.7", + "npm-check-updates": "17.1.11", + "nyc": "17.1.0", "pre-commit": "1.2.2", "proxyquire": "2.1.3", "replace": "^1.2.2", - "sinon": "18.0.0", - "standard": "17.1.0", + "sinon": "19.0.2", + "standard": "17.1.2", "standard-version": "^9.5.0", "swagmock": "1.0.0" }, @@ -126,30 +129,30 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.7.tgz", - "integrity": "sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==", + "version": "7.24.9", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.9.tgz", + "integrity": "sha512-e701mcfApCJqMMueQI0Fb68Amflj83+dvAvHawoBpAz+GDjCIyGHzNwnefjsWJ3xiYAqqiQFoWbspGYBdb2/ng==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.7.tgz", - "integrity": "sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==", + "version": "7.24.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.9.tgz", + "integrity": "sha512-5e3FI4Q3M3Pbr21+5xJwCv6ZT6KmGkI0vw3Tozy5ODAQFTIWe37iT8Cr7Ice2Ntb+M3iSKCEWMB1MBgKrW3whg==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.7", - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helpers": "^7.24.7", - "@babel/parser": "^7.24.7", + "@babel/generator": "^7.24.9", + "@babel/helper-compilation-targets": "^7.24.8", + "@babel/helper-module-transforms": "^7.24.9", + "@babel/helpers": "^7.24.8", + "@babel/parser": "^7.24.8", "@babel/template": "^7.24.7", - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7", + "@babel/traverse": "^7.24.8", + "@babel/types": "^7.24.9", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -174,12 +177,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.7.tgz", - "integrity": "sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==", + "version": "7.24.10", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.10.tgz", + "integrity": "sha512-o9HBZL1G2129luEUlG1hB4N/nlYNWHnpwlND9eOMclRqqu1YDy2sSYVCFUZwl8I1Gxh+QSRrP2vD7EpUmFVXxg==", "dev": true, "dependencies": { - "@babel/types": "^7.24.7", + "@babel/types": "^7.24.9", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" @@ -189,14 +192,14 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.7.tgz", - "integrity": "sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.8.tgz", + "integrity": "sha512-oU+UoqCHdp+nWVDkpldqIQL/i/bvAv53tRqLG/s+cOXxe66zOYLU7ar/Xs3LdmBihrUMEUhwu6dMZwbNOYDwvw==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.24.7", - "@babel/helper-validator-option": "^7.24.7", - "browserslist": "^4.22.2", + "@babel/compat-data": "^7.24.8", + "@babel/helper-validator-option": "^7.24.8", + "browserslist": "^4.23.1", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, @@ -279,9 +282,9 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.7.tgz", - "integrity": "sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ==", + "version": "7.24.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.9.tgz", + "integrity": "sha512-oYbh+rtFKj/HwBQkFlUzvcybzklmVdVV3UU+mN7n2t/q3yGHbuVdNxyFvSBO1tfvjyArpHNcWMAzsSPdyI46hw==", "dev": true, "dependencies": { "@babel/helper-environment-visitor": "^7.24.7", @@ -298,9 +301,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.7.tgz", - "integrity": "sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz", + "integrity": "sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==", "dev": true, "engines": { "node": ">=6.9.0" @@ -332,9 +335,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.7.tgz", - "integrity": "sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", + "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", "dev": true, "engines": { "node": ">=6.9.0" @@ -350,22 +353,22 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.7.tgz", - "integrity": "sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz", + "integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.7.tgz", - "integrity": "sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.8.tgz", + "integrity": "sha512-gV2265Nkcz7weJJfvDoAEVzC1e2OTDpkGbEsebse8koXUJUXPsCMi7sRo/+SPMuMZ9MtUPnGwITTnQnU5YjyaQ==", "dev": true, "dependencies": { "@babel/template": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/types": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -458,9 +461,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.7.tgz", - "integrity": "sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.8.tgz", + "integrity": "sha512-WzfbgXOkGzZiXXCqk43kKwZjzwx4oulxZi3nq2TYL9mOjQv6kYwul9mz6ID36njuL7Xkp6nJEfok848Zj10j/w==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -661,19 +664,19 @@ } }, "node_modules/@babel/traverse": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.7.tgz", - "integrity": "sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.8.tgz", + "integrity": "sha512-t0P1xxAPzEDcEPmjprAQq19NWum4K0EQPjMwZQZbHt+GiZqvjCHjj755Weq1YRPVzBI+3zSfvScfpnuIecVFJQ==", "dev": true, "dependencies": { "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.7", + "@babel/generator": "^7.24.8", "@babel/helper-environment-visitor": "^7.24.7", "@babel/helper-function-name": "^7.24.7", "@babel/helper-hoist-variables": "^7.24.7", "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/types": "^7.24.7", + "@babel/parser": "^7.24.8", + "@babel/types": "^7.24.8", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -682,12 +685,12 @@ } }, "node_modules/@babel/types": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.7.tgz", - "integrity": "sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==", + "version": "7.24.9", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.9.tgz", + "integrity": "sha512-xm8XrMKz0IlUdocVbYJe0Z9xEgidU7msskG8BbhnTPK/HZ2z/7FP7ykqPgrUH+C+r414mNfNWam1f2vqOjqjYQ==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.24.7", + "@babel/helper-string-parser": "^7.24.8", "@babel/helper-validator-identifier": "^7.24.7", "to-fast-properties": "^2.0.0" }, @@ -702,11 +705,9 @@ "dev": true }, "node_modules/@colors/colors": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", - "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", - "dev": true, - "optional": true, + "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" } @@ -737,9 +738,9 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.1.tgz", - "integrity": "sha512-Zm2NGpWELsQAD1xsJzGQpYfvICSsFkEpU0jxBjfdC6uNEWXcHnfs9hScFWtXVDVl+rBQJGrl4g1vcKIejpH9dA==", + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", + "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" @@ -849,16 +850,10 @@ "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.10.9", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.10.9.tgz", - "integrity": "sha512-5tcgUctCG0qoNyfChZifz2tJqbRbXVO9J7X6duFcOjY3HUNCxg5D0ZCK7EP9vIcZ0zRpLU9bWkyCqVCLZ46IbQ==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.11.1.tgz", + "integrity": "sha512-gyt/WayZrVPH2w/UTLansS7F9Nwld472JxxaETamrM8HNlsa+jSLNyKAZmhxI2Me4c3mQHFiS1wWHDY1g1Kthw==", "dependencies": { "@grpc/proto-loader": "^0.7.13", "@js-sdsl/ordered-map": "^4.4.2" @@ -941,9 +936,9 @@ } }, "node_modules/@hapi/bounce": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@hapi/bounce/-/bounce-3.0.1.tgz", - "integrity": "sha512-G+/Pp9c1Ha4FDP+3Sy/Xwg2O4Ahaw3lIZFSX+BL4uWi64CmiETuZPxhKDUD4xBMOUZbBlzvO8HjiK8ePnhBadA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@hapi/bounce/-/bounce-3.0.2.tgz", + "integrity": "sha512-d0XmlTi3H9HFDHhQLjg4F4auL1EY3Wqj7j7/hGDhFFe6xAbnm3qiGrXeT93zZnPH8gH+SKAFYiRzu26xkXcH3g==", "dependencies": { "@hapi/boom": "^10.0.1", "@hapi/hoek": "^11.0.2" @@ -975,9 +970,9 @@ } }, "node_modules/@hapi/catbox-memory": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@hapi/catbox-memory/-/catbox-memory-6.0.1.tgz", - "integrity": "sha512-sVb+/ZxbZIvaMtJfAbdyY+QJUQg9oKTwamXpEg/5xnfG5WbJLTjvEn4kIGKz9pN3ENNbIL/bIdctmHmqi/AdGA==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@hapi/catbox-memory/-/catbox-memory-6.0.2.tgz", + "integrity": "sha512-H1l4ugoFW/ZRkqeFrIo8p1rWN0PA4MDTfu4JmcoNDvnY975o29mqoZblqFTotxNHlEkMPpIiIBJTV+Mbi+aF0g==", "dependencies": { "@hapi/boom": "^10.0.1", "@hapi/hoek": "^11.0.2" @@ -1039,19 +1034,19 @@ "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" }, "node_modules/@hapi/hapi": { - "version": "21.3.9", - "resolved": "https://registry.npmjs.org/@hapi/hapi/-/hapi-21.3.9.tgz", - "integrity": "sha512-AT5m+Rb8iSOFG3zWaiEuTJazf4HDYl5UpRpyxMJ3yR+g8tOEmqDv6FmXrLHShdvDOStAAepHGnr1G7egkFSRdw==", + "version": "21.3.12", + "resolved": "https://registry.npmjs.org/@hapi/hapi/-/hapi-21.3.12.tgz", + "integrity": "sha512-GCUP12dkb3QMjpFl+wEFO73nqKRmsnD5um/QDOn6lj2GjGBrDXPcT194mNARO+PPNXZOR4KmvIpHt/lceUncfg==", "dependencies": { - "@hapi/accept": "^6.0.1", + "@hapi/accept": "^6.0.3", "@hapi/ammo": "^6.0.1", "@hapi/boom": "^10.0.1", - "@hapi/bounce": "^3.0.1", + "@hapi/bounce": "^3.0.2", "@hapi/call": "^9.0.1", "@hapi/catbox": "^12.1.1", - "@hapi/catbox-memory": "^6.0.1", + "@hapi/catbox-memory": "^6.0.2", "@hapi/heavy": "^8.0.1", - "@hapi/hoek": "^11.0.2", + "@hapi/hoek": "^11.0.6", "@hapi/mimos": "^7.0.1", "@hapi/podium": "^5.0.1", "@hapi/shot": "^6.0.1", @@ -1059,7 +1054,7 @@ "@hapi/statehood": "^8.1.1", "@hapi/subtext": "^8.1.0", "@hapi/teamwork": "^6.0.0", - "@hapi/topo": "^6.0.1", + "@hapi/topo": "^6.0.2", "@hapi/validate": "^2.0.1" }, "engines": { @@ -1095,9 +1090,9 @@ } }, "node_modules/@hapi/hoek": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-11.0.4.tgz", - "integrity": "sha512-PnsP5d4q7289pS2T2EgGz147BFJ2Jpb4yrEdkpz2IhgEUzos1S7HTl7ezWh1yfYzYlj89KzLdCRkqsP6SIryeQ==" + "version": "11.0.7", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-11.0.7.tgz", + "integrity": "sha512-HV5undWkKzcB4RZUusqOpcgxOaq6VOAH7zhhIr2g3G8NF/MlFO75SjOr2NfuSx0Mh40+1FqCkagKLJRykUWoFQ==" }, "node_modules/@hapi/inert": { "version": "7.1.0", @@ -1413,72 +1408,16 @@ "node": ">=6.9.0" } }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "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==", - "dev": true, - "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/@ioredis/as-callback": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@ioredis/as-callback/-/as-callback-3.0.0.tgz", + "integrity": "sha512-Kqv1rZ3WbgOrS+hgzJ5xG5WQuhvzzSTRYvNeyPMLOAM78MHSnuKI20JeJGbpuAt//LCuP0vsexZcorqW7kWhJg==", "dev": true }, - "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==", - "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/@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==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } + "node_modules/@ioredis/commands": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz", + "integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==" }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", @@ -1816,9 +1755,9 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", "dev": true }, "node_modules/@jridgewell/trace-mapping": { @@ -1857,27 +1796,13 @@ "node": ">=v12.0.0" } }, - "node_modules/@korzio/djv-draft-04": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@korzio/djv-draft-04/-/djv-draft-04-2.0.1.tgz", - "integrity": "sha512-MeTVcNsfCIYxK6T7jW1sroC7dBAb4IfLmQe6RoCqlxHN5NFkzNpgdnBPR+/0D2wJDUJHM9s9NQv+ouhxKjvUjg==", - "dev": true, - "optional": true - }, "node_modules/@mojaloop/central-services-error-handling": { - "version": "13.0.1", - "resolved": "https://registry.npmjs.org/@mojaloop/central-services-error-handling/-/central-services-error-handling-13.0.1.tgz", - "integrity": "sha512-Hl0KBHX30LbF127tgqNK/fdo0hwa6Bt23tb8DesLstYawKtCesJtk9lPuo6jE+dafNeG2QusUwVQyI+7kwAUHQ==", + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/@mojaloop/central-services-error-handling/-/central-services-error-handling-13.0.2.tgz", + "integrity": "sha512-HSxI7OrtPdA94aHNWmAD50Ve8lR6FmgOX2LaZSL/TPfx22PVTTht0eXU+IQSN/srF20f2tvCa2CdFxWBQf6Ilg==", "dependencies": { + "fast-safe-stringify": "2.1.1", "lodash": "4.17.21" - }, - "peerDependencies": { - "@mojaloop/sdk-standard-components": ">=18.x.x" - }, - "peerDependenciesMeta": { - "@mojaloop/sdk-standard-components": { - "optional": false - } } }, "node_modules/@mojaloop/central-services-health": { @@ -1939,47 +1864,48 @@ } }, "node_modules/@mojaloop/central-services-logger": { - "version": "11.3.1", - "resolved": "https://registry.npmjs.org/@mojaloop/central-services-logger/-/central-services-logger-11.3.1.tgz", - "integrity": "sha512-XVU2K5grE1ZcIyxUXeMlvoVkeIcs9y1/0EKxa2Bk5sEbqXUtHuR8jqbAGlwaUIi9T9YWZRJyVC77nOQe/X1teA==", + "version": "11.5.1", + "resolved": "https://registry.npmjs.org/@mojaloop/central-services-logger/-/central-services-logger-11.5.1.tgz", + "integrity": "sha512-l+6+w35NqFJn1Xl82l55x71vCARWTkO6hYAgwbFuqVRqX0jqaRi4oiXG2WwPRVMLqVv8idAboCMX/I6vg/d4Kw==", "dependencies": { - "@types/node": "^20.12.7", "parse-strings-in-object": "2.0.0", "rc": "1.2.8", "safe-stable-stringify": "^2.4.3", - "winston": "3.13.0" + "winston": "3.14.2" } }, "node_modules/@mojaloop/central-services-metrics": { - "version": "12.0.8", - "resolved": "https://registry.npmjs.org/@mojaloop/central-services-metrics/-/central-services-metrics-12.0.8.tgz", - "integrity": "sha512-eYWX56zMlj0M0bE6qBLzhwDjo0C4LUQLcQW8du3xJ3mhxH0fSmw+Y5wsmuPmUVQZ90EU4S8l39VcXwh6ludLVg==", + "version": "12.4.1", + "resolved": "https://registry.npmjs.org/@mojaloop/central-services-metrics/-/central-services-metrics-12.4.1.tgz", + "integrity": "sha512-KkbXfKDAxuy//v0q4cSQ52YSL7QGndiQXSK6cUBTywHViXSkeCMe+0bV8FLScgnLKRJTgLTDAbmXVWen5SoLMw==", "dependencies": { - "prom-client": "14.2.0" + "prom-client": "15.1.3" } }, "node_modules/@mojaloop/central-services-shared": { - "version": "18.3.8", - "resolved": "https://registry.npmjs.org/@mojaloop/central-services-shared/-/central-services-shared-18.3.8.tgz", - "integrity": "sha512-Wk+uG+mnOFrFNeDq0ffE+OXvcAtfemSPocPdCRFvnF0p123tV9CiH540R29XrXlRTLt78JS4N3GBYyR7E3ZfBA==", + "version": "18.7.1", + "resolved": "https://registry.npmjs.org/@mojaloop/central-services-shared/-/central-services-shared-18.7.1.tgz", + "integrity": "sha512-2rqFlO8AppDxRDnJ0uj9IsjxrB7WHXpEc028GiTIdK4TMm3AyZfO2uGUA5Gd2vENazet7LrdJdTa6jAqzlQzmw==", "dependencies": { "@hapi/catbox": "12.1.1", "@hapi/catbox-memory": "5.0.1", - "axios": "1.7.2", + "@mojaloop/inter-scheme-proxy-cache-lib": "2.2.0", + "axios": "1.7.4", "clone": "2.1.2", "dotenv": "16.4.5", "env-var": "7.5.0", "event-stream": "4.0.1", - "immutable": "4.3.6", + "fast-safe-stringify": "^2.1.1", + "immutable": "4.3.7", "lodash": "4.17.21", "mustache": "4.2.0", "openapi-backend": "5.10.6", - "raw-body": "2.5.2", + "raw-body": "3.0.0", "rc": "1.2.8", "shins": "2.6.0", "uuid4": "2.0.3", "widdershins": "^4.0.1", - "yaml": "2.4.5" + "yaml": "2.5.0" }, "peerDependencies": { "@mojaloop/central-services-error-handling": ">=13.x.x", @@ -2032,10 +1958,60 @@ "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" }, + "node_modules/@mojaloop/central-services-shared/node_modules/@mojaloop/inter-scheme-proxy-cache-lib": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@mojaloop/inter-scheme-proxy-cache-lib/-/inter-scheme-proxy-cache-lib-2.2.0.tgz", + "integrity": "sha512-QrbJlhy7f7Tf1DTjspxqtw0oN3eUAm5zKfCm7moQIYFEV3MYF3rsbODLpgxyzmAO8FFi2Dky/ff7QMVnlA/P9A==", + "dependencies": { + "@mojaloop/central-services-logger": "11.5.0", + "ajv": "^8.17.1", + "convict": "^6.2.4", + "fast-safe-stringify": "^2.1.1", + "ioredis": "^5.4.1" + }, + "engines": { + "node": ">=18.x" + } + }, + "node_modules/@mojaloop/central-services-shared/node_modules/axios": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.4.tgz", + "integrity": "sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/@mojaloop/central-services-shared/node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/@mojaloop/central-services-shared/node_modules/raw-body": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", + "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.6.3", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/@mojaloop/central-services-stream": { - "version": "11.3.0", - "resolved": "https://registry.npmjs.org/@mojaloop/central-services-stream/-/central-services-stream-11.3.0.tgz", - "integrity": "sha512-Yg50/pg6Jk3h8qJHuIkOlN1ZzZkMreEP5ukl6IDNJ758bpr+0sME0JGL5DwbwHCXTD0T/vemMrxIr5igtobq1Q==", + "version": "11.3.1", + "resolved": "https://registry.npmjs.org/@mojaloop/central-services-stream/-/central-services-stream-11.3.1.tgz", + "integrity": "sha512-mSdWvEFJEjKkZdDs+e1yeZm/gFfXTqA+eVRIBmp8p67QJy36ZTaAvrvebGYKZ60MBN2syDrqL+DbQMJdoxHLEA==", "dependencies": { "async": "3.2.5", "async-exit-hook": "2.0.1", @@ -2043,7 +2019,7 @@ "node-rdkafka": "2.18.0" }, "peerDependencies": { - "@mojaloop/central-services-error-handling": ">=12.x.x", + "@mojaloop/central-services-error-handling": ">=13.x.x", "@mojaloop/central-services-logger": ">=11.x.x" }, "peerDependenciesMeta": { @@ -2056,9 +2032,9 @@ } }, "node_modules/@mojaloop/database-lib": { - "version": "11.0.5", - "resolved": "https://registry.npmjs.org/@mojaloop/database-lib/-/database-lib-11.0.5.tgz", - "integrity": "sha512-u7MOtJIwwlyxeFlUplf7kcdjnyOZpXS1rqEQw21WBIRTl4RXqQl6/ThTCIjCxxGc4dK/BfZz7Spo10RHcWvSgw==", + "version": "11.0.6", + "resolved": "https://registry.npmjs.org/@mojaloop/database-lib/-/database-lib-11.0.6.tgz", + "integrity": "sha512-5rg8aBkHEaz6MkgVZqXkYFFVKAc80iQejmyZaws3vuZnrG6YfAhTGQTSZCDfYX3WqtDpt4OE8yhYeBua82ftMA==", "dependencies": { "knex": "3.1.0", "lodash": "4.17.21", @@ -2098,15 +2074,64 @@ } } }, + "node_modules/@mojaloop/event-sdk/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==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/@mojaloop/event-sdk/node_modules/tslib": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" }, + "node_modules/@mojaloop/event-sdk/node_modules/winston": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.13.0.tgz", + "integrity": "sha512-rwidmA1w3SE4j0E5MuIufFhyJPBDG7Nu71RkZor1p2+qHvJSZ9GYDA81AyleQcZbh/+V6HjeBdfnTZJm9rSeQQ==", + "dependencies": { + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.2", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.4.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.7.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@mojaloop/inter-scheme-proxy-cache-lib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@mojaloop/inter-scheme-proxy-cache-lib/-/inter-scheme-proxy-cache-lib-2.3.1.tgz", + "integrity": "sha512-94HhBs/DJOwyE24CSVBpySrulMHN/xntc9c/0ZjpOzVHBsu/HJmfiA/CuwTo0GGNFrCxM9FgwjccafQeEs2B1A==", + "dependencies": { + "@mojaloop/central-services-logger": "11.5.1", + "ajv": "^8.17.1", + "convict": "^6.2.4", + "fast-safe-stringify": "^2.1.1", + "ioredis": "^5.4.1" + }, + "engines": { + "node": ">=18.x" + } + }, "node_modules/@mojaloop/sdk-standard-components": { - "version": "18.1.0", - "resolved": "https://registry.npmjs.org/@mojaloop/sdk-standard-components/-/sdk-standard-components-18.1.0.tgz", - "integrity": "sha512-8g4JuVl3f9t80OEtvn9BeUtlZIW4kcL40f72FZobtqQjAZ+yz4J0BlWS/OEJDpuYV1qoyxGiuMRojKqP2Yio7g==", + "version": "18.4.0", + "resolved": "https://registry.npmjs.org/@mojaloop/sdk-standard-components/-/sdk-standard-components-18.4.0.tgz", + "integrity": "sha512-ewXcNSDt9SnwmwVzTgFp8a6Ohb7taZzLEAp0LmMjlE5NyTS/1633q1UFBYFU/3ETKIjwEORwgT2333hgSz6G/w==", "dependencies": { "base64url": "3.0.1", "fast-safe-stringify": "^2.1.1", @@ -2184,259 +2209,47 @@ "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" }, - "node_modules/@npmcli/ci-detect": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@npmcli/ci-detect/-/ci-detect-3.0.2.tgz", - "integrity": "sha512-P7nZG0skRVa9lH0OQmFG62CrzOySUiuPbKopjVAj3sXP0m1om9XfIvTp46h+NvlpTyd121JekiXFZj+1pnbm9g==", - "deprecated": "this package has been deprecated, use `ci-info` instead", - "dev": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@npmcli/fs": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.1.tgz", - "integrity": "sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==", - "dev": true, - "dependencies": { - "semver": "^7.3.5" - }, + "node_modules/@opentelemetry/api": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=8.0.0" } }, - "node_modules/@npmcli/git": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-4.1.0.tgz", - "integrity": "sha512-9hwoB3gStVfa0N31ymBmrX+GuDGdVA/QWShZVqE0HK2Af+7QGGrCTbZia/SW0ImUTjTne7SP91qxDmtXvDHRPQ==", - "dev": true, - "dependencies": { - "@npmcli/promise-spawn": "^6.0.0", - "lru-cache": "^7.4.4", - "npm-pick-manifest": "^8.0.0", - "proc-log": "^3.0.0", - "promise-inflight": "^1.0.1", - "promise-retry": "^2.0.1", - "semver": "^7.3.5", - "which": "^3.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" }, - "node_modules/@npmcli/git/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/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" }, - "node_modules/@npmcli/installed-package-contents": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-2.1.0.tgz", - "integrity": "sha512-c8UuGLeZpm69BryRykLuKRyKFZYJsZSCT4aVY5ds4omyZqJ172ApzgfKJ5eV/r3HgLdUYgFVe54KSFVjKoe27w==", - "dev": true, - "dependencies": { - "npm-bundled": "^3.0.0", - "npm-normalize-package-bin": "^3.0.0" - }, - "bin": { - "installed-package-contents": "bin/index.js" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" }, - "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": { - "mkdirp": "^1.0.4", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" }, - "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==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.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==", - "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": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-6.0.2.tgz", - "integrity": "sha512-NCcr1uQo1k5U+SYlnIrbAh3cxy+OQT1VtqiAbxdymSlptbzBb62AjH2xXgjNCoP073hoa1CfCAcwoZ8k96C4nA==", - "dev": true, - "dependencies": { - "@npmcli/node-gyp": "^3.0.0", - "@npmcli/promise-spawn": "^6.0.0", - "node-gyp": "^9.0.0", - "read-package-json-fast": "^3.0.0", - "which": "^3.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@npmcli/run-script/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/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@pnpm/config.env-replace": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", - "integrity": "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==", - "dev": true, - "engines": { - "node": ">=12.22.0" - } - }, - "node_modules/@pnpm/network.ca-file": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz", - "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==", - "dev": true, - "dependencies": { - "graceful-fs": "4.2.10" - }, - "engines": { - "node": ">=12.22.0" - } - }, - "node_modules/@pnpm/network.ca-file/node_modules/graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", - "dev": true - }, - "node_modules/@pnpm/npm-conf": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-2.2.2.tgz", - "integrity": "sha512-UA91GwWPhFExt3IizW6bOeY/pQ0BkuNwKjk9iQW9KqxluGCrg4VenZ0/L+2Y0+ZOtme72EVvg6v0zo3AMQRCeA==", - "dev": true, - "dependencies": { - "@pnpm/config.env-replace": "^1.1.0", - "@pnpm/network.ca-file": "^1.0.1", - "config-chain": "^1.1.11" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@protobufjs/aspromise": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" - }, - "node_modules/@protobufjs/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" - }, - "node_modules/@protobufjs/codegen": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" - }, - "node_modules/@protobufjs/eventemitter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" - }, - "node_modules/@protobufjs/fetch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", - "dependencies": { - "@protobufjs/aspromise": "^1.1.1", - "@protobufjs/inquire": "^1.1.0" - } - }, - "node_modules/@protobufjs/float": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" }, "node_modules/@protobufjs/inquire": { "version": "1.1.0", @@ -2481,72 +2294,12 @@ "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" }, - "node_modules/@sigstore/bundle": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-1.1.0.tgz", - "integrity": "sha512-PFutXEy0SmQxYI4texPw3dd2KewuNqv7OuK1ZFtY2fM754yhvG2KdgwIhRnoEE2uHdtdGNQ8s0lb94dW9sELog==", - "dev": true, - "dependencies": { - "@sigstore/protobuf-specs": "^0.2.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@sigstore/protobuf-specs": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.2.1.tgz", - "integrity": "sha512-XTWVxnWJu+c1oCshMLwnKvz8ZQJJDVOlciMfgpJBQbThVjKTCG8dwyhgLngBD2KN0ap9F/gOV8rFDEx8uh7R2A==", - "dev": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@sigstore/sign": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@sigstore/sign/-/sign-1.0.0.tgz", - "integrity": "sha512-INxFVNQteLtcfGmcoldzV6Je0sbbfh9I16DM4yJPw3j5+TFP8X6uIiA18mvpEa9yyeycAKgPmOA3X9hVdVTPUA==", - "dev": true, - "dependencies": { - "@sigstore/bundle": "^1.1.0", - "@sigstore/protobuf-specs": "^0.2.0", - "make-fetch-happen": "^11.0.1" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@sigstore/tuf": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-1.0.3.tgz", - "integrity": "sha512-2bRovzs0nJZFlCN3rXirE4gwxCn97JNjMmwpecqlbgV9WcxX7WRuIrgzx/X7Ib7MYRbyUTpBYE0s2x6AmZXnlg==", - "dev": true, - "dependencies": { - "@sigstore/protobuf-specs": "^0.2.0", - "tuf-js": "^1.1.7" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", "dev": true }, - "node_modules/@sindresorhus/is": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", - "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==", - "dev": true, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sindresorhus/is?sponsor=1" - } - }, "node_modules/@sinonjs/commons": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", @@ -2566,98 +2319,31 @@ } }, "node_modules/@sinonjs/samsam": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.0.tgz", - "integrity": "sha512-Bp8KUVlLp8ibJZrnvq2foVhP0IVX2CIprMJPK0vqGqgrDa0OHVKeZyBykqskkrdxV6yKBPmGasO8LVjAKR3Gew==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.2.tgz", + "integrity": "sha512-v46t/fwnhejRSFTGqbpn9u+LQ9xJDse10gNnPgAcxgdoCDMXj/G2asWAC/8Qs+BAZDicX+MNZouXT1A7c83kVw==", "dev": true, "dependencies": { - "@sinonjs/commons": "^2.0.0", + "@sinonjs/commons": "^3.0.1", "lodash.get": "^4.4.2", - "type-detect": "^4.0.8" + "type-detect": "^4.1.0" } }, - "node_modules/@sinonjs/samsam/node_modules/@sinonjs/commons": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", - "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "node_modules/@sinonjs/samsam/node_modules/type-detect": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", "dev": true, - "dependencies": { - "type-detect": "4.0.8" + "engines": { + "node": ">=4" } }, "node_modules/@sinonjs/text-encoding": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz", - "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==", + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.3.tgz", + "integrity": "sha512-DE427ROAphMQzU4ENbliGYrBSYPXF+TtLg9S8vzeA+OF4ZKzoDdzfL8sxuMUGS/lgRhM6j1URSk9ghf7Xo1tyA==", "dev": true }, - "node_modules/@szmarczak/http-timer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", - "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", - "dev": true, - "dependencies": { - "defer-to-connect": "^2.0.1" - }, - "engines": { - "node": ">=14.16" - } - }, - "node_modules/@tootallnate/once": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", - "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", - "dev": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tufjs/canonical-json": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-1.0.0.tgz", - "integrity": "sha512-QTnf++uxunWvG2z3UFNzAoQPHxnSXOwtaI3iJ+AohhV+5vONuArPjJE7aPXPVXfXJsqrVbZBu9b81AJoSd09IQ==", - "dev": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@tufjs/models": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tufjs/models/-/models-1.0.4.tgz", - "integrity": "sha512-qaGV9ltJP0EO25YfFUPhxRVK0evXFIAGicsVXuRim4Ed9cjPxYhNnNJ49SFmbeLgtxpslIkX317IgpfcHPVj/A==", - "dev": true, - "dependencies": { - "@tufjs/canonical-json": "1.0.0", - "minimatch": "^9.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@tufjs/models/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/@tufjs/models/node_modules/minimatch": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", - "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -2708,11 +2394,16 @@ "@types/node": "*" } }, - "node_modules/@types/http-cache-semantics": { - "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/ioredis-mock": { + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/@types/ioredis-mock/-/ioredis-mock-8.2.5.tgz", + "integrity": "sha512-cZyuwC9LGtg7s5G9/w6rpy3IOZ6F/hFR0pQlWYZESMo1xQUYbDpa6haqB4grTePjsGzcB/YLBFCjqRunK5wieg==", + "dev": true, + "peer": true, + "dependencies": { + "@types/node": "*", + "ioredis": ">=5" + } }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.6", @@ -2739,9 +2430,9 @@ } }, "node_modules/@types/jest": { - "version": "29.5.12", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.12.tgz", - "integrity": "sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==", + "version": "29.5.14", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", + "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", "dev": true, "dependencies": { "expect": "^29.0.0", @@ -2765,6 +2456,11 @@ "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", "dev": true }, + "node_modules/@types/luxon": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.4.2.tgz", + "integrity": "sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA==" + }, "node_modules/@types/markdown-it": { "version": "14.1.1", "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.1.tgz", @@ -2788,9 +2484,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.14.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.2.tgz", - "integrity": "sha512-xyu6WAMVwv6AKFLB+e/7ySZVr/0zLCzOa7rSpq6jNwpqOrUbcACDWC+53d4n2QHOnDou0fbIsg8wZu/sxrnI4Q==", + "version": "20.14.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.12.tgz", + "integrity": "sha512-r7wNXakLeSsGT0H1AU863vS2wa5wBOK4bWMjZz2wj+8nBx+m5PeIn0k8AloSLpRuiwdRQZwarZqHE4FNArPuJQ==", "dependencies": { "undici-types": "~5.26.4" } @@ -2801,12 +2497,6 @@ "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", "dev": true }, - "node_modules/@types/semver-utils": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@types/semver-utils/-/semver-utils-1.1.3.tgz", - "integrity": "sha512-T+YwkslhsM+CeuhYUxyAjWm7mJ5am/K10UX40RuA6k6Lc7eGtq8iY2xOzy7Vq0GOqhl/xZl5l2FwURZMTPTUww==", - "dev": true - }, "node_modules/@types/stack-utils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", @@ -2839,12 +2529,6 @@ "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", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true - }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -2858,9 +2542,9 @@ } }, "node_modules/acorn": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -2884,30 +2568,6 @@ "integrity": "sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ==", "dev": true }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/agentkeepalive": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", - "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==", - "dev": true, - "dependencies": { - "humanize-ms": "^1.2.1" - }, - "engines": { - "node": ">= 8.0.0" - } - }, "node_modules/aggregate-error": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", @@ -2922,14 +2582,14 @@ } }, "node_modules/ajv": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.16.0.tgz", - "integrity": "sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dependencies": { "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.4.1" + "require-from-string": "^2.0.2" }, "funding": { "type": "github", @@ -2987,15 +2647,6 @@ "node": ">=0.10.0" } }, - "node_modules/ansi-align": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", - "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", - "dev": true, - "dependencies": { - "string-width": "^4.1.0" - } - }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -3057,46 +2708,12 @@ "node": ">=8" } }, - "node_modules/aproba": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", - "dev": true - }, "node_modules/archy": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", "dev": true }, - "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==", - "deprecated": "This package is no longer supported.", - "dev": true, - "dependencies": { - "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": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -3106,6 +2723,12 @@ "sprintf-js": "~1.0.2" } }, + "node_modules/argparse/node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, "node_modules/array-buffer-byte-length": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", @@ -3152,15 +2775,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/array.prototype.findlast": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", @@ -3237,22 +2851,10 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array.prototype.toreversed": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/array.prototype.toreversed/-/array.prototype.toreversed-1.1.2.tgz", - "integrity": "sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - } - }, - "node_modules/array.prototype.tosorted": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", - "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", + "node_modules/array.prototype.tosorted": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", + "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", "dev": true, "dependencies": { "call-bind": "^1.0.7", @@ -3314,9 +2916,9 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/audit-ci": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/audit-ci/-/audit-ci-7.0.1.tgz", - "integrity": "sha512-NAZuQYyZHmtrNGpS4qfUp8nFvB+6UdfSOg7NUcsyvuDVfulXH3lpnN2PcXOUj7Jr3epAoQ6BCpXmjMODC8SBgQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/audit-ci/-/audit-ci-7.1.0.tgz", + "integrity": "sha512-PjjEejlST57S/aDbeWLic0glJ8CNl/ekY3kfGFPMrPkmuaYaDKcMH0F9x9yS9Vp6URhuefSCubl/G0Y2r6oP0g==", "dev": true, "dependencies": { "cross-spawn": "^7.0.3", @@ -3336,17 +2938,6 @@ "node": ">=16" } }, - "node_modules/audit-resolve-core": { - "version": "3.0.0-3", - "resolved": "https://registry.npmjs.org/audit-resolve-core/-/audit-resolve-core-3.0.0-3.tgz", - "integrity": "sha512-37Qkk1EerVIzSF824BytESWeEtUcbAmdWyTGA/MqnHgVzO+PnU9oNqOpZTMst54xLpJci70Jszq/sLogqfvHmQ==", - "dev": true, - "dependencies": { - "debug": "^4.3.1", - "djv": "^2.1.4", - "yargs-parser": "^21.0.0" - } - }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -3362,15 +2953,28 @@ } }, "node_modules/axios": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", - "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", + "version": "1.7.9", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", + "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", + "dev": true, "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } }, + "node_modules/axios-retry": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/axios-retry/-/axios-retry-4.5.0.tgz", + "integrity": "sha512-aR99oXhpEDGo0UuAlYcn2iGRds30k366Zfa05XWScR9QaQD4JYiP3/1Qt1u7YlefUOK+cn0CcwoL1oefavQUlQ==", + "dev": true, + "dependencies": { + "is-retry-allowed": "^2.2.0" + }, + "peerDependencies": { + "axios": "0.x || 1.x" + } + }, "node_modules/babel-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", @@ -3560,9 +3164,9 @@ "dev": true }, "node_modules/body-parser": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", - "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", "dependencies": { "bytes": "3.1.2", "content-type": "~1.0.5", @@ -3572,7 +3176,7 @@ "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", - "qs": "6.11.0", + "qs": "6.13.0", "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" @@ -3606,133 +3210,11 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "node_modules/body-parser/node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" }, - "node_modules/boxen": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-7.1.1.tgz", - "integrity": "sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog==", - "dev": true, - "dependencies": { - "ansi-align": "^3.0.1", - "camelcase": "^7.0.1", - "chalk": "^5.2.0", - "cli-boxes": "^3.0.0", - "string-width": "^5.1.2", - "type-fest": "^2.13.0", - "widest-line": "^4.0.1", - "wrap-ansi": "^8.1.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "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==", - "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" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/boxen/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", - "dev": true, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "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", - "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", - "dev": true, - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -3759,9 +3241,9 @@ "integrity": "sha512-UcQusNAX7nnuXf9tvvLRC6DtZ8/YkDJRtTIbiA5ayb8MehwtSwtkvd5ZTXNLUTTtU6J/yJsi+1LJXqgRz1obwg==" }, "node_modules/browserslist": { - "version": "4.23.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.1.tgz", - "integrity": "sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw==", + "version": "4.23.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.2.tgz", + "integrity": "sha512-qkqSyistMYdxAcw+CzbZwlBy8AGmS/eEWs+sEV5TnLRGDOL+C5M2EnH6tlZyg0YoAxGJAFKh61En9BR941GnHA==", "dev": true, "funding": [ { @@ -3778,10 +3260,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001629", - "electron-to-chromium": "^1.4.796", + "caniuse-lite": "^1.0.30001640", + "electron-to-chromium": "^1.4.820", "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.16" + "update-browserslist-db": "^1.1.0" }, "bin": { "browserslist": "cli.js" @@ -3827,139 +3309,6 @@ "node": ">= 0.8" } }, - "node_modules/cacache": { - "version": "17.1.4", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-17.1.4.tgz", - "integrity": "sha512-/aJwG2l3ZMJ1xNAnqbMpA40of9dj/pIH3QfiuQSqjfPJF747VR0J/bHn+/KdNnHKc6XQcWt/AfRSBft82W1d2A==", - "dev": true, - "dependencies": { - "@npmcli/fs": "^3.1.0", - "fs-minipass": "^3.0.0", - "glob": "^10.2.2", - "lru-cache": "^7.7.1", - "minipass": "^7.0.3", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "p-map": "^4.0.0", - "ssri": "^10.0.0", - "tar": "^6.1.11", - "unique-filename": "^3.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "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/cacache/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==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/cacache/node_modules/glob": { - "version": "10.4.1", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.1.tgz", - "integrity": "sha512-2jelhlq3E4ho74ZyVLN03oKdAZVUa6UDZzFLVH1H7dnoax+y9qyaq8zBkfDIggjniU19z0wU18y16jMB2eyVIw==", - "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/cacache/node_modules/minimatch": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", - "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/cacache/node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/cacache/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==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/cacheable-lookup": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", - "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==", - "dev": true, - "engines": { - "node": ">=14.16" - } - }, - "node_modules/cacheable-request": { - "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.2", - "get-stream": "^6.0.1", - "http-cache-semantics": "^4.1.1", - "keyv": "^4.5.3", - "mimic-response": "^4.0.0", - "normalize-url": "^8.0.0", - "responselike": "^3.0.0" - }, - "engines": { - "node": ">=14.16" - } - }, "node_modules/caching-transform": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", @@ -4047,6 +3396,7 @@ "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, "engines": { "node": ">=6" } @@ -4069,9 +3419,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001632", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001632.tgz", - "integrity": "sha512-udx3o7yHJfUxMLkGohMlVHCvFvWmirKh9JAH/d7WOLPetlH+LTL5cocMZ0t7oZx/mdlOWXti97xLZWc8uURRHg==", + "version": "1.0.30001643", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001643.tgz", + "integrity": "sha512-ERgWGNleEilSrHM6iUz/zJNSQTP8Mr21wDWpdgvRwcTXGAq6jMtOUPP4dqFPTdKqZ2wKTdtB+uucZ3MRpAUSmg==", "dev": true, "funding": [ { @@ -4128,9 +3478,9 @@ } }, "node_modules/chance": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/chance/-/chance-1.1.11.tgz", - "integrity": "sha512-kqTg3WWywappJPqtgrdvbA380VoXO2eu9VCV895JgbyHsaErXdyHK9LOZ911OvAk6L0obK7kDk9CGs8+oBawVA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/chance/-/chance-1.1.12.tgz", + "integrity": "sha512-vVBIGQVnwtUG+SYe0ge+3MvF78cvSpuCOEUJr7sVEk2vSBuMW6OXNJjSzdtzrlxNUEaoqH2GBd5Y/+18BEB01Q==", "dev": true }, "node_modules/char-regex": { @@ -4201,15 +3551,6 @@ "fsevents": "~2.3.2" } }, - "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/ci-info": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", @@ -4240,33 +3581,6 @@ "node": ">=6" } }, - "node_modules/cli-boxes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", - "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-table3": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", - "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0" - }, - "engines": { - "node": "10.* || >= 12.*" - }, - "optionalDependencies": { - "@colors/colors": "1.5.0" - } - }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -4304,6 +3618,14 @@ "node": ">=0.8" } }, + "node_modules/cluster-key-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -4362,15 +3684,6 @@ "simple-swizzle": "^0.2.2" } }, - "node_modules/color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "dev": true, - "bin": { - "color-support": "bin.js" - } - }, "node_modules/color/node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -4493,17 +3806,17 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "node_modules/concat-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", - "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", "dev": true, "engines": [ - "node >= 6.0" + "node >= 0.8" ], "dependencies": { "buffer-from": "^1.0.0", "inherits": "^2.0.3", - "readable-stream": "^3.0.2", + "readable-stream": "^2.2.2", "typedarray": "^0.0.6" } }, @@ -4521,103 +3834,23 @@ "node": ">= 6" } }, - "node_modules/config-chain": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", - "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", - "dev": true, - "dependencies": { - "ini": "^1.3.4", - "proto-list": "~1.2.1" - } - }, - "node_modules/config-chain/node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true - }, - "node_modules/configstore": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-6.0.0.tgz", - "integrity": "sha512-cD31W1v3GqUlQvbBCGcXmd2Nj9SvLDOP1oQ0YFuLETufzSPaKp11rYBsSOm7rCsW3OnIRAFM3OxRhceaXNYHkA==", - "dev": true, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "dependencies": { - "dot-prop": "^6.0.1", - "graceful-fs": "^4.2.6", - "unique-string": "^3.0.0", - "write-file-atomic": "^3.0.3", - "xdg-basedir": "^5.0.1" + "safe-buffer": "5.2.1" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/yeoman/configstore?sponsor=1" + "node": ">= 0.6" } }, - "node_modules/configstore/node_modules/dot-prop": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", - "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==", - "dev": true, - "dependencies": { - "is-obj": "^2.0.0" - }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/configstore/node_modules/write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "node_modules/configstore/node_modules/xdg-basedir": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-5.1.0.tgz", - "integrity": "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", - "dev": true - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "engines": { - "node": ">= 0.6" + "node": ">= 0.6" } }, "node_modules/conventional-changelog": { @@ -4920,12 +4153,53 @@ "node": ">=10" } }, + "node_modules/conventional-recommended-bump/node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "dev": true, + "engines": [ + "node >= 6.0" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/conventional-recommended-bump/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/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/convict": { + "version": "6.2.4", + "resolved": "https://registry.npmjs.org/convict/-/convict-6.2.4.tgz", + "integrity": "sha512-qN60BAwdMVdofckX7AlohVJ2x9UvjTNoKVXCL2LxFk1l7757EJqf1nySdMkPQer0bt8kQ5lQiyZ9/2NvrFBuwQ==", + "dependencies": { + "lodash.clonedeep": "^4.5.0", + "yargs-parser": "^20.2.7" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/cookie": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", @@ -4973,6 +4247,15 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/cron": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/cron/-/cron-3.3.0.tgz", + "integrity": "sha512-uwRMLudSn2vPPjg392dKOm3KoJRw/eOOFtcHbrmRJxCd/NDF3187JVnajTvHPIlam0UwhKUZWwVPd1V5vBlmsw==", + "dependencies": { + "@types/luxon": "~3.4.0", + "luxon": "~3.5.0" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -4987,33 +4270,6 @@ "node": ">= 8" } }, - "node_modules/crypto-random-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz", - "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==", - "dev": true, - "dependencies": { - "type-fest": "^1.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/crypto-random-string/node_modules/type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/css-select": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", @@ -5110,7 +4366,6 @@ "version": "4.3.5", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", - "dev": true, "dependencies": { "ms": "2.1.2" }, @@ -5156,33 +4411,6 @@ "node": ">=0.10.0" } }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "dev": true, - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/decompress-response/node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/dedent": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", @@ -5234,15 +4462,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/default-shell": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/default-shell/-/default-shell-1.0.1.tgz", - "integrity": "sha512-/Os8tTMPSriNHCsVj3VLjMZblIl1sIg8EXz3qg7C5K+y9calfTA/qzlfPvCQ+LEgLWmtZ9wCnzE1w+S6TPPFyQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/defaults": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", @@ -5264,15 +4483,6 @@ "node": ">=0.8" } }, - "node_modules/defer-to-connect": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", - "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -5313,11 +4523,13 @@ "node": ">=0.4.0" } }, - "node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", - "dev": true + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "engines": { + "node": ">=0.10" + } }, "node_modules/depd": { "version": "2.0.0", @@ -5360,9 +4572,9 @@ } }, "node_modules/diff": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", - "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", "dev": true, "engines": { "node": ">=0.3.1" @@ -5377,27 +4589,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/djv": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/djv/-/djv-2.1.4.tgz", - "integrity": "sha512-giDn+BVbtLlwtkvtcsZjbjzpALHB77skiv3FIu6Wp8b5j8BunDcVJYH0cGUaexp6s0Sb7IkquXXjsLBJhXwQpA==", - "dev": true, - "optionalDependencies": { - "@korzio/djv-draft-04": "^2.0.1" - } - }, "node_modules/docdash": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/docdash/-/docdash-2.0.2.tgz", @@ -5494,9 +4685,10 @@ } }, "node_modules/dotenv": { - "version": "16.4.5", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", - "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "dev": true, "engines": { "node": ">=12" }, @@ -5616,12 +4808,6 @@ "node": ">= 6" } }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true - }, "node_modules/easy-table": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/easy-table/-/easy-table-1.2.0.tgz", @@ -5661,9 +4847,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.798", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.798.tgz", - "integrity": "sha512-by9J2CiM9KPGj9qfp5U4FcPSbXJG7FNzqnYaY4WLzX+v2PHieVGmnsA4dxfpGE3QEC7JofpPZmn7Vn1B9NR2+Q==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.0.tgz", + "integrity": "sha512-Vb3xHHYnLseK8vlMJQKJYXJ++t4u1/qJ3vykuVrVjvdiOEhYyT1AuP4x03G8EnPmYvYOhe9T+dADTmthjRQMkA==", "dev": true }, "node_modules/emittery": { @@ -5689,9 +4875,9 @@ "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" }, "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "engines": { "node": ">= 0.8" } @@ -5701,6 +4887,7 @@ "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", "optional": true, + "peer": true, "dependencies": { "iconv-lite": "^0.6.2" } @@ -5724,15 +4911,6 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/env-paths": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/env-var": { "version": "7.5.0", "resolved": "https://registry.npmjs.org/env-var/-/env-var-7.5.0.tgz", @@ -5741,12 +4919,6 @@ "node": ">=10" } }, - "node_modules/err-code": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", - "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", - "dev": true - }, "node_modules/error-callsites": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/error-callsites/-/error-callsites-2.0.4.tgz", @@ -5935,18 +5107,6 @@ "node": ">=6" } }, - "node_modules/escape-goat": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-4.0.0.tgz", - "integrity": "sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -6247,9 +5407,9 @@ } }, "node_modules/eslint-plugin-promise": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.2.0.tgz", - "integrity": "sha512-QmAqwizauvnKOlifxyDj2ObfULpHQawlg/zQdgEixur9vl0CvZGv/LCJV2rtj3210QCoeGBzVMfMXqGAOr/4fA==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.6.0.tgz", + "integrity": "sha512-57Zzfw8G6+Gq7axm2Pdo3gW/Rx3h9Yywgn61uE/3elTCOePEHVrn2i5CdfBwA1BLK0Q0WqctICIUSqXZW/VprQ==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -6262,35 +5422,35 @@ } }, "node_modules/eslint-plugin-react": { - "version": "7.34.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.2.tgz", - "integrity": "sha512-2HCmrU+/JNigDN6tg55cRDKCQWicYAPB38JGSFDQt95jDm8rrvSUo7YPkOIm5l6ts1j1zCvysNcasvfTMQzUOw==", + "version": "7.37.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.0.tgz", + "integrity": "sha512-IHBePmfWH5lKhJnJ7WB1V+v/GolbB0rjS8XYVCSQCZKaQCAUhMoVoOEn1Ef8Z8Wf0a7l8KTJvuZg5/e4qrZ6nA==", "dev": true, "dependencies": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", "array.prototype.flatmap": "^1.3.2", - "array.prototype.toreversed": "^1.1.2", - "array.prototype.tosorted": "^1.1.3", + "array.prototype.tosorted": "^1.1.4", "doctrine": "^2.1.0", "es-iterator-helpers": "^1.0.19", "estraverse": "^5.3.0", + "hasown": "^2.0.2", "jsx-ast-utils": "^2.4.1 || ^3.0.0", "minimatch": "^3.1.2", "object.entries": "^1.1.8", "object.fromentries": "^2.0.8", - "object.hasown": "^1.1.4", "object.values": "^1.2.0", "prop-types": "^15.8.1", "resolve": "^2.0.0-next.5", "semver": "^6.3.1", - "string.prototype.matchall": "^4.0.11" + "string.prototype.matchall": "^4.0.11", + "string.prototype.repeat": "^1.0.0" }, "engines": { "node": ">=4" }, "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" } }, "node_modules/eslint-plugin-react/node_modules/doctrine": { @@ -6550,9 +5710,9 @@ } }, "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, "dependencies": { "estraverse": "^5.1.0" @@ -6669,43 +5829,37 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/exponential-backoff": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.1.tgz", - "integrity": "sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==", - "dev": true - }, "node_modules/express": { - "version": "4.19.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", - "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", + "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.2", + "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", "cookie": "0.6.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "1.2.0", + "finalhandler": "1.3.1", "fresh": "0.5.2", "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", + "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", + "path-to-regexp": "0.1.10", "proxy-addr": "~2.0.7", - "qs": "6.11.0", + "qs": "6.13.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", + "send": "0.19.0", + "serve-static": "1.16.2", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "~1.6.18", @@ -6732,30 +5886,11 @@ "ms": "2.0.0" } }, - "node_modules/express/node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" - }, "node_modules/express/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "node_modules/express/node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/extensible-error": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/extensible-error/-/extensible-error-1.0.2.tgz", @@ -6766,22 +5901,6 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, - "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -6793,17 +5912,16 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, - "node_modules/fast-memoize": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/fast-memoize/-/fast-memoize-2.5.2.tgz", - "integrity": "sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw==", - "dev": true - }, "node_modules/fast-safe-stringify": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" }, + "node_modules/fast-uri": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.1.tgz", + "integrity": "sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==" + }, "node_modules/fastq": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", @@ -6827,6 +5945,26 @@ "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" }, + "node_modules/fengari": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/fengari/-/fengari-0.1.4.tgz", + "integrity": "sha512-6ujqUuiIYmcgkGz8MGAdERU57EIluGGPSUgGPTsco657EHa+srq0S3/YUl/r9kx1+D+d4rGfYObd+m8K22gB1g==", + "dev": true, + "dependencies": { + "readline-sync": "^1.4.9", + "sprintf-js": "^1.1.1", + "tmp": "^0.0.33" + } + }, + "node_modules/fengari-interop": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/fengari-interop/-/fengari-interop-0.1.3.tgz", + "integrity": "sha512-EtZ+oTu3kEwVJnoymFPBVLIbQcCoy9uWCVnMA6h3M/RqHkUBsLYp29+RRHf9rKr6GwjubWREU1O7RretFIXjHw==", + "dev": true, + "peerDependencies": { + "fengari": "^0.1.0" + } + }, "node_modules/figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -6920,12 +6058,12 @@ } }, "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", "dependencies": { "debug": "2.6.9", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "2.4.1", "parseurl": "~1.3.3", @@ -7003,15 +6141,6 @@ "node": ">=8" } }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true, - "bin": { - "flat": "cli.js" - } - }, "node_modules/flat-cache": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", @@ -7111,15 +6240,6 @@ "node": ">= 6" } }, - "node_modules/form-data-encoder": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz", - "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==", - "dev": true, - "engines": { - "node": ">= 14.17" - } - }, "node_modules/format-util": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/format-util/-/format-util-1.0.5.tgz", @@ -7134,15 +6254,6 @@ "node": ">= 0.6" } }, - "node_modules/fp-and-or": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/fp-and-or/-/fp-and-or-0.1.4.tgz", - "integrity": "sha512-+yRYRhpnFPWXSly/6V4Lw9IfOV26uu30kynGJ03PW+MnjOEQe45RZ141QcS0aJehYBYA50GfCDnsRbFJdhssRw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", @@ -7176,27 +6287,6 @@ } ] }, - "node_modules/fs-minipass": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz", - "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==", - "dev": true, - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/fs-minipass/node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "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", @@ -7288,26 +6378,6 @@ "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==", - "deprecated": "This package is no longer supported.", - "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/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -7455,12 +6525,12 @@ } }, "node_modules/get-pkg-repo/node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "version": "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": ">=10" + "node": ">=12" } }, "node_modules/get-port": { @@ -7633,30 +6703,6 @@ "node": ">= 6" } }, - "node_modules/global-dirs": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", - "integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==", - "dev": true, - "dependencies": { - "ini": "2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/global-dirs/node_modules/ini": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", - "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", @@ -7681,26 +6727,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -7712,31 +6738,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/got": { - "version": "12.6.1", - "resolved": "https://registry.npmjs.org/got/-/got-12.6.1.tgz", - "integrity": "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==", - "dev": true, - "dependencies": { - "@sindresorhus/is": "^5.2.0", - "@szmarczak/http-timer": "^5.0.1", - "cacheable-lookup": "^7.0.0", - "cacheable-request": "^10.2.8", - "decompress-response": "^6.0.0", - "form-data-encoder": "^2.1.2", - "get-stream": "^6.0.1", - "http2-wrapper": "^2.1.10", - "lowercase-keys": "^3.0.0", - "p-cancelable": "^3.0.0", - "responselike": "^3.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sindresorhus/got?sponsor=1" - } - }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -7770,9 +6771,9 @@ } }, "node_modules/handlebars/node_modules/uglify-js": { - "version": "3.18.0", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.18.0.tgz", - "integrity": "sha512-SyVVbcNBCk0dzr9XL/R/ySrmYf0s372K6/hFklzgcp2lBFyXtw4I7BOdDjlLhE1aVqaI/SHWXWmYdlZxuyF38A==", + "version": "3.19.0", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.0.tgz", + "integrity": "sha512-wNKHUY2hYYkf6oSFfhwwiHo4WCHzHmzcXsqXYTN9ja3iApYIFbb2U6ics9hBcYLHcYGQoAlwnZlTrf3oF+BL/Q==", "dev": true, "optional": true, "bin": { @@ -7935,24 +6936,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", - "dev": true - }, - "node_modules/has-yarn": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-3.0.0.tgz", - "integrity": "sha512-IrsVwUHhEULx3R8f/aA8AHuEzAorplsab/v8HBzEiIukwq5i/EC+xmOW+HfP1OaDP+2JkgT1yILHN2O3UFIbcA==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/hasha": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", @@ -8003,18 +6986,6 @@ "integrity": "sha512-FK1vmMj8BbEipEy8DLIvp71t5UsC7n2D6En/UfM/91PCwmOpj6f2iu0Y0coRC62KSRHHC+dquM2xMULV/X7NFg==", "deprecated": "Use the 'highlight.js' package instead https://npm.im/highlight.js" }, - "node_modules/hosted-git-info": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-5.2.1.tgz", - "integrity": "sha512-xIcQYMnhcx2Nr4JTjsFmwwnr9vldugPy9uVm0o87bjqqWMv9GaqsTeT+i99wTl0mk1uLxJtHxLb8kymqTENQsw==", - "dev": true, - "dependencies": { - "lru-cache": "^7.5.1" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -8039,12 +7010,6 @@ "entities": "^4.4.0" } }, - "node_modules/http-cache-semantics": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", - "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", - "dev": true - }, "node_modules/http-errors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", @@ -8060,63 +7025,11 @@ "node": ">= 0.8" } }, - "node_modules/http-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", - "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", - "dev": true, - "dependencies": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/http2-client": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/http2-client/-/http2-client-1.3.5.tgz", "integrity": "sha512-EC2utToWl4RKfs5zd36Mxq7nzHHBuomZboI0yYL6Y0RmBgT7Sgkq4rQ0ezFTYoIsSs7Tm9SJe+o2FcAg6GBhGA==" }, - "node_modules/http2-wrapper": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz", - "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==", - "dev": true, - "dependencies": { - "quick-lru": "^5.1.1", - "resolve-alpn": "^1.2.0" - }, - "engines": { - "node": ">=10.19.0" - } - }, - "node_modules/http2-wrapper/node_modules/quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dev": true, - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/httpsnippet": { "version": "1.25.0", "resolved": "https://registry.npmjs.org/httpsnippet/-/httpsnippet-1.25.0.tgz", @@ -8276,20 +7189,10 @@ "node": ">=10.17.0" } }, - "node_modules/humanize-ms": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", - "dev": true, - "dependencies": { - "ms": "^2.0.0" - } - }, "node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "optional": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -8312,42 +7215,6 @@ "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", "dev": true }, - "node_modules/ignore-walk": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-6.0.5.tgz", - "integrity": "sha512-VuuG0wCnjhnylG1ABXT3dAuIpTNDs/G8jlpmwXY03fXoXy/8ZK8/T+hMzt8L4WnrLCJgdybqgPagnF/f97cg3A==", - "dev": true, - "dependencies": { - "minimatch": "^9.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/ignore-walk/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/ignore-walk/node_modules/minimatch": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", - "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/ilp-packet": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/ilp-packet/-/ilp-packet-2.2.0.tgz", @@ -8368,9 +7235,9 @@ } }, "node_modules/immutable": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.6.tgz", - "integrity": "sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==" + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", + "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==" }, "node_modules/import-fresh": { "version": "3.3.0", @@ -8397,19 +7264,10 @@ "node": ">=4" } }, - "node_modules/import-lazy": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", - "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", "dev": true, "dependencies": { "pkg-dir": "^4.2.0", @@ -8443,12 +7301,6 @@ "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", @@ -8465,15 +7317,6 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, - "node_modules/ini": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.3.tgz", - "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==", - "dev": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/internal-slot": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", @@ -8503,24 +7346,48 @@ "node": ">=4" } }, - "node_modules/ip-address": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", - "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", - "dev": true, + "node_modules/ioredis": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.4.1.tgz", + "integrity": "sha512-2YZsvl7jopIa1gaePkeMtd9rAcSjOOjPtpcLlOeusyO+XH2SK5ZcT+UCrElPP+WVIInh2TzeI4XW9ENaSLVVHA==", "dependencies": { - "jsbn": "1.1.0", - "sprintf-js": "^1.1.3" + "@ioredis/commands": "^1.1.1", + "cluster-key-slot": "^1.1.0", + "debug": "^4.3.4", + "denque": "^2.1.0", + "lodash.defaults": "^4.2.0", + "lodash.isarguments": "^3.1.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0", + "standard-as-callback": "^2.1.0" }, "engines": { - "node": ">= 12" + "node": ">=12.22.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ioredis" } }, - "node_modules/ip-address/node_modules/sprintf-js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", - "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", - "dev": true + "node_modules/ioredis-mock": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/ioredis-mock/-/ioredis-mock-8.9.0.tgz", + "integrity": "sha512-yIglcCkI1lvhwJVoMsR51fotZVsPsSk07ecTCgRTRlicG0Vq3lke6aAaHklyjmRNRsdYAgswqC2A0bPtQK4LSw==", + "dev": true, + "dependencies": { + "@ioredis/as-callback": "^3.0.0", + "@ioredis/commands": "^1.2.0", + "fengari": "^0.1.4", + "fengari-interop": "^0.1.3", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12.22" + }, + "peerDependencies": { + "@types/ioredis-mock": "^8", + "ioredis": "^5" + } }, "node_modules/ipaddr.js": { "version": "1.9.1", @@ -8618,24 +7485,15 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-ci": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", - "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", - "dev": true, - "dependencies": { - "ci-info": "^3.2.0" - }, - "bin": { - "is-ci": "bin.js" - } - }, "node_modules/is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.0.tgz", + "integrity": "sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA==", "dependencies": { - "hasown": "^2.0.0" + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -8732,28 +7590,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-installed-globally": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", - "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", - "dev": true, - "dependencies": { - "global-dirs": "^3.0.0", - "is-path-inside": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-lambda": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", - "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", - "dev": true - }, "node_modules/is-map": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", @@ -8777,18 +7613,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-npm": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-6.0.0.tgz", - "integrity": "sha512-JEjxbSmtPSt1c8XTkVrlujcXdKV1/tvuQ7GwKcAlyiVLeYFQ2VHat8xfrDJsIkhCdF/tZ7CiIR3sy141c6+gPQ==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -8878,6 +7702,18 @@ "node": ">=0.10.0" } }, + "node_modules/is-retry-allowed": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-2.2.0.tgz", + "integrity": "sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-set": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", @@ -9031,15 +7867,6 @@ "node": ">=4" } }, - "node_modules/is-yarn-global": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.4.1.tgz", - "integrity": "sha512-/kppl+R+LO5VmhYSEWARUFjodS25D68gvj8W7z0I7OWhUla5xWu8KL6CtB2V0R6yqhnRgbcaREMr4EEM6htLPQ==", - "dev": true, - "engines": { - "node": ">=12" - } - }, "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -9072,9 +7899,9 @@ } }, "node_modules/istanbul-lib-instrument": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.2.tgz", - "integrity": "sha512-1WUsZ9R1lA0HtBSohTkm39WTPlNKSJ5iFk7UwqXkBLoHQT+hfqPsfsTDVuZdKGaBwn7din9bS7SsnoAr943hvw==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", "dev": true, "dependencies": { "@babel/core": "^7.23.9", @@ -9186,28 +8013,10 @@ "set-function-name": "^2.0.1" } }, - "node_modules/jackspeak": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.0.tgz", - "integrity": "sha512-JVYhQnN59LVPFCEcVa2C3CrEKYacvjRfqIQl+h8oi91aLYQVWRYbxjPcv1bUiUy/kLmQaANrYfNMCO3kuEDHfw==", - "dev": true, - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, "node_modules/jake": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.1.tgz", - "integrity": "sha512-61btcOHNnLnsOdtLgA5efqQWjnSi/vow5HbI7HMdKKWqvrKR1bLK3BPlJn9gcSaP2ewuamUSMB5XEy76KUIS2w==", + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", + "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", "dependencies": { "async": "^3.2.3", "chalk": "^4.0.2", @@ -9808,9 +8617,9 @@ "dev": true }, "node_modules/joi": { - "version": "17.13.1", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.1.tgz", - "integrity": "sha512-vaBlIKCyo4FCUtCm7Eu4QZd/q02bWcxfUO6YSXAZOWF6gzcLBeba8kwotUdYJjDLW8Cz8RywsSOqiNJZW0mNvg==", + "version": "17.13.3", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", + "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", "dependencies": { "@hapi/hoek": "^9.3.0", "@hapi/topo": "^5.1.0", @@ -9860,17 +8669,12 @@ "xmlcreate": "^2.0.4" } }, - "node_modules/jsbn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", - "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", - "dev": true - }, "node_modules/jsdoc": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.3.tgz", - "integrity": "sha512-Nu7Sf35kXJ1MWDZIMAuATRQTg1iIPdzh7tqJ6jjvaU/GfDf+qi5UV8zJR3Mo+/pYFvm8mzay4+6O5EWigaQBQw==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.4.tgz", + "integrity": "sha512-zeFezwyXeG4syyYHbvh1A967IAqq/67yXtXvuL5wnqCkFZe8I0vKfm+EO+YEvLguo6w9CDUbrAXVtJSHh2E8rw==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@babel/parser": "^7.20.15", "@jsdoc/salty": "^0.2.1", @@ -9933,15 +8737,6 @@ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, - "node_modules/json-parse-helpfulerror": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/json-parse-helpfulerror/-/json-parse-helpfulerror-1.0.3.tgz", - "integrity": "sha512-XgP0FGR77+QhUxjXkwOMkC94k3WtqEBfcnjWqhRd82qTat4SWKRE+9kUnynz/shm3I4ea2+qISvTIeGTNU7kJg==", - "dev": true, - "dependencies": { - "jju": "^1.1.0" - } - }, "node_modules/json-pointer": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/json-pointer/-/json-pointer-0.6.2.tgz", @@ -10020,12 +8815,6 @@ "node": ">=6" } }, - "node_modules/jsonlines": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsonlines/-/jsonlines-0.1.1.tgz", - "integrity": "sha512-ekDrAGso79Cvf+dtm+mL8OBI2bmAOt3gssYs833De/C9NmIpWDWyUO4zPgB5x2/OhY366dkhgfPMYfwZF7yOZA==", - "dev": true - }, "node_modules/jsonparse": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", @@ -10256,25 +9045,10 @@ "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" }, - "node_modules/latest-version": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-7.0.0.tgz", - "integrity": "sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg==", - "dev": true, - "dependencies": { - "package-json": "^8.1.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lazy-cache": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", - "integrity": "sha512-RE2g0b5VGZsOCFOCgP7omTRYFqydmZkBwl5oNnQ1lDYC57uyO9KqNnNVxT7COSHTxrRCWVcAVOcbjk+tvh/rgQ==", + "node_modules/lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha512-RE2g0b5VGZsOCFOCgP7omTRYFqydmZkBwl5oNnQ1lDYC57uyO9KqNnNVxT7COSHTxrRCWVcAVOcbjk+tvh/rgQ==", "engines": { "node": ">=0.10.0" } @@ -10383,6 +9157,16 @@ "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==" + }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" + }, "node_modules/lodash.flattendeep": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", @@ -10395,6 +9179,11 @@ "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", "dev": true }, + "node_modules/lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==" + }, "node_modules/lodash.isequal": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", @@ -10413,9 +9202,9 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, "node_modules/logform": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.6.0.tgz", - "integrity": "sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.6.1.tgz", + "integrity": "sha512-CdaO738xRapbKIMVn2m4F6KTj4j7ooJ8POVnebSgKo3KBz5axNXRAL7ZdRjIV6NOr2Uf4vjtRkxrFETOioCqSA==", "dependencies": { "@colors/colors": "1.6.0", "@types/triple-beam": "^1.3.2", @@ -10428,14 +9217,6 @@ "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": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", @@ -10461,18 +9242,6 @@ "loose-envify": "cli.js" } }, - "node_modules/lowercase-keys": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", - "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/lru-cache": { "version": "7.18.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", @@ -10481,6 +9250,14 @@ "node": ">=12" } }, + "node_modules/luxon": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.5.0.tgz", + "integrity": "sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==", + "engines": { + "node": ">=12" + } + }, "node_modules/make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", @@ -10496,32 +9273,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/make-fetch-happen": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-11.1.1.tgz", - "integrity": "sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w==", - "dev": true, - "dependencies": { - "agentkeepalive": "^4.2.1", - "cacache": "^17.0.0", - "http-cache-semantics": "^4.1.1", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", - "is-lambda": "^1.0.1", - "lru-cache": "^7.7.1", - "minipass": "^5.0.0", - "minipass-fetch": "^3.0.0", - "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": "^10.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/makeerror": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", @@ -10802,51 +9553,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/meow/node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/merge-descriptors": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", - "dev": true, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/merge-options": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-options/-/merge-options-1.0.1.tgz", - "integrity": "sha512-iuPV41VWKWBIOpBsjoxjDZw8/GbSfZ2mk7N1453bwMrfzdrIk7EzBd+8UVR6rkw67th7xnk9Dytl3J+lHPdxvg==", - "dev": true, - "dependencies": { - "is-plain-obj": "^1.1" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, "node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", @@ -10856,9 +9576,9 @@ } }, "node_modules/micromatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", - "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "dependencies": { "braces": "^3.0.3", @@ -10880,9 +9600,9 @@ } }, "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "version": "1.53.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.53.0.tgz", + "integrity": "sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg==", "engines": { "node": ">= 0.6" } @@ -10898,6 +9618,14 @@ "node": ">= 0.6" } }, + "node_modules/mime-types/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -10906,18 +9634,6 @@ "node": ">=6" } }, - "node_modules/mimic-response": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", - "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -10960,184 +9676,6 @@ "node": ">= 6" } }, - "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/minipass-collect": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", - "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", - "dev": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minipass-collect/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/minipass-fetch": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.5.tgz", - "integrity": "sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==", - "dev": true, - "dependencies": { - "minipass": "^7.0.3", - "minipass-sized": "^1.0.3", - "minizlib": "^2.1.2" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - }, - "optionalDependencies": { - "encoding": "^0.1.13" - } - }, - "node_modules/minipass-fetch/node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "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", - "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", - "dev": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minipass-flush/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/minipass-json-stream": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minipass-json-stream/-/minipass-json-stream-1.0.1.tgz", - "integrity": "sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg==", - "dev": true, - "dependencies": { - "jsonparse": "^1.3.1", - "minipass": "^3.0.0" - } - }, - "node_modules/minipass-json-stream/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/minipass-pipeline": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", - "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", - "dev": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-pipeline/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/minipass-sized": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", - "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", - "dev": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-sized/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/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "dev": true, - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minizlib/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/mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", @@ -11205,15 +9743,6 @@ "mustache": "bin/mustache" } }, - "node_modules/mute-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", - "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", - "dev": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/mysql": { "version": "2.18.1", "resolved": "https://registry.npmjs.org/mysql/-/mysql-2.18.1.tgz", @@ -11242,9 +9771,9 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "node_modules/nan": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.19.0.tgz", - "integrity": "sha512-nO1xXxfh/RWNxfd/XPfbIfFk5vgLsAxUR9y5O0cHMJu/AW9U95JLXqthYHjEp+8gQ5p96K9jUp8nbVOxCdRbtw==" + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.20.0.tgz", + "integrity": "sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw==" }, "node_modules/nanoid": { "version": "3.3.7", @@ -11289,32 +9818,35 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" }, "node_modules/nise": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/nise/-/nise-6.0.0.tgz", - "integrity": "sha512-K8ePqo9BFvN31HXwEtTNGzgrPpmvgciDsFz8aztFjt4LqKO/JeFD8tBOeuDiCMXrIl/m1YvfH8auSpxfaD09wg==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/nise/-/nise-6.1.1.tgz", + "integrity": "sha512-aMSAzLVY7LyeM60gvBS423nBmIPP+Wy7St7hsb+8/fc1HmeoHJfLO8CKse4u3BtOZvQLJghYPI2i/1WZrEj5/g==", "dev": true, "dependencies": { - "@sinonjs/commons": "^3.0.0", - "@sinonjs/fake-timers": "^11.2.2", - "@sinonjs/text-encoding": "^0.7.2", + "@sinonjs/commons": "^3.0.1", + "@sinonjs/fake-timers": "^13.0.1", + "@sinonjs/text-encoding": "^0.7.3", "just-extend": "^6.2.0", - "path-to-regexp": "^6.2.1" + "path-to-regexp": "^8.1.0" } }, "node_modules/nise/node_modules/@sinonjs/fake-timers": { - "version": "11.2.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-11.2.2.tgz", - "integrity": "sha512-G2piCSxQ7oWOxwGSAyFHfPIsyeJGXYtc6mFbnFA+kRXkiEnTl8c/8jul2S329iFBnDI9HGoeWWAZvuvOkZccgw==", + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.2.tgz", + "integrity": "sha512-4Bb+oqXZTSTZ1q27Izly9lv8B9dlV61CROxPiVtywwzv5SnytJqhvYe6FclHYuXml4cd1VHPo1zd5PmTeJozvA==", "dev": true, "dependencies": { - "@sinonjs/commons": "^3.0.0" + "@sinonjs/commons": "^3.0.1" } }, "node_modules/nise/node_modules/path-to-regexp": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.2.tgz", - "integrity": "sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==", - "dev": true + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", + "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", + "dev": true, + "engines": { + "node": ">=16" + } }, "node_modules/node-fetch": { "version": "2.7.0", @@ -11346,271 +9878,43 @@ "node": "4.x || >=6.0.0" } }, - "node_modules/node-gyp": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-9.4.1.tgz", - "integrity": "sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ==", + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true + }, + "node_modules/node-preload": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", + "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", "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": "^10.0.3", - "nopt": "^6.0.0", - "npmlog": "^6.0.0", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.2", - "which": "^2.0.2" - }, - "bin": { - "node-gyp": "bin/node-gyp.js" + "process-on-spawn": "^1.0.0" }, "engines": { - "node": "^12.13 || ^14.13 || >=16" + "node": ">=8" } }, - "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, + "node_modules/node-rdkafka": { + "version": "2.18.0", + "resolved": "https://registry.npmjs.org/node-rdkafka/-/node-rdkafka-2.18.0.tgz", + "integrity": "sha512-jYkmO0sPvjesmzhv1WFOO4z7IMiAFpThR6/lcnFDWgSPkYL95CtcuVNo/R5PpjujmqSgS22GMkL1qvU4DTAvEQ==", + "hasInstallScript": true, "dependencies": { - "@gar/promisify": "^1.1.3", - "semver": "^7.3.5" + "bindings": "^1.3.1", + "nan": "^2.17.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=6.0.0" } }, - "node_modules/node-gyp/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, + "node_modules/node-readfiles": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/node-readfiles/-/node-readfiles-0.2.0.tgz", + "integrity": "sha512-SU00ZarexNlE4Rjdm83vglt5Y9yiQ+XI1XpflWlb7q7UTN1JUItm69xMeiQCTxtTfnzt+83T8Cx+vI2ED++VDA==", "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "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": { - "@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/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "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/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/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": "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/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==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "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": { - "minipass": "^3.1.1" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "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": { - "unique-slug": "^3.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "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": { - "imurmurhash": "^0.1.4" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true - }, - "node_modules/node-preload": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", - "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", - "dev": true, - "dependencies": { - "process-on-spawn": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/node-rdkafka": { - "version": "2.18.0", - "resolved": "https://registry.npmjs.org/node-rdkafka/-/node-rdkafka-2.18.0.tgz", - "integrity": "sha512-jYkmO0sPvjesmzhv1WFOO4z7IMiAFpThR6/lcnFDWgSPkYL95CtcuVNo/R5PpjujmqSgS22GMkL1qvU4DTAvEQ==", - "hasInstallScript": true, - "dependencies": { - "bindings": "^1.3.1", - "nan": "^2.17.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/node-readfiles": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/node-readfiles/-/node-readfiles-0.2.0.tgz", - "integrity": "sha512-SU00ZarexNlE4Rjdm83vglt5Y9yiQ+XI1XpflWlb7q7UTN1JUItm69xMeiQCTxtTfnzt+83T8Cx+vI2ED++VDA==", - "dependencies": { - "es6-promise": "^3.2.1" + "es6-promise": "^3.2.1" } }, "node_modules/node-readfiles/node_modules/es6-promise": { @@ -11619,15 +9923,15 @@ "integrity": "sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==" }, "node_modules/node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", "dev": true }, "node_modules/nodemon": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.3.tgz", - "integrity": "sha512-m4Vqs+APdKzDFpuaL9F9EVOF85+h070FnkHVEoU4+rmT6Vw0bmNl7s61VEkY/cJkL7RCv1p4urnUDUMrS5rk2w==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.7.tgz", + "integrity": "sha512-hLj7fuMow6f0lbB0cD14Lz2xNjwsyruH251Pk4t/yIitCFJbmY1myuLlHm/q06aST4jg6EgAh74PIBBrRqpVAQ==", "dev": true, "dependencies": { "chokidar": "^3.5.2", @@ -11673,48 +9977,6 @@ "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", - "integrity": "sha512-h9iPVIfrVZ9wVYQnxFgtw1ugSvGEMOlyPWWtm8BMJhnwyEL/FLbYbTY3V3PpjI/BUK67n9PEWDu6eHzu1fB15Q==", - "dev": true, - "dependencies": { - "hosted-git-info": "^6.0.0", - "is-core-module": "^2.8.1", - "semver": "^7.3.5", - "validate-npm-package-license": "^3.0.4" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/normalize-package-data/node_modules/hosted-git-info": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-6.1.1.tgz", - "integrity": "sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w==", - "dev": true, - "dependencies": { - "lru-cache": "^7.5.1" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -11723,349 +9985,18 @@ "node": ">=0.10.0" } }, - "node_modules/normalize-url": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.1.tgz", - "integrity": "sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==", - "dev": true, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm-audit-resolver": { - "version": "3.0.0-RC.0", - "resolved": "https://registry.npmjs.org/npm-audit-resolver/-/npm-audit-resolver-3.0.0-RC.0.tgz", - "integrity": "sha512-UuVC7HIxGMhkGfj6IcilBO0cbAz/Y1OhRHA49g3ccpyHDy6Bpd4nkPnLe+xuyZmTgUjiac509IIN3YPVdJZ0Hw==", - "dev": true, - "dependencies": { - "@npmcli/ci-detect": "^3.0.2", - "audit-resolve-core": "^3.0.0-3", - "chalk": "^4.1.2", - "concat-stream": "^2.0.0", - "djv": "^2.1.4", - "jsonlines": "^0.1.1", - "read": "^2.0.0", - "spawn-shell": "^2.1.0", - "yargs-parser": "^21.1.1", - "yargs-unparser": "^2.0.0" - }, - "bin": { - "check-audit": "check.js", - "resolve-audit": "resolve.js" - } - }, - "node_modules/npm-bundled": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-3.0.1.tgz", - "integrity": "sha512-+AvaheE/ww1JEwRHOrn4WHNzOxGtVp+adrg2AeZS/7KuxGUYFuBta98wYpfHBbJp6Tg6j1NKSEVHNcfZzJHQwQ==", - "dev": true, - "dependencies": { - "npm-normalize-package-bin": "^3.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/npm-check-updates": { - "version": "16.14.20", - "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-16.14.20.tgz", - "integrity": "sha512-sYbIhun4DrjO7NFOTdvs11nCar0etEhZTsEjL47eM0TuiGMhmYughRCxG2SpGRmGAQ7AkwN7bw2lWzoE7q6yOQ==", - "dev": true, - "dependencies": { - "@types/semver-utils": "^1.1.1", - "chalk": "^5.3.0", - "cli-table3": "^0.6.3", - "commander": "^10.0.1", - "fast-memoize": "^2.5.2", - "find-up": "5.0.0", - "fp-and-or": "^0.1.4", - "get-stdin": "^8.0.0", - "globby": "^11.0.4", - "hosted-git-info": "^5.1.0", - "ini": "^4.1.1", - "js-yaml": "^4.1.0", - "json-parse-helpfulerror": "^1.0.3", - "jsonlines": "^0.1.1", - "lodash": "^4.17.21", - "make-fetch-happen": "^11.1.1", - "minimatch": "^9.0.3", - "p-map": "^4.0.0", - "pacote": "15.2.0", - "parse-github-url": "^1.0.2", - "progress": "^2.0.3", - "prompts-ncu": "^3.0.0", - "rc-config-loader": "^4.1.3", - "remote-git-tags": "^3.0.0", - "rimraf": "^5.0.5", - "semver": "^7.5.4", - "semver-utils": "^1.1.4", - "source-map-support": "^0.5.21", - "spawn-please": "^2.0.2", - "strip-ansi": "^7.1.0", - "strip-json-comments": "^5.0.1", - "untildify": "^4.0.0", - "update-notifier": "^6.0.2" - }, - "bin": { - "ncu": "build/src/bin/cli.js", - "npm-check-updates": "build/src/bin/cli.js" - }, - "engines": { - "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/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/npm-check-updates/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "version": "17.1.11", + "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-17.1.11.tgz", + "integrity": "sha512-TR2RuGIH7P3Qrb0jfdC/nT7JWqXPKjDlxuNQt3kx4oNVf1Pn5SBRB7KLypgYZhruivJthgTtfkkyK4mz342VjA==", "dev": true, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/npm-check-updates/node_modules/commander": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", - "dev": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/npm-check-updates/node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "dependencies": { - "p-locate": "^5.0.0" + "ncu": "build/cli.js", + "npm-check-updates": "build/cli.js" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm-check-updates/node_modules/minimatch": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", - "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm-check-updates/node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm-check-updates/node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "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", - "integrity": "sha512-0fk9zBqO67Nq5M/m45qHCJxylV/DhBlIOVExqgOMiCCrzrhU6tCibRXNqE3jwJLftzE9SNuZtYbpzcO+i9FiKw==", - "dev": true, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm-install-checks": { - "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" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm-normalize-package-bin": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz", - "integrity": "sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==", - "dev": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm-package-arg": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-10.1.0.tgz", - "integrity": "sha512-uFyyCEmgBfZTtrKk/5xDfHp6+MdrqGotX/VoOyEEl3mBwiEE5FlBaePanazJSVMPT7vKepcjYBY2ztg9A3yPIA==", - "dev": true, - "dependencies": { - "hosted-git-info": "^6.0.0", - "proc-log": "^3.0.0", - "semver": "^7.3.5", - "validate-npm-package-name": "^5.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm-package-arg/node_modules/hosted-git-info": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-6.1.1.tgz", - "integrity": "sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w==", - "dev": true, - "dependencies": { - "lru-cache": "^7.5.1" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm-packlist": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-7.0.4.tgz", - "integrity": "sha512-d6RGEuRrNS5/N84iglPivjaJPxhDbZmlbTwTDX2IbcRHG5bZCdtysYMhwiPvcF4GisXHGn7xsxv+GQ7T/02M5Q==", - "dev": true, - "dependencies": { - "ignore-walk": "^6.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm-pick-manifest": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-8.0.2.tgz", - "integrity": "sha512-1dKY+86/AIiq1tkKVD3l0WI+Gd3vkknVGAggsFeBkTvbhMQ1OND/LKkYv4JtXPKUJ8bOTCyLiqEg2P6QNdK+Gg==", - "dev": true, - "dependencies": { - "npm-install-checks": "^6.0.0", - "npm-normalize-package-bin": "^3.0.0", - "npm-package-arg": "^10.0.0", - "semver": "^7.3.5" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm-registry-fetch": { - "version": "14.0.5", - "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-14.0.5.tgz", - "integrity": "sha512-kIDMIo4aBm6xg7jOttupWZamsZRkAqMqwqqbVXnUqstY5+tapvv6bkH/qMR76jdgV+YljEUCyWx3hRYMrJiAgA==", - "dev": true, - "dependencies": { - "make-fetch-happen": "^11.0.0", - "minipass": "^5.0.0", - "minipass-fetch": "^3.0.0", - "minipass-json-stream": "^1.0.1", - "minizlib": "^2.1.2", - "npm-package-arg": "^10.0.0", - "proc-log": "^3.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0", + "npm": ">=8.12.1" } }, "node_modules/npm-run-all": { @@ -12229,22 +10160,6 @@ "node": ">=8" } }, - "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==", - "deprecated": "This package is no longer supported.", - "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", @@ -12265,9 +10180,9 @@ } }, "node_modules/nyc": { - "version": "17.0.0", - "resolved": "https://registry.npmjs.org/nyc/-/nyc-17.0.0.tgz", - "integrity": "sha512-ISp44nqNCaPugLLGGfknzQwSwt10SSS5IMoPR7GLoMAyS18Iw5js8U7ga2VF9lYuMZ42gOHr3UddZw4WZltxKg==", + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-17.1.0.tgz", + "integrity": "sha512-U42vQ4czpKa0QdI1hu950XuNhYqgoM+ZF1HT+VuUHL9hPfDPVvNQyltmMqdE9bUHMVa+8yNbc3QKTj8zQhlVxQ==", "dev": true, "dependencies": { "@istanbuljs/load-nyc-config": "^1.0.0", @@ -12277,7 +10192,7 @@ "decamelize": "^1.2.0", "find-cache-dir": "^3.2.0", "find-up": "^4.1.0", - "foreground-child": "^2.0.0", + "foreground-child": "^3.3.0", "get-package-type": "^0.1.0", "glob": "^7.1.6", "istanbul-lib-coverage": "^3.0.0", @@ -12322,6 +10237,34 @@ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", "dev": true }, + "node_modules/nyc/node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/nyc/node_modules/foreground-child/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==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/nyc/node_modules/make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -12417,16 +10360,12 @@ } }, "node_modules/nyc/node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "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, - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, "engines": { - "node": ">=6" + "node": ">=12" } }, "node_modules/oas-kit-common": { @@ -12528,9 +10467,12 @@ } }, "node_modules/object-inspect": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", - "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", + "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -12606,23 +10548,6 @@ "node": ">= 0.4" } }, - "node_modules/object.hasown": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.4.tgz", - "integrity": "sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==", - "dev": true, - "dependencies": { - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/object.values": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", @@ -12910,13 +10835,13 @@ "node": ">= 0.4.0" } }, - "node_modules/p-cancelable": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", - "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==", + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", "dev": true, "engines": { - "node": ">=12.20" + "node": ">=0.10.0" } }, "node_modules/p-defer": { @@ -12972,29 +10897,14 @@ }, "node_modules/p-locate/node_modules/p-limit": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "dependencies": { - "aggregate-error": "^3.0.0" + "p-try": "^2.0.0" }, "engines": { - "node": ">=10" + "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -13023,56 +10933,6 @@ "node": ">=8" } }, - "node_modules/package-json": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-8.1.1.tgz", - "integrity": "sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==", - "dev": true, - "dependencies": { - "got": "^12.1.0", - "registry-auth-token": "^5.0.1", - "registry-url": "^6.0.0", - "semver": "^7.3.7" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pacote": { - "version": "15.2.0", - "resolved": "https://registry.npmjs.org/pacote/-/pacote-15.2.0.tgz", - "integrity": "sha512-rJVZeIwHTUta23sIZgEIM62WYwbmGbThdbnkt81ravBplQv+HjyroqnLRNH2+sLJHcGZmLRmhPwACqhfTcOmnA==", - "dev": true, - "dependencies": { - "@npmcli/git": "^4.0.0", - "@npmcli/installed-package-contents": "^2.0.1", - "@npmcli/promise-spawn": "^6.0.1", - "@npmcli/run-script": "^6.0.0", - "cacache": "^17.0.0", - "fs-minipass": "^3.0.0", - "minipass": "^5.0.0", - "npm-package-arg": "^10.0.0", - "npm-packlist": "^7.0.0", - "npm-pick-manifest": "^8.0.0", - "npm-registry-fetch": "^14.0.0", - "proc-log": "^3.0.0", - "promise-retry": "^2.0.1", - "read-package-json": "^6.0.0", - "read-package-json-fast": "^3.0.0", - "sigstore": "^1.3.0", - "ssri": "^10.0.0", - "tar": "^6.1.11" - }, - "bin": { - "pacote": "lib/bin.js" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -13085,18 +10945,6 @@ "node": ">=6" } }, - "node_modules/parse-github-url": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/parse-github-url/-/parse-github-url-1.0.2.tgz", - "integrity": "sha512-kgBf6avCbO3Cn6+RnzRGLkUsv4ZVqv/VfAYkRsyBcgkshNvVBkRn1FEZcW0Jb+npXQWm2vHPnnOqFteZxRRGNw==", - "dev": true, - "bin": { - "parse-github-url": "cli.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -13188,44 +11036,10 @@ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, - "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", - "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", - "dev": true, - "engines": { - "node": "14 || >=16.14" - } - }, "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "engines": { - "node": ">=8" - } + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", + "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==" }, "node_modules/pause-stream": { "version": "0.0.11", @@ -13454,9 +11268,9 @@ } }, "node_modules/postcss": { - "version": "8.4.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", - "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "version": "8.4.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.39.tgz", + "integrity": "sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==", "funding": [ { "type": "opencollective", @@ -13473,7 +11287,7 @@ ], "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.0.0", + "picocolors": "^1.0.1", "source-map-js": "^1.2.0" }, "engines": { @@ -13587,15 +11401,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/proc-log": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-3.0.0.tgz", - "integrity": "sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==", - "dev": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -13613,43 +11418,16 @@ "node": ">=8" } }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/prom-client": { - "version": "14.2.0", - "resolved": "https://registry.npmjs.org/prom-client/-/prom-client-14.2.0.tgz", - "integrity": "sha512-sF308EhTenb/pDRPakm+WgiN+VdM/T1RaHj1x+MvAuT8UiQP8JmOEbxVqtkbfR4LrvOg5n7ic01kRBDGXjYikA==", + "version": "15.1.3", + "resolved": "https://registry.npmjs.org/prom-client/-/prom-client-15.1.3.tgz", + "integrity": "sha512-6ZiOBfCywsD4k1BN9IX0uZhF+tJkV8q8llP64G5Hajs4JOeVLPCwpPVcpXy3BwYiUGgyJzsJJQeOIv7+hDSq8g==", "dependencies": { + "@opentelemetry/api": "^1.4.0", "tdigest": "^0.1.1" }, "engines": { - "node": ">=10" - } - }, - "node_modules/promise-inflight": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", - "dev": true - }, - "node_modules/promise-retry": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", - "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", - "dev": true, - "dependencies": { - "err-code": "^2.0.2", - "retry": "^0.12.0" - }, - "engines": { - "node": ">=10" + "node": "^16 || ^18 || >=20" } }, "node_modules/prompts": { @@ -13665,28 +11443,6 @@ "node": ">= 6" } }, - "node_modules/prompts-ncu": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/prompts-ncu/-/prompts-ncu-3.0.0.tgz", - "integrity": "sha512-qyz9UxZ5MlPKWVhWrCmSZ1ahm2GVYdjLb8og2sg0IPth1KRuhcggHGuijz0e41dkx35p1t1q3GRISGH7QGALFA==", - "dev": true, - "dependencies": { - "kleur": "^4.0.1", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/prompts-ncu/node_modules/kleur": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", - "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -13704,12 +11460,6 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "dev": true }, - "node_modules/proto-list": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", - "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", - "dev": true - }, "node_modules/protobufjs": { "version": "7.3.0", "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.3.0.tgz", @@ -13809,21 +11559,6 @@ "node": ">=6" } }, - "node_modules/pupa": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/pupa/-/pupa-3.1.0.tgz", - "integrity": "sha512-FLpr4flz5xZTSJxSeaheeMKN/EDzMdK7b8PTOC6a5PYFKTucWbdqjgqaEyH0shFiSJrVB1+Qqi4Tk19ccU6Aug==", - "dev": true, - "dependencies": { - "escape-goat": "^4.0.0" - }, - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/pure-rand": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", @@ -13844,6 +11579,7 @@ "version": "1.5.1", "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", + "deprecated": "You or someone you depend on is using Q, the JavaScript Promise library that gave JavaScript developers strong feelings about promises. They can almost certainly migrate to the native JavaScript promise now. Thank you literally everyone for joining me in this bet against the odds. Be excellent to each other.\n\n(For a CapTP with native promises, see @endo/eventual-send and @endo/captp)", "dev": true, "engines": { "node": ">=0.6.0", @@ -13851,9 +11587,9 @@ } }, "node_modules/qs": { - "version": "6.12.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.1.tgz", - "integrity": "sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", "dependencies": { "side-channel": "^1.0.6" }, @@ -13886,269 +11622,97 @@ }, "node_modules/quick-lru": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", - "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/randexp": { - "version": "0.4.9", - "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.9.tgz", - "integrity": "sha512-maAX1cnBkzIZ89O4tSQUOF098xjGMC8N+9vuY/WfHwg87THw6odD2Br35donlj5e6KnB1SB0QBHhTQhhDHuTPQ==", - "dev": true, - "dependencies": { - "drange": "^1.0.0", - "ret": "^0.2.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/random-poly-fill": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/random-poly-fill/-/random-poly-fill-1.0.1.tgz", - "integrity": "sha512-bMOL0hLfrNs52+EHtIPIXxn2PxYwXb0qjnKruTjXiM/sKfYqj506aB2plFwWW1HN+ri724bAVVGparh4AtlJKw==" - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/raw-body/node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "node_modules/rc-config-loader": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/rc-config-loader/-/rc-config-loader-4.1.3.tgz", - "integrity": "sha512-kD7FqML7l800i6pS6pvLyIE2ncbk9Du8Q0gp/4hMPhJU6ZxApkoLcGD8ZeqgiAlfwZ6BlETq6qqe+12DUL207w==", - "dev": true, - "dependencies": { - "debug": "^4.3.4", - "js-yaml": "^4.1.0", - "json5": "^2.2.2", - "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", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" - }, - "node_modules/rc/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true - }, - "node_modules/read": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/read/-/read-2.1.0.tgz", - "integrity": "sha512-bvxi1QLJHcaywCAEsAk4DG3nVoqiY2Csps3qzWalhj5hFqRn1d/OixkFXtLO1PrgHUcAP0FNaSY/5GYNfENFFQ==", - "dev": true, - "dependencies": { - "mute-stream": "~1.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/read-package-json": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-6.0.4.tgz", - "integrity": "sha512-AEtWXYfopBj2z5N5PbkAOeNHRPUg5q+Nen7QLxV8M2zJq1ym6/lCz3fYNTCXe19puu2d06jfHhrP7v/S2PtMMw==", - "deprecated": "This package is no longer supported. Please use @npmcli/package-json instead.", - "dev": true, - "dependencies": { - "glob": "^10.2.2", - "json-parse-even-better-errors": "^3.0.0", - "normalize-package-data": "^5.0.0", - "npm-normalize-package-bin": "^3.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/read-package-json-fast": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-3.0.2.tgz", - "integrity": "sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw==", - "dev": true, - "dependencies": { - "json-parse-even-better-errors": "^3.0.0", - "npm-normalize-package-bin": "^3.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/read-package-json-fast/node_modules/json-parse-even-better-errors": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.2.tgz", - "integrity": "sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==", - "dev": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/read-package-json/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/read-package-json/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==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" - }, + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "dev": true, "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=8" } }, - "node_modules/read-package-json/node_modules/glob": { - "version": "10.4.1", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.1.tgz", - "integrity": "sha512-2jelhlq3E4ho74ZyVLN03oKdAZVUa6UDZzFLVH1H7dnoax+y9qyaq8zBkfDIggjniU19z0wU18y16jMB2eyVIw==", + "node_modules/randexp": { + "version": "0.4.9", + "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.9.tgz", + "integrity": "sha512-maAX1cnBkzIZ89O4tSQUOF098xjGMC8N+9vuY/WfHwg87THw6odD2Br35donlj5e6KnB1SB0QBHhTQhhDHuTPQ==", "dev": true, "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" + "drange": "^1.0.0", + "ret": "^0.2.0" }, "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=4" } }, - "node_modules/read-package-json/node_modules/json-parse-even-better-errors": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.2.tgz", - "integrity": "sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==", - "dev": true, + "node_modules/random-poly-fill": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/random-poly-fill/-/random-poly-fill-1.0.1.tgz", + "integrity": "sha512-bMOL0hLfrNs52+EHtIPIXxn2PxYwXb0qjnKruTjXiM/sKfYqj506aB2plFwWW1HN+ri724bAVVGparh4AtlJKw==" + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">= 0.6" } }, - "node_modules/read-package-json/node_modules/minimatch": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", - "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", - "dev": true, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", "dependencies": { - "brace-expansion": "^2.0.1" + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">= 0.8" } }, - "node_modules/read-package-json/node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, + "node_modules/raw-body/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=0.10.0" } }, - "node_modules/read-package-json/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==", - "dev": true, - "engines": { - "node": ">=14" + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "engines": { + "node": ">=0.10.0" } }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, "node_modules/read-pkg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", @@ -14307,6 +11871,15 @@ "node": ">=8.10.0" } }, + "node_modules/readline-sync": { + "version": "1.4.10", + "resolved": "https://registry.npmjs.org/readline-sync/-/readline-sync-1.4.10.tgz", + "integrity": "sha512-gNva8/6UAe8QYepIQH/jQ2qn91Qj0B9sYjMBBs3QOB8F2CXcKgLxQaJRP76sWVRQt+QU+8fAkCbCvjjMFu7Ycw==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/readline-transform": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/readline-transform/-/readline-transform-1.0.0.tgz", @@ -14340,6 +11913,25 @@ "node": ">=8" } }, + "node_modules/redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==", + "engines": { + "node": ">=4" + } + }, + "node_modules/redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", + "dependencies": { + "redis-errors": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/reflect.getprototypeof": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz", @@ -14398,33 +11990,6 @@ "url": "https://github.com/sponsors/mysticatea" } }, - "node_modules/registry-auth-token": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.0.2.tgz", - "integrity": "sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ==", - "dev": true, - "dependencies": { - "@pnpm/npm-conf": "^2.1.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/registry-url": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-6.0.1.tgz", - "integrity": "sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==", - "dev": true, - "dependencies": { - "rc": "1.2.8" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/release-zalgo": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", @@ -14437,15 +12002,6 @@ "node": ">=4" } }, - "node_modules/remote-git-tags": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/remote-git-tags/-/remote-git-tags-3.0.0.tgz", - "integrity": "sha512-C9hAO4eoEsX+OXA4rla66pXZQ+TLQ8T9dttgQj18yuKlPMTVkIkdYXvlMC55IuUsIkV6DpmQYi10JKFLaU+l7w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/repeat-string": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", @@ -14642,16 +12198,12 @@ } }, "node_modules/replace/node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "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, - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, "engines": { - "node": ">=6" + "node": ">=12" } }, "node_modules/require-directory": { @@ -14686,209 +12238,78 @@ } }, "node_modules/resolve": { - "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", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-alpn": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", - "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", - "dev": true - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve.exports": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", - "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/responselike": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", - "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==", - "dev": true, - "dependencies": { - "lowercase-keys": "^3.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ret": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.2.2.tgz", - "integrity": "sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/right-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", - "integrity": "sha512-yqINtL/G7vs2v+dFIZmFUDbnVyFUJFKd6gK22Kgo6R4jfJGFtisKyncWDDULgjfqf4ASQuIQyjJ7XZ+3aWpsAg==", - "dependencies": { - "align-text": "^0.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.7.tgz", - "integrity": "sha512-nV6YcJo5wbLW77m+8KjH8aB/7/rxQy9SZ0HY5shnwULfS+9nmTtVXAJET5NdZmCzA4fPI/Hm1wo/Po/4mopOdg==", - "dev": true, - "dependencies": { - "glob": "^10.3.7" - }, - "bin": { - "rimraf": "dist/esm/bin.mjs" - }, - "engines": { - "node": ">=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rimraf/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/rimraf/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==", - "dev": true, + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" }, - "engines": { - "node": ">=14" + "bin": { + "resolve": "bin/resolve" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/rimraf/node_modules/glob": { - "version": "10.4.1", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.1.tgz", - "integrity": "sha512-2jelhlq3E4ho74ZyVLN03oKdAZVUa6UDZzFLVH1H7dnoax+y9qyaq8zBkfDIggjniU19z0wU18y16jMB2eyVIw==", + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", "dev": true, "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" + "resolve-from": "^5.0.0" }, "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "engines": { + "node": ">=8" } }, - "node_modules/rimraf/node_modules/minimatch": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", - "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "node_modules/resolve.exports": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", + "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=10" } }, - "node_modules/rimraf/node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "node_modules/ret": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.2.2.tgz", + "integrity": "sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==", "dev": true, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=4" } }, - "node_modules/rimraf/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==", + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true, "engines": { - "node": ">=14" + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/right-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "integrity": "sha512-yqINtL/G7vs2v+dFIZmFUDbnVyFUJFKd6gK22Kgo6R4jfJGFtisKyncWDDULgjfqf4ASQuIQyjJ7XZ+3aWpsAg==", + "dependencies": { + "align-text": "^0.1.1" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "engines": { + "node": ">=0.10.0" } }, "node_modules/run-parallel": { @@ -14998,9 +12419,9 @@ } }, "node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "bin": { "semver": "bin/semver.js" }, @@ -15008,31 +12429,10 @@ "node": ">=10" } }, - "node_modules/semver-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-4.0.0.tgz", - "integrity": "sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==", - "dev": true, - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/semver-utils": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/semver-utils/-/semver-utils-1.1.4.tgz", - "integrity": "sha512-EjnoLE5OGmDAVV/8YDoN5KiajNadjzIp9BAHOhYeQHt7j0UWxjmgsx4YD48wp4Ue1Qogq38F1GNUJNqF1kKKxA==", - "dev": true - }, "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", "dependencies": { "debug": "2.6.9", "depd": "2.0.0", @@ -15065,6 +12465,14 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/send/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -15096,14 +12504,14 @@ } }, "node_modules/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", "dependencies": { - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.18.0" + "send": "0.19.0" }, "engines": { "node": ">= 0.8.0" @@ -15339,25 +12747,6 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, - "node_modules/sigstore": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/sigstore/-/sigstore-1.9.0.tgz", - "integrity": "sha512-0Zjz0oe37d08VeOtBIuB6cRriqXse2e8w+7yIy2XSXjshRKxbc2KkhXjL229jXSxEm7UbcjS76wcJDGQddVI9A==", - "dev": true, - "dependencies": { - "@sigstore/bundle": "^1.1.0", - "@sigstore/protobuf-specs": "^0.2.0", - "@sigstore/sign": "^1.0.0", - "@sigstore/tuf": "^1.0.3", - "make-fetch-happen": "^11.0.1" - }, - "bin": { - "sigstore": "bin/sigstore.js" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/simple-swizzle": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", @@ -15384,17 +12773,17 @@ } }, "node_modules/sinon": { - "version": "18.0.0", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-18.0.0.tgz", - "integrity": "sha512-+dXDXzD1sBO6HlmZDd7mXZCR/y5ECiEiGCBSGuFD/kZ0bDTofPYc6JaeGmPSF+1j1MejGUWkORbYOLDyvqCWpA==", + "version": "19.0.2", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-19.0.2.tgz", + "integrity": "sha512-euuToqM+PjO4UgXeLETsfQiuoyPXlqFezr6YZDFwHR3t4qaX0fZUe1MfPMznTL5f8BWrVS89KduLdMUsxFCO6g==", "dev": true, "dependencies": { "@sinonjs/commons": "^3.0.1", - "@sinonjs/fake-timers": "^11.2.2", - "@sinonjs/samsam": "^8.0.0", - "diff": "^5.2.0", - "nise": "^6.0.0", - "supports-color": "^7" + "@sinonjs/fake-timers": "^13.0.2", + "@sinonjs/samsam": "^8.0.1", + "diff": "^7.0.0", + "nise": "^6.1.1", + "supports-color": "^7.2.0" }, "funding": { "type": "opencollective", @@ -15402,12 +12791,12 @@ } }, "node_modules/sinon/node_modules/@sinonjs/fake-timers": { - "version": "11.2.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-11.2.2.tgz", - "integrity": "sha512-G2piCSxQ7oWOxwGSAyFHfPIsyeJGXYtc6mFbnFA+kRXkiEnTl8c/8jul2S329iFBnDI9HGoeWWAZvuvOkZccgw==", + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.2.tgz", + "integrity": "sha512-4Bb+oqXZTSTZ1q27Izly9lv8B9dlV61CROxPiVtywwzv5SnytJqhvYe6FclHYuXml4cd1VHPo1zd5PmTeJozvA==", "dev": true, "dependencies": { - "@sinonjs/commons": "^3.0.0" + "@sinonjs/commons": "^3.0.1" } }, "node_modules/sisteransi": { @@ -15425,44 +12814,6 @@ "node": ">=8" } }, - "node_modules/smart-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "dev": true, - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", - "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", - "dev": true, - "dependencies": { - "ip-address": "^9.0.5", - "smart-buffer": "^4.2.0" - }, - "engines": { - "node": ">= 10.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks-proxy-agent": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", - "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", - "dev": true, - "dependencies": { - "agent-base": "^6.0.2", - "debug": "^4.3.3", - "socks": "^2.6.2" - }, - "engines": { - "node": ">= 10" - } - }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -15490,53 +12841,6 @@ "source-map": "^0.6.0" } }, - "node_modules/spawn-please": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/spawn-please/-/spawn-please-2.0.2.tgz", - "integrity": "sha512-KM8coezO6ISQ89c1BzyWNtcn2V2kAVtwIXd3cN/V5a0xPYc1F/vydrRc01wsKFEQ/p+V1a4sw4z2yMITIXrgGw==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/spawn-shell": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/spawn-shell/-/spawn-shell-2.1.0.tgz", - "integrity": "sha512-mjlYAQbZPHd4YsoHEe+i0Xbp9sJefMKN09JPp80TqrjC5NSuo+y1RG3NBireJlzl1dDV2NIkIfgS6coXtyqN/A==", - "dev": true, - "dependencies": { - "default-shell": "^1.0.1", - "merge-options": "~1.0.1", - "npm-run-path": "^2.0.2" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/spawn-shell/node_modules/npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", - "dev": true, - "dependencies": { - "path-key": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/spawn-shell/node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/spawn-sync": { "version": "1.0.15", "resolved": "https://registry.npmjs.org/spawn-sync/-/spawn-sync-1.0.15.tgz", @@ -15548,21 +12852,6 @@ "os-shim": "^0.1.2" } }, - "node_modules/spawn-sync/node_modules/concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "dev": true, - "engines": [ - "node >= 0.8" - ], - "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, "node_modules/spawn-wrap": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", @@ -15683,9 +12972,9 @@ } }, "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", "dev": true }, "node_modules/sqlstring": { @@ -15696,27 +12985,6 @@ "node": ">= 0.6" } }, - "node_modules/ssri": { - "version": "10.0.6", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.6.tgz", - "integrity": "sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==", - "dev": true, - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/ssri/node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "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", @@ -15747,9 +13015,9 @@ } }, "node_modules/standard": { - "version": "17.1.0", - "resolved": "https://registry.npmjs.org/standard/-/standard-17.1.0.tgz", - "integrity": "sha512-jaDqlNSzLtWYW4lvQmU0EnxWMUGQiwHasZl5ZEIwx3S/ijZDjZOzs1y1QqKwKs5vqnFpGtizo4NOYX2s0Voq/g==", + "version": "17.1.2", + "resolved": "https://registry.npmjs.org/standard/-/standard-17.1.2.tgz", + "integrity": "sha512-WLm12WoXveKkvnPnPnaFUUHuOB2cUdAsJ4AiGHL2G0UNMrcRAWY2WriQaV8IQ3oRmYr0AWUbLNr94ekYFAHOrA==", "dev": true, "funding": [ { @@ -15772,8 +13040,8 @@ "eslint-plugin-import": "^2.27.5", "eslint-plugin-n": "^15.7.0", "eslint-plugin-promise": "^6.1.1", - "eslint-plugin-react": "^7.32.2", - "standard-engine": "^15.0.0", + "eslint-plugin-react": "^7.36.1", + "standard-engine": "^15.1.0", "version-guard": "^1.1.1" }, "bin": { @@ -15783,6 +13051,11 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/standard-as-callback": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", + "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==" + }, "node_modules/standard-engine": { "version": "15.1.0", "resolved": "https://registry.npmjs.org/standard-engine/-/standard-engine-15.1.0.tgz", @@ -16037,12 +13310,12 @@ } }, "node_modules/standard-version/node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "version": "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": ">=10" + "node": ">=12" } }, "node_modules/statuses": { @@ -16106,21 +13379,6 @@ "node": ">=8" } }, - "node_modules/string-width-cjs": { - "name": "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/string.prototype.matchall": { "version": "4.0.11", "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz", @@ -16164,6 +13422,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/string.prototype.repeat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, "node_modules/string.prototype.trim": { "version": "1.2.9", "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", @@ -16249,19 +13517,6 @@ "node": ">=8" } }, - "node_modules/strip-ansi-cjs": { - "name": "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/strip-bom": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", @@ -16414,53 +13669,12 @@ "dependencies": { "call-me-maybe": "^1.0.1", "chance": "^1.0.3", - "moment": "^2.13.0", - "randexp": "^0.4.2", - "swagger-parser": "^3.4.1" - }, - "engines": { - "node": ">=6.x" - } - }, - "node_modules/tar": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", - "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", - "dev": true, - "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/tar/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/tar/node_modules/fs-minipass/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" + "moment": "^2.13.0", + "randexp": "^0.4.2", + "swagger-parser": "^3.4.1" }, "engines": { - "node": ">=8" + "node": ">=6.x" } }, "node_modules/tarn": { @@ -16554,6 +13768,18 @@ "resolved": "https://registry.npmjs.org/tiny-opts-parser/-/tiny-opts-parser-0.0.3.tgz", "integrity": "sha512-JSb93x8gZWbfJ9GJSEcRj9Y/GsfcECgeUWX98vsBsIxo2khv3u+rtf3tySVn0QRBL5hzHrm41cnem7/vgMb53A==" }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -16665,20 +13891,6 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, - "node_modules/tuf-js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-1.1.7.tgz", - "integrity": "sha512-i3P9Kgw3ytjELUfpuKVDNBJvk4u5bXL6gskv572mcevPbSKCV3zt3djhmlEQ65yERjIbOSncy7U4cQJaB1CBCg==", - "dev": true, - "dependencies": { - "@tufjs/models": "1.0.4", - "debug": "^4.3.4", - "make-fetch-happen": "^11.1.1" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -16914,45 +14126,6 @@ "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", - "integrity": "sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==", - "dev": true, - "dependencies": { - "unique-slug": "^4.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/unique-slug": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-4.0.0.tgz", - "integrity": "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==", - "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/unique-string": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz", - "integrity": "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==", - "dev": true, - "dependencies": { - "crypto-random-string": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -16961,19 +14134,10 @@ "node": ">= 0.8" } }, - "node_modules/untildify": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", - "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/update-browserslist-db": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz", - "integrity": "sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", + "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", "dev": true, "funding": [ { @@ -17000,58 +14164,6 @@ "browserslist": ">= 4.21.0" } }, - "node_modules/update-notifier": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-6.0.2.tgz", - "integrity": "sha512-EDxhTEVPZZRLWYcJ4ZXjGFN0oP7qYvbXWzEgRm/Yql4dHX5wDbvh89YHP6PK1lzZJYrMtXUuZZz8XGK+U6U1og==", - "dev": true, - "dependencies": { - "boxen": "^7.0.0", - "chalk": "^5.0.1", - "configstore": "^6.0.0", - "has-yarn": "^3.0.0", - "import-lazy": "^4.0.0", - "is-ci": "^3.0.1", - "is-installed-globally": "^0.4.0", - "is-npm": "^6.0.0", - "is-yarn-global": "^0.4.0", - "latest-version": "^7.0.0", - "pupa": "^3.1.0", - "semver": "^7.3.7", - "semver-diff": "^4.0.0", - "xdg-basedir": "^5.1.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/yeoman/update-notifier?sponsor=1" - } - }, - "node_modules/update-notifier/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", - "dev": true, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/update-notifier/node_modules/xdg-basedir": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-5.1.0.tgz", - "integrity": "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -17093,9 +14205,9 @@ "integrity": "sha512-CTpAkEVXMNJl2ojgtpLXHgz23dh8z81u6/HEPiQFOvBc/c2pde6TVHmH4uwY0d/GLF3tb7+VDAj4+2eJaQSdZQ==" }, "node_modules/v8-to-istanbul": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz", - "integrity": "sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==", + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", "dev": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.12", @@ -17115,15 +14227,6 @@ "spdx-expression-parse": "^3.0.0" } }, - "node_modules/validate-npm-package-name": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz", - "integrity": "sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==", - "dev": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/validator": { "version": "10.11.0", "resolved": "https://registry.npmjs.org/validator/-/validator-10.11.0.tgz", @@ -17561,86 +14664,11 @@ } }, "node_modules/widdershins/node_modules/yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - }, - "node_modules/wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "dev": true, - "dependencies": { - "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==", - "dev": true, - "dependencies": { - "string-width": "^5.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/widest-line/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/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==", - "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/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" - }, + "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" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, "node_modules/window-size": { @@ -17652,15 +14680,15 @@ } }, "node_modules/winston": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.13.0.tgz", - "integrity": "sha512-rwidmA1w3SE4j0E5MuIufFhyJPBDG7Nu71RkZor1p2+qHvJSZ9GYDA81AyleQcZbh/+V6HjeBdfnTZJm9rSeQQ==", + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.14.2.tgz", + "integrity": "sha512-CO8cdpBB2yqzEf8v895L+GNKYJiEq8eKlHU38af3snQBQ+sdAIUepjMSguOIJC7ICbzm0ZI+Af2If4vIJrtmOg==", "dependencies": { "@colors/colors": "^1.6.0", "@dabh/diagnostics": "^2.0.2", "async": "^3.2.3", "is-stream": "^2.0.0", - "logform": "^2.4.0", + "logform": "^2.6.0", "one-time": "^1.0.0", "readable-stream": "^3.4.0", "safe-stable-stringify": "^2.3.1", @@ -17673,12 +14701,12 @@ } }, "node_modules/winston-transport": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.7.0.tgz", - "integrity": "sha512-ajBj65K5I7denzer2IYW6+2bNIVqLGDHqDw3Ow8Ohh+vdW+rv4MZ6eiDvHoKhfJFZ2auyN8byXieDDJ96ViONg==", + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.7.1.tgz", + "integrity": "sha512-wQCXXVgfv/wUPOfb2x0ruxzwkcZfxcktz6JIMUaPLmcNhO4bZTwA/WtDWK74xV3F2dKu8YadrFv0qhwYjVEwhA==", "dependencies": { - "logform": "^2.3.2", - "readable-stream": "^3.6.0", + "logform": "^2.6.1", + "readable-stream": "^3.6.2", "triple-beam": "^1.3.0" }, "engines": { @@ -17698,14 +14726,6 @@ "node": ">= 6" } }, - "node_modules/winston/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/winston/node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -17734,103 +14754,6 @@ "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", "dev": true }, - "node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "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/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==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=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==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "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==", - "dev": true - }, - "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==", - "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/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==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -17894,9 +14817,9 @@ "dev": true }, "node_modules/yaml": { - "version": "2.4.5", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.5.tgz", - "integrity": "sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.0.tgz", + "integrity": "sha512-2wWLbGbYDiSqqIKoPjar3MPgB94ErzCtrNE1FdqGuaO0pi2JGjmE8aW8TDZwzU7vuxcGRdL/4gPQwQ7hD5AMSw==", "bin": { "yaml": "bin.mjs" }, @@ -17922,69 +14845,19 @@ } }, "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/yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", - "dev": true, - "dependencies": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-unparser/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yargs-unparser/node_modules/decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true, + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "engines": { "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yargs-unparser/node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true, - "engines": { - "node": ">=8" } }, "node_modules/yargs/node_modules/yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" + "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": { diff --git a/package.json b/package.json index f447779f..fca999a7 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "account-lookup-service", "description": "Account Lookup Service is used to validate Party and Participant lookups.", - "version": "15.3.4", + "version": "15.4.0-snapshot.35", "license": "Apache-2.0", "author": "ModusBox", "contributors": [ @@ -23,7 +23,8 @@ "node": ">=18.x" }, "config": { - "knex": "--knexfile ./config/knexfile.js" + "knex": "--knexfile ./config/knexfile.js", + "env_file": "--env-file ./test/integration/.env" }, "standard": { "env": [ @@ -33,27 +34,30 @@ "pre-commit": [ "lint", "dep:check", + "audit:check", "test" ], "scripts": { "start": "node src/index.js server", - "start:all": "run-p start:api start:admin", + "start:all": "run-p start:api start:admin start:handlers", "start:api": "node src/index.js server --api", "start:admin": "node src/index.js server --admin", + "start:handlers": "node src/handlers/index.js handlers --timeout", "standard": "standard", "standard:fix": "standard --fix", "lint": "npm run standard", "lint:fix": "npm run standard:fix", "dev": "nodemon src/index.js server", "test": "npm run test:unit", - "test:unit": "jest --runInBand --testMatch '**/test/unit/**/*.test.js'", - "test:coverage": "jest --runInBand --coverage --coverageThreshold='{}' --testMatch '**/test/unit/**/*.test.js'", - "test:coverage-check": "jest --runInBand --coverage --testMatch '**/test/unit/**/*.test.js'", - "test:junit": "jest --runInBand --reporters=default --reporters=jest-junit --testMatch '**/test/unit/**/*.test.js'", + "test:unit": "jest --runInBand", + "test:coverage": "npm test -- --coverage --coverageThreshold='{}'", + "test:coverage-check": "npm test -- --coverage", + "test:junit": "npm test -- --reporters=default --reporters=jest-junit", "test:xunit": "npm run test:junit", "test:functional": "sh ./scripts/test-functional.sh", - "test:int": "npm run migrate && jest --runInBand --reporters=default --reporters=jest-junit --testMatch '**/test/integration/**/*.test.js'", + "test:int": "npm run migrate && npm run onboarding && jest --config=./jest-int.config.js --runInBand", "test:integration": "TEST_MODE=rm ./test/integration-runner.sh", + "onboarding": "node test/integration/prepareTestParticipants.js", "cover": "npx nyc --all report --reporter=lcov npm run test", "migrate": "run-s migrate:latest seed:run", "migrate:latest": "knex $npm_package_config_knex migrate:latest", @@ -63,9 +67,11 @@ "seed:run": "knex seed:run $npm_package_config_knex", "seed:create": "knex seed:make $npm_package_config_knex", "regenerate": "yo swaggerize:test --framework hapi --apiPath './config/api_swagger.json'", + "dc:build": "docker compose $npm_package_config_env_file build", + "dc:up": ". ./test/integration/env.sh && docker compose $npm_package_config_env_file up -d && docker ps", + "dc:down": ". ./test/integration/env.sh && docker compose $npm_package_config_env_file down -v", "docker:build": "docker build --build-arg NODE_VERSION=\"$(cat .nvmrc)-alpine\" -t mojaloop/account-lookup-service:local -f ./Dockerfile .", "docker:run": "docker run -p 3000:3000 --rm central-directory:local", - "package-lock": "docker run --rm -it central-directory:local cat package-lock.json > package-lock.json", "generate-docs": "jsdoc -c jsdoc.json", "audit:fix": "npm audit fix", "audit:check": "npx audit-ci --config ./audit-ci.jsonc", @@ -78,38 +84,51 @@ "dependencies": { "@hapi/basic": "7.0.2", "@hapi/boom": "10.0.1", - "@hapi/catbox-memory": "6.0.1", + "@hapi/catbox-memory": "6.0.2", "@hapi/good": "9.0.1", - "@hapi/hapi": "21.3.9", + "@hapi/hapi": "21.3.12", "@hapi/inert": "7.1.0", "@hapi/joi": "17.1.1", "@hapi/vision": "7.0.3", - "@mojaloop/central-services-error-handling": "13.0.1", + "@mojaloop/central-services-error-handling": "13.0.2", "@mojaloop/central-services-health": "15.0.0", - "@mojaloop/central-services-logger": "11.3.1", - "@mojaloop/central-services-metrics": "12.0.8", - "@mojaloop/central-services-shared": "18.3.8", - "@mojaloop/central-services-stream": "11.3.0", - "@mojaloop/database-lib": "11.0.5", + "@mojaloop/central-services-logger": "11.5.1", + "@mojaloop/central-services-metrics": "12.4.1", + "@mojaloop/central-services-shared": "18.7.1", + "@mojaloop/central-services-stream": "11.3.1", + "@mojaloop/database-lib": "11.0.6", "@mojaloop/event-sdk": "14.1.1", - "@mojaloop/sdk-standard-components": "18.1.0", + "@mojaloop/inter-scheme-proxy-cache-lib": "2.3.1", + "@mojaloop/sdk-standard-components": "18.4.0", "@now-ims/hapi-now-auth": "2.1.0", - "ajv": "8.16.0", + "ajv": "8.17.1", "ajv-keywords": "5.1.0", "blipp": "4.0.2", "commander": "12.1.0", + "cron": "3.3.0", "fast-safe-stringify": "^2.1.1", "hapi-auth-bearer-token": "8.0.0", - "joi": "17.13.1", + "joi": "17.13.3", "knex": "3.1.0", "mustache": "4.2.0", "mysql": "2.18.1", "npm-run-all": "4.1.5", "parse-strings-in-object": "2.0.0", - "rc": "1.2.8", - "uuid4": "2.0.3" + "rc": "1.2.8" }, "overrides": { + "@mojaloop/central-services-error-handling": { + "@mojaloop/sdk-standard-components": "$@mojaloop/sdk-standard-components" + }, + "@mojaloop/central-services-health": { + "@mojaloop/central-services-logger": ">=11.4.0" + }, + "@mojaloop/central-services-shared": { + "@mojaloop/central-services-logger": ">=11.4.0" + }, + "@mojaloop/central-services-stream": { + "@mojaloop/central-services-logger": ">=11.4.0" + }, "shins": { "ajv": "6.12.3", "ejs": "3.1.10", @@ -122,27 +141,32 @@ "yargs-parser": "13.1.2", "markdown-it": "12.3.2" }, + "yargs": { + "yargs-parser": "^21.1.1" + }, "jsonwebtoken": "9.0.0", "jsonpointer": "5.0.0" }, "devDependencies": { - "@types/jest": "29.5.12", - "audit-ci": "^7.0.1", - "axios": "1.7.2", + "@types/jest": "29.5.14", + "audit-ci": "^7.1.0", + "axios": "1.7.9", + "axios-retry": "^4.5.0", "docdash": "2.0.2", + "dotenv": "^16.4.7", "get-port": "5.1.1", + "ioredis-mock": "^8.9.0", "jest": "29.7.0", "jest-junit": "16.0.0", - "jsdoc": "4.0.3", - "nodemon": "3.1.3", - "npm-audit-resolver": "3.0.0-RC.0", - "npm-check-updates": "16.14.20", - "nyc": "17.0.0", + "jsdoc": "4.0.4", + "nodemon": "3.1.7", + "npm-check-updates": "17.1.11", + "nyc": "17.1.0", "pre-commit": "1.2.2", "proxyquire": "2.1.3", "replace": "^1.2.2", - "sinon": "18.0.0", - "standard": "17.1.0", + "sinon": "19.0.2", + "standard": "17.1.2", "standard-version": "^9.5.0", "swagmock": "1.0.0" }, diff --git a/scripts/_wait4_all.js b/scripts/_wait4_all.js index 6895e45b..d67f2f32 100755 --- a/scripts/_wait4_all.js +++ b/scripts/_wait4_all.js @@ -17,8 +17,8 @@ const expectedContainers = [ // 'kowl' ] -let retries = 40 -const waitTimeMs = 60000 +let retries = 30 +const waitTimeMs = 5000 async function main () { const waitingMap = {} diff --git a/scripts/test-functional.sh b/scripts/test-functional.sh index 44dc7089..a026121c 100755 --- a/scripts/test-functional.sh +++ b/scripts/test-functional.sh @@ -4,7 +4,7 @@ echo "--=== Running Functional Test Runner ===--" echo export ACCOUNT_LOOKUP_SERVICE_VERSION=${ACCOUNT_LOOKUP_SERVICE_VERSION:-"local"} -export ML_CORE_TEST_HARNESS_VERSION=${ML_CORE_TEST_HARNESS_VERSION:-"v1.2.3"} +export ML_CORE_TEST_HARNESS_VERSION=${ML_CORE_TEST_HARNESS_VERSION:-"v1.2.4-fx-snapshot.12"} export ML_CORE_TEST_HARNESS_GIT=${ML_CORE_TEST_HARNESS_GIT:-"https://github.com/mojaloop/ml-core-test-harness.git"} export ML_CORE_TEST_HARNESS_TEST_PROV_CONT_NAME=${ML_CORE_TEST_HARNESS_TEST_PROV_CONT_NAME:-"ttk-func-ttk-provisioning-1"} export ML_CORE_TEST_HARNESS_TEST_FUNC_CONT_NAME=${ML_CORE_TEST_HARNESS_TEST_FUNC_CONT_NAME:-"ttk-func-ttk-tests-1"} diff --git a/seeds/endpointType.js b/seeds/endpointType.js index 0227f0dd..454ba0f5 100644 --- a/seeds/endpointType.js +++ b/seeds/endpointType.js @@ -23,10 +23,32 @@ ******/ 'use strict' +const { FspEndpointTypes } = require('@mojaloop/central-services-shared').Enum.EndPoints + const endpointTypes = [ { type: 'URL', description: 'REST URLs' + }, + { + type: FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_PUT, + description: 'Participant callback URL to which put participant information can be sent' + }, + { + type: FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_PUT_ERROR, + description: 'Participant callback URL to which put participant error information can be sent' + }, + { + type: FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_GET, + description: 'Parties callback URL to which get parties information can be requested' + }, + { + type: FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_PUT, + description: 'Parties callback URL to which put parties information can be sent' + }, + { + type: FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_PUT_ERROR, + description: 'Parties callback URL to which put participant error information can be sent' } ] diff --git a/src/handlers/endpointcache.js b/src/api/endpointcache.js similarity index 96% rename from src/handlers/endpointcache.js rename to src/api/endpointcache.js index beeb68de..4a9d72dd 100644 --- a/src/handlers/endpointcache.js +++ b/src/api/endpointcache.js @@ -29,7 +29,7 @@ const Enum = require('@mojaloop/central-services-shared').Enum const EventSdk = require('@mojaloop/event-sdk') const Metrics = require('@mojaloop/central-services-metrics') const Config = require('../lib/config.js') -const LibUtil = require('../lib/util') +const LibUtil = require('../lib/util.js') /** * Operations on /endpointcache @@ -56,7 +56,7 @@ module.exports = { }, EventSdk.AuditEventAction.start) try { await ParticipantEndpointCache.stopCache() - await ParticipantEndpointCache.initializeCache(Config.CENTRAL_SHARED_ENDPOINT_CACHE_CONFIG) + await ParticipantEndpointCache.initializeCache(Config.CENTRAL_SHARED_ENDPOINT_CACHE_CONFIG, LibUtil.hubNameConfig) histTimerEnd({ success: true }) } catch (err) { histTimerEnd({ success: false }) diff --git a/src/handlers/health.js b/src/api/health.js similarity index 75% rename from src/handlers/health.js rename to src/api/health.js index 2fb82522..e1c80ed9 100644 --- a/src/handlers/health.js +++ b/src/api/health.js @@ -27,10 +27,23 @@ 'use strict' const HealthCheck = require('@mojaloop/central-services-shared').HealthCheck.HealthCheck -const { getSubServiceHealthDatastore } = require('../lib/healthCheck/subServiceHealth') +const { getSubServiceHealthDatastore, getProxyCacheHealth } = require('../lib/healthCheck/subServiceHealth') const packageJson = require('../../package.json') +const Config = require('../lib/config') -const healthCheck = new HealthCheck(packageJson, [getSubServiceHealthDatastore]) +const getSubServices = (appConfig, isAdmin) => { + if (isAdmin) { + return [getSubServiceHealthDatastore] + } + + const subServices = [getSubServiceHealthDatastore] + if (appConfig.PROXY_CACHE_CONFIG.enabled) { + subServices.push(getProxyCacheHealth) + } + return subServices +} + +const healthCheck = new HealthCheck(packageJson, []) /** * Operations on /health @@ -44,7 +57,9 @@ module.exports = { * responses: 200, 400, 401, 403, 404, 405, 406, 501, 503 */ get: async (context, request, h) => { - const health = await healthCheck.getHealth() + const { isAdmin, proxyCache } = request.server.app + healthCheck.serviceChecks = getSubServices(Config, isAdmin) + const health = await healthCheck.getHealth(proxyCache) const statusCode = health.status !== 'OK' ? 503 : 200 return h.response(health).code(statusCode) } diff --git a/src/api/index.js b/src/api/index.js new file mode 100644 index 00000000..459f55e8 --- /dev/null +++ b/src/api/index.js @@ -0,0 +1,85 @@ +/***** + 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 + + * ModusBox + - Rajiv Mothilal + + -------------- + ******/ + +'use strict' + +const OpenapiBackend = require('@mojaloop/central-services-shared').Util.OpenapiBackend + +const participants = require('./participants') +const participantsId = require('./participants/{ID}') +const participantsErrorById = require('./participants/{ID}/error') +const participantsTypeId = require('./participants/{Type}/{ID}') +const participantsErrorTypeId = require('./participants/{Type}/{ID}/error') +const participantsTypeIdSubId = require('./participants/{Type}/{ID}/{SubId}') +const participantsErrorTypeIdSubId = require('./participants/{Type}/{ID}/{SubId}/error') + +const partiesTypeId = require('./parties/{Type}/{ID}') +const partiesTypeIdSubId = require('./parties/{Type}/{ID}/{SubId}') +const partiesErrorTypeId = require('./parties/{Type}/{ID}/error') +const partiesErrorTypeIdSubId = require('./parties/{Type}/{ID}/{SubId}/error') + +const oracles = require('./oracles') +const oraclesId = require('./oracles/{ID}') + +const endpointCache = require('./endpointcache') +const health = require('./health') + +module.exports.ApiHandlers = { + HealthGet: health.get, + ParticipantsErrorByIDPut: participantsErrorById.put, + ParticipantsByIDPut: participantsId.put, + ParticipantsErrorByTypeAndIDPut: participantsErrorTypeId.put, + ParticipantsErrorBySubIdTypeAndIDPut: participantsErrorTypeIdSubId.put, + ParticipantsSubIdByTypeAndIDGet: participantsTypeIdSubId.get, + ParticipantsSubIdByTypeAndIDPut: participantsTypeIdSubId.put, + ParticipantsSubIdByTypeAndIDPost: participantsTypeIdSubId.post, + ParticipantsSubIdByTypeAndIDDelete: participantsTypeIdSubId.delete, + ParticipantsByTypeAndIDGet: participantsTypeId.get, + ParticipantsByTypeAndIDPut: participantsTypeId.put, + ParticipantsByIDAndTypePost: participantsTypeId.post, + ParticipantsByTypeAndIDDelete: participantsTypeId.delete, + ParticipantsPost: participants.post, + PartiesByTypeAndIDGet: partiesTypeId.get, + PartiesByTypeAndIDPut: partiesTypeId.put, + PartiesErrorByTypeAndIDPut: partiesErrorTypeId.put, + PartiesBySubIdTypeAndIDGet: partiesTypeIdSubId.get, + PartiesSubIdByTypeAndIDPut: partiesTypeIdSubId.put, + PartiesErrorBySubIdTypeAndIDPut: partiesErrorTypeIdSubId.put, + EndpointCacheDelete: endpointCache.delete, + validationFail: OpenapiBackend.validationFail, + notFound: OpenapiBackend.notFound, + methodNotAllowed: OpenapiBackend.methodNotAllowed +} + +module.exports.AdminHandlers = { + HealthGet: health.get, + OraclesGet: oracles.get, + OraclesPost: oracles.post, + OraclesByIdPut: oraclesId.put, + OraclesByIdDelete: oraclesId.delete, + validationFail: OpenapiBackend.validationFail, + notFound: OpenapiBackend.notFound, + methodNotAllowed: OpenapiBackend.methodNotAllowed +} diff --git a/src/handlers/oracles.js b/src/api/oracles.js similarity index 100% rename from src/handlers/oracles.js rename to src/api/oracles.js diff --git a/src/handlers/oracles/{ID}.js b/src/api/oracles/{ID}.js similarity index 100% rename from src/handlers/oracles/{ID}.js rename to src/api/oracles/{ID}.js diff --git a/src/handlers/participants.js b/src/api/participants.js similarity index 100% rename from src/handlers/participants.js rename to src/api/participants.js diff --git a/src/handlers/participants/{ID}.js b/src/api/participants/{ID}.js similarity index 100% rename from src/handlers/participants/{ID}.js rename to src/api/participants/{ID}.js diff --git a/src/handlers/participants/{ID}/error.js b/src/api/participants/{ID}/error.js similarity index 100% rename from src/handlers/participants/{ID}/error.js rename to src/api/participants/{ID}/error.js diff --git a/src/handlers/participants/{Type}/{ID}.js b/src/api/participants/{Type}/{ID}.js similarity index 100% rename from src/handlers/participants/{Type}/{ID}.js rename to src/api/participants/{Type}/{ID}.js diff --git a/src/handlers/participants/{Type}/{ID}/error.js b/src/api/participants/{Type}/{ID}/error.js similarity index 100% rename from src/handlers/participants/{Type}/{ID}/error.js rename to src/api/participants/{Type}/{ID}/error.js diff --git a/src/handlers/participants/{Type}/{ID}/{SubId}.js b/src/api/participants/{Type}/{ID}/{SubId}.js similarity index 100% rename from src/handlers/participants/{Type}/{ID}/{SubId}.js rename to src/api/participants/{Type}/{ID}/{SubId}.js diff --git a/src/handlers/participants/{Type}/{ID}/{SubId}/error.js b/src/api/participants/{Type}/{ID}/{SubId}/error.js similarity index 100% rename from src/handlers/participants/{Type}/{ID}/{SubId}/error.js rename to src/api/participants/{Type}/{ID}/{SubId}/error.js diff --git a/src/handlers/parties/{Type}/{ID}.js b/src/api/parties/{Type}/{ID}.js similarity index 83% rename from src/handlers/parties/{Type}/{ID}.js rename to src/api/parties/{Type}/{ID}.js index 81576f2b..e54914ab 100644 --- a/src/handlers/parties/{Type}/{ID}.js +++ b/src/api/parties/{Type}/{ID}.js @@ -24,11 +24,11 @@ ******/ 'use strict' -const Enum = require('@mojaloop/central-services-shared').Enum +const { Action, Type } = require('@mojaloop/central-services-shared').Enum.Events.Event const EventSdk = require('@mojaloop/event-sdk') -const LibUtil = require('../../../lib/util') -const parties = require('../../../domain/parties') const Metrics = require('@mojaloop/central-services-metrics') +const parties = require('../../../domain/parties') +const LibUtil = require('../../../lib/util') /** * Operations on /parties/{Type}/{ID} @@ -47,16 +47,18 @@ module.exports = { 'Ingress - Get party by Type and Id', ['success'] ).startTimer() - const span = request.span - const spanTags = LibUtil.getSpanTags(request, Enum.Events.Event.Type.PARTY, Enum.Events.Event.Action.LOOKUP) + const { headers, payload, params, method, query, span } = request + const { cache, proxyCache } = request.server.app + + const spanTags = LibUtil.getSpanTags({ headers }, Type.PARTY, Action.LOOKUP) span.setTags(spanTags) await span.audit({ - headers: request.headers, - payload: request.payload + headers, + payload }, EventSdk.AuditEventAction.start) // Here we call an async function- but as we send an immediate sync response, _all_ errors // _must_ be handled by getPartiesByTypeAndID. - parties.getPartiesByTypeAndID(request.headers, request.params, request.method, request.query, span, request.server.app.cache).catch(err => { + parties.getPartiesByTypeAndID(headers, params, method, query, span, cache, proxyCache).catch(err => { request.server.log(['error'], `ERROR - getPartiesByTypeAndID: ${LibUtil.getStackOrInspect(err)}`) }) histTimerEnd({ success: true }) @@ -76,16 +78,18 @@ module.exports = { 'Ingress - Put party by Type and Id', ['success'] ).startTimer() - const span = request.span - const spanTags = LibUtil.getSpanTags(request, Enum.Events.Event.Type.PARTY, Enum.Events.Event.Action.PUT) + const { headers, payload, params, method, dataUri, span } = request + const { cache, proxyCache } = request.server.app + + const spanTags = LibUtil.getSpanTags({ headers }, Type.PARTY, Action.PUT) span.setTags(spanTags) await span.audit({ - headers: request.headers, - payload: request.payload + headers, + payload }, EventSdk.AuditEventAction.start) // Here we call an async function- but as we send an immediate sync response, _all_ errors // _must_ be handled by putPartiesByTypeAndID. - parties.putPartiesByTypeAndID(request.headers, request.params, request.method, request.payload, request.dataUri).catch(err => { + parties.putPartiesByTypeAndID(headers, params, method, payload, dataUri, cache, proxyCache).catch(err => { request.server.log(['error'], `ERROR - putPartiesByTypeAndID: ${LibUtil.getStackOrInspect(err)}`) }) histTimerEnd({ success: true }) diff --git a/src/handlers/parties/{Type}/{ID}/error.js b/src/api/parties/{Type}/{ID}/error.js similarity index 91% rename from src/handlers/parties/{Type}/{ID}/error.js rename to src/api/parties/{Type}/{ID}/error.js index 32d46000..8365d2fe 100644 --- a/src/handlers/parties/{Type}/{ID}/error.js +++ b/src/api/parties/{Type}/{ID}/error.js @@ -47,14 +47,17 @@ module.exports = { 'Ingress - Put parties error by Type and Id', ['success'] ).startTimer() - const span = request.span + const { headers, payload, params, dataUri, span } = request + const { cache, proxyCache } = request.server.app + const spanTags = LibUtil.getSpanTags(request, Enum.Events.Event.Type.PARTY, Enum.Events.Event.Action.PUT) span.setTags(spanTags) await span.audit({ - headers: request.headers, - payload: request.payload + headers, + payload }, EventSdk.AuditEventAction.start) - parties.putPartiesErrorByTypeAndID(request.headers, request.params, request.payload, request.dataUri, span).catch(err => { + + parties.putPartiesErrorByTypeAndID(headers, params, payload, dataUri, span, cache, proxyCache).catch(err => { request.server.log(['error'], `ERROR - putPartiesErrorByTypeAndID: ${LibUtil.getStackOrInspect(err)}`) }) histTimerEnd({ success: true }) diff --git a/src/handlers/parties/{Type}/{ID}/{SubId}.js b/src/api/parties/{Type}/{ID}/{SubId}.js similarity index 92% rename from src/handlers/parties/{Type}/{ID}/{SubId}.js rename to src/api/parties/{Type}/{ID}/{SubId}.js index 6e4bd02b..fabd7280 100644 --- a/src/handlers/parties/{Type}/{ID}/{SubId}.js +++ b/src/api/parties/{Type}/{ID}/{SubId}.js @@ -47,7 +47,8 @@ module.exports = { 'Ingress - Get party by Type, ID and SubId', ['success'] ).startTimer() - parties.getPartiesByTypeAndID(request.headers, request.params, request.method, request.query, request.span).catch(err => { + const { cache, proxyCache } = request.server.app + parties.getPartiesByTypeAndID(request.headers, request.params, request.method, request.query, request.span, cache, proxyCache).catch(err => { request.server.log(['error'], `ERROR - getPartiesByTypeAndID: ${LibUtil.getStackOrInspect(err)}`) }) histTimerEnd({ success: true }) @@ -66,7 +67,8 @@ module.exports = { 'Ingress - Put parties by Type, ID and SubId', ['success'] ).startTimer() - parties.putPartiesByTypeAndID(request.headers, request.params, request.method, request.payload, request.dataUri).catch(err => { + const { cache, proxyCache } = request.server.app + parties.putPartiesByTypeAndID(request.headers, request.params, request.method, request.payload, request.dataUri, cache, proxyCache).catch(err => { request.server.log(['error'], `ERROR - putPartiesByTypeAndID: ${LibUtil.getStackOrInspect(err)}`) }) histTimerEnd({ success: true }) diff --git a/src/handlers/parties/{Type}/{ID}/{SubId}/error.js b/src/api/parties/{Type}/{ID}/{SubId}/error.js similarity index 95% rename from src/handlers/parties/{Type}/{ID}/{SubId}/error.js rename to src/api/parties/{Type}/{ID}/{SubId}/error.js index e56e2334..9ccbf65d 100644 --- a/src/handlers/parties/{Type}/{ID}/{SubId}/error.js +++ b/src/api/parties/{Type}/{ID}/{SubId}/error.js @@ -46,7 +46,8 @@ module.exports = { 'Ingress - Put parties error by Type, ID and SubId', ['success'] ).startTimer() - parties.putPartiesErrorByTypeAndID(request.headers, request.params, request.payload, request.dataUri, request.span).catch(err => { + const { cache, proxyCache } = request.server.app + parties.putPartiesErrorByTypeAndID(request.headers, request.params, request.payload, request.dataUri, request.span, cache, proxyCache).catch(err => { request.server.log(['error'], `ERROR - putPartiesErrorByTypeAndID: ${LibUtil.getStackOrInspect(err)}`) }) histTimerEnd({ success: true }) diff --git a/src/handlers/routes.js b/src/api/routes.js similarity index 100% rename from src/handlers/routes.js rename to src/api/routes.js diff --git a/src/constants.js b/src/constants.js new file mode 100644 index 00000000..2d9f9d8e --- /dev/null +++ b/src/constants.js @@ -0,0 +1,16 @@ +const ERROR_MESSAGES = Object.freeze({ + partySourceFspNotFound: 'Requester FSP not found', + partyDestinationFspNotFound: 'Destination FSP not found', + partyProxyNotFound: 'Proxy not found', + proxyConnectionError: 'Proxy connection error', + failedToCacheSendToProxiesList: 'Failed to cache sendToProxiesList' +}) + +const HANDLER_TYPES = Object.freeze({ + TIMEOUT: 'timeout' +}) + +module.exports = { + ERROR_MESSAGES, + HANDLER_TYPES +} diff --git a/src/domain/oracle/oracle.js b/src/domain/oracle/oracle.js index aaa287c5..dc8441d6 100644 --- a/src/domain/oracle/oracle.js +++ b/src/domain/oracle/oracle.js @@ -26,14 +26,15 @@ 'use strict' const Logger = require('@mojaloop/central-services-logger') +const ErrorHandler = require('@mojaloop/central-services-error-handling') +const Metrics = require('@mojaloop/central-services-metrics') + +const Config = require('../../lib/config') const oracleEndpoint = require('../../models/oracle') +const cachedOracleEndpoint = require('../../models/oracle/oracleEndpointCached') const partyIdType = require('../../models/partyIdType') const endpointType = require('../../models/endpointType') const currency = require('../../models/currency') -const ErrorHandler = require('@mojaloop/central-services-error-handling') -const Config = require('../../lib/config') -const Metrics = require('@mojaloop/central-services-metrics') -const cachedOracleEndpoint = require('../../models/oracle/oracleEndpointCached') /** * @function createOracle diff --git a/src/domain/participants/participants.js b/src/domain/participants/participants.js index c924a395..10e6fd42 100644 --- a/src/domain/participants/participants.js +++ b/src/domain/participants/participants.js @@ -26,12 +26,13 @@ ******/ 'use strict' -const Logger = require('@mojaloop/central-services-logger') const Enums = require('@mojaloop/central-services-shared').Enum +const { decodePayload } = require('@mojaloop/central-services-shared').Util.StreamingProtocol +const Logger = require('@mojaloop/central-services-logger') const ErrorHandler = require('@mojaloop/central-services-error-handling') const EventSdk = require('@mojaloop/event-sdk') -const { decodePayload } = require('@mojaloop/central-services-shared').Util.StreamingProtocol const Metrics = require('@mojaloop/central-services-metrics') + const oracle = require('../../models/oracle/facade') const participant = require('../../models/participantEndpoint/facade') const Config = require('../../lib/config') @@ -78,7 +79,7 @@ const getParticipantsByTypeAndID = async (headers, params, method, query, span, if (!clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION] || clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION] === '') { clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION] = clonedHeaders[Enums.Http.Headers.FSPIOP.SOURCE] } - clonedHeaders[Enums.Http.Headers.FSPIOP.SOURCE] = Enums.Http.Headers.FSPIOP.SWITCH.value + clonedHeaders[Enums.Http.Headers.FSPIOP.SOURCE] = Config.HUB_NAME await participant.sendRequest(clonedHeaders, requesterName, callbackEndpointType, Enums.Http.RestMethods.PUT, payload, options, childSpan) } else { await participant.sendErrorToParticipant(requesterName, errorCallbackEndpointType, @@ -168,7 +169,7 @@ const putParticipantsByTypeAndID = async (headers, params, method, payload, cach const clonedHeaders = { ...headers } if (!clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION] || clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION] === '') { clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION] = payload.fspId - clonedHeaders[Enums.Http.Headers.FSPIOP.SOURCE] = Enums.Http.Headers.FSPIOP.SWITCH.value + clonedHeaders[Enums.Http.Headers.FSPIOP.SOURCE] = Config.HUB_NAME } await participant.sendRequest(clonedHeaders, clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION], callbackEndpointType, Enums.Http.RestMethods.PUT, responsePayload, options) } else { @@ -279,8 +280,13 @@ const postParticipants = async (headers, method, params, payload, span, cache) = Logger.isInfoEnabled && Logger.info('postParticipants::begin') const type = params.Type const partySubIdOrType = params.SubId - const callbackEndpointType = partySubIdOrType ? Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_SUB_ID_PUT : Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_PUT - const errorCallbackEndpointType = partySubIdOrType ? Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_SUB_ID_PUT_ERROR : Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_PUT_ERROR + const callbackEndpointType = partySubIdOrType + ? Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_SUB_ID_PUT + : Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_PUT + const errorCallbackEndpointType = partySubIdOrType + ? Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_SUB_ID_PUT_ERROR + : Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_PUT_ERROR + if (Object.values(Enums.Accounts.PartyAccountTypes).includes(type)) { const requesterParticipantModel = await participant.validateParticipant(headers[Enums.Http.Headers.FSPIOP.SOURCE], childSpan) if (requesterParticipantModel) { @@ -307,7 +313,7 @@ const postParticipants = async (headers, method, params, payload, span, cache) = const clonedHeaders = { ...headers } if (!clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION] || clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION] === '') { clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION] = payload.fspId - clonedHeaders[Enums.Http.Headers.FSPIOP.SOURCE] = Enums.Http.Headers.FSPIOP.SWITCH.value + clonedHeaders[Enums.Http.Headers.FSPIOP.SOURCE] = Config.HUB_NAME } await participant.sendRequest(clonedHeaders, clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION], callbackEndpointType, Enums.Http.RestMethods.PUT, responsePayload, options, childSpan) if (childSpan && !childSpan.isFinished) { @@ -331,16 +337,18 @@ const postParticipants = async (headers, method, params, payload, span, cache) = Logger.isInfoEnabled && Logger.info('postParticipants::end') histTimerEnd({ success: true }) } catch (err) { - Logger.isErrorEnabled && Logger.error(err) + Logger.isErrorEnabled && Logger.error(`error in postParticipants: ${err?.message}`) fspiopError = ErrorHandler.Factory.reformatFSPIOPError(err, ErrorHandler.Enums.FSPIOPErrorCodes.ADD_PARTY_INFO_ERROR) try { - const errorCallbackEndpointType = params.SubId ? Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_SUB_ID_PUT_ERROR : Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_PUT_ERROR + const errorCallbackEndpointType = params.SubId + ? Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_SUB_ID_PUT_ERROR + : Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_PUT_ERROR await participant.sendErrorToParticipant(headers[Enums.Http.Headers.FSPIOP.SOURCE], errorCallbackEndpointType, fspiopError.toApiErrorObject(Config.ERROR_HANDLING), headers, params, childSpan) } catch (exc) { // We can't do anything else here- we _must_ handle all errors _within_ this function because // we've already sent a sync response- we cannot throw. - Logger.isErrorEnabled && Logger.error(exc) + Logger.isErrorEnabled && Logger.error(`failed to handle exception in postParticipants due to error: ${exc?.stack}`) } histTimerEnd({ success: false }) } finally { @@ -439,7 +447,7 @@ const postParticipantsBatch = async (headers, method, requestPayload, span) => { const clonedHeaders = { ...headers } if (!clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION] || clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION] === '') { clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION] = payload.partyList[0].partyId.fspId - clonedHeaders[Enums.Http.Headers.FSPIOP.SOURCE] = Enums.Http.Headers.FSPIOP.SWITCH.value + clonedHeaders[Enums.Http.Headers.FSPIOP.SOURCE] = Config.HUB_NAME } await participant.sendRequest(clonedHeaders, clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION], Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_BATCH_PUT, Enums.Http.RestMethods.PUT, payload, { requestId }, childSpan) if (childSpan && !childSpan.isFinished) { @@ -511,7 +519,7 @@ const deleteParticipants = async (headers, params, method, query, cache) => { options = partySubIdOrType ? { ...options, partySubIdOrType } : options const clonedHeaders = { ...headers } clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION] = headers[Enums.Http.Headers.FSPIOP.SOURCE] - clonedHeaders[Enums.Http.Headers.FSPIOP.SOURCE] = Enums.Http.Headers.FSPIOP.SWITCH.value + clonedHeaders[Enums.Http.Headers.FSPIOP.SOURCE] = Config.HUB_NAME await participant.sendRequest(clonedHeaders, clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION], callbackEndpointType, Enums.Http.RestMethods.PUT, responsePayload, options) } else { await participant.sendErrorToParticipant(headers[Enums.Http.Headers.FSPIOP.SOURCE], errorCallbackEndpointType, diff --git a/src/domain/parties/getPartiesByTypeAndID.js b/src/domain/parties/getPartiesByTypeAndID.js new file mode 100644 index 00000000..ff61fe04 --- /dev/null +++ b/src/domain/parties/getPartiesByTypeAndID.js @@ -0,0 +1,239 @@ +/***** + 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 + + * Eugen Klymniuk + -------------- + **********/ + +const { Enum, Util } = require('@mojaloop/central-services-shared') +const ErrorHandler = require('@mojaloop/central-services-error-handling') +const Metrics = require('@mojaloop/central-services-metrics') + +const config = require('../../lib/config') +const oracle = require('../../models/oracle/facade') +const participant = require('../../models/participantEndpoint/facade') +const { createCallbackHeaders } = require('../../lib/headers') +const { ERROR_MESSAGES } = require('../../constants') +const { loggerFactory } = require('../../lib') +const Config = require('../../lib/config') +const utils = require('./utils') + +const { FspEndpointTypes, FspEndpointTemplates } = Enum.EndPoints +const { Headers, RestMethods } = Enum.Http + +const logger = loggerFactory('domain:get-parties') + +const proxyCacheTtlSec = 40 // todo: make configurable + +const validateRequester = async ({ source, proxy, proxyCache }) => { + const sourceParticipant = await participant.validateParticipant(source) + if (sourceParticipant) { + logger.debug('source is in scheme', { source }) + return source + } + + if (!proxy) { + const errMessage = ERROR_MESSAGES.partySourceFspNotFound + throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, errMessage) + } + + const proxyParticipant = await participant.validateParticipant(proxy) + if (!proxyParticipant) { + const errMessage = ERROR_MESSAGES.partyProxyNotFound + throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, errMessage) + } + + const isCached = await proxyCache.addDfspIdToProxyMapping(source, proxy) + // think, what if isCached !== true? + logger.info('source is added to proxyMapping cache:', { source, proxy, isCached }) + return proxy +} + +/** + * @function getPartiesByTypeAndID + * + * @description sends request to applicable oracle based on type and sends results to a callback url + * + * @param {object} headers - incoming http request headers + * @param {object} params - uri parameters of the http request + * @param {string} method - http request method + * @param {object} query - uri query parameters of the http request + * @param {object} span + * @param {object} cache + * @param {IProxyCache} [proxyCache] - IProxyCache instance + */ +const getPartiesByTypeAndID = async (headers, params, method, query, span, cache, proxyCache = undefined) => { + const histTimerEnd = Metrics.getHistogram( + 'getPartiesByTypeAndID', + 'Get party by Type and Id', + ['success'] + ).startTimer() + const proxyEnabled = !!(Config.PROXY_CACHE_CONFIG.enabled && proxyCache) + const type = params.Type + const partySubId = params.SubId + const source = headers[Headers.FSPIOP.SOURCE] + const proxy = proxyEnabled && headers[Headers.FSPIOP.PROXY] + const callbackEndpointType = utils.getPartyCbType(partySubId) + + const childSpan = span ? span.getChild('getPartiesByTypeAndID') : undefined + logger.info('parties::getPartiesByTypeAndID::begin', { source, proxy, params }) + + let requester + let fspiopError + + try { + requester = await validateRequester({ source, proxy, proxyCache }) + + const options = { + partyIdType: type, + partyIdentifier: params.ID, + ...(partySubId && { partySubIdOrType: partySubId }) + } + + let destination = headers[Headers.FSPIOP.DESTINATION] + // see https://github.com/mojaloop/design-authority/issues/79 + // the requester has specified a destination routing header. We should respect that and forward the request directly to the destination + // without consulting any oracles. + if (destination) { + const destParticipantModel = await participant.validateParticipant(destination) + if (!destParticipantModel) { + const proxyId = proxyEnabled && await proxyCache.lookupProxyByDfspId(destination) + + if (!proxyId) { + const errMessage = ERROR_MESSAGES.partyDestinationFspNotFound + throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, errMessage) + } + destination = proxyId + } + // all ok, go ahead and forward the request + await participant.sendRequest(headers, destination, callbackEndpointType, RestMethods.GET, undefined, options, childSpan) + + histTimerEnd({ success: true }) + logger.info('discovery getPartiesByTypeAndID request was sent to destination', { destination }) + return + } + + const response = await oracle.oracleRequest(headers, method, params, query, undefined, cache) + if (Array.isArray(response?.data?.partyList) && response.data.partyList.length > 0) { + // Oracle's API is a standard rest-style end-point Thus a GET /party on the oracle will return all participant-party records. We must filter the results based on the callbackEndpointType to make sure we remove records containing partySubIdOrType when we are in FSPIOP_CALLBACK_URL_PARTIES_GET mode: + let filteredResponsePartyList + switch (callbackEndpointType) { + case FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_GET: + filteredResponsePartyList = response.data.partyList.filter(party => party.partySubIdOrType == null) // Filter records that DON'T contain a partySubIdOrType + break + case FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_SUB_ID_GET: + filteredResponsePartyList = response.data.partyList.filter(party => party.partySubIdOrType === partySubId) // Filter records that match partySubIdOrType + break + default: + filteredResponsePartyList = response // Fallback to providing the standard list + } + + if (!Array.isArray(filteredResponsePartyList) || !filteredResponsePartyList.length) { + const errMessage = 'Requested FSP/Party not found' + throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, errMessage) + } + + let sentCount = 0 // if sentCount === 0 after sending, should we restart the whole process? + const sending = filteredResponsePartyList.map(async party => { + const { fspId } = party + const clonedHeaders = { ...headers } + if (!destination) { + clonedHeaders[Headers.FSPIOP.DESTINATION] = fspId + } + const schemeParticipant = await participant.validateParticipant(fspId) + if (schemeParticipant) { + sentCount++ + logger.debug('participant is in scheme', { fspId }) + return participant.sendRequest(clonedHeaders, party.fspId, callbackEndpointType, RestMethods.GET, undefined, options, childSpan) + } + + // If the participant is not in the scheme and proxy routing is enabled, + // we should check if there is a proxy for it and send the request to the proxy + if (proxyEnabled) { + const proxyName = await proxyCache.lookupProxyByDfspId(fspId) + if (!proxyName) { + logger.warn('no proxyMapping for participant! TODO: Delete reference in oracle...', { fspId }) + // todo: delete reference in oracle + } else { + sentCount++ + logger.debug('participant NOT is in scheme, use proxy name', { fspId, proxyName }) + return participant.sendRequest(clonedHeaders, proxyName, callbackEndpointType, RestMethods.GET, undefined, options, childSpan) + } + } + }) + await Promise.all(sending) + logger.info('participant.sendRequests to filtered oracle partyList are done', { sentCount }) + // todo: think what if sentCount === 0 here + } else { + logger.info('empty partyList form oracle, getting proxies list...', { proxyEnabled, params }) + let filteredProxyNames = [] + + if (proxyEnabled) { + const proxyNames = await Util.proxies.getAllProxiesNames(Config.SWITCH_ENDPOINT) + filteredProxyNames = proxyNames.filter(name => name !== proxy) + } + + if (!filteredProxyNames.length) { + const callbackHeaders = createCallbackHeaders({ + requestHeaders: headers, + partyIdType: type, + partyIdentifier: params.ID, + endpointTemplate: partySubId + ? FspEndpointTemplates.PARTIES_SUB_ID_PUT_ERROR + : FspEndpointTemplates.PARTIES_PUT_ERROR + }) + fspiopError = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.PARTY_NOT_FOUND) + const errorCallbackEndpointType = utils.errorPartyCbType(partySubId) + await participant.sendErrorToParticipant(requester, errorCallbackEndpointType, + fspiopError.toApiErrorObject(config.ERROR_HANDLING), callbackHeaders, params, childSpan) + } else { + const alsReq = utils.alsRequestDto(source, params) + logger.info('starting setSendToProxiesList flow: ', { filteredProxyNames, alsReq, proxyCacheTtlSec }) + const isCached = await proxyCache.setSendToProxiesList(alsReq, filteredProxyNames, proxyCacheTtlSec) + if (!isCached) { + throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, ERROR_MESSAGES.failedToCacheSendToProxiesList) + } + + const sending = filteredProxyNames.map( + proxyName => participant.sendRequest(headers, proxyName, callbackEndpointType, RestMethods.GET, undefined, options, childSpan) + .then(({ status, data } = {}) => ({ status, data })) + ) + const results = await Promise.allSettled(sending) + const isOk = results.some(result => result.status === 'fulfilled') + // If, at least, one request is sent to proxy, we treat the whole flow as successful. + // Failed requests should be handled by TTL expired/timeout handler + // todo: - think, if we should handle failed requests here (e.g., by calling receivedErrorResponse) + logger.info('setSendToProxiesList flow is done:', { isOk, results, filteredProxyNames, alsReq }) + if (!isOk) { + throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, ERROR_MESSAGES.proxyConnectionError) + } + } + } + histTimerEnd({ success: true }) + } catch (err) { + fspiopError = await utils.handleErrorOnSendingCallback(err, headers, params, requester) + histTimerEnd({ success: false }) + } finally { + await utils.finishSpanWithError(childSpan, fspiopError) + } +} + +module.exports = getPartiesByTypeAndID diff --git a/src/domain/parties/index.js b/src/domain/parties/index.js index 426d86c9..fc430fae 100644 --- a/src/domain/parties/index.js +++ b/src/domain/parties/index.js @@ -25,8 +25,6 @@ 'use strict' -'use strict' - const parties = require('./parties') exports.getPartiesByTypeAndID = parties.getPartiesByTypeAndID diff --git a/src/domain/parties/parties.js b/src/domain/parties/parties.js index b1f1c5bd..03af0063 100644 --- a/src/domain/parties/parties.js +++ b/src/domain/parties/parties.js @@ -29,145 +29,21 @@ 'use strict' -const Logger = require('@mojaloop/central-services-logger') -const Enums = require('@mojaloop/central-services-shared').Enum -const ErrorHandler = require('@mojaloop/central-services-error-handling') +const { Headers, RestMethods } = require('@mojaloop/central-services-shared').Enum.Http const { decodePayload } = require('@mojaloop/central-services-shared').Util.StreamingProtocol +const { MojaloopApiErrorCodes } = require('@mojaloop/sdk-standard-components').Errors +const ErrorHandler = require('@mojaloop/central-services-error-handling') const Metrics = require('@mojaloop/central-services-metrics') -const EventSdk = require('@mojaloop/event-sdk') -const participant = require('../../models/participantEndpoint/facade') const oracle = require('../../models/oracle/facade') -const createCallbackHeaders = require('../../lib/headers').createCallbackHeaders +const participant = require('../../models/participantEndpoint/facade') +const { ERROR_MESSAGES } = require('../../constants') +const { loggerFactory } = require('../../lib') const Config = require('../../lib/config') +const utils = require('./utils') +const getPartiesByTypeAndID = require('./getPartiesByTypeAndID') -/** - * @function getPartiesByTypeAndID - * - * @description sends request to applicable oracle based on type and sends results to a callback url - * - * @param {object} headers - incoming http request headers - * @param {object} params - uri parameters of the http request - * @param {string} method - http request method - * @param {object} query - uri query parameters of the http request - * @param {object} span - */ -const getPartiesByTypeAndID = async (headers, params, method, query, span = undefined, cache) => { - const histTimerEnd = Metrics.getHistogram( - 'getPartiesByTypeAndID', - 'Get party by Type and Id', - ['success'] - ).startTimer() - const childSpan = span ? span.getChild('getPartiesByTypeAndID') : undefined - let fspiopError - try { - Logger.isInfoEnabled && Logger.info('parties::getPartiesByTypeAndID::begin') - const type = params.Type - const partySubIdOrType = params.SubId || undefined - const callbackEndpointType = partySubIdOrType ? Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_SUB_ID_GET : Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_GET - const errorCallbackEndpointType = partySubIdOrType ? Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_SUB_ID_PUT_ERROR : Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_PUT_ERROR - const requesterParticipantModel = await participant.validateParticipant(headers[Enums.Http.Headers.FSPIOP.SOURCE], childSpan) - - if (requesterParticipantModel) { - let options = { - partyIdType: type, - partyIdentifier: params.ID - } - options = partySubIdOrType ? { ...options, partySubIdOrType } : options - - // see https://github.com/mojaloop/design-authority/issues/79 - if (headers[Enums.Http.Headers.FSPIOP.DESTINATION]) { - // the requester has specifid a destination routing header. We should respect that and forward the request directly to the destination - // without consulting any oracles. - - // first check the destination is a valid participant - const destParticipantModel = await participant.validateParticipant(headers[Enums.Http.Headers.FSPIOP.DESTINATION], childSpan) - if (!destParticipantModel) { - Logger.isErrorEnabled && Logger.error('Destination FSP not found') - throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, 'Destination FSP not found') - } - - // all ok, go ahead and forward the request - await participant.sendRequest(headers, headers[Enums.Http.Headers.FSPIOP.DESTINATION], callbackEndpointType, Enums.Http.RestMethods.GET, undefined, options, childSpan) - histTimerEnd({ success: true }) - if (childSpan && !childSpan.isFinished) { - await childSpan.finish() - } - return - } - const response = await oracle.oracleRequest(headers, method, params, query, undefined, cache) - if (response && response.data && Array.isArray(response.data.partyList) && response.data.partyList.length > 0) { - // Oracle's API is a standard rest-style end-point Thus a GET /party on the oracle will return all participant-party records. We must filter the results based on the callbackEndpointType to make sure we remove records containing partySubIdOrType when we are in FSPIOP_CALLBACK_URL_PARTIES_GET mode: - let filteredResponsePartyList - switch (callbackEndpointType) { - case Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_GET: - filteredResponsePartyList = response.data.partyList.filter(party => party.partySubIdOrType == null) // Filter records that DON'T contain a partySubIdOrType - break - case Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_SUB_ID_GET: - filteredResponsePartyList = response.data.partyList.filter(party => party.partySubIdOrType === partySubIdOrType) // Filter records that match partySubIdOrType - break - default: - filteredResponsePartyList = response // Fallback to providing the standard list - } - - if (filteredResponsePartyList == null || !(Array.isArray(filteredResponsePartyList) && filteredResponsePartyList.length > 0)) { - Logger.isErrorEnabled && Logger.error('Requested FSP/Party not found') - throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, 'Requested FSP/Party not found') - } - - for (const party of filteredResponsePartyList) { - const clonedHeaders = { ...headers } - if (!clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION]) { - clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION] = party.fspId - } - await participant.sendRequest(clonedHeaders, party.fspId, callbackEndpointType, Enums.Http.RestMethods.GET, undefined, options, childSpan) - } - if (childSpan && !childSpan.isFinished) { - await childSpan.finish() - } - } else { - const callbackHeaders = createCallbackHeaders({ - requestHeaders: headers, - partyIdType: params.Type, - partyIdentifier: params.ID, - endpointTemplate: partySubIdOrType ? Enums.EndPoints.FspEndpointTemplates.PARTIES_SUB_ID_PUT_ERROR : Enums.EndPoints.FspEndpointTemplates.PARTIES_PUT_ERROR - }) - fspiopError = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.PARTY_NOT_FOUND) - await participant.sendErrorToParticipant(headers[Enums.Http.Headers.FSPIOP.SOURCE], errorCallbackEndpointType, - fspiopError.toApiErrorObject(Config.ERROR_HANDLING), callbackHeaders, params, childSpan) - if (childSpan && !childSpan.isFinished && fspiopError) { - const state = new EventSdk.EventStateMetadata(EventSdk.EventStatusType.failed, fspiopError.apiErrorCode.code, fspiopError.apiErrorCode.message) - await childSpan.error(fspiopError, state) - await childSpan.finish(fspiopError.message, state) - } - } - } else { - Logger.isErrorEnabled && Logger.error('Requester FSP not found') - throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, 'Requester FSP not found') - } - histTimerEnd({ success: true }) - } catch (err) { - Logger.isErrorEnabled && Logger.error(err) - fspiopError = ErrorHandler.Factory.reformatFSPIOPError(err) - try { - const errorCallbackEndpointType = params.SubId ? Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_SUB_ID_PUT_ERROR : Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_PUT_ERROR - await participant.sendErrorToParticipant(headers[Enums.Http.Headers.FSPIOP.SOURCE], errorCallbackEndpointType, - fspiopError.toApiErrorObject(Config.ERROR_HANDLING), headers, params, childSpan) - } catch (exc) { - fspiopError = ErrorHandler.Factory.reformatFSPIOPError(exc) - // We can't do anything else here- we _must_ handle all errors _within_ this function because - // we've already sent a sync response- we cannot throw. - Logger.isErrorEnabled && Logger.error(exc) - } - histTimerEnd({ success: false }) - } finally { - if (childSpan && !childSpan.isFinished && fspiopError) { - const state = new EventSdk.EventStateMetadata(EventSdk.EventStatusType.failed, fspiopError.apiErrorCode.code, fspiopError.apiErrorCode.message) - await childSpan.error(fspiopError, state) - await childSpan.finish(fspiopError.message, state) - } - } -} +const logger = loggerFactory('domain:put-parties') /** * @function putPartiesByTypeAndID @@ -179,51 +55,76 @@ const getPartiesByTypeAndID = async (headers, params, method, query, span = unde * @param {string} method - http request method * @param {object} payload - payload of the request being sent out * @param {string} dataUri - encoded payload of the request being sent out + * @param {CacheClient} cache - in-memory cache with CatboxMemory engine + * @param {IProxyCache} [proxyCache] - IProxyCache instance */ -const putPartiesByTypeAndID = async (headers, params, method, payload, dataUri) => { +const putPartiesByTypeAndID = async (headers, params, method, payload, dataUri, cache, proxyCache = undefined) => { const histTimerEnd = Metrics.getHistogram( 'putPartiesByTypeAndID', 'Put parties by type and id', ['success'] ).startTimer() + const type = params.Type + const partySubId = params.SubId + const source = headers[Headers.FSPIOP.SOURCE] + const destination = headers[Headers.FSPIOP.DESTINATION] + const proxy = headers[Headers.FSPIOP.PROXY] + const proxyEnabled = !!(Config.PROXY_CACHE_CONFIG.enabled && proxyCache) + logger.info('parties::putPartiesByTypeAndID::begin', { source, destination, proxy, params }) + + let sendTo try { - Logger.isInfoEnabled && Logger.info('parties::putPartiesByTypeAndID::begin') - const requesterParticipant = await participant.validateParticipant(headers[Enums.Http.Headers.FSPIOP.SOURCE]) - const type = params.Type - const partySubIdOrType = params.SubId || undefined - const callbackEndpointType = partySubIdOrType ? Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_SUB_ID_PUT : Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_PUT - const errorCallbackEndpointType = partySubIdOrType ? Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_SUB_ID_PUT_ERROR : Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_PUT_ERROR - if (requesterParticipant) { - const destinationParticipant = await participant.validateParticipant(headers[Enums.Http.Headers.FSPIOP.DESTINATION]) - if (destinationParticipant) { - let options = { - partyIdType: type, - partyIdentifier: params.ID - } - options = partySubIdOrType ? { ...options, partySubIdOrType } : options - const decodedPayload = decodePayload(dataUri, { asParsed: false }) - await participant.sendRequest(headers, destinationParticipant.name, callbackEndpointType, Enums.Http.RestMethods.PUT, decodedPayload.body.toString(), options) - Logger.isInfoEnabled && Logger.info('parties::putPartiesByTypeAndID::end') + const requesterParticipant = await participant.validateParticipant(source) + if (!requesterParticipant) { + if (!proxyEnabled || !proxy) { + const errMessage = ERROR_MESSAGES.partySourceFspNotFound + throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, errMessage) + } + const isCached = await proxyCache.addDfspIdToProxyMapping(source, proxy) + // think,if we should throw error if isCached === false? + logger.info('addDfspIdToProxyMapping is done', { source, proxy, isCached }) + } + + if (proxyEnabled && proxy) { + const alsReq = utils.alsRequestDto(destination, params) // or source? + const isExists = await proxyCache.receivedSuccessResponse(alsReq) + if (!isExists) { + logger.warn('destination is NOT in scheme, and no cached sendToProxiesList', { destination, alsReq }) + // think, if we need to throw an error here } else { - await participant.sendErrorToParticipant(headers[Enums.Http.Headers.FSPIOP.SOURCE], errorCallbackEndpointType, - ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.DESTINATION_FSP_ERROR).toApiErrorObject(Config.ERROR_HANDLING), headers, params) + const mappingPayload = { + fspId: source + } + await oracle.oracleRequest(headers, RestMethods.POST, params, null, mappingPayload, cache) + logger.info('oracle was updated with mappingPayload', { mappingPayload, params }) } + } + + const destinationParticipant = await participant.validateParticipant(destination) + if (!destinationParticipant) { + const proxyName = proxyEnabled && await proxyCache.lookupProxyByDfspId(destination) + if (!proxyName) { + const errMessage = ERROR_MESSAGES.partyDestinationFspNotFound + throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.DESTINATION_FSP_ERROR, errMessage) + } + sendTo = proxyName } else { - Logger.isErrorEnabled && Logger.error('Requester FSP not found') - throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, 'Requester FSP not found') + sendTo = destinationParticipant.name + } + + const decodedPayload = decodePayload(dataUri, { asParsed: false }) + const callbackEndpointType = utils.putPartyCbType(partySubId) + const options = { + partyIdType: type, + partyIdentifier: params.ID, + ...(partySubId && { partySubIdOrType: partySubId }) } + await participant.sendRequest(headers, sendTo, callbackEndpointType, RestMethods.PUT, decodedPayload.body.toString(), options) + + logger.info('parties::putPartiesByTypeAndID::callback was sent', { sendTo, options }) histTimerEnd({ success: true }) } catch (err) { - Logger.isErrorEnabled && Logger.error(err) - try { - const errorCallbackEndpointType = params.SubId ? Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_SUB_ID_PUT_ERROR : Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_PUT_ERROR - await participant.sendErrorToParticipant(headers[Enums.Http.Headers.FSPIOP.SOURCE], errorCallbackEndpointType, - ErrorHandler.Factory.reformatFSPIOPError(err).toApiErrorObject(Config.ERROR_HANDLING), headers, params) - } catch (exc) { - // We can't do anything else here- we _must_ handle all errors _within_ this function because - // we've already sent a sync response- we cannot throw. - Logger.isErrorEnabled && Logger.error(exc) - } + await utils.handleErrorOnSendingCallback(err, headers, params, sendTo) histTimerEnd({ success: false }) } } @@ -238,53 +139,75 @@ const putPartiesByTypeAndID = async (headers, params, method, payload, dataUri) * @param {object} payload - payload of the request being sent out * @param {string} dataUri - encoded payload of the request being sent out * @param {object} span + * @param {CacheClient} cache - in-memory cache with CatboxMemory engine + * @param {IProxyCache} [proxyCache] - IProxyCache instance */ -const putPartiesErrorByTypeAndID = async (headers, params, payload, dataUri, span) => { +const putPartiesErrorByTypeAndID = async (headers, params, payload, dataUri, span, cache, proxyCache = undefined) => { const histTimerEnd = Metrics.getHistogram( - 'puttPartiesErrorByTypeAndID', + 'putPartiesErrorByTypeAndID', 'Put parties error by type and id', ['success'] ).startTimer() + const partySubId = params.SubId + const destination = headers[Headers.FSPIOP.DESTINATION] + const callbackEndpointType = utils.errorPartyCbType(partySubId) + const proxyEnabled = !!(Config.PROXY_CACHE_CONFIG.enabled && proxyCache) + const childSpan = span ? span.getChild('putPartiesErrorByTypeAndID') : undefined + + let sendTo let fspiopError + try { - const partySubIdOrType = params.SubId || undefined - const callbackEndpointType = partySubIdOrType ? Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_SUB_ID_PUT_ERROR : Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_PUT_ERROR - const destinationParticipant = await participant.validateParticipant(headers[Enums.Http.Headers.FSPIOP.DESTINATION], childSpan) - if (destinationParticipant) { - const decodedPayload = decodePayload(dataUri, { asParsed: false }) - await participant.sendErrorToParticipant(headers[Enums.Http.Headers.FSPIOP.DESTINATION], callbackEndpointType, decodedPayload.body.toString(), headers, params, childSpan) - if (childSpan && !childSpan.isFinished) { - await childSpan.finish() + const proxy = proxyEnabled && headers[Headers.FSPIOP.PROXY] + if (proxy) { + if (isNotValidPayeeCase(payload)) { + const swappedHeaders = utils.swapSourceDestinationHeaders(headers) + await oracle.oracleRequest(swappedHeaders, RestMethods.DELETE, params, null, null, cache) + getPartiesByTypeAndID(swappedHeaders, params, RestMethods.GET, {}, span, cache, proxyCache) + // todo: - think if we need to send errorCallback? + // - or sentCallback after getPartiesByTypeAndID is done + logger.info('notValidPayee case - deleted Participants and run getPartiesByTypeAndID:', { proxy, params, payload }) + return } + + const alsReq = utils.alsRequestDto(destination, params) // or source? + const isLast = await proxyCache.receivedErrorResponse(alsReq, proxy) + if (!isLast) { + logger.info('got NOT last error callback from proxy:', { proxy, alsReq }) + return + } + } + + const destinationParticipant = await participant.validateParticipant(destination) + + if (destinationParticipant) { + sendTo = destination } else { - fspiopError = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.DESTINATION_FSP_ERROR) - await participant.sendErrorToParticipant(headers[Enums.Http.Headers.FSPIOP.SOURCE], callbackEndpointType, - fspiopError.toApiErrorObject(Config.ERROR_HANDLING), headers, params, payload, childSpan) + const proxyName = proxyEnabled && await proxyCache.lookupProxyByDfspId(destination) + if (!proxyName) { + const errMessage = ERROR_MESSAGES.partyDestinationFspNotFound + throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.DESTINATION_FSP_ERROR, errMessage) + } + sendTo = proxyName } + const decodedPayload = decodePayload(dataUri, { asParsed: false }) + await participant.sendErrorToParticipant(sendTo, callbackEndpointType, decodedPayload.body.toString(), headers, params, childSpan) + + logger.info('putPartiesErrorByTypeAndID callback was sent', { sendTo }) histTimerEnd({ success: true }) } catch (err) { - Logger.isErrorEnabled && Logger.error(err) - try { - fspiopError = ErrorHandler.Factory.reformatFSPIOPError(err) - const callbackEndpointType = params.SubId ? Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_SUB_ID_PUT_ERROR : Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_PUT_ERROR - await participant.sendErrorToParticipant(headers[Enums.Http.Headers.FSPIOP.SOURCE], callbackEndpointType, - fspiopError.toApiErrorObject(Config.ERROR_HANDLING), headers, params, childSpan) - } catch (exc) { - // We can't do anything else here- we _must_ handle all errors _within_ this function because - // we've already sent a sync response- we cannot throw. - Logger.isErrorEnabled && Logger.error(exc) - } + fspiopError = await utils.handleErrorOnSendingCallback(err, headers, params, sendTo) histTimerEnd({ success: false }) } finally { - if (childSpan && !childSpan.isFinished && fspiopError) { - const state = new EventSdk.EventStateMetadata(EventSdk.EventStatusType.failed, fspiopError.apiErrorCode.code, fspiopError.apiErrorCode.message) - await childSpan.error(fspiopError, state) - await childSpan.finish(fspiopError.message, state) - } + await utils.finishSpanWithError(childSpan, fspiopError) } } +function isNotValidPayeeCase (payload) { + return payload?.errorInformation?.errorCode === MojaloopApiErrorCodes.PAYEE_IDENTIFIER_NOT_VALID.code +} + module.exports = { getPartiesByTypeAndID, putPartiesByTypeAndID, diff --git a/src/domain/parties/utils.js b/src/domain/parties/utils.js new file mode 100644 index 00000000..da5f1d20 --- /dev/null +++ b/src/domain/parties/utils.js @@ -0,0 +1,84 @@ +const { Enum } = require('@mojaloop/central-services-shared') +const EventSdk = require('@mojaloop/event-sdk') +const ErrorHandler = require('@mojaloop/central-services-error-handling') + +const participant = require('../../models/participantEndpoint/facade') +const Config = require('../../lib/config') +const { logger } = require('../../lib') + +const { FspEndpointTypes } = Enum.EndPoints +const { Headers } = Enum.Http + +const getPartyCbType = (partySubId) => partySubId + ? FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_SUB_ID_GET + : FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_GET + +const putPartyCbType = (partySubId) => partySubId + ? FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_SUB_ID_PUT + : FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_PUT + +const errorPartyCbType = (partySubId) => partySubId + ? FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_SUB_ID_PUT_ERROR + : FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_PUT_ERROR + +const finishSpanWithError = async (childSpan, fspiopError) => { + if (childSpan && !childSpan.isFinished) { + if (fspiopError) { + const state = new EventSdk.EventStateMetadata(EventSdk.EventStatusType.failed, fspiopError.apiErrorCode.code, fspiopError.apiErrorCode.message) + await childSpan.error(fspiopError, state) + await childSpan.finish(fspiopError.message, state) + } else { + await childSpan.finish() + } + } +} + +const alsRequestDto = (sourceId, params) => ({ + sourceId, + type: params.Type, + partyId: params.ID +}) + +const swapSourceDestinationHeaders = (headers) => { + const { + [Headers.FSPIOP.SOURCE]: source, + [Headers.FSPIOP.DESTINATION]: destination, + [Headers.FSPIOP.PROXY]: proxy, + ...restHeaders + } = headers + return { + ...restHeaders, + [Headers.FSPIOP.SOURCE]: destination, + [Headers.FSPIOP.DESTINATION]: source + } +} + +// change signature to accept object +const handleErrorOnSendingCallback = async (err, headers, params, requester) => { + try { + logger.error('error in sending parties callback', err) + const sendTo = requester || headers[Headers.FSPIOP.SOURCE] + const errorCallbackEndpointType = errorPartyCbType(params.SubId) + const fspiopError = ErrorHandler.Factory.reformatFSPIOPError(err) + const errInfo = fspiopError.toApiErrorObject(Config.ERROR_HANDLING) + + await participant.sendErrorToParticipant(sendTo, errorCallbackEndpointType, errInfo, headers, params) + + logger.info('handleErrorOnSendingCallback in done', { sendTo, params, errInfo }) + return fspiopError + } catch (exc) { + // We can't do anything else here- we _must_ handle all errors _within_ this function because + // we've already sent a sync response- we cannot throw. + logger.error('error in handleErrorOnSendingCallback', exc) + } +} + +module.exports = { + getPartyCbType, + putPartyCbType, + errorPartyCbType, + finishSpanWithError, + handleErrorOnSendingCallback, + alsRequestDto, + swapSourceDestinationHeaders +} diff --git a/src/domain/timeout/dto.js b/src/domain/timeout/dto.js new file mode 100644 index 00000000..d685b735 --- /dev/null +++ b/src/domain/timeout/dto.js @@ -0,0 +1,28 @@ +const LibUtil = require('../../lib/util') +const Config = require('../../lib/config') +const { + Factory: { createFSPIOPError }, + Enums: { FSPIOPErrorCodes } +} = require('@mojaloop/central-services-error-handling') +const { + Http: { Headers: { FSPIOP: FSPIOPHeaders } }, + Events: { Event: { Type: EventType, Action: EventAction } }, + EndPoints: { FspEndpointTypes } +} = require('@mojaloop/central-services-shared').Enum +const { Tracer } = require('@mojaloop/event-sdk') + +const timeoutCallbackDto = ({ destination, partyId, partyType }) => { + const dto = { + errorInformation: createFSPIOPError(FSPIOPErrorCodes.EXPIRED_ERROR).toApiErrorObject(Config.ERROR_HANDLING), + params: { ID: partyId, Type: partyType }, + headers: { [FSPIOPHeaders.SOURCE]: Config.HUB_NAME, [FSPIOPHeaders.DESTINATION]: destination }, + endpointType: FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_PUT_ERROR + } + const span = Tracer.createSpan('timeoutInterschemePartiesLookups', { headers: dto.headers }) + const spanTags = LibUtil.getSpanTags({ headers: dto.headers }, EventType.PARTY, EventAction.PUT) + span.setTags(spanTags) + + return { ...dto, span } +} + +module.exports = { timeoutCallbackDto } diff --git a/src/domain/timeout/index.js b/src/domain/timeout/index.js new file mode 100644 index 00000000..715113be --- /dev/null +++ b/src/domain/timeout/index.js @@ -0,0 +1,101 @@ +/***** + License + -------------- + Copyright © 2020-2024 Mojaloop Foundation + + The Mojaloop files are made available by the Mojaloop Foundation under the Apache License, Version 2.0 + (the "License") and you may not use these files except in compliance with the [License](http://www.apache.org/licenses/LICENSE-2.0). + + You may obtain a copy of the License at [http://www.apache.org/licenses/LICENSE-2.0](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](http://www.apache.org/licenses/LICENSE-2.0). + + 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 + - Steven Oderayi + + -------------- + ******/ +'use strict' + +const Metrics = require('@mojaloop/central-services-metrics') +const Participant = require('../../models/participantEndpoint/facade') +const { ERROR_MESSAGES } = require('../../constants') +const { timeoutCallbackDto } = require('./dto') +const { + Factory: { createFSPIOPError, reformatFSPIOPError }, + Enums: { FSPIOPErrorCodes } +} = require('@mojaloop/central-services-error-handling') +const { + EventStateMetadata, + EventStatusType, + AuditEventAction +} = require('@mojaloop/event-sdk') +const { logger } = require('../../lib') + +const timeoutInterschemePartiesLookups = async ({ proxyCache, batchSize }) => { + return proxyCache.processExpiredAlsKeys(sendTimeoutCallback, batchSize) +} + +const sendTimeoutCallback = async (cacheKey) => { + const histTimerEnd = Metrics.getHistogram( + 'eg_timeoutInterschemePartiesLookups', + 'Egress - Interscheme parties lookup timeout callback', + ['success'] + ).startTimer() + + const [, destination, partyType, partyId] = cacheKey.split(':') + const { errorInformation, params, headers, endpointType, span } = timeoutCallbackDto({ destination, partyId, partyType }) + + try { + await validateParticipant(destination) + await span.audit({ headers, errorInformation }, AuditEventAction.start) + await Participant.sendErrorToParticipant(destination, endpointType, errorInformation, headers, params, undefined, span) + histTimerEnd({ success: true }) + } catch (err) { + histTimerEnd({ success: false }) + const fspiopError = reformatFSPIOPError(err) + finishSpan(span, fspiopError) + throw fspiopError + } +} + +const validateParticipant = async (fspId) => { + const participant = await Participant.validateParticipant(fspId) + if (!participant) { + const errMessage = ERROR_MESSAGES.partyDestinationFspNotFound + logger.error(`error in validateParticipant: ${errMessage}`) + throw createFSPIOPError(FSPIOPErrorCodes.DESTINATION_FSP_ERROR, errMessage) + } + return participant +} + +const finishSpan = async (span, err) => { + if (!span.isFinished) { + const state = new EventStateMetadata( + EventStatusType.failed, + err.apiErrorCode.code, + err.apiErrorCode.message + ) + await span.error(err, state) + await span.finish(err.message, state) + } +} + +module.exports = { + timeoutInterschemePartiesLookups, + sendTimeoutCallback // Exposed for testing +} diff --git a/src/handlers/TimeoutHandler.js b/src/handlers/TimeoutHandler.js new file mode 100644 index 00000000..a415e24f --- /dev/null +++ b/src/handlers/TimeoutHandler.js @@ -0,0 +1,94 @@ +/***** + License + -------------- + Copyright © 2020-2024 Mojaloop Foundation + + The Mojaloop files are made available by the Mojaloop Foundation under the Apache License, Version 2.0 + (the "License") and you may not use these files except in compliance with the [License](http://www.apache.org/licenses/LICENSE-2.0). + + You may obtain a copy of the License at [http://www.apache.org/licenses/LICENSE-2.0](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](http://www.apache.org/licenses/LICENSE-2.0). + + 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 + - Steven Oderayi + -------------- + ******/ +'use strict' + +const CronJob = require('cron').CronJob +const ErrorHandler = require('@mojaloop/central-services-error-handling') +const TimeoutService = require('../domain/timeout') +const Config = require('../lib/config') + +let timeoutJob +let isRegistered +let isRunning + +const timeout = async (options) => { + if (isRunning) return + + const { logger } = options + + try { + isRunning = true + logger.debug('Timeout handler triggered') + await TimeoutService.timeoutInterschemePartiesLookups(options) + } catch (err) { + logger.error('error in timeout: ', err) + } finally { + isRunning = false + } +} + +const register = async (options) => { + if (Config.HANDLERS_TIMEOUT_DISABLED) return false + + const { logger } = options + + try { + if (isRegistered) { + await stop() + } + timeoutJob = CronJob.from({ + start: false, + onTick: () => timeout(options), + cronTime: Config.HANDLERS_TIMEOUT_TIMEXP, + timeZone: Config.HANDLERS_TIMEOUT_TIMEZONE + }) + timeoutJob.start() + isRegistered = true + logger.info('Timeout handler registered') + return true + } catch (err) { + logger.error('error in register:', err) + throw ErrorHandler.Factory.reformatFSPIOPError(err) + } +} + +const stop = async () => { + if (isRegistered) { + await timeoutJob.stop() + isRegistered = undefined + } +} + +module.exports = { + timeout, + register, + stop +} diff --git a/src/handlers/index.js b/src/handlers/index.js index 459f55e8..24fe6703 100644 --- a/src/handlers/index.js +++ b/src/handlers/index.js @@ -1,10 +1,15 @@ /***** 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. + Copyright © 2020-2024 Mojaloop Foundation + + The Mojaloop files are made available by the Mojaloop Foundation under the Apache License, Version 2.0 + (the "License") and you may not use these files except in compliance with the [License](http://www.apache.org/licenses/LICENSE-2.0). + + You may obtain a copy of the License at [http://www.apache.org/licenses/LICENSE-2.0](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](http://www.apache.org/licenses/LICENSE-2.0). + Contributors -------------- This is the official list of the Mojaloop project contributors for this file. @@ -15,71 +20,51 @@ 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 - * ModusBox - - Rajiv Mothilal + * Gates Foundation + - Name Surname + * INFITX + - Steven Oderayi -------------- ******/ - 'use strict' -const OpenapiBackend = require('@mojaloop/central-services-shared').Util.OpenapiBackend +const { Command } = require('commander') +const Package = require('../../package.json') +const Server = require('../server') +const { HANDLER_TYPES } = require('../constants') +const Config = require('../lib/config') +const logger = require('../lib').loggerFactory('ALS-timeout-handler') -const participants = require('./participants') -const participantsId = require('./participants/{ID}') -const participantsErrorById = require('./participants/{ID}/error') -const participantsTypeId = require('./participants/{Type}/{ID}') -const participantsErrorTypeId = require('./participants/{Type}/{ID}/error') -const participantsTypeIdSubId = require('./participants/{Type}/{ID}/{SubId}') -const participantsErrorTypeIdSubId = require('./participants/{Type}/{ID}/{SubId}/error') +const Program = new Command() -const partiesTypeId = require('./parties/{Type}/{ID}') -const partiesTypeIdSubId = require('./parties/{Type}/{ID}/{SubId}') -const partiesErrorTypeId = require('./parties/{Type}/{ID}/error') -const partiesErrorTypeIdSubId = require('./parties/{Type}/{ID}/{SubId}/error') +Program + .version(Package.version) + .description('CLI to manage Handlers') -const oracles = require('./oracles') -const oraclesId = require('./oracles/{ID}') +Program.command('handlers') + .alias('h') + .description('Start specified handler(s)') + .option('--timeout', 'Start the Timeout Handler') + .action(async (args) => { + const handlers = [] -const endpointCache = require('./endpointcache') -const health = require('./health') + if (args.timeout) { + logger.debug('CLI: Executing --timeout') + handlers.push(HANDLER_TYPES.TIMEOUT) + } -module.exports.ApiHandlers = { - HealthGet: health.get, - ParticipantsErrorByIDPut: participantsErrorById.put, - ParticipantsByIDPut: participantsId.put, - ParticipantsErrorByTypeAndIDPut: participantsErrorTypeId.put, - ParticipantsErrorBySubIdTypeAndIDPut: participantsErrorTypeIdSubId.put, - ParticipantsSubIdByTypeAndIDGet: participantsTypeIdSubId.get, - ParticipantsSubIdByTypeAndIDPut: participantsTypeIdSubId.put, - ParticipantsSubIdByTypeAndIDPost: participantsTypeIdSubId.post, - ParticipantsSubIdByTypeAndIDDelete: participantsTypeIdSubId.delete, - ParticipantsByTypeAndIDGet: participantsTypeId.get, - ParticipantsByTypeAndIDPut: participantsTypeId.put, - ParticipantsByIDAndTypePost: participantsTypeId.post, - ParticipantsByTypeAndIDDelete: participantsTypeId.delete, - ParticipantsPost: participants.post, - PartiesByTypeAndIDGet: partiesTypeId.get, - PartiesByTypeAndIDPut: partiesTypeId.put, - PartiesErrorByTypeAndIDPut: partiesErrorTypeId.put, - PartiesBySubIdTypeAndIDGet: partiesTypeIdSubId.get, - PartiesSubIdByTypeAndIDPut: partiesTypeIdSubId.put, - PartiesErrorBySubIdTypeAndIDPut: partiesErrorTypeIdSubId.put, - EndpointCacheDelete: endpointCache.delete, - validationFail: OpenapiBackend.validationFail, - notFound: OpenapiBackend.notFound, - methodNotAllowed: OpenapiBackend.methodNotAllowed -} + if (handlers.length === 0) { + logger.debug('CLI: No handlers specified') + return + } + + module.exports = await Server.initializeHandlers(handlers, Config, logger) + }) -module.exports.AdminHandlers = { - HealthGet: health.get, - OraclesGet: oracles.get, - OraclesPost: oracles.post, - OraclesByIdPut: oraclesId.put, - OraclesByIdDelete: oraclesId.delete, - validationFail: OpenapiBackend.validationFail, - notFound: OpenapiBackend.notFound, - methodNotAllowed: OpenapiBackend.methodNotAllowed +if (Array.isArray(process.argv) && process.argv.length > 2) { + Program.parse(process.argv) +} else { + Program.help() } diff --git a/src/handlers/monitoring/index.js b/src/handlers/monitoring/index.js new file mode 100644 index 00000000..2c8a0aaa --- /dev/null +++ b/src/handlers/monitoring/index.js @@ -0,0 +1,51 @@ +/***** + LICENSE + + Copyright © 2020 Mojaloop Foundation + + The Mojaloop files are made available by the Mojaloop Foundation under the Apache License, Version 2.0 + (the "License") and you may not use these files except in compliance with the [License](http://www.apache.org/licenses/LICENSE-2.0). + + You may obtain a copy of the License at [http://www.apache.org/licenses/LICENSE-2.0](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](http://www.apache.org/licenses/LICENSE-2.0). + + * Infitx + - Steven Oderayi +-------------- +******/ +const Hapi = require('@hapi/hapi') +const Metrics = require('@mojaloop/central-services-metrics') +const { plugin: HealthPlugin } = require('./plugins/health') +const { plugin: MetricsPlugin } = require('./plugins/metrics') +const { logger } = require('../../lib') + +let server + +const create = async ({ port, metricsConfig }) => { + Metrics.setup(metricsConfig) + server = new Hapi.Server({ port }) + await server.register([HealthPlugin, MetricsPlugin]) +} + +const start = async ({ enabled, port, metricsConfig, proxyCache }) => { + if (!enabled) return + if (!server) await create({ port, metricsConfig }) + server.app.proxyCache = proxyCache + await server.start() + logger.info(`Monitoring server running at: ${server.info.uri}`) +} + +const stop = async () => { + await Promise.all([ + server?.stop(), + server.app.proxyCache?.disconnect() + ]) + server = null +} + +module.exports = { + start, + stop +} diff --git a/src/handlers/monitoring/plugins/health.js b/src/handlers/monitoring/plugins/health.js new file mode 100644 index 00000000..78ddf761 --- /dev/null +++ b/src/handlers/monitoring/plugins/health.js @@ -0,0 +1,61 @@ +/***** + LICENSE + + Copyright © 2020 Mojaloop Foundation + + The Mojaloop files are made available by the Mojaloop Foundation under the Apache License, Version 2.0 + (the "License") and you may not use these files except in compliance with the [License](http://www.apache.org/licenses/LICENSE-2.0). + + You may obtain a copy of the License at [http://www.apache.org/licenses/LICENSE-2.0](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](http://www.apache.org/licenses/LICENSE-2.0). + + * Infitx + - Steven Oderayi +-------------- +******/ + +'use strict' + +const { HealthCheck } = require('@mojaloop/central-services-shared').HealthCheck +const { defaultHealthHandler } = require('@mojaloop/central-services-health') +const packageJson = require('../../../../package.json') +const { getProxyCacheHealth } = require('../../../lib/healthCheck/subServiceHealth') + +let healthCheck + +const createHealthCheck = ({ proxyCache }) => { + return new HealthCheck(packageJson, [ + () => getProxyCacheHealth(proxyCache) + ]) +} + +const handler = { + get: (request, reply) => { + const proxyCache = request.server.app.proxyCache + healthCheck = healthCheck || createHealthCheck({ proxyCache }) + return defaultHealthHandler(healthCheck)(request, reply) + } +} + +const routes = [ + { + method: 'GET', + path: '/health', + handler: handler.get + } +] + +const plugin = { + name: 'Health', + register (server) { + server.route(routes) + } +} + +module.exports = { + plugin, + handler, + createHealthCheck +} diff --git a/src/handlers/monitoring/plugins/metrics.js b/src/handlers/monitoring/plugins/metrics.js new file mode 100644 index 00000000..be056fb8 --- /dev/null +++ b/src/handlers/monitoring/plugins/metrics.js @@ -0,0 +1,48 @@ +/***** + LICENSE + + Copyright © 2020 Mojaloop Foundation + + The Mojaloop files are made available by the Mojaloop Foundation under the Apache License, Version 2.0 + (the "License") and you may not use these files except in compliance with the [License](http://www.apache.org/licenses/LICENSE-2.0). + + You may obtain a copy of the License at [http://www.apache.org/licenses/LICENSE-2.0](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](http://www.apache.org/licenses/LICENSE-2.0). + + * Infitx + - Steven Oderayi +-------------- +******/ + +'use strict' + +const HTTPENUM = require('@mojaloop/central-services-shared').Enum.Http +const Metrics = require('@mojaloop/central-services-metrics') + +const handler = { + get: async (_request, reply) => { + return reply.response(await Metrics.getMetricsForPrometheus()).code(HTTPENUM.ReturnCodes.OK.CODE) + } +} + +const routes = [ + { + method: 'GET', + path: '/metrics', + handler: handler.get + } +] + +const plugin = { + name: 'Metrics', + register (server) { + server.route(routes) + } +} + +module.exports = { + plugin, + handler +} diff --git a/src/handlers/register.js b/src/handlers/register.js new file mode 100644 index 00000000..bdc892d1 --- /dev/null +++ b/src/handlers/register.js @@ -0,0 +1,102 @@ +/***** + License + -------------- + Copyright © 2020-2024 Mojaloop Foundation + + The Mojaloop files are made available by the Mojaloop Foundation under the Apache License, Version 2.0 + (the "License") and you may not use these files except in compliance with the [License](http://www.apache.org/licenses/LICENSE-2.0). + + You may obtain a copy of the License at [http://www.apache.org/licenses/LICENSE-2.0](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](http://www.apache.org/licenses/LICENSE-2.0). + + 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 + - Steven Oderayi + -------------- + ******/ +'use strict' + +const Metrics = require('@mojaloop/central-services-metrics') +const { Endpoints, Participants } = require('@mojaloop/central-services-shared').Util +const TimeoutHandler = require('./TimeoutHandler') +const { HANDLER_TYPES } = require('../constants') +const Config = require('../lib/config') +const Util = require('../lib/util') +const Monitoring = require('./monitoring') + +/** + * Register handlers + * + * @param {*} handlers Array of handlers names + * @param {*} options { proxyCache: ProxyCache, batchSize: number, logger: Logger } + */ +const registerHandlers = async (handlers, options) => { + const { logger } = options + await init(options) + handlers.forEach(handler => { + switch (handler) { + case HANDLER_TYPES.TIMEOUT: + logger.debug('Registering Timeout Handler') + TimeoutHandler.register(options) + break + default: + logger.warn(`Handler ${handler} not found`) + break + } + }) +} + +/** + * Register all handlers + * + * @param {*} options { proxyCache: ProxyCache, batchSize: number } + */ + +const registerAllHandlers = async (options) => { + options.logger.debug('Registering all handlers') + await init(options) + TimeoutHandler.register(options) +} + +const stopAllHandlers = async (options) => { + options.logger.debug('Stopping all handlers') + await Promise.all([ + TimeoutHandler.stop(), + Monitoring.stop() + ]) +} + +const init = async (options) => { + options.logger.debug('Initializing caches and metrics') + !Config.INSTRUMENTATION_METRICS_DISABLED && Metrics.setup(Config.INSTRUMENTATION_METRICS_CONFIG) + await Promise.all([ + Participants.initializeCache(Config.CENTRAL_SHARED_PARTICIPANT_CACHE_CONFIG, Util.hubNameConfig), + Endpoints.initializeCache(Config.CENTRAL_SHARED_ENDPOINT_CACHE_CONFIG, Util.hubNameConfig), + Monitoring.start({ + enabled: !Config.INSTRUMENTATION_METRICS_DISABLED, + port: Config.HANDLERS_MONITORING_PORT, + metricsConfig: Config.INSTRUMENTATION_METRICS_CONFIG, + proxyCache: options.proxyCache + }) + ]) +} + +module.exports = { + registerHandlers, + registerAllHandlers, + stopAllHandlers +} diff --git a/src/index.js b/src/index.js index cfd5d2af..6bbf9b24 100644 --- a/src/index.js +++ b/src/index.js @@ -47,13 +47,13 @@ Program.command('server') // sub-command name, coffeeType = type, required .action(async (args) => { if (args.api) { Logger.isDebugEnabled && Logger.debug('CLI: Executing --api') - module.exports = Server.initializeApi(Config.API_PORT) + module.exports = Server.initializeApi(Config) } else if (args.admin) { Logger.isDebugEnabled && Logger.debug('CLI: Executing --admin') - module.exports = Server.initializeAdmin(Config.ADMIN_PORT) + module.exports = Server.initializeAdmin(Config) } else { - module.exports = Server.initializeAdmin(Config.ADMIN_PORT) - module.exports = Server.initializeApi(Config.API_PORT) + module.exports = Server.initializeAdmin(Config) + module.exports = Server.initializeApi(Config) } }) diff --git a/src/lib/config.js b/src/lib/config.js index f41fcbf0..e8be032b 100644 --- a/src/lib/config.js +++ b/src/lib/config.js @@ -27,8 +27,9 @@ -------------- ******/ +const fs = require('node:fs') const RC = require('parse-strings-in-object')(require('rc')('ALS', require('../../config/default.json'))) -const fs = require('fs') +const { storageTypeValues } = require('@mojaloop/inter-scheme-proxy-cache-lib') function getFileContent (path) { if (!fs.existsSync(path)) { @@ -98,7 +99,13 @@ const getProtocolVersions = (defaultProtocolVersions, overrideProtocolVersions) return T_PROTOCOL_VERSION } +if (RC.PROXY_CACHE?.enabled && !storageTypeValues.includes(RC.PROXY_CACHE.type)) { + throw new TypeError(`Incorrect proxyCache type: ${RC.PROXY_CACHE.type}`) +} + const config = { + HUB_ID: RC.HUB_PARTICIPANT.ID, + HUB_NAME: RC.HUB_PARTICIPANT.NAME, API_PORT: RC.API_PORT, DATABASE: { client: RC.DATABASE.DIALECT, @@ -145,17 +152,27 @@ const config = { CENTRAL_SHARED_ENDPOINT_CACHE_CONFIG: RC.CENTRAL_SHARED_ENDPOINT_CACHE_CONFIG, CENTRAL_SHARED_PARTICIPANT_CACHE_CONFIG: RC.CENTRAL_SHARED_PARTICIPANT_CACHE_CONFIG, GENERAL_CACHE_CONFIG: RC.GENERAL_CACHE_CONFIG, + HANDLERS: RC.HANDLERS, + HANDLERS_DISABLED: RC.HANDLERS.DISABLED, + HANDLERS_MONITORING_PORT: RC.HANDLERS.MONITORING_PORT, + HANDLERS_TIMEOUT: RC.HANDLERS.TIMEOUT, + HANDLERS_TIMEOUT_DISABLED: RC.HANDLERS.TIMEOUT.DISABLED, + HANDLERS_TIMEOUT_TIMEXP: RC.HANDLERS.TIMEOUT.TIMEXP, + HANDLERS_TIMEOUT_TIMEZONE: RC.HANDLERS.TIMEOUT.TIMEZONE, + HANDLERS_TIMEOUT_BATCH_SIZE: RC.HANDLERS.TIMEOUT.BATCH_SIZE, ERROR_HANDLING: RC.ERROR_HANDLING, SWITCH_ENDPOINT: RC.SWITCH_ENDPOINT, INSTRUMENTATION_METRICS_DISABLED: RC.INSTRUMENTATION.METRICS.DISABLED, INSTRUMENTATION_METRICS_LABELS: RC.INSTRUMENTATION.METRICS.labels, INSTRUMENTATION_METRICS_CONFIG: RC.INSTRUMENTATION.METRICS.config, JWS_SIGN: RC.ENDPOINT_SECURITY.JWS.JWS_SIGN, - FSPIOP_SOURCE_TO_SIGN: RC.ENDPOINT_SECURITY.JWS.FSPIOP_SOURCE_TO_SIGN, + FSPIOP_SOURCE_TO_SIGN: RC.HUB_PARTICIPANT.NAME, JWS_SIGNING_KEY_PATH: RC.ENDPOINT_SECURITY.JWS.JWS_SIGNING_KEY_PATH, API_DOC_ENDPOINTS_ENABLED: RC.API_DOC_ENDPOINTS_ENABLED || false, FEATURE_ENABLE_EXTENDED_PARTY_ID_TYPE: RC.FEATURE_ENABLE_EXTENDED_PARTY_ID_TYPE || false, - PROTOCOL_VERSIONS: getProtocolVersions(DEFAULT_PROTOCOL_VERSION, RC.PROTOCOL_VERSIONS) + PROTOCOL_VERSIONS: getProtocolVersions(DEFAULT_PROTOCOL_VERSION, RC.PROTOCOL_VERSIONS), + PROXY_CACHE_CONFIG: RC.PROXY_CACHE, + proxyMap: RC.PROXY_MAP } if (config.JWS_SIGN) { diff --git a/src/lib/headers.js b/src/lib/headers.js index aa000826..db6641b9 100644 --- a/src/lib/headers.js +++ b/src/lib/headers.js @@ -30,6 +30,7 @@ const Mustache = require('mustache') const Enums = require('@mojaloop/central-services-shared').Enum +const Config = require('../lib/config') /** * @function createErrorCallbackHeaders @@ -41,7 +42,7 @@ const Enums = require('@mojaloop/central-services-shared').Enum exports.createCallbackHeaders = (params) => { const callbackHeaders = { ...params.requestHeaders } - callbackHeaders[Enums.Http.Headers.FSPIOP.SOURCE] = Enums.Http.Headers.FSPIOP.SWITCH.value + callbackHeaders[Enums.Http.Headers.FSPIOP.SOURCE] = Config.HUB_NAME callbackHeaders[Enums.Http.Headers.FSPIOP.DESTINATION] = params.requestHeaders[Enums.Http.Headers.FSPIOP.SOURCE] callbackHeaders[Enums.Http.Headers.FSPIOP.HTTP_METHOD] = Enums.Http.RestMethods.PUT callbackHeaders[Enums.Http.Headers.FSPIOP.URI] = Mustache.render(params.endpointTemplate, { diff --git a/src/lib/healthCheck/subServiceHealth.js b/src/lib/healthCheck/subServiceHealth.js index 951ca4cd..3b080136 100644 --- a/src/lib/healthCheck/subServiceHealth.js +++ b/src/lib/healthCheck/subServiceHealth.js @@ -31,9 +31,8 @@ const MigrationLockModel = require('../../models/misc/migrationLock') /** * @function getSubServiceHealthDatastore * - * @description - * Gets the health of the Datastore by ensuring the table is currently locked - * in a migration state. This implicity checks the connection with the database. + * @description Gets the health of the Datastore by ensuring the table is currently locked + * in a migration state. This implicity checks the connection with the database. * * @returns Promise The SubService health object for the datastore */ @@ -56,6 +55,30 @@ const getSubServiceHealthDatastore = async () => { } } +/** + * @function getProxyCacheHealth + * + * @description Gets the health of the proxy cache by checking the connection to the cache. + * + * @returns Promise The SubService health object for the proxy cache + */ +const getProxyCacheHealth = async (proxyCache) => { + let status = statusEnum.OK + + try { + status = await proxyCache.healthCheck() ? statusEnum.OK : statusEnum.DOWN + } catch (err) { + Logger.isDebugEnabled && Logger.debug(`getProxyCacheHealth failed with error ${err.message}.`) + status = statusEnum.DOWN + } + + return { + name: serviceName.proxyCache, + status + } +} + module.exports = { - getSubServiceHealthDatastore + getSubServiceHealthDatastore, + getProxyCacheHealth } diff --git a/src/lib/index.js b/src/lib/index.js new file mode 100644 index 00000000..a9d7e5b3 --- /dev/null +++ b/src/lib/index.js @@ -0,0 +1,9 @@ +const { loggerFactory, asyncStorage } = require('@mojaloop/central-services-logger/src/contextLogger') + +const logger = loggerFactory('ALS') // global logger without context + +module.exports = { + logger, + loggerFactory, + asyncStorage +} diff --git a/src/lib/requestLogger.js b/src/lib/requestLogger.js index 9ebf7b50..f3b1dac5 100644 --- a/src/lib/requestLogger.js +++ b/src/lib/requestLogger.js @@ -24,32 +24,27 @@ 'use strict' -const Logger = require('@mojaloop/central-services-logger') -const Util = require('util') +const { logger, asyncStorage } = require('./index') const logRequest = function (request) { - const traceId = request.headers.traceid - Logger.isDebugEnabled && Logger.debug(`ALS-Trace=${traceId} - Method: ${request.method} Path: ${request.path} Query: ${JSON.stringify(request.query)}`) - Logger.isDebugEnabled && Logger.debug(`ALS-Trace=${traceId} - Headers: ${JSON.stringify(request.headers, null, 2)}`) - if (request.payload) { - Logger.isDebugEnabled && Logger.debug(`ALS-Trace=${traceId} - Body: ${JSON.stringify(request.payload, null, 2)}`) - } + const { path, method, headers, payload, query } = request + const requestId = request.info.id = `${request.info.id}__${headers.traceid}` + asyncStorage.enterWith({ requestId }) + + logger.isInfoEnabled && logger.info(`[==> req] ${method.toUpperCase()} ${path}`, { headers, payload, query }) } const logResponse = function (request) { - if (Logger.isDebugEnabled && request.response) { - const traceId = request.headers.traceid - let response - try { - response = JSON.stringify(request.response, null, 2) - } catch (e) { - response = Util.inspect(request.response) - } - if (!response) { - Logger.isDebugEnabled && Logger.debug(`ALS-Trace=${traceId} - Response: ${request.response}`) - } else { - Logger.isDebugEnabled && Logger.debug(`ALS-Trace=${traceId} - Response: ${response} Status: ${request.response.statusCode || request.response.httpStatusCode}, Stack: ${request.response.stack}`) - } + if (logger.isInfoEnabled) { + const { path, method, headers, payload, query, response } = request + const { received } = request.info + + const statusCode = response instanceof Error + ? response.output?.statusCode + : response.statusCode + const respTimeSec = ((Date.now() - received) / 1000).toFixed(3) + + logger.info(`[<== ${statusCode}][${respTimeSec} s] ${method.toUpperCase()} ${path}`, { headers, payload, query }) } } diff --git a/src/lib/util.js b/src/lib/util.js index ee16efe6..7e390b0f 100644 --- a/src/lib/util.js +++ b/src/lib/util.js @@ -1,6 +1,7 @@ const util = require('util') const Path = require('path') const Enum = require('@mojaloop/central-services-shared').Enum +const { HeaderValidation } = require('@mojaloop/central-services-shared').Util const Config = require('../lib/config') const getSpanTags = ({ headers }, transactionType, transactionAction) => { @@ -55,5 +56,9 @@ function getStackOrInspect (err) { module.exports = { getSpanTags, pathForInterface, - getStackOrInspect + getStackOrInspect, + hubNameConfig: { + hubName: Config.HUB_NAME, + hubNameRegex: HeaderValidation.getHubNameRegex(Config.HUB_NAME) + } } diff --git a/src/metrics/plugin.js b/src/metrics/plugin.js index 41dac532..75722800 100644 --- a/src/metrics/plugin.js +++ b/src/metrics/plugin.js @@ -33,7 +33,7 @@ 'use strict' /** - * @module src/handlers/api/plugin + * @module src/metrics/plugin */ /** diff --git a/src/models/oracle/facade.js b/src/models/oracle/facade.js index 1aa43685..63cff283 100644 --- a/src/models/oracle/facade.js +++ b/src/models/oracle/facade.js @@ -26,14 +26,16 @@ 'use strict' -const request = require('@mojaloop/central-services-shared').Util.Request const Mustache = require('mustache') -const Logger = require('@mojaloop/central-services-logger') +const request = require('@mojaloop/central-services-shared').Util.Request const Enums = require('@mojaloop/central-services-shared').Enum +const Logger = require('@mojaloop/central-services-logger') const ErrorHandler = require('@mojaloop/central-services-error-handling') -const Config = require('../../lib/config') const Metrics = require('@mojaloop/central-services-metrics') -const cachedOracleEndpoint = require('../oracle/oracleEndpointCached') + +const Config = require('../../lib/config') +const oracleEndpointCached = require('../oracle/oracleEndpointCached') +const { hubNameRegex } = require('../../lib/util').hubNameConfig /** * @function oracleRequest @@ -79,14 +81,15 @@ exports.oracleRequest = async (headers, method, params = {}, query = {}, payload let cachedOracleFspResponse cachedOracleFspResponse = cache && cache.get(cache.createKey(`oracleSendRequest_${url}`)) if (!cachedOracleFspResponse) { - cachedOracleFspResponse = await request.sendRequest( + cachedOracleFspResponse = await request.sendRequest({ url, headers, - headers[Enums.Http.Headers.FSPIOP.SOURCE], - headers[Enums.Http.Headers.FSPIOP.DESTINATION] || Enums.Http.Headers.FSPIOP.SWITCH.value, - method.toUpperCase(), - payload || undefined - ) + source: headers[Enums.Http.Headers.FSPIOP.SOURCE], + destination: headers[Enums.Http.Headers.FSPIOP.DESTINATION] || Config.HUB_NAME, + method: method.toUpperCase(), + payload, + hubNameRegex + }) // Trying to cache the whole response object will fail because it contains circular references // so we'll just cache the data property of the response. cachedOracleFspResponse = { @@ -106,21 +109,21 @@ exports.oracleRequest = async (headers, method, params = {}, query = {}, payload return cachedOracleFspResponse } - return await request.sendRequest( + return await request.sendRequest({ url, headers, - headers[Enums.Http.Headers.FSPIOP.SOURCE], - headers[Enums.Http.Headers.FSPIOP.DESTINATION] || Enums.Http.Headers.FSPIOP.SWITCH.value, - method.toUpperCase(), - payload || undefined - ) + source: headers[Enums.Http.Headers.FSPIOP.SOURCE], + destination: headers[Enums.Http.Headers.FSPIOP.DESTINATION] || Config.HUB_NAME, + method: method.toUpperCase(), + payload, + hubNameRegex + }) } catch (err) { histTimerEnd({ success: false, hit: false }) - Logger.isErrorEnabled && Logger.error(err) throw err } } catch (err) { - Logger.isErrorEnabled && Logger.error(err) + Logger.isErrorEnabled && Logger.error(`error in oracleRequest: ${err?.stack}`) // If the error was a 400 from the Oracle, we'll modify the error to generate a response to the // initiator of the request. if ( @@ -153,7 +156,7 @@ exports.oracleRequest = async (headers, method, params = {}, query = {}, payload */ const _getOracleEndpointByTypeAndCurrency = async (partyIdType, partyIdentifier, currency) => { let url - const oracleEndpointModel = await cachedOracleEndpoint.getOracleEndpointByTypeAndCurrency(partyIdType, currency) + const oracleEndpointModel = await oracleEndpointCached.getOracleEndpointByTypeAndCurrency(partyIdType, currency) if (oracleEndpointModel.length > 0) { if (oracleEndpointModel.length > 1) { const defautOracle = oracleEndpointModel.filter(oracle => oracle.isDefault).pop() @@ -188,7 +191,7 @@ const _getOracleEndpointByTypeAndCurrency = async (partyIdType, partyIdentifier, */ const _getOracleEndpointByType = async (partyIdType, partyIdentifier) => { let url - const oracleEndpointModel = await cachedOracleEndpoint.getOracleEndpointByType(partyIdType) + const oracleEndpointModel = await oracleEndpointCached.getOracleEndpointByType(partyIdType) if (oracleEndpointModel.length > 0) { if (oracleEndpointModel.length > 1) { const defaultOracle = oracleEndpointModel.filter(oracle => oracle.isDefault).pop() @@ -224,7 +227,7 @@ const _getOracleEndpointByType = async (partyIdType, partyIdentifier) => { */ const _getOracleEndpointByTypeAndSubId = async (partyIdType, partyIdentifier, partySubIdOrType) => { let url - const oracleEndpointModel = await cachedOracleEndpoint.getOracleEndpointByType(partyIdType) + const oracleEndpointModel = await oracleEndpointCached.getOracleEndpointByType(partyIdType) if (oracleEndpointModel.length > 0) { if (oracleEndpointModel.length > 1) { const defautOracle = oracleEndpointModel.filter(oracle => oracle.isDefault).pop() @@ -261,7 +264,7 @@ const _getOracleEndpointByTypeAndSubId = async (partyIdType, partyIdentifier, pa */ const _getOracleEndpointByTypeCurrencyAndSubId = async (partyIdType, partyIdentifier, currency, partySubIdOrType) => { let url - const oracleEndpointModel = await cachedOracleEndpoint.getOracleEndpointByTypeAndCurrency(partyIdType, currency) + const oracleEndpointModel = await oracleEndpointCached.getOracleEndpointByTypeAndCurrency(partyIdType, currency) if (oracleEndpointModel.length > 0) { if (oracleEndpointModel.length > 1) { const defautOracle = oracleEndpointModel.filter(oracle => oracle.isDefault).pop() @@ -302,9 +305,9 @@ exports.oracleBatchRequest = async (headers, method, requestPayload, type, paylo let oracleEndpointModel let url if ((requestPayload && requestPayload.currency && requestPayload.currency.length !== 0)) { - oracleEndpointModel = await cachedOracleEndpoint.getOracleEndpointByTypeAndCurrency(type, requestPayload.currency) + oracleEndpointModel = await oracleEndpointCached.getOracleEndpointByTypeAndCurrency(type, requestPayload.currency) } else { - oracleEndpointModel = await cachedOracleEndpoint.getOracleEndpointByType(type) + oracleEndpointModel = await oracleEndpointCached.getOracleEndpointByType(type) } if (oracleEndpointModel.length > 0) { if (oracleEndpointModel.length > 1) { @@ -318,7 +321,15 @@ exports.oracleBatchRequest = async (headers, method, requestPayload, type, paylo url = oracleEndpointModel[0].value + Enums.EndPoints.FspEndpointTemplates.ORACLE_PARTICIPANTS_BATCH } Logger.isDebugEnabled && Logger.debug(`Oracle endpoints: ${url}`) - return await request.sendRequest(url, headers, headers[Enums.Http.Headers.FSPIOP.SOURCE], headers[Enums.Http.Headers.FSPIOP.DESTINATION] || Enums.Http.Headers.FSPIOP.SWITCH.value, method, payload || undefined) + return await request.sendRequest({ + url, + headers, + source: headers[Enums.Http.Headers.FSPIOP.SOURCE], + destination: headers[Enums.Http.Headers.FSPIOP.DESTINATION] || Config.HUB_NAME, + method, + payload, + hubNameRegex + }) } else { Logger.isErrorEnabled && Logger.error(`Oracle type:${type} not found`) throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ADD_PARTY_INFO_ERROR, `Oracle type:${type} not found`) diff --git a/src/models/participantEndpoint/facade.js b/src/models/participantEndpoint/facade.js index 33283191..e84806a3 100644 --- a/src/models/participantEndpoint/facade.js +++ b/src/models/participantEndpoint/facade.js @@ -33,6 +33,7 @@ const ErrorHandler = require('@mojaloop/central-services-error-handling') const JwsSigner = require('@mojaloop/sdk-standard-components').Jws.signer const Metrics = require('@mojaloop/central-services-metrics') const Config = require('../../lib/config') +const { hubNameRegex } = require('../../lib/util').hubNameConfig const uriRegex = /(?:^.*)(\/(participants|parties|quotes|transfers)(\/.*)*)$/ /** @@ -85,7 +86,7 @@ exports.sendRequest = async (headers, requestedParticipant, endpointType, method Logger.isDebugEnabled && Logger.debug(`participant endpoint url: ${requestedEndpoint} for endpoint type ${endpointType}`) } catch (err) { histTimerEndGetParticipantEndpoint({ success: false, endpointType, participantName: requestedParticipant }) - Logger.isErrorEnabled && Logger.error(err) + Logger.isErrorEnabled && Logger.error(`error in getEndpoint: ${err?.stack}`) throw ErrorHandler.Factory.reformatFSPIOPError(err) } @@ -103,13 +104,25 @@ exports.sendRequest = async (headers, requestedParticipant, endpointType, method } const jwsSigner = defineJwsSigner(Config, headers, requestedEndpoint) - const resp = await Util.Request.sendRequest(requestedEndpoint, headers, headers[Enums.Http.Headers.FSPIOP.SOURCE], - headers[Enums.Http.Headers.FSPIOP.DESTINATION], method, payload, Enums.Http.ResponseTypes.JSON, span, jwsSigner, protocolVersions) + const resp = await Util.Request.sendRequest({ + url: requestedEndpoint, + headers, + source: headers[Enums.Http.Headers.FSPIOP.SOURCE], + destination: headers[Enums.Http.Headers.FSPIOP.DESTINATION], + method, + payload, + responseType: Enums.Http.ResponseTypes.JSON, + span, + jwsSigner, + protocolVersions, + hubNameRegex + }) histTimerEndSendRequestToParticipant({ success: true, endpointType, participantName: requestedParticipant }) return resp } catch (err) { histTimerEndSendRequestToParticipant({ success: false, endpointType, participantName: requestedParticipant }) - Logger.isErrorEnabled && Logger.error(err) + Logger.isErrorEnabled && Logger.error(`error in sendRequest: ${err?.stack}`) + throw ErrorHandler.Factory.reformatFSPIOPError(err) } } @@ -120,10 +133,9 @@ exports.sendRequest = async (headers, requestedParticipant, endpointType, method * @description sends a request to central-ledger to retrieve participant details and validate that they exist within the switch * * @param {string} fsp The FSPIOP-Source fsp id - * @param {object} span * @returns the participants info in a successful case and */ -exports.validateParticipant = async (fsp, span = undefined) => { +exports.validateParticipant = async (fsp) => { const histTimerEnd = Metrics.getHistogram( 'egress_validateParticipant', 'Egress: Validate participant', @@ -135,7 +147,7 @@ exports.validateParticipant = async (fsp, span = undefined) => { return resp } catch (err) { histTimerEnd({ success: false }) - Logger.isErrorEnabled && Logger.error(err) + Logger.isErrorEnabled && Logger.error(`error in validateParticipant: ${err?.stack}`) throw ErrorHandler.Factory.reformatFSPIOPError(err) } } @@ -164,20 +176,18 @@ exports.sendErrorToParticipant = async (participantName, endpointType, errorInfo ['success', 'endpointType', 'participantName'] ).startTimer() try { - let requestIdExists = false - if (payload && payload.requestId) { - requestIdExists = true - } + const { requestId } = payload || {} + requesterErrorEndpoint = await Util.Endpoints.getEndpoint(Config.SWITCH_ENDPOINT, participantName, endpointType, { partyIdType: params.Type || undefined, partyIdentifier: params.ID || undefined, partySubIdOrType: params.SubId || undefined, - requestId: requestIdExists ? payload.requestId : undefined + requestId }) histTimerEndGetParticipantEndpoint({ success: true, endpointType, participantName }) } catch (err) { histTimerEndGetParticipantEndpoint({ success: false, endpointType, participantName }) - Logger.isErrorEnabled && Logger.error(err) + Logger.isWarnEnabled && Logger.warn(`error in getEndpoint: ${err?.message}`) throw ErrorHandler.Factory.reformatFSPIOPError(err) } @@ -198,18 +208,29 @@ exports.sendErrorToParticipant = async (participantName, endpointType, errorInfo if (!clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION] || clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION] === '') { clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION] = clonedHeaders[Enums.Http.Headers.FSPIOP.SOURCE] - clonedHeaders[Enums.Http.Headers.FSPIOP.SOURCE] = Enums.Http.Headers.FSPIOP.SWITCH.value + clonedHeaders[Enums.Http.Headers.FSPIOP.SOURCE] = Config.HUB_NAME } Logger.isDebugEnabled && Logger.debug(`participant endpoint url: ${requesterErrorEndpoint} for endpoint type ${endpointType}`) const jwsSigner = defineJwsSigner(Config, clonedHeaders, requesterErrorEndpoint) - await Util.Request.sendRequest(requesterErrorEndpoint, clonedHeaders, clonedHeaders[Enums.Http.Headers.FSPIOP.SOURCE], - clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION], Enums.Http.RestMethods.PUT, errorInformation, Enums.Http.ResponseTypes.JSON, span, jwsSigner, protocolVersions) + await Util.Request.sendRequest({ + url: requesterErrorEndpoint, + headers: clonedHeaders, + source: clonedHeaders[Enums.Http.Headers.FSPIOP.SOURCE], + destination: clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION], + method: Enums.Http.RestMethods.PUT, + payload: errorInformation, + responseType: Enums.Http.ResponseTypes.JSON, + hubNameRegex, + span, + jwsSigner, + protocolVersions + }) histTimerEndSendRequestToParticipant({ success: true, endpointType, participantName }) } catch (err) { histTimerEndSendRequestToParticipant({ success: false, endpointType, participantName }) - Logger.isErrorEnabled && Logger.error(err) + Logger.isWarnEnabled && Logger.warn(`error in sendErrorToParticipant: ${err?.message}`) throw ErrorHandler.Factory.reformatFSPIOPError(err) } } diff --git a/src/server.js b/src/server.js index 9ec5d6e3..8b7c2796 100644 --- a/src/server.js +++ b/src/server.js @@ -23,32 +23,43 @@ ******/ 'use strict' +const { randomUUID } = require('node:crypto') const Hapi = require('@hapi/hapi') const Boom = require('@hapi/boom') -const Uuid = require('uuid4') -const ParticipantEndpointCache = require('@mojaloop/central-services-shared').Util.Endpoints -const ParticipantCache = require('@mojaloop/central-services-shared').Util.Participants -const OpenapiBackend = require('@mojaloop/central-services-shared').Util.OpenapiBackend + const ErrorHandler = require('@mojaloop/central-services-error-handling') const Logger = require('@mojaloop/central-services-logger') const Metrics = require('@mojaloop/central-services-metrics') +const { Endpoints, Participants, proxies, OpenapiBackend } = require('@mojaloop/central-services-shared').Util +const { createProxyCache } = require('@mojaloop/inter-scheme-proxy-cache-lib') + +const { name, version } = require('../package.json') const Db = require('./lib/db') -const Config = require('./lib/config.js') const Util = require('./lib/util') const Plugins = require('./plugins') const RequestLogger = require('./lib/requestLogger') const Migrator = require('./lib/migrator') -const Handlers = require('./handlers') -const Routes = require('./handlers/routes') +const APIHandlers = require('./api') +const Routes = require('./api/routes') const Cache = require('./lib/cache') const OracleEndpointCache = require('./models/oracle/oracleEndpointCached') +const Handlers = require('./handlers/register') -const connectDatabase = async () => { - return Db.connect(Config.DATABASE) +const connectDatabase = async (dbConfig) => { + return Db.connect(dbConfig) } const migrate = async () => { - return Config.RUN_MIGRATIONS ? Migrator.migrate() : {} + return Migrator.migrate() +} + +const createConnectedProxyCache = async (proxyCacheConfig) => { + const proxyCache = createProxyCache( + proxyCacheConfig.type, + proxyCacheConfig.proxyConfig + ) + await proxyCache.connect() + return proxyCache } /** @@ -61,7 +72,7 @@ const migrate = async () => { * @param {array} routes array of API routes * @returns {Promise} Returns the Server object */ -const createServer = async (port, api, routes, isAdmin) => { +const createServer = async (port, api, routes, isAdmin, proxyCacheConfig, proxyMap) => { const server = await new Hapi.Server({ port, routes: { @@ -77,16 +88,27 @@ const createServer = async (port, api, routes, isAdmin) => { } } }) + server.app.isAdmin = isAdmin + server.app.cache = Cache.registerCacheClient({ id: 'serverGeneralCache', preloadCache: async () => Promise.resolve() }) - await Plugins.registerPlugins(server, api, isAdmin) + + if (!isAdmin && proxyCacheConfig.enabled) { + server.app.proxyCache = await createConnectedProxyCache(proxyCacheConfig) + if (proxyMap) { + for (const [dfspId, proxyId] of Object.entries(proxyMap)) { + await server.app.proxyCache.addDfspIdToProxyMapping(dfspId, proxyId) + } + } + } + await server.ext([ { - type: 'onPostAuth', + type: 'onPreHandler', method: (request, h) => { - request.headers.traceid = request.headers.traceid || Uuid() + request.headers.traceid = request.headers.traceid || randomUUID() RequestLogger.logRequest(request) return h.continue } @@ -99,45 +121,79 @@ const createServer = async (port, api, routes, isAdmin) => { } } ]) + await Plugins.registerPlugins(server, api, isAdmin) server.route(routes) // TODO: follow instructions https://github.com/anttiviljami/openapi-backend/blob/master/DOCS.md#postresponsehandler-handler await server.start() + + Logger.isInfoEnabled && Logger.info(`${name}${isAdmin ? '-admin' : ''}@${version} is running on port ${server.info.port}...`) return server } -const initializeInstrumentation = () => { - if (!Config.INSTRUMENTATION_METRICS_DISABLED) { - Metrics.setup(Config.INSTRUMENTATION_METRICS_CONFIG) - } +const initializeInstrumentation = (metricsConfig) => { + Metrics.setup(metricsConfig) } -const initializeApi = async (port = Config.API_PORT) => { - initializeInstrumentation() - await connectDatabase() +const initializeApi = async (appConfig) => { + const { + INSTRUMENTATION_METRICS_DISABLED, + INSTRUMENTATION_METRICS_CONFIG, + CENTRAL_SHARED_ENDPOINT_CACHE_CONFIG, + CENTRAL_SHARED_PARTICIPANT_CACHE_CONFIG, + DATABASE, + API_PORT, + PROXY_CACHE_CONFIG, + proxyMap + } = appConfig + + if (!INSTRUMENTATION_METRICS_DISABLED) { + initializeInstrumentation(INSTRUMENTATION_METRICS_CONFIG) + } + await connectDatabase(DATABASE) const OpenAPISpecPath = Util.pathForInterface({ isAdmin: false, isMockInterface: false }) - const api = await OpenapiBackend.initialise(OpenAPISpecPath, Handlers.ApiHandlers) - const server = await createServer(port, api, Routes.APIRoutes(api), false) - Logger.isInfoEnabled && Logger.info(`Server running on ${server.info.host}:${server.info.port}`) - await ParticipantEndpointCache.initializeCache(Config.CENTRAL_SHARED_ENDPOINT_CACHE_CONFIG) - await ParticipantCache.initializeCache(Config.CENTRAL_SHARED_PARTICIPANT_CACHE_CONFIG) - await OracleEndpointCache.initialize() - await Cache.initCache() - return server + const api = await OpenapiBackend.initialise(OpenAPISpecPath, APIHandlers.ApiHandlers) + + await Promise.all([ + Endpoints.initializeCache(CENTRAL_SHARED_ENDPOINT_CACHE_CONFIG, Util.hubNameConfig), + Participants.initializeCache(CENTRAL_SHARED_PARTICIPANT_CACHE_CONFIG, Util.hubNameConfig), + proxies.initializeCache(CENTRAL_SHARED_PARTICIPANT_CACHE_CONFIG, Util.hubNameConfig), + OracleEndpointCache.initialize(), + Cache.initCache() + ]) + + return createServer(API_PORT, api, Routes.APIRoutes(api), false, PROXY_CACHE_CONFIG, proxyMap) } -const initializeAdmin = async (port = Config.ADMIN_PORT) => { - initializeInstrumentation() - await connectDatabase() - await migrate() +const initializeAdmin = async (appConfig) => { + const { + INSTRUMENTATION_METRICS_DISABLED, + INSTRUMENTATION_METRICS_CONFIG, + DATABASE, + RUN_MIGRATIONS, + ADMIN_PORT, + PROXY_CACHE_CONFIG + } = appConfig + + if (!INSTRUMENTATION_METRICS_DISABLED) { + initializeInstrumentation(INSTRUMENTATION_METRICS_CONFIG) + } + await connectDatabase(DATABASE) + RUN_MIGRATIONS && await migrate() const OpenAPISpecPath = Util.pathForInterface({ isAdmin: true, isMockInterface: false }) - const api = await OpenapiBackend.initialise(OpenAPISpecPath, Handlers.AdminHandlers) - const server = await createServer(port, api, Routes.AdminRoutes(api), true) - Logger.isInfoEnabled && Logger.info(`Server running on ${server.info.host}:${server.info.port}`) - return server + const api = await OpenapiBackend.initialise(OpenAPISpecPath, APIHandlers.AdminHandlers) + + return createServer(ADMIN_PORT, api, Routes.AdminRoutes(api), true, PROXY_CACHE_CONFIG) +} + +const initializeHandlers = async (handlers, appConfig, logger) => { + const proxyCache = await createConnectedProxyCache(appConfig.PROXY_CACHE_CONFIG) + const options = { proxyCache, batchSize: appConfig.HANDLERS_TIMEOUT_BATCH_SIZE, logger } + await Handlers.registerHandlers(handlers, options) } module.exports = { initializeApi, - initializeAdmin + initializeAdmin, + initializeHandlers } diff --git a/test/fixtures.js b/test/fixtures.js new file mode 100644 index 00000000..d53e1fa9 --- /dev/null +++ b/test/fixtures.js @@ -0,0 +1,129 @@ +const { randomUUID } = require('node:crypto') +const { Enum } = require('@mojaloop/central-services-shared') + +const { Headers } = Enum.Http + +const headersDto = ({ + source = 'fromDfsp', + destination = 'toDfsp', + proxy = '', + date = '2024-05-24 08:52:19', + accept +} = {}) => Object.freeze({ + [Headers.FSPIOP.SOURCE]: source, + ...(destination && { [Headers.FSPIOP.DESTINATION]: destination }), + ...(proxy && { [Headers.FSPIOP.PROXY]: proxy }), + date, + accept, + 'content-type': accept +}) + +const protocolVersionsDto = () => ({ + CONTENT: { + DEFAULT: '2.1', + VALIDATELIST: ['2.1'] + }, + ACCEPT: { + DEFAULT: '2', + VALIDATELIST: ['2', '2.1'] + } +}) + +const partiesCallHeadersDto = ({ + source, + destination, + proxy, + date +} = {}) => headersDto({ + source, + destination, + proxy, + date, + accept: 'application/vnd.interoperability.parties+json;version=1.1' +}) + +const participantsCallHeadersDto = ({ + source, + destination, + proxy, + date +} = {}) => headersDto({ + source, + destination, + proxy, + date, + accept: 'application/vnd.interoperability.participants+json;version=1' +}) + +const oracleRequestResponseDto = ({ + partyList = [{ fspId: 'dfspFromOracle' }] +} = {}) => ({ + data: { + partyList + } +}) + +const putPartiesSuccessResponseDto = ({ + partyIdType = 'MSISDN', + partyId = `test-party-${randomUUID()}`, + fspId = `fspId-${randomUUID()}`, + partySubIdOrType = '' +} = {}) => ({ + party: { + partyIdInfo: { + partyIdType, + partyIdentifier: partyId, + ...(partySubIdOrType && { partySubIdOrType }), + ...(fspId && { fspId }) + }, + merchantClassificationCode: '32', + name: `testPartyName-${partyId}` + // personalInfo: { ... } + } +}) + +const errorCallbackResponseDto = ({ + errorCode = '1234', + errorDescription = 'Error description', + extension = [{ + key: 'k1', value: 'v1' + }] +} = {}) => ({ + errorInformation: { + errorCode, + errorDescription, + ...(extension && { + extensionList: { extension } + }) + } +}) + +const mockAlsRequestDto = (sourceId, type, partyId) => ({ + sourceId, + type, + partyId +}) + +const mockHapiRequestDto = ({ // https://hapi.dev/api/?v=21.3.3#request-properties + method = 'GET', + traceid = randomUUID(), + id = randomUUID() +} = {}) => ({ + method, + headers: { traceid }, + info: { + id, + received: 123456789 + } +}) + +module.exports = { + partiesCallHeadersDto, + participantsCallHeadersDto, + oracleRequestResponseDto, + putPartiesSuccessResponseDto, + errorCallbackResponseDto, + mockAlsRequestDto, + protocolVersionsDto, + mockHapiRequestDto +} diff --git a/test/integration-config.json b/test/integration-config.json index 2fa2049e..f5b41c3a 100644 --- a/test/integration-config.json +++ b/test/integration-config.json @@ -1,4 +1,8 @@ { + "HUB_PARTICIPANT": { + "ID": 1, + "NAME": "Hub" + }, "TEST_ALS_HOST":"account-lookup-service", "ADMIN_PORT": 4001, "API_PORT": 4002, @@ -42,6 +46,15 @@ "expiresIn": 180000, "generateTimeout": 30000 }, + "PROXY_CACHE": { + "enabled": true, + "type": "redis-cluster", + "proxyConfig": { + "cluster": [ + { "host": "redis-node-0", "port": 6379 } + ] + } + }, "SWITCH_ENDPOINT": "http://localhost:3001", "INSTRUMENTATION": { "METRICS": { @@ -61,7 +74,6 @@ "ENDPOINT_SECURITY":{ "JWS": { "JWS_SIGN": false, - "FSPIOP_SOURCE_TO_SIGN": "switch", "JWS_SIGNING_KEY_PATH": "secrets/jwsSigningKey.key" } }, diff --git a/test/integration/.env b/test/integration/.env new file mode 100644 index 00000000..8d2de83f --- /dev/null +++ b/test/integration/.env @@ -0,0 +1,8 @@ +# PROXY_HOST=localhost +PROXY_HOST=proxy +PROXY_PORT=4300 +PROXY_NAME=proxyXY + +# CL_HOST=localhost +CL_HOST=central-ledger +CL_PORT=3001 diff --git a/test/integration/api/parties.test.js b/test/integration/api/parties.test.js new file mode 100644 index 00000000..2304e45d --- /dev/null +++ b/test/integration/api/parties.test.js @@ -0,0 +1,137 @@ +/***** + 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 + + * Eugen Klymniuk + -------------- + **********/ + +const { setTimeout: sleep } = require('node:timers/promises') +const { randomUUID } = require('node:crypto') +const { createProxyCache } = require('@mojaloop/inter-scheme-proxy-cache-lib') + +const config = require('../../../src/lib/config') +const fixtures = require('../../fixtures') +const { AlsApiClient, ProxyApiClient } = require('../../util') +const { PAYER_DFSP, PARTY_ID_TYPE } = require('../constants') + +const alsClient = new AlsApiClient() +const proxyClient = new ProxyApiClient() // mock ISPA + +describe('Parties Endpoints Tests -->', () => { + const { type, proxyConfig } = config.PROXY_CACHE_CONFIG + const proxyCache = createProxyCache(type, proxyConfig) + + beforeAll(async () => { + await proxyCache.connect() + }) + + afterAll(async () => { + await proxyCache.disconnect() + }) + + describe('GET /parties... endpoints tests -->', () => { + test('should do GET /parties/{Type}/{ID} call to proxy', async () => { + const partyId = 'PT123456789' + const alsReq = fixtures.mockAlsRequestDto(PAYER_DFSP, PARTY_ID_TYPE, partyId) + let isExists = await proxyCache.receivedSuccessResponse(alsReq) + expect(isExists).toBe(false) + + let history = await proxyClient.deleteHistory() + expect(history).toEqual([]) + + const result = await alsClient.getPartyByIdAndType({ + partyId, + partyIdType: PARTY_ID_TYPE, + source: PAYER_DFSP, + destination: '' + }) + expect(result.status).toBe(202) + + await sleep(1000) + history = await proxyClient.getHistory() + expect(history.length).toBe(2) + expect(history[0].path).toBe(`/oracle/participants/${PARTY_ID_TYPE}/${partyId}`) + expect(history[1].path).toBe(`/parties/${PARTY_ID_TYPE}/${partyId}`) + + isExists = await proxyCache.receivedSuccessResponse(alsReq) + expect(isExists).toBe(true) + }) + + test('should handle PUT /parties/{Type}/{ID}/error without accept-header', async () => { + const partyId = `PT-${Date.now()}` + const body = fixtures.errorCallbackResponseDto() + + const { accept, ...headers } = fixtures.partiesCallHeadersDto({ proxy: 'proxyAB' }) + expect(headers.accept).toBeUndefined() + + const result = await alsClient.putPartiesError({ + partyId, + partyIdType: PARTY_ID_TYPE, + body, + headers + }) + expect(result.status).toBe(200) + }) + }) + + describe('PUT /parties... endpoints tests -->', () => { + const generatePutPartiesTestDataDto = ({ + partyId = `party-${randomUUID()}`, + partyIdType = PARTY_ID_TYPE, + source = `sourceFsp-${Date.now()}`, + destination = PAYER_DFSP, + proxy = `proxy-${randomUUID()}` + } = {}) => ({ + partyId, + partyIdType, + source, + destination, + proxy, + body: fixtures.putPartiesSuccessResponseDto({ partyId, partyIdType, fspId: source }) + }) + + test('should add dfspIdToProxyMapping after callback response from proxy', async () => { + const testData = generatePutPartiesTestDataDto() + + let cached = await proxyCache.lookupProxyByDfspId(testData.source) + expect(cached).toBeNull() + + await alsClient.putPartiesSuccess(testData) + await sleep(1000) + + cached = await proxyCache.lookupProxyByDfspId(testData.source) + expect(cached).toBe(testData.proxy) + }) + + test('should update oracle with party details from proxy callback response', async () => { + const testData = generatePutPartiesTestDataDto() + let history = await proxyClient.deleteHistory() + expect(history).toEqual([]) + + const result = await alsClient.putPartiesSuccess(testData) + expect(result.status).toBe(200) + await sleep(1000) + + history = await proxyClient.getHistory() + expect(history.length).toBeGreaterThan(0) + }) + }) +}) diff --git a/test/integration/constants.js b/test/integration/constants.js new file mode 100644 index 00000000..bfe429c5 --- /dev/null +++ b/test/integration/constants.js @@ -0,0 +1,18 @@ +const { + PROXY_HOST, + PROXY_PORT, + PROXY_NAME, + CL_PORT +} = process.env + +const PARTY_ID_TYPE = 'IBAN' // for proxy testing +const PAYER_DFSP = 'testPayerDfsp' + +module.exports = { + PROXY_HOST, + PROXY_PORT, + PROXY_NAME, + CL_PORT, + PARTY_ID_TYPE, + PAYER_DFSP +} diff --git a/test/integration/domain/timeout/index.test.js b/test/integration/domain/timeout/index.test.js new file mode 100644 index 00000000..f052a2a5 --- /dev/null +++ b/test/integration/domain/timeout/index.test.js @@ -0,0 +1,70 @@ +const { createProxyCache } = require('@mojaloop/inter-scheme-proxy-cache-lib') +const { PAYER_DFSP, PARTY_ID_TYPE, PROXY_NAME } = require('../../constants') +const fixtures = require('../../../fixtures') +const { ProxyApiClient } = require('../../../util') +const config = require('../../../../src/lib/config') + +const wait = (ms) => new Promise(resolve => setTimeout(resolve, ms)) + +describe('Timeout Handler', () => { + const { type, proxyConfig } = config.PROXY_CACHE_CONFIG + const proxyCache = createProxyCache(type, proxyConfig) + const proxyClient = new ProxyApiClient() + + beforeAll(async () => { + await proxyCache.connect() + const redisClient = proxyCache.redisClient + const redisNodes = redisClient.nodes ? redisClient.nodes('master') : [redisClient] + await Promise.all(redisNodes.map(async node => { + await node.flushall() + })) + }) + + afterAll(async () => { + return Promise.all([ + proxyClient.deleteHistory(), + proxyCache.disconnect() + ]) + }) + + it('test', async () => { + let history = await proxyClient.deleteHistory() + expect(history).toEqual([]) + + // send a couple of keys to redis + const partyIds = ['1234567', '7654321'] + const keys = [ + `'als:${PAYER_DFSP}:${PARTY_ID_TYPE}:${partyIds[0]}:expiresAt'`, + `'als:${PAYER_DFSP}:${PARTY_ID_TYPE}:${partyIds[1]}:expiresAt'` + ] + const proxies = [PROXY_NAME] + const alsReq1 = fixtures.mockAlsRequestDto(PAYER_DFSP, PARTY_ID_TYPE, partyIds[0]) + const alsReq2 = fixtures.mockAlsRequestDto(PAYER_DFSP, PARTY_ID_TYPE, partyIds[1]) + const results = await Promise.all([ + proxyCache.setSendToProxiesList(alsReq1, proxies), + proxyCache.setSendToProxiesList(alsReq2, proxies) + ]) + expect(results.includes(false)).toBe(false) + + // wait for the timeout handler to process the keys + await wait(35_000) + + // check that the keys are no longer in redis + const exists = await Promise.all(keys.map(key => proxyCache.redisClient.exists(key))) + expect(exists.includes(1)).toBe(false) + + // check that the callbacks are sent and received at the FSP + // for test resilience, we will retry the history check a few times + let retryCount = 0; const retryMaxCount = 10; const retryInterval = 2000 + while (history.length < 2 && retryCount < retryMaxCount) { + await wait(retryInterval) + history = await proxyClient.getHistory() + retryCount++ + } + expect(history.length).toBe(2) + const path0 = history.find(h => h.path.includes(partyIds[0])).path + const path1 = history.find(h => h.path.includes(partyIds[1])).path + expect(path0).toBe(`/parties/${PARTY_ID_TYPE}/${partyIds[0]}/error`) + expect(path1).toBe(`/parties/${PARTY_ID_TYPE}/${partyIds[1]}/error`) + }, 60_000) +}) diff --git a/test/integration/env.sh b/test/integration/env.sh new file mode 100755 index 00000000..d3e0da0e --- /dev/null +++ b/test/integration/env.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +# Retrieve the external IP address of the host machine (on macOS) +# or the IP address of the docker0 interface (on Linux) +get_external_ip() { + if [ "$(uname)" = "Linux" ]; then + echo "$(ip addr show docker0 | grep 'inet ' | awk '{print $2}' | cut -d/ -f1)" + else + # Need to find a way to support Windows here + echo "$(route get ifconfig.me | grep interface | sed -e 's/.*: //' | xargs ipconfig getifaddr)" + fi +} + +# set/override dynamic variables +export REDIS_CLUSTER_ANNOUNCE_IP=$(get_external_ip) diff --git a/test/integration/prepareTestParticipants.js b/test/integration/prepareTestParticipants.js new file mode 100644 index 00000000..effef3e7 --- /dev/null +++ b/test/integration/prepareTestParticipants.js @@ -0,0 +1,30 @@ +require('./setup') + +const Logger = require('@mojaloop/central-services-logger') +const { onboarding } = require('../util') +const { PROXY_NAME, PAYER_DFSP } = require('../integration/constants') + +const pause = async (ms = 1000) => new Promise(resolve => { + Logger.info(`pause for ${ms / 1000} sec....`) + setTimeout(resolve, ms) +}) + +const prepareTestParticipants = async () => { + await pause(10_000) // sometimes on CircleCI env we have error: socket hang up + await onboarding.createHubAccounts() + await pause() + + await onboarding.createTestParticipant({ name: PAYER_DFSP }) + await onboarding.createTestParticipant({ + name: PROXY_NAME, + isProxy: true + }) + + await onboarding.createOracle() + Logger.info('prepareTestParticipants is finished') +} + +prepareTestParticipants().catch(err => { + Logger.error(err) + throw err +}) diff --git a/test/integration/setup.js b/test/integration/setup.js new file mode 100644 index 00000000..07ed69a7 --- /dev/null +++ b/test/integration/setup.js @@ -0,0 +1,5 @@ +const path = require('node:path') + +require('dotenv').config({ + path: path.join(__dirname, '.env') +}) diff --git a/test/unit/handlers/health.test.js b/test/unit/api/health.test.js similarity index 69% rename from test/unit/handlers/health.test.js rename to test/unit/api/health.test.js index e3018dc0..0ca57c6e 100644 --- a/test/unit/handlers/health.test.js +++ b/test/unit/api/health.test.js @@ -37,6 +37,7 @@ const getPort = require('get-port') const Sinon = require('sinon') const MigrationLockModel = require('../../../src/models/misc/migrationLock') const Logger = require('@mojaloop/central-services-logger') +const Config = require('../../../src/lib/config') Logger.isDebugEnabled = jest.fn(() => true) Logger.isErrorEnabled = jest.fn(() => true) @@ -46,9 +47,11 @@ let server describe('/health', () => { beforeEach(async () => { + Config.PROXY_CACHE_CONFIG.enabled = false sandbox = Sinon.createSandbox() sandbox.stub(Db, 'connect').returns(Promise.resolve({})) - server = await initServer(await getPort()) + Config.API_PORT = await getPort() + server = await initServer(Config) }) afterEach(async () => { @@ -79,6 +82,10 @@ describe('/health', () => { // Assert expect(response.statusCode).toBe(200) + const payload = JSON.parse(response.payload) + expect(payload.status).toBe('OK') + expect(payload.services.length).toBe(1) + expect(payload.services[0].name).toBe('datastore') }) it('GET /health service unavailable', async () => { @@ -97,5 +104,39 @@ describe('/health', () => { // Assert expect(response.statusCode).toBe(503) + const payload = JSON.parse(response.payload) + expect(payload.status).toBe('DOWN') + expect(payload.services.length).toBe(1) + expect(payload.services[0].name).toBe('datastore') + }) + + it('GET /health should include proxy health', async () => { + // Arrange + Config.PROXY_CACHE_CONFIG.enabled = true + Config.API_PORT = await getPort() + Config.proxyMap = { proxied: 'proxy' } + let serverWithProxy + try { + serverWithProxy = await initServer(Config) + sandbox.stub(MigrationLockModel, 'getIsMigrationLocked').resolves(false) + const mock = await Helper.generateMockRequest('/health', 'get') + + const options = { + method: 'get', + url: mock.request.path, + headers: Helper.defaultAdminHeaders() + } + + // Act + const response = await serverWithProxy.inject(options) + + // Assert + const payload = JSON.parse(response.payload) + expect(response.statusCode).toBe(200) + expect(payload.services.length).toBe(2) + expect(payload.services[1].name).toBe('proxyCache') + } finally { + serverWithProxy && await serverWithProxy.stop() + } }) }) diff --git a/test/unit/handlers/oracles.test.js b/test/unit/api/oracles.test.js similarity index 97% rename from test/unit/handlers/oracles.test.js rename to test/unit/api/oracles.test.js index cca0e3cf..db95106f 100644 --- a/test/unit/handlers/oracles.test.js +++ b/test/unit/api/oracles.test.js @@ -29,14 +29,15 @@ 'use strict' -const oracle = require('../../../src/domain/oracle') const Sinon = require('sinon') +const getPort = require('get-port') +const Logger = require('@mojaloop/central-services-logger') +const oracle = require('../../../src/domain/oracle') const Helper = require('../../util/helper') const initServer = require('../../../src/server').initializeAdmin const Db = require('../../../src/lib/db') -const getPort = require('get-port') const Migrator = require('../../../src/lib/migrator') -const Logger = require('@mojaloop/central-services-logger') +const Config = require('../../../src/lib/config') Logger.isDebugEnabled = jest.fn(() => true) Logger.isErrorEnabled = jest.fn(() => true) @@ -60,7 +61,8 @@ describe('/oracles', () => { sandbox = Sinon.createSandbox() sandbox.stub(Db, 'connect').returns(Promise.resolve({})) sandbox.stub(Migrator, 'migrate').returns(Promise.resolve({})) - server = await initServer(await getPort(), false) + Config.ADMIN_PORT = await getPort() + server = await initServer(Config) }) afterAll(async () => { diff --git a/test/unit/handlers/oracles/{ID}.test.js b/test/unit/api/oracles/{ID}.test.js similarity index 95% rename from test/unit/handlers/oracles/{ID}.test.js rename to test/unit/api/oracles/{ID}.test.js index 997ca0a1..c61320a8 100644 --- a/test/unit/handlers/oracles/{ID}.test.js +++ b/test/unit/api/oracles/{ID}.test.js @@ -30,14 +30,15 @@ 'use strict' const Mockgen = require('../../../util/mockgen.js') -const helper = require('../../../util/helper') +const helper = require('../../../util/helper.js') const Sinon = require('sinon') -const oracle = require('../../../../src/domain/oracle') -const initServer = require('../../../../src/server').initializeAdmin +const oracle = require('../../../../src/domain/oracle/index.js') +const initServer = require('../../../../src/server.js').initializeAdmin const getPort = require('get-port') -const Db = require('../../../../src/lib/db') -const Migrator = require('../../../../src/lib/migrator') +const Db = require('../../../../src/lib/db.js') +const Migrator = require('../../../../src/lib/migrator.js') const Logger = require('@mojaloop/central-services-logger') +const Config = require('../../../../src/lib/config.js') Logger.isDebugEnabled = jest.fn(() => true) Logger.isErrorEnabled = jest.fn(() => true) @@ -50,7 +51,8 @@ describe('/oracles/{ID} handler', () => { sandbox = Sinon.createSandbox() sandbox.stub(Db, 'connect').returns(Promise.resolve({})) sandbox.stub(Migrator, 'migrate').returns(Promise.resolve({})) - server = await initServer(await getPort(), false) + Config.ADMIN_PORT = await getPort() + server = await initServer(Config) }) afterAll(async () => { diff --git a/test/unit/handlers/participants.test.js b/test/unit/api/participants.test.js similarity index 96% rename from test/unit/handlers/participants.test.js rename to test/unit/api/participants.test.js index d137602b..a945ef25 100644 --- a/test/unit/handlers/participants.test.js +++ b/test/unit/api/participants.test.js @@ -30,11 +30,12 @@ 'use strict' const Sinon = require('sinon') +const getPort = require('get-port') +const Logger = require('@mojaloop/central-services-logger') const initServer = require('../../../src/server').initializeApi const Helper = require('../../util/helper') const Db = require('../../../src/lib/db') -const getPort = require('get-port') -const Logger = require('@mojaloop/central-services-logger') +const Config = require('../../../src/lib/config') Logger.isDebugEnabled = jest.fn(() => true) Logger.isErrorEnabled = jest.fn(() => true) @@ -46,7 +47,8 @@ describe('/participants', () => { beforeEach(async () => { sandbox = Sinon.createSandbox() sandbox.stub(Db, 'connect').returns(Promise.resolve({})) - server = await initServer(await getPort()) + Config.API_PORT = await getPort() + server = await initServer(Config) }) afterEach(async () => { diff --git a/test/unit/handlers/participants/participants.test.js b/test/unit/api/participants/participants.test.js similarity index 96% rename from test/unit/handlers/participants/participants.test.js rename to test/unit/api/participants/participants.test.js index 2dc29b81..e0dc6a4b 100644 --- a/test/unit/handlers/participants/participants.test.js +++ b/test/unit/api/participants/participants.test.js @@ -36,6 +36,7 @@ const participants = require('../../../../src/domain/participants') const initServer = require('../../../../src/server').initializeApi const getPort = require('get-port') const Logger = require('@mojaloop/central-services-logger') +const Config = require('../../../../src/lib/config') Logger.isDebugEnabled = jest.fn(() => true) Logger.isErrorEnabled = jest.fn(() => true) @@ -47,7 +48,8 @@ describe('/participants', () => { beforeAll(async () => { sandbox = Sinon.createSandbox() sandbox.stub(Db, 'connect').returns(Promise.resolve({})) - server = await initServer(await getPort()) + Config.API_PORT = await getPort() + server = await initServer(Config) }) afterAll(async () => { diff --git a/test/unit/handlers/participants/{Type}/{ID}.test.js b/test/unit/api/participants/{Type}/{ID}.test.js similarity index 99% rename from test/unit/handlers/participants/{Type}/{ID}.test.js rename to test/unit/api/participants/{Type}/{ID}.test.js index 8ca95639..9b8ccd6c 100644 --- a/test/unit/handlers/participants/{Type}/{ID}.test.js +++ b/test/unit/api/participants/{Type}/{ID}.test.js @@ -31,6 +31,11 @@ 'use strict' const Sinon = require('sinon') +const getPort = require('get-port') +const ErrorHandler = require('@mojaloop/central-services-error-handling') +const Logger = require('@mojaloop/central-services-logger') +const requestUtil = require('@mojaloop/central-services-shared').Util.Request +const Enums = require('@mojaloop/central-services-shared').Enum const Db = require('../../../../../src/lib/db') const oracleEndpointCached = require('../../../../../src/models/oracle/oracleEndpointCached') const participant = require('../../../../../src/models/participantEndpoint/facade') @@ -38,11 +43,7 @@ const participants = require('../../../../../src/domain/participants') const requestLogger = require('../../../../../src/lib/requestLogger') const Helper = require('../../../../util/helper') const initServer = require('../../../../../src/server').initializeApi -const getPort = require('get-port') -const ErrorHandler = require('@mojaloop/central-services-error-handling') -const Logger = require('@mojaloop/central-services-logger') -const requestUtil = require('@mojaloop/central-services-shared').Util.Request -const Enums = require('@mojaloop/central-services-shared').Enum +const Config = require('../../../../../src/lib/config') Logger.isDebugEnabled = jest.fn(() => true) Logger.isErrorEnabled = jest.fn(() => true) @@ -56,7 +57,8 @@ describe('/participants/{Type}/{ID}', () => { sandbox.stub(Db, 'connect').returns(Promise.resolve({})) sandbox.stub(requestLogger, 'logRequest').returns({}) sandbox.stub(requestLogger, 'logResponse').returns({}) - server = await initServer(await getPort()) + Config.API_PORT = await getPort() + server = await initServer(Config) sandbox.stub(Logger) Logger.error = sandbox.stub() }) diff --git a/test/unit/handlers/participants/{Type}/{ID}/error.test.js b/test/unit/api/participants/{Type}/{ID}/error.test.js similarity index 96% rename from test/unit/handlers/participants/{Type}/{ID}/error.test.js rename to test/unit/api/participants/{Type}/{ID}/error.test.js index 1e011722..2899e98a 100644 --- a/test/unit/handlers/participants/{Type}/{ID}/error.test.js +++ b/test/unit/api/participants/{Type}/{ID}/error.test.js @@ -34,10 +34,11 @@ const src = '../../../../../../src' const initServer = require(`${src}/server`).initializeApi const Db = require(`${src}/lib/db`) const participants = require(`${src}/domain/participants`) -const ErrHandler = require(`${src}/handlers/participants/{Type}/{ID}/error`) +const ErrHandler = require(`${src}/api/participants/{Type}/{ID}/error`) const Helper = require('../../../../../util/helper') const LibUtil = require(`${src}/lib/util`) const Logger = require('@mojaloop/central-services-logger') +const Config = require(`${src}/lib/config`) Logger.isDebugEnabled = jest.fn(() => true) Logger.isErrorEnabled = jest.fn(() => true) @@ -50,7 +51,8 @@ describe('/participants/{Type}/{ID}/error', () => { beforeAll(async () => { sandbox = Sinon.createSandbox() sandbox.stub(Db, 'connect').returns(Promise.resolve({})) - server = await initServer(await getPort()) + Config.API_PORT = await getPort() + server = await initServer(Config) sandbox.stub(LibUtil, 'getSpanTags').returns({}) }) diff --git a/test/unit/handlers/participants/{Type}/{ID}/{SubId}.test.js b/test/unit/api/participants/{Type}/{ID}/{SubId}.test.js similarity index 99% rename from test/unit/handlers/participants/{Type}/{ID}/{SubId}.test.js rename to test/unit/api/participants/{Type}/{ID}/{SubId}.test.js index 996ef960..c55b0d08 100644 --- a/test/unit/handlers/participants/{Type}/{ID}/{SubId}.test.js +++ b/test/unit/api/participants/{Type}/{ID}/{SubId}.test.js @@ -39,6 +39,7 @@ const requestLogger = require('../../../../../../src/lib/requestLogger') const Helper = require('../../../../../util/helper') const initServer = require('../../../../../../src/server').initializeApi const Logger = require('@mojaloop/central-services-logger') +const Config = require('../../../../../../src/lib/config') Logger.isDebugEnabled = jest.fn(() => true) Logger.isErrorEnabled = jest.fn(() => true) @@ -52,7 +53,8 @@ describe('/participants/{Type}/{ID}/{SubId}', () => { sandbox.stub(Db, 'connect').returns(Promise.resolve({})) sandbox.stub(requestLogger, 'logRequest').returns({}) sandbox.stub(requestLogger, 'logResponse').returns({}) - server = await initServer(await getPort()) + Config.API_PORT = await getPort() + server = await initServer(Config) }) afterAll(async () => { diff --git a/test/unit/handlers/participants/{Type}/{ID}/{SubId}/error.test.js b/test/unit/api/participants/{Type}/{ID}/{SubId}/error.test.js similarity index 95% rename from test/unit/handlers/participants/{Type}/{ID}/{SubId}/error.test.js rename to test/unit/api/participants/{Type}/{ID}/{SubId}/error.test.js index 0e91166d..1e53f82c 100644 --- a/test/unit/handlers/participants/{Type}/{ID}/{SubId}/error.test.js +++ b/test/unit/api/participants/{Type}/{ID}/{SubId}/error.test.js @@ -28,12 +28,13 @@ const Sinon = require('sinon') const getPort = require('get-port') +const Logger = require('@mojaloop/central-services-logger') const initServer = require('../../../../../../../src/server').initializeApi const Db = require('../../../../../../../src/lib/db') const participants = require('../../../../../../../src/domain/participants') -const ErrHandler = require('../../../../../../../src/handlers/participants/{Type}/{ID}/{SubId}/error') +const ErrHandler = require('../../../../../../../src/api/participants/{Type}/{ID}/{SubId}/error') const Helper = require('../../../../../../util/helper') -const Logger = require('@mojaloop/central-services-logger') +const Config = require('../../../../../../../src/lib/config') Logger.isDebugEnabled = jest.fn(() => true) Logger.isErrorEnabled = jest.fn(() => true) @@ -46,7 +47,8 @@ describe('/participants/{Type}/{ID}/{SubId}/error', () => { beforeAll(async () => { sandbox = Sinon.createSandbox() sandbox.stub(Db, 'connect').returns(Promise.resolve({})) - server = await initServer(await getPort()) + Config.API_PORT = await getPort() + server = await initServer(Config) }) afterAll(async () => { diff --git a/test/unit/handlers/parties/endpointcache.test.js b/test/unit/api/parties/endpointcache.test.js similarity index 93% rename from test/unit/handlers/parties/endpointcache.test.js rename to test/unit/api/parties/endpointcache.test.js index aaea7f84..15a12d53 100644 --- a/test/unit/handlers/parties/endpointcache.test.js +++ b/test/unit/api/parties/endpointcache.test.js @@ -27,14 +27,16 @@ 'use strict' -const Helper = require('../../../util/helper') -const Db = require('../../../../src/lib/db') -const initServer = require('../../../../src/server').initializeApi -const getPort = require('get-port') const Sinon = require('sinon') -const MigrationLockModel = require('../../../../src/models/misc/migrationLock') +const getPort = require('get-port') const Logger = require('@mojaloop/central-services-logger') +const { initializeApi } = require('../../../../src/server') +const Db = require('../../../../src/lib/db') +const MigrationLockModel = require('../../../../src/models/misc/migrationLock') +const Helper = require('../../../util/helper') +const Config = require('../../../../src/lib/config') + Logger.isDebugEnabled = jest.fn(() => true) Logger.isErrorEnabled = jest.fn(() => true) Logger.isInfoEnabled = jest.fn(() => true) @@ -45,7 +47,8 @@ describe('/endpointcache', () => { beforeEach(async () => { sandbox = Sinon.createSandbox() sandbox.stub(Db, 'connect').returns(Promise.resolve({})) - server = await initServer(await getPort()) + Config.API_PORT = await getPort() + server = await initializeApi(Config) }) afterEach(async () => { diff --git a/test/unit/handlers/parties/parties.test.js b/test/unit/api/parties/parties.test.js similarity index 96% rename from test/unit/handlers/parties/parties.test.js rename to test/unit/api/parties/parties.test.js index b14182b0..a989abb4 100644 --- a/test/unit/handlers/parties/parties.test.js +++ b/test/unit/api/parties/parties.test.js @@ -36,6 +36,7 @@ const participants = require('../../../../src/domain/participants') const initServer = require('../../../../src/server').initializeApi const getPort = require('get-port') const Logger = require('@mojaloop/central-services-logger') +const Config = require('../../../../src/lib/config') Logger.isDebugEnabled = jest.fn(() => true) Logger.isErrorEnabled = jest.fn(() => true) @@ -47,7 +48,8 @@ describe('/parties', () => { beforeAll(async () => { sandbox = Sinon.createSandbox() sandbox.stub(Db, 'connect').returns(Promise.resolve({})) - server = await initServer(await getPort()) + Config.API_PORT = await getPort() + server = await initServer(Config) }) afterAll(async () => { diff --git a/test/unit/handlers/parties/{Type}/{ID}.test.js b/test/unit/api/parties/{Type}/{ID}.test.js similarity index 98% rename from test/unit/handlers/parties/{Type}/{ID}.test.js rename to test/unit/api/parties/{Type}/{ID}.test.js index 6c13224b..4ca4457b 100644 --- a/test/unit/handlers/parties/{Type}/{ID}.test.js +++ b/test/unit/api/parties/{Type}/{ID}.test.js @@ -41,6 +41,7 @@ const requestUtil = require('@mojaloop/central-services-shared').Util.Request const Enums = require('@mojaloop/central-services-shared').Enum const oracleEndpointCached = require('../../../../../src/models/oracle/oracleEndpointCached') const Logger = require('@mojaloop/central-services-logger') +const Config = require('../../../../../src/lib/config') Logger.isDebugEnabled = jest.fn(() => true) Logger.isErrorEnabled = jest.fn(() => true) @@ -52,7 +53,8 @@ describe('/parties', () => { beforeAll(async () => { sandbox = Sinon.createSandbox() sandbox.stub(Db, 'connect').returns(Promise.resolve({})) - server = await initServer(await getPort()) + Config.API_PORT = await getPort() + server = await initServer(Config) }) afterEach(async () => { diff --git a/test/unit/handlers/parties/{Type}/{ID}/error.test.js b/test/unit/api/parties/{Type}/{ID}/error.test.js similarity index 94% rename from test/unit/handlers/parties/{Type}/{ID}/error.test.js rename to test/unit/api/parties/{Type}/{ID}/error.test.js index 4c0feb66..2b4b8969 100644 --- a/test/unit/handlers/parties/{Type}/{ID}/error.test.js +++ b/test/unit/api/parties/{Type}/{ID}/error.test.js @@ -34,10 +34,11 @@ const src = '../../../../../../src' const initServer = require(`${src}/server`).initializeApi const Db = require(`${src}/lib/db`) const parties = require(`${src}/domain/parties`) -const ErrHandler = require(`${src}/handlers/parties/{Type}/{ID}/error`) +const ErrHandler = require(`${src}/api/parties/{Type}/{ID}/error`) const Helper = require('../../../../../util/helper') const LibUtil = require(`${src}/lib/util`) const Logger = require('@mojaloop/central-services-logger') +const Config = require(`${src}/lib/config`) Logger.isDebugEnabled = jest.fn(() => true) Logger.isErrorEnabled = jest.fn(() => true) @@ -50,7 +51,8 @@ describe('/parties/{Type}/{ID}/error', () => { beforeAll(async () => { sandbox = Sinon.createSandbox() sandbox.stub(Db, 'connect').returns(Promise.resolve({})) - server = await initServer(await getPort()) + Config.API_PORT = await getPort() + server = await initServer(Config) sandbox.stub(LibUtil, 'getSpanTags').returns({}) }) @@ -74,7 +76,8 @@ describe('/parties/{Type}/{ID}/error', () => { mock.request = { log: sandbox.stub(), server: { - log: sandbox.stub() + log: sandbox.stub(), + app: {} }, span: { setTags: setTagsStub, @@ -114,7 +117,8 @@ describe('/parties/{Type}/{ID}/error', () => { mock.request = { log: sandbox.stub(), server: { - log: sandbox.stub() + log: sandbox.stub(), + app: {} }, span: { setTags: setTagsStub, diff --git a/test/unit/handlers/parties/{Type}/{ID}/{SubId}.test.js b/test/unit/api/parties/{Type}/{ID}/{SubId}.test.js similarity index 98% rename from test/unit/handlers/parties/{Type}/{ID}/{SubId}.test.js rename to test/unit/api/parties/{Type}/{ID}/{SubId}.test.js index dfbb1e54..c629fd46 100644 --- a/test/unit/handlers/parties/{Type}/{ID}/{SubId}.test.js +++ b/test/unit/api/parties/{Type}/{ID}/{SubId}.test.js @@ -29,6 +29,7 @@ const Sinon = require('sinon') const getPort = require('get-port') const ErrorHandler = require('@mojaloop/central-services-error-handling') +const Logger = require('@mojaloop/central-services-logger') const requestUtil = require('@mojaloop/central-services-shared').Util.Request const Enums = require('@mojaloop/central-services-shared').Enum const initServer = require('../../../../../../src/server').initializeApi @@ -37,7 +38,7 @@ const parties = require('../../../../../../src/domain/parties') const participant = require('../../../../../../src/models/participantEndpoint/facade') const Helper = require('../../../../../util/helper') const oracleEndpointCached = require('../../../../../../src/models/oracle/oracleEndpointCached') -const Logger = require('@mojaloop/central-services-logger') +const Config = require('../../../../../../src/lib/config') Logger.isDebugEnabled = jest.fn(() => true) Logger.isErrorEnabled = jest.fn(() => true) @@ -49,7 +50,8 @@ describe('/parties/{Type}/{ID}/{SubId}', () => { beforeAll(async () => { sandbox = Sinon.createSandbox() sandbox.stub(Db, 'connect').returns(Promise.resolve({})) - server = await initServer(await getPort()) + Config.API_PORT = await getPort() + server = await initServer(Config) }) afterAll(async () => { diff --git a/test/unit/handlers/parties/{Type}/{ID}/{SubId}/error.test.js b/test/unit/api/parties/{Type}/{ID}/{SubId}/error.test.js similarity index 94% rename from test/unit/handlers/parties/{Type}/{ID}/{SubId}/error.test.js rename to test/unit/api/parties/{Type}/{ID}/{SubId}/error.test.js index 82ab7c15..325faa15 100644 --- a/test/unit/handlers/parties/{Type}/{ID}/{SubId}/error.test.js +++ b/test/unit/api/parties/{Type}/{ID}/{SubId}/error.test.js @@ -33,8 +33,9 @@ const src = '../../../../../../../src' const initServer = require(`${src}/server`).initializeApi const Db = require(`${src}/lib/db`) const parties = require(`${src}/domain/parties`) -const ErrHandler = require(`${src}/handlers/parties/{Type}/{ID}/{SubId}/error`) +const ErrHandler = require(`${src}/api/parties/{Type}/{ID}/{SubId}/error`) const Helper = require('../../../../../../util/helper') +const Config = require(`${src}/lib/config`) let server let sandbox @@ -44,7 +45,8 @@ describe('/parties/{Type}/{ID}/{SubId}/error', () => { beforeAll(async () => { sandbox = Sinon.createSandbox() sandbox.stub(Db, 'connect').returns(Promise.resolve({})) - server = await initServer(await getPort()) + Config.API_PORT = await getPort() + server = await initServer(Config) }) afterAll(async () => { @@ -63,7 +65,8 @@ describe('/parties/{Type}/{ID}/{SubId}/error', () => { const mock = await Helper.generateMockRequest('/parties/{Type}/{ID}/{SubId}/error', 'put') mock.request.server = { - log: sandbox.stub() + log: sandbox.stub(), + app: {} } const stub = sandbox.stub(parties, 'putPartiesErrorByTypeAndID').resolves({}) @@ -90,7 +93,8 @@ describe('/parties/{Type}/{ID}/{SubId}/error', () => { } const mock = await Helper.generateMockRequest('/parties/{Type}/{ID}/{SubId}/error', 'put') mock.request.server = { - log: sandbox.stub() + log: sandbox.stub(), + app: {} } const throwError = new Error('Unknown error') const stub = sandbox.stub(parties, 'putPartiesErrorByTypeAndID').rejects(throwError) diff --git a/test/unit/domain/participants/participants.test.js b/test/unit/domain/participants/participants.test.js index 7982984a..574739ca 100644 --- a/test/unit/domain/participants/participants.test.js +++ b/test/unit/domain/participants/participants.test.js @@ -312,7 +312,7 @@ describe('participant Tests', () => { participant.sendRequest = sandbox.stub() const headers = { accept: 'application/vnd.interoperability.participants+json;version=1', - 'fspiop-destination': Enums.Http.Headers.FSPIOP.SWITCH.value, + 'fspiop-destination': Config.HUB_NAME, 'content-type': 'application/vnd.interoperability.participants+json;version=1.1', date: '2019-05-24 08:52:19', 'fspiop-source': 'fsp1' @@ -333,7 +333,7 @@ describe('participant Tests', () => { // Assert expect(participant.sendRequest.callCount).toBe(1) const firstCallArgs = participant.sendRequest.getCall(0).args - expect(firstCallArgs[0][Enums.Http.Headers.FSPIOP.DESTINATION]).toBe('switch') + expect(firstCallArgs[0][Enums.Http.Headers.FSPIOP.DESTINATION]).toBe(Config.HUB_NAME) }) it('sends put request to the participant with SubId', async () => { @@ -351,7 +351,7 @@ describe('participant Tests', () => { const expectedCallbackEndpointType = Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_SUB_ID_PUT const headers = { accept: 'application/vnd.interoperability.participants+json;version=1', - 'fspiop-destination': Enums.Http.Headers.FSPIOP.SWITCH.value, + 'fspiop-destination': Config.HUB_NAME, 'content-type': 'application/vnd.interoperability.participants+json;version=1.1', date: '2019-05-24 08:52:19', 'fspiop-source': 'fsp1' @@ -373,7 +373,7 @@ describe('participant Tests', () => { // Assert expect(participant.sendRequest.callCount).toBe(1) const firstCallArgs = participant.sendRequest.getCall(0).args - expect(firstCallArgs[0][Enums.Http.Headers.FSPIOP.DESTINATION]).toBe('switch') + expect(firstCallArgs[0][Enums.Http.Headers.FSPIOP.DESTINATION]).toBe(Config.HUB_NAME) expect(firstCallArgs[2]).toBe(expectedCallbackEndpointType) expect(firstCallArgs[4].partyList[0].partySubIdOrType).toBe('subId') }) @@ -423,7 +423,7 @@ describe('participant Tests', () => { participant.sendErrorToParticipant = sandbox.stub() const headers = { accept: 'application/vnd.interoperability.participants+json;version=1', - 'fspiop-destination': Enums.Http.Headers.FSPIOP.SWITCH.value, + 'fspiop-destination': Config.HUB_NAME, 'content-type': 'application/vnd.interoperability.participants+json;version=1.1', date: '2019-05-24 08:52:19', 'fspiop-source': 'fsp1' @@ -456,7 +456,7 @@ describe('participant Tests', () => { const expectedErrorCallbackEndpointType = Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_SUB_ID_PUT_ERROR const headers = { accept: 'application/vnd.interoperability.participants+json;version=1', - 'fspiop-destination': Enums.Http.Headers.FSPIOP.SWITCH.value, + 'fspiop-destination': Config.HUB_NAME, 'content-type': 'application/vnd.interoperability.participants+json;version=1.1', date: '2019-05-24 08:52:19', 'fspiop-source': 'fsp1' @@ -491,7 +491,7 @@ describe('participant Tests', () => { participant.sendErrorToParticipant = sandbox.stub() const headers = { accept: 'application/vnd.interoperability.participants+json;version=1', - 'fspiop-destination': Enums.Http.Headers.FSPIOP.SWITCH.value, + 'fspiop-destination': Config.HUB_NAME, 'content-type': 'application/vnd.interoperability.participants+json;version=1.1', date: '2019-05-24 08:52:19', 'fspiop-source': 'fsp1' @@ -523,7 +523,7 @@ describe('participant Tests', () => { const headers = { accept: 'application/vnd.interoperability.participants+json;version=1', - 'fspiop-destination': Enums.Http.Headers.FSPIOP.SWITCH.value, + 'fspiop-destination': Config.HUB_NAME, 'content-type': 'application/vnd.interoperability.participants+json;version=1.1', date: '2019-05-24 08:52:19', 'fspiop-source': 'fsp1' @@ -555,7 +555,7 @@ describe('participant Tests', () => { const headers = { accept: 'application/vnd.interoperability.participants+json;version=1', - 'fspiop-destination': Enums.Http.Headers.FSPIOP.SWITCH.value, + 'fspiop-destination': Config.HUB_NAME, 'content-type': 'application/vnd.interoperability.participants+json;version=1.1', date: '2019-05-24 08:52:19', 'fspiop-source': 'fsp1' @@ -587,7 +587,7 @@ describe('participant Tests', () => { const expectedErrorCallbackEndpointType = Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_SUB_ID_PUT_ERROR const headers = { accept: 'application/vnd.interoperability.participants+json;version=1', - 'fspiop-destination': Enums.Http.Headers.FSPIOP.SWITCH.value, + 'fspiop-destination': Config.HUB_NAME, 'content-type': 'application/vnd.interoperability.participants+json;version=1.1', date: '2019-05-24 08:52:19', 'fspiop-source': 'fsp1' @@ -638,7 +638,7 @@ describe('participant Tests', () => { 'fspiop-destination': 'payerfsp', 'content-type': 'application/vnd.interoperability.participants+json;version=1.1', date: '2019-05-24 08:52:19', - 'fspiop-source': Enums.Http.Headers.FSPIOP.SWITCH.value + 'fspiop-source': Config.HUB_NAME } const params = { ID: '123456', @@ -675,7 +675,7 @@ describe('participant Tests', () => { 'fspiop-destination': 'payerfsp', 'content-type': 'application/vnd.interoperability.participants+json;version=1.1', date: '2019-05-24 08:52:19', - 'fspiop-source': Enums.Http.Headers.FSPIOP.SWITCH.value + 'fspiop-source': Config.HUB_NAME } const params = { ID: '123456', @@ -713,7 +713,7 @@ describe('participant Tests', () => { 'fspiop-destination': 'payerfsp', 'content-type': 'application/vnd.interoperability.participants+json;version=1.1', date: '2019-05-24 08:52:19', - 'fspiop-source': Enums.Http.Headers.FSPIOP.SWITCH.value + 'fspiop-source': Config.HUB_NAME } const params = { ID: '123456', @@ -751,7 +751,7 @@ describe('participant Tests', () => { 'fspiop-destination': 'payerfsp', 'content-type': 'application/vnd.interoperability.participants+json;version=1.1', date: '2019-05-24 08:52:19', - 'fspiop-source': Enums.Http.Headers.FSPIOP.SWITCH.value + 'fspiop-source': Config.HUB_NAME } const params = { ID: '123456', @@ -788,7 +788,7 @@ describe('participant Tests', () => { 'fspiop-destination': 'payerfsp', 'content-type': 'application/vnd.interoperability.participants+json;version=1.1', date: '2019-05-24 08:52:19', - 'fspiop-source': Enums.Http.Headers.FSPIOP.SWITCH.value + 'fspiop-source': Config.HUB_NAME } const params = { ID: '123456', @@ -808,7 +808,7 @@ describe('participant Tests', () => { const firstCallArgs = participant.sendErrorToParticipant.getCall(0).args const secondCallArgs = participant.sendErrorToParticipant.getCall(1).args expect(firstCallArgs[1]).toBe(expectedCallbackEndpointType) - expect(secondCallArgs[0]).toBe('switch') + expect(secondCallArgs[0]).toBe(Config.HUB_NAME) expect(Logger.error.callCount).toBe(2) }) @@ -826,7 +826,7 @@ describe('participant Tests', () => { 'fspiop-destination': 'payerfsp', 'content-type': 'application/vnd.interoperability.participants+json;version=1.1', date: '2019-05-24 08:52:19', - 'fspiop-source': Enums.Http.Headers.FSPIOP.SWITCH.value + 'fspiop-source': Config.HUB_NAME } const params = { ID: '123456', @@ -847,7 +847,7 @@ describe('participant Tests', () => { const firstCallArgs = participant.sendErrorToParticipant.getCall(0).args const secondCallArgs = participant.sendErrorToParticipant.getCall(1).args expect(firstCallArgs[1]).toBe(expectedCallbackEndpointType) - expect(secondCallArgs[0]).toBe('switch') + expect(secondCallArgs[0]).toBe(Config.HUB_NAME) expect(Logger.error.callCount).toBe(2) }) }) @@ -878,7 +878,7 @@ describe('participant Tests', () => { participant.sendRequest = sandbox.stub() const headers = { accept: 'application/vnd.interoperability.participants+json;version=1', - 'fspiop-destination': Enums.Http.Headers.FSPIOP.SWITCH.value, + 'fspiop-destination': Config.HUB_NAME, 'content-type': 'application/vnd.interoperability.participants+json;version=1.1', date: '2019-05-24 08:52:19', 'fspiop-source': 'fsp1' @@ -898,7 +898,7 @@ describe('participant Tests', () => { // Assert expect(participant.sendRequest.callCount).toBe(1) const firstCallArgs = participant.sendRequest.getCall(0).args - expect(firstCallArgs[0][Enums.Http.Headers.FSPIOP.DESTINATION]).toBe('switch') + expect(firstCallArgs[0][Enums.Http.Headers.FSPIOP.DESTINATION]).toBe(Config.HUB_NAME) }) it('sends the request to the participant with SubId', async () => { @@ -917,7 +917,7 @@ describe('participant Tests', () => { const expectedCallbackEndpointType = Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_SUB_ID_PUT const headers = { accept: 'application/vnd.interoperability.participants+json;version=1', - 'fspiop-destination': Enums.Http.Headers.FSPIOP.SWITCH.value, + 'fspiop-destination': Config.HUB_NAME, 'content-type': 'application/vnd.interoperability.participants+json;version=1.1', date: '2019-05-24 08:52:19', 'fspiop-source': 'fsp1' @@ -938,7 +938,7 @@ describe('participant Tests', () => { // Assert expect(participant.sendRequest.callCount).toBe(1) const firstCallArgs = participant.sendRequest.getCall(0).args - expect(firstCallArgs[0][Enums.Http.Headers.FSPIOP.DESTINATION]).toBe('switch') + expect(firstCallArgs[0][Enums.Http.Headers.FSPIOP.DESTINATION]).toBe(Config.HUB_NAME) expect(firstCallArgs[2]).toBe(expectedCallbackEndpointType) expect(firstCallArgs[4].partyList[0].partySubIdOrType).toBe('subId') }) @@ -988,7 +988,7 @@ describe('participant Tests', () => { participant.sendErrorToParticipant = sandbox.stub() const headers = { accept: 'application/vnd.interoperability.participants+json;version=1', - 'fspiop-destination': Enums.Http.Headers.FSPIOP.SWITCH.value, + 'fspiop-destination': Config.HUB_NAME, 'content-type': 'application/vnd.interoperability.participants+json;version=1.1', date: '2019-05-24 08:52:19', 'fspiop-source': 'fsp1' @@ -1020,7 +1020,7 @@ describe('participant Tests', () => { const expectedErrorCallbackEndpointType = Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_SUB_ID_PUT_ERROR const headers = { accept: 'application/vnd.interoperability.participants+json;version=1', - 'fspiop-destination': Enums.Http.Headers.FSPIOP.SWITCH.value, + 'fspiop-destination': Config.HUB_NAME, 'content-type': 'application/vnd.interoperability.participants+json;version=1.1', date: '2019-05-24 08:52:19', 'fspiop-source': 'fsp1' @@ -1054,7 +1054,7 @@ describe('participant Tests', () => { participant.sendErrorToParticipant = sandbox.stub() const headers = { accept: 'application/vnd.interoperability.participants+json;version=1', - 'fspiop-destination': Enums.Http.Headers.FSPIOP.SWITCH.value, + 'fspiop-destination': Config.HUB_NAME, 'content-type': 'application/vnd.interoperability.participants+json;version=1.1', date: '2019-05-24 08:52:19', 'fspiop-source': 'fsp1' @@ -1085,7 +1085,7 @@ describe('participant Tests', () => { const headers = { accept: 'application/vnd.interoperability.participants+json;version=1', - 'fspiop-destination': Enums.Http.Headers.FSPIOP.SWITCH.value, + 'fspiop-destination': Config.HUB_NAME, 'content-type': 'application/vnd.interoperability.participants+json;version=1.1', date: '2019-05-24 08:52:19', 'fspiop-source': 'fsp1' @@ -1116,7 +1116,7 @@ describe('participant Tests', () => { const headers = { accept: 'application/vnd.interoperability.participants+json;version=1', - 'fspiop-destination': Enums.Http.Headers.FSPIOP.SWITCH.value, + 'fspiop-destination': Config.HUB_NAME, 'content-type': 'application/vnd.interoperability.participants+json;version=1.1', date: '2019-05-24 08:52:19', 'fspiop-source': 'fsp1' @@ -1147,7 +1147,7 @@ describe('participant Tests', () => { const expectedErrorCallbackEndpointType = Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_SUB_ID_PUT_ERROR const headers = { accept: 'application/vnd.interoperability.participants+json;version=1', - 'fspiop-destination': Enums.Http.Headers.FSPIOP.SWITCH.value, + 'fspiop-destination': Config.HUB_NAME, 'content-type': 'application/vnd.interoperability.participants+json;version=1.1', date: '2019-05-24 08:52:19', 'fspiop-source': 'fsp1' @@ -1202,7 +1202,7 @@ describe('participant Tests', () => { const headers = { accept: 'application/vnd.interoperability.participants+json;version=1', - 'fspiop-destination': Enums.Http.Headers.FSPIOP.SWITCH.value, + 'fspiop-destination': Config.HUB_NAME, 'content-type': 'application/vnd.interoperability.participants+json;version=1.1', date: '2019-05-24 08:52:19', 'fspiop-source': 'fsp1' @@ -1301,7 +1301,7 @@ describe('participant Tests', () => { const headers = { accept: 'application/vnd.interoperability.participants+json;version=1', - 'fspiop-destination': Enums.Http.Headers.FSPIOP.SWITCH.value, + 'fspiop-destination': Config.HUB_NAME, 'content-type': 'application/vnd.interoperability.participants+json;version=1.1', date: '2019-05-24 08:52:19', 'fspiop-source': 'fsp1' @@ -1349,7 +1349,7 @@ describe('participant Tests', () => { const headers = { accept: 'application/vnd.interoperability.participants+json;version=1', - 'fspiop-destination': Enums.Http.Headers.FSPIOP.SWITCH.value, + 'fspiop-destination': Config.HUB_NAME, 'content-type': 'application/vnd.interoperability.participants+json;version=1.1', date: '2019-05-24 08:52:19', 'fspiop-source': 'fsp1' @@ -1383,7 +1383,7 @@ describe('participant Tests', () => { const headers = { accept: 'application/vnd.interoperability.participants+json;version=1', - 'fspiop-destination': Enums.Http.Headers.FSPIOP.SWITCH.value, + 'fspiop-destination': Config.HUB_NAME, 'content-type': 'application/vnd.interoperability.participants+json;version=1.1', date: '2019-05-24 08:52:19', 'fspiop-source': 'fsp1' @@ -1422,7 +1422,7 @@ describe('participant Tests', () => { const headers = { accept: 'application/vnd.interoperability.participants+json;version=1', - 'fspiop-destination': Enums.Http.Headers.FSPIOP.SWITCH.value, + 'fspiop-destination': Config.HUB_NAME, 'content-type': 'application/vnd.interoperability.participants+json;version=1.1', date: '2019-05-24 08:52:19', 'fspiop-source': 'fsp1' diff --git a/test/unit/domain/parties/parties.test.js b/test/unit/domain/parties/parties.test.js index a3245e7b..fc64f7d4 100644 --- a/test/unit/domain/parties/parties.test.js +++ b/test/unit/domain/parties/parties.test.js @@ -30,20 +30,32 @@ 'use strict' +const { randomUUID } = require('node:crypto') +const { setTimeout: sleep } = require('node:timers/promises') const Sinon = require('sinon') -const request = require('@mojaloop/central-services-shared').Util.Request -const Endpoints = require('@mojaloop/central-services-shared').Util.Endpoints -const Util = require('@mojaloop/central-services-shared').Util +const { createProxyCache } = require('@mojaloop/inter-scheme-proxy-cache-lib') +const { Enum, Util } = require('@mojaloop/central-services-shared') +const { MojaloopApiErrorCodes } = require('@mojaloop/sdk-standard-components').Errors const Logger = require('@mojaloop/central-services-logger') -const { encodePayload } = require('@mojaloop/central-services-shared').Util.StreamingProtocol -const Enums = require('@mojaloop/central-services-shared').Enum -const Helper = require('../../../util/helper') +const Config = require('../../../../src/lib/config') const Db = require('../../../../src/lib/db') const partiesDomain = require('../../../../src/domain/parties/parties') -const Config = require('../../../../src/lib/config') +const partiesUtils = require('../../../../src/domain/parties/utils') const participant = require('../../../../src/models/participantEndpoint/facade') const oracle = require('../../../../src/models/oracle/facade') +const oracleEndpointCached = require('../../../../src/models/oracle/oracleEndpointCached') +const libUtil = require('../../../../src/lib/util') +const { logger } = require('../../../../src/lib') +const { ERROR_MESSAGES } = require('../../../../src/constants') + +const Helper = require('../../../util/helper') +const fixtures = require('../../../fixtures') + +const { type: proxyCacheType, proxyConfig: proxyCacheConfig } = Config.PROXY_CACHE_CONFIG + +const { encodePayload } = Util.StreamingProtocol +const { RestMethods } = Enum.Http Logger.isDebugEnabled = jest.fn(() => true) Logger.isErrorEnabled = jest.fn(() => true) @@ -51,26 +63,34 @@ Logger.isInfoEnabled = jest.fn(() => true) let sandbox describe('Parties Tests', () => { + let proxyCache + beforeEach(async () => { - await Endpoints.initializeCache(Config.CENTRAL_SHARED_ENDPOINT_CACHE_CONFIG) + await Util.Endpoints.initializeCache(Config.CENTRAL_SHARED_ENDPOINT_CACHE_CONFIG, libUtil.hubNameConfig) sandbox = Sinon.createSandbox() - sandbox.stub(request) + sandbox.stub(Util.Request) sandbox.stub(Util.Http, 'SwitchDefaultHeaders').returns(Helper.defaultSwitchHeaders) + sandbox.stub(Util.proxies) + Db.oracleEndpoint = { query: sandbox.stub() } Db.from = (table) => { return Db[table] } + proxyCache = createProxyCache(proxyCacheType, proxyCacheConfig) + await proxyCache.connect() }) - afterEach(() => { + afterEach(async () => { + await proxyCache.disconnect() sandbox.restore() }) describe('getPartiesByTypeAndID', () => { beforeEach(() => { sandbox.stub(participant) + Config.PROXY_CACHE_CONFIG.enabled = false }) afterEach(() => { @@ -143,26 +163,105 @@ describe('Parties Tests', () => { expect(lastCallHeaderArgs[1]).toBe('destfsp') }) - it('handles error when `participant.validateParticipant()`cannot be found', async () => { + it('should set source proxyMapping if source is not in scheme, and there is proxy-header', async () => { + Config.PROXY_CACHE_CONFIG.enabled = true + participant.validateParticipant = sandbox.stub() + .onFirstCall().resolves(null) // source + .onSecondCall().resolves({}) // proxy + const source = `source-${Date.now()}` + const proxy = `proxy-${Date.now()}` + + let cached = await proxyCache.lookupProxyByDfspId(source) + expect(cached).toBe(null) + + const headers = fixtures.partiesCallHeadersDto({ source, proxy }) + const { params, method, query } = Helper.getByTypeIdRequest + + await partiesDomain.getPartiesByTypeAndID(headers, params, method, query, Helper.mockSpan(), null, proxyCache) + await sleep(1000) + + cached = await proxyCache.lookupProxyByDfspId(source) + expect(cached).toBe(proxy) + }) + + it('should send error callback if destination is not in the scheme, and not in proxyCache', async () => { + participant.validateParticipant = sandbox.stub() + .onFirstCall().resolves({}) // source + .onSecondCall().resolves(null) // destination + participant.sendRequest = sandbox.stub().resolves() + participant.sendErrorToParticipant = sandbox.stub().resolves() + sandbox.stub(oracle, 'oracleRequest') + const headers = fixtures.partiesCallHeadersDto() + + await partiesDomain.getPartiesByTypeAndID(headers, Helper.getByTypeIdRequest.params, Helper.getByTypeIdRequest.method, Helper.getByTypeIdRequest.query, Helper.mockSpan(), null, proxyCache) + + expect(participant.sendRequest.callCount).toBe(0) + expect(oracle.oracleRequest.callCount).toBe(0) + expect(participant.sendErrorToParticipant.callCount).toBe(1) + + const { errorInformation } = participant.sendErrorToParticipant.getCall(0).args[2] + expect(errorInformation.errorCode).toBe('3200') + expect(errorInformation.errorDescription).toContain(ERROR_MESSAGES.partyDestinationFspNotFound) + }) + + it('should send request to proxy, if destination is not in the scheme, but has proxyMapping', async () => { + Config.PROXY_CACHE_CONFIG.enabled = true + participant.validateParticipant = sandbox.stub() + .onFirstCall().resolves({}) // source + .onSecondCall().resolves(null) // destination + participant.sendRequest = sandbox.stub().resolves() + participant.sendErrorToParticipant = sandbox.stub().resolves() + + const destination = `destination-${Date.now()}` + const proxyId = `proxy-${Date.now()}` + await proxyCache.addDfspIdToProxyMapping(destination, proxyId) + const headers = fixtures.partiesCallHeadersDto({ destination }) + + await partiesDomain.getPartiesByTypeAndID(headers, Helper.getByTypeIdRequest.params, Helper.getByTypeIdRequest.method, Helper.getByTypeIdRequest.query, Helper.mockSpan(), null, proxyCache) + + expect(participant.sendErrorToParticipant.callCount).toBe(0) + expect(participant.sendRequest.callCount).toBe(1) + + const [proxyHeaders, proxyDestination] = participant.sendRequest.getCall(0).args + expect(proxyHeaders).toEqual(headers) + expect(proxyDestination).toEqual(proxyId) + }) + + it('handles error when sourceDfsp cannot be found (no fspiop-proxy in headers)', async () => { expect.hasAssertions() // Arrange participant.validateParticipant = sandbox.stub().resolves(null) participant.sendErrorToParticipant = sandbox.stub().resolves(null) - const loggerStub = sandbox.stub(Logger, 'error') + const loggerStub = sandbox.stub(logger.mlLogger, 'error') // Act await partiesDomain.getPartiesByTypeAndID(Helper.getByTypeIdRequest.headers, Helper.getByTypeIdRequest.params, Helper.getByTypeIdRequest.method, Helper.getByTypeIdRequest.query) // Assert - /* - The function catches all exceptions. The only way to inspect what happens - in the error cases is by mocking out the logger and ensuring it gets - called correctly - */ - const firstCallArgs = loggerStub.getCall(0).args - const secondCallArgs = loggerStub.getCall(1).args - expect(firstCallArgs[0]).toBe('Requester FSP not found') - expect(secondCallArgs[0].name).toBe('FSPIOPError') + expect(loggerStub.callCount).toBe(1) + expect(participant.sendErrorToParticipant.callCount).toBe(1) + + const { errorInformation } = participant.sendErrorToParticipant.getCall(0).args[2] + expect(errorInformation.errorCode).toBe('3200') + expect(errorInformation.errorDescription).toContain(ERROR_MESSAGES.partySourceFspNotFound) + }) + + it('should send error callback, if proxy-header is present, but no proxy in the scheme', async () => { + Config.PROXY_CACHE_CONFIG.enabled = true + participant.validateParticipant = sandbox.stub().resolves(null) + participant.sendErrorToParticipant = sandbox.stub().resolves() + participant.sendRequest = sandbox.stub().resolves() + const proxy = `proxy-${Date.now()}` + const headers = fixtures.partiesCallHeadersDto({ proxy }) + + await partiesDomain.getPartiesByTypeAndID(headers, Helper.getByTypeIdRequest.params, Helper.getByTypeIdRequest.method, Helper.getByTypeIdRequest.query, null, null, proxyCache) + + expect(participant.sendRequest.callCount).toBe(0) + expect(participant.sendErrorToParticipant.callCount).toBe(1) + + const { errorInformation } = participant.sendErrorToParticipant.getCall(0).args[2] + expect(errorInformation.errorCode).toBe('3200') + expect(errorInformation.errorDescription).toContain(ERROR_MESSAGES.partyProxyNotFound) }) it('handles error when `participant.validateParticipant()`cannot be found and `sendErrorToParticipant()` fails', async () => { @@ -178,7 +277,7 @@ describe('Parties Tests', () => { }) participant.sendRequest = sandbox.stub().throws(new Error('Error sending request')) participant.sendErrorToParticipant = sandbox.stub().throws(new Error('Error sending Error')) - const loggerStub = sandbox.stub(Logger, 'error') + const loggerStub = sandbox.stub(logger.mlLogger, 'error') const headers = { accept: 'application/vnd.interoperability.participants+json;version=1', @@ -193,19 +292,12 @@ describe('Parties Tests', () => { await partiesDomain.getPartiesByTypeAndID(headers, Helper.getByTypeIdRequest.params, Helper.getByTypeIdRequest.method, Helper.getByTypeIdRequest.query) // Assert - /* - The function catches all exceptions. The only way to inspect what happens - in the error cases is by mocking out the logger and ensuring it gets - called correctly - */ - const firstCallArgs = loggerStub.getCall(0).args - const secondCallArgs = loggerStub.getCall(1).args - expect(firstCallArgs[0].name).toBe('Error') - expect(secondCallArgs[0].name).toBe('Error') + expect(participant.sendRequest.callCount).toBe(1) + expect(participant.sendErrorToParticipant.callCount).toBe(1) expect(loggerStub.callCount).toBe(2) }) - it('handles error when SubId is supplied but `participant.validateParticipant()`cannot be found and `sendErrorToParticipant()` fails', async () => { + it('handles error when SubId is supplied but `participant.validateParticipant()` cannot be found and `sendErrorToParticipant()` fails', async () => { expect.hasAssertions() // Arrange participant.validateParticipant = sandbox.stub().returns({}) @@ -227,7 +319,7 @@ describe('Parties Tests', () => { 'fspiop-source': 'payerfsp' } const params = { ...Helper.getByTypeIdRequest.params, SubId: 'subId' } - const expectedErrorCallbackEnpointType = Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_SUB_ID_PUT_ERROR + const expectedErrorCallbackEnpointType = Enum.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_SUB_ID_PUT_ERROR // Act await partiesDomain.getPartiesByTypeAndID(headers, params, Helper.getByTypeIdRequest.method, Helper.getByTypeIdRequest.query, Helper.mockSpan()) @@ -256,7 +348,7 @@ describe('Parties Tests', () => { 'fspiop-source': 'payerfsp' } const params = { ...Helper.getByTypeIdRequest.params } - const expectedCallbackEnpointType = Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_GET + const expectedCallbackEnpointType = Enum.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_GET // Act await partiesDomain.getPartiesByTypeAndID(headers, params, Helper.getByTypeIdRequest.method, Helper.getByTypeIdRequest.query) @@ -289,7 +381,7 @@ describe('Parties Tests', () => { 'fspiop-source': 'payerfsp' } const params = { ...Helper.getByTypeIdRequest.params, SubId: 'subId' } - const expectedCallbackEnpointType = Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_SUB_ID_GET + const expectedCallbackEnpointType = Enum.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_SUB_ID_GET // Act await partiesDomain.getPartiesByTypeAndID(headers, params, Helper.getByTypeIdRequest.method, Helper.getByTypeIdRequest.query) @@ -324,7 +416,7 @@ describe('Parties Tests', () => { 'fspiop-source': 'payerfsp' } const params = { ...Helper.getByTypeIdRequest.params, SubId: 'subIdNOTFOUND' } - const expectedErrorCallbackEnpointType = Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_SUB_ID_PUT_ERROR + const expectedErrorCallbackEnpointType = Enum.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_SUB_ID_PUT_ERROR // Act await partiesDomain.getPartiesByTypeAndID(headers, params, Helper.getByTypeIdRequest.method, Helper.getByTypeIdRequest.query) @@ -359,7 +451,7 @@ describe('Parties Tests', () => { 'fspiop-source': 'payerfsp' } const params = { ...Helper.getByTypeIdRequest.params, SubId: 'subId' } - const expectedCallbackEnpointType = Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_SUB_ID_GET + const expectedCallbackEnpointType = Enum.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_SUB_ID_GET // Act await partiesDomain.getPartiesByTypeAndID(headers, params, Helper.getByTypeIdRequest.method, Helper.getByTypeIdRequest.query) @@ -370,6 +462,34 @@ describe('Parties Tests', () => { expect(firstCallArgs[2]).toBe(expectedCallbackEnpointType) }) + it('should send request to proxy if oracle returns dfsp NOT from the scheme', async () => { + Config.PROXY_CACHE_CONFIG.enabled = true + const proxyName = `proxy-${Date.now()}` + const fspId = `dfspNotFromScheme-${Date.now()}` + const oracleResponse = fixtures.oracleRequestResponseDto({ + partyList: [{ fspId }] + }) + sandbox.stub(oracle, 'oracleRequest').resolves(oracleResponse) + participant.validateParticipant = sandbox.stub() + .onFirstCall().resolves({}) // source + .onSecondCall().resolves(null) // oracle dfsp + participant.sendRequest = sandbox.stub().resolves() + participant.sendErrorToParticipant = sandbox.stub().resolves() + + const isAdded = await proxyCache.addDfspIdToProxyMapping(fspId, proxyName) + expect(isAdded).toBe(true) + + const headers = fixtures.partiesCallHeadersDto({ destination: '' }) + const { params, method, query } = Helper.getByTypeIdRequest + + await partiesDomain.getPartiesByTypeAndID(headers, params, method, query, null, null, proxyCache) + + expect(participant.sendErrorToParticipant.callCount).toBe(0) + expect(participant.sendRequest.callCount).toBe(1) + const calledProxy = participant.sendRequest.getCall(0).args[1] + expect(calledProxy).toBe(proxyName) + }) + it('handles error when `oracleRequest` returns no result', async () => { expect.hasAssertions() // Arrange @@ -377,7 +497,7 @@ describe('Parties Tests', () => { participant.sendErrorToParticipant = sandbox.stub().resolves(null) oracle.oracleRequest = sandbox.stub().resolves(null) sandbox.stub(Logger) - const expectedErrorCallbackEnpointType = Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_PUT_ERROR + const expectedErrorCallbackEnpointType = Enum.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_PUT_ERROR // Act const headers = { ...Helper.getByTypeIdRequest.headers } @@ -389,11 +509,59 @@ describe('Parties Tests', () => { const firstCallArgs = participant.sendErrorToParticipant.getCall(0).args expect(firstCallArgs[1]).toBe(expectedErrorCallbackEnpointType) }) + + it('should perform sendToProxies alsRequest, if no destination-header and no data in oracle response', async () => { + Config.PROXY_CACHE_CONFIG.enabled = true + const proxyNames = ['proxyA', 'proxyB'] + Util.proxies.getAllProxiesNames = sandbox.stub().resolves(proxyNames) + oracle.oracleRequest = sandbox.stub().resolves(null) + participant.validateParticipant = sandbox.stub().resolves({}) + participant.sendRequest = sandbox.stub().resolves() + participant.sendErrorToParticipant = sandbox.stub().resolves() + + const source = `fromDfsp-${Date.now()}` + const destination = '' + const headers = fixtures.partiesCallHeadersDto({ source, destination }) + const alsReq = partiesUtils.alsRequestDto(source, Helper.getByTypeIdRequest.params) + + let isExists = await proxyCache.receivedSuccessResponse(alsReq) // no in cache + expect(isExists).toBe(false) + + await partiesDomain.getPartiesByTypeAndID(headers, Helper.getByTypeIdRequest.params, Helper.getByTypeIdRequest.method, Helper.getByTypeIdRequest.query, Helper.mockSpan(), null, proxyCache) + + isExists = await proxyCache.receivedSuccessResponse(alsReq) + expect(isExists).toBe(true) + expect(participant.sendErrorToParticipant.callCount).toBe(0) + expect(participant.sendRequest.callCount).toBe(proxyNames.length) + const calledProxies = participant.sendRequest.args.map(args => args[1]) + expect(calledProxies).toEqual(proxyNames) + }) + + it('should send error callback, if no successful sendToProxiesList requests', async () => { + Config.PROXY_CACHE_CONFIG.enabled = true + const proxyNames = ['proxyA', 'proxyB'] + Util.proxies.getAllProxiesNames = sandbox.stub().resolves(proxyNames) + oracle.oracleRequest = sandbox.stub().resolves(null) + participant.validateParticipant = sandbox.stub().resolves({}) + participant.sendRequest = sandbox.stub().rejects(new Error('Some network issue')) + participant.sendErrorToParticipant = sandbox.stub().resolves() + const headers = fixtures.partiesCallHeadersDto({ destination: '' }) + + await partiesDomain.getPartiesByTypeAndID(headers, Helper.getByTypeIdRequest.params, Helper.getByTypeIdRequest.method, Helper.getByTypeIdRequest.query, Helper.mockSpan(), null, proxyCache) + + expect(participant.sendRequest.callCount).toBe(proxyNames.length) + expect(participant.sendErrorToParticipant.callCount).toBe(1) + + const { errorInformation } = participant.sendErrorToParticipant.getCall(0).args[2] + expect(errorInformation.errorCode).toBe('3200') + expect(errorInformation.errorDescription).toContain(ERROR_MESSAGES.proxyConnectionError) + }) }) describe('putPartiesByTypeAndID', () => { beforeEach(() => { sandbox.stub(participant) + Config.PROXY_CACHE_CONFIG.enabled = false }) afterEach(() => { @@ -411,7 +579,7 @@ describe('Parties Tests', () => { const dataUri = encodePayload(payload, 'application/json') // Act - await partiesDomain.putPartiesByTypeAndID(Helper.putByTypeIdRequest.headers, Helper.putByTypeIdRequest.params, 'put', payload, dataUri) + await partiesDomain.putPartiesByTypeAndID(Helper.putByTypeIdRequest.headers, Helper.putByTypeIdRequest.params, 'put', payload, dataUri, null, proxyCache) // Assert expect(participant.sendRequest.callCount).toBe(1) @@ -431,10 +599,10 @@ describe('Parties Tests', () => { participant.sendRequest = sandbox.stub().resolves() const payload = {} const params = { ...Helper.putByTypeIdRequest.params, SubId: 'subId' } - const expectedCallbackEnpointType = Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_SUB_ID_PUT + const expectedCallbackEnpointType = Enum.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_SUB_ID_PUT // Act - await partiesDomain.putPartiesByTypeAndID(Helper.putByTypeIdRequest.headers, params, 'put', payload, null) + await partiesDomain.putPartiesByTypeAndID(Helper.putByTypeIdRequest.headers, params, 'put', payload, null, null, proxyCache) // Assert expect(participant.sendRequest.callCount).toBe(1) @@ -445,23 +613,84 @@ describe('Parties Tests', () => { it('handles error when `participant.validateParticipant()` returns no participant', async () => { expect.hasAssertions() // Arrange - const loggerStub = sandbox.stub(Logger, 'error') + const loggerStub = sandbox.stub(logger, 'error') participant.sendErrorToParticipant = sandbox.stub().resolves() const payload = JSON.stringify({ testPayload: true }) const dataUri = encodePayload(payload, 'application/json') + const { headers, params, method } = Helper.putByTypeIdRequest // Act - await partiesDomain.putPartiesByTypeAndID(Helper.putByTypeIdRequest.headers, Helper.putByTypeIdRequest.params, 'put', payload, dataUri) + await partiesDomain.putPartiesByTypeAndID(headers, params, method, payload, dataUri, proxyCache) // Assert expect(participant.sendErrorToParticipant.callCount).toBe(1) const firstLoggerCallArgs = loggerStub.getCall(0).args - expect(firstLoggerCallArgs[0]).toStrictEqual('Requester FSP not found') + expect(firstLoggerCallArgs[1].message).toBe(ERROR_MESSAGES.partySourceFspNotFound) loggerStub.reset() participant.sendErrorToParticipant.reset() }) + it('should add proxyMapping, if source is not in scheme, and there is fspiop-proxy header', async () => { + Config.PROXY_CACHE_CONFIG.enabled = true + participant.validateParticipant = sandbox.stub().resolves(null) + participant.sendRequest = sandbox.stub().resolves() + participant.sendErrorToParticipant = sandbox.stub().resolves() + + const source = `source-${Date.now()}` + let cachedProxy = await proxyCache.lookupProxyByDfspId(source) + expect(cachedProxy).toBeNull() + + const proxy = `proxy-${Date.now()}` + const headers = fixtures.partiesCallHeadersDto({ source, proxy }) + const payload = { test: true } + const dataUri = encodePayload(JSON.stringify(payload), 'application/json') + const { params, method } = Helper.putByTypeIdRequest + + await partiesDomain.putPartiesByTypeAndID(headers, params, method, payload, dataUri, null, proxyCache) + + cachedProxy = await proxyCache.lookupProxyByDfspId(source) + expect(cachedProxy).toBe(proxy) + }) + + it('should update oracle with partyDetails received from proxy, if previous alsReq is cached', async () => { + Config.PROXY_CACHE_CONFIG.enabled = true + const source = `source-${Date.now()}` + const destination = `payer-fsp-${Date.now()}` + const proxy = `proxy-${Date.now()}` + + participant.validateParticipant = sandbox.stub() + .onFirstCall().resolves(null) // payee + .onSecondCall().resolves({ name: destination }) // payer + oracleEndpointCached.getOracleEndpointByType = sandbox.stub().resolves([ + { value: 'http://oracle.endpoint' } + ]) + oracle.oracleRequest = sandbox.stub().resolves() + + const headers = fixtures.partiesCallHeadersDto({ source, destination, proxy }) + const partyId = `testParty-${randomUUID()}` + const partyIdType = 'MSISDN' + const partyDetails = fixtures.putPartiesSuccessResponseDto({ + partyId, + partyIdType, + fspId: source + }) + const dataUri = encodePayload(JSON.stringify(partyDetails), 'application/json') + const params = { ID: partyId, Type: partyIdType } + + const alsReq = fixtures.mockAlsRequestDto(destination, partyIdType, partyId) + const isSet = await proxyCache.setSendToProxiesList(alsReq, [proxy]) + expect(isSet).toBe(true) + + await partiesDomain.putPartiesByTypeAndID(headers, params, 'PUT', partyDetails, dataUri, null, proxyCache) + await sleep(1000) + + expect(oracle.oracleRequest.callCount).toBe(1) + const [, method, , , payload] = oracle.oracleRequest.getCall(0).args + expect(payload.fspId).toBe(source) + expect(method).toBe(RestMethods.POST) + }) + it('handles error when SubId is supplied but `participant.validateParticipant()` returns no participant', async () => { expect.hasAssertions() // Arrange @@ -479,7 +708,7 @@ describe('Parties Tests', () => { const payload = JSON.stringify({ testPayload: true }) const dataUri = encodePayload(payload, 'application/json') const params = { ...Helper.putByTypeIdRequest.params, SubId: 'subId' } - const expectedErrorCallbackEnpointType = Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_SUB_ID_PUT_ERROR + const expectedErrorCallbackEnpointType = Enum.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_SUB_ID_PUT_ERROR // Act await partiesDomain.putPartiesByTypeAndID(Helper.putByTypeIdRequest.headers, params, 'put', payload, dataUri) @@ -507,11 +736,11 @@ describe('Parties Tests', () => { const dataUri = encodePayload(payload, 'application/json') // Act - await partiesDomain.putPartiesByTypeAndID(Helper.putByTypeIdRequest.headers, Helper.putByTypeIdRequest.params, 'put', payload, dataUri) + await partiesDomain.putPartiesByTypeAndID(Helper.putByTypeIdRequest.headers, Helper.putByTypeIdRequest.params, 'put', payload, dataUri, null, proxyCache) // Assert expect(participant.validateParticipant.callCount).toBe(2) - expect(participant.sendErrorToParticipant.callCount).toBe(2) + expect(participant.sendErrorToParticipant.callCount).toBe(1) participant.validateParticipant.reset() participant.sendErrorToParticipant.reset() }) @@ -533,14 +762,14 @@ describe('Parties Tests', () => { const payload = JSON.stringify({ testPayload: true }) const dataUri = encodePayload(payload, 'application/json') const params = { ...Helper.putByTypeIdRequest.params, SubId: 'subId' } - const expectedErrorCallbackEnpointType = Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_SUB_ID_PUT_ERROR + const expectedErrorCallbackEnpointType = Enum.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_SUB_ID_PUT_ERROR // Act - await partiesDomain.putPartiesByTypeAndID(Helper.putByTypeIdRequest.headers, params, 'put', payload, dataUri) + await partiesDomain.putPartiesByTypeAndID(Helper.putByTypeIdRequest.headers, params, 'put', payload, dataUri, null, proxyCache) // Assert expect(participant.validateParticipant.callCount).toBe(2) - expect(participant.sendErrorToParticipant.callCount).toBe(2) + expect(participant.sendErrorToParticipant.callCount).toBe(1) const firstCallArgs = participant.sendErrorToParticipant.getCall(0).args expect(firstCallArgs[1]).toBe(expectedErrorCallbackEnpointType) }) @@ -549,6 +778,8 @@ describe('Parties Tests', () => { describe('putPartiesErrorByTypeAndID', () => { beforeEach(() => { sandbox.stub(participant) + sandbox.stub(partiesDomain, 'getPartiesByTypeAndID') + Config.PROXY_CACHE_CONFIG.enabled = false }) afterEach(() => { @@ -588,7 +819,7 @@ describe('Parties Tests', () => { const payload = JSON.stringify({ errorPayload: true }) const dataUri = encodePayload(payload, 'application/json') const params = { ...Helper.putByTypeIdRequest.params, SubId: 'subId' } - const expectedCallbackEnpointType = Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_SUB_ID_PUT_ERROR + const expectedCallbackEnpointType = Enum.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_SUB_ID_PUT_ERROR // Act await partiesDomain.putPartiesErrorByTypeAndID(Helper.putByTypeIdRequest.headers, params, payload, dataUri) @@ -609,10 +840,10 @@ describe('Parties Tests', () => { const dataUri = encodePayload(payload, 'application/json') // Act - await partiesDomain.putPartiesErrorByTypeAndID(Helper.putByTypeIdRequest.headers, Helper.putByTypeIdRequest.params, payload, dataUri) + await partiesDomain.putPartiesErrorByTypeAndID(Helper.putByTypeIdRequest.headers, Helper.putByTypeIdRequest.params, payload, dataUri, null, proxyCache) // Assert - expect(participant.sendErrorToParticipant.callCount).toBe(2) + expect(participant.sendErrorToParticipant.callCount).toBe(1) const sendErrorCallArgs = participant.sendErrorToParticipant.getCall(0).args expect(sendErrorCallArgs[0]).toStrictEqual('payerfsp') }) @@ -629,24 +860,25 @@ describe('Parties Tests', () => { const payload = JSON.stringify({ errorPayload: true }) // Send a data uri that will cause `decodePayload` to throw const invalidDataUri = () => 'invalid uri' + const { headers, params } = Helper.putByTypeIdRequest // Act - await partiesDomain.putPartiesErrorByTypeAndID(Helper.putByTypeIdRequest.headers, Helper.putByTypeIdRequest.params, payload, invalidDataUri, Helper.mockSpan()) + await partiesDomain.putPartiesErrorByTypeAndID(headers, params, payload, invalidDataUri, Helper.mockSpan(), null) // Assert expect(participant.sendErrorToParticipant.callCount).toBe(1) const sendErrorCallArgs = participant.sendErrorToParticipant.getCall(0).args - expect(sendErrorCallArgs[0]).toStrictEqual('payerfsp') + expect(sendErrorCallArgs[0]).toStrictEqual(headers['fspiop-destination']) }) it('handles error when `validateParticipant()` fails', async () => { expect.hasAssertions() // Arrange) - const loggerStub = sandbox.stub(Logger, 'error') + const loggerStub = sandbox.stub(logger.mlLogger, 'error') participant.validateParticipant = sandbox.stub().throws(new Error('Validation fails')) participant.sendErrorToParticipant = sandbox.stub().resolves({}) const payload = JSON.stringify({ errorPayload: true }) - const expectedCallbackEnpointType = Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_PUT_ERROR + const expectedCallbackEnpointType = Enum.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_PUT_ERROR // Act await partiesDomain.putPartiesErrorByTypeAndID(Helper.putByTypeIdRequest.headers, Helper.putByTypeIdRequest.params, payload) @@ -660,13 +892,14 @@ describe('Parties Tests', () => { it('handles error when SubID is supplied but `validateParticipant()` fails', async () => { expect.hasAssertions() - // Arrange) - const loggerStub = sandbox.stub(Logger, 'error') + // Arrange + + const loggerStub = sandbox.stub(logger.mlLogger, 'error') participant.validateParticipant = sandbox.stub().throws(new Error('Validation fails')) participant.sendErrorToParticipant = sandbox.stub().resolves({}) const payload = JSON.stringify({ errorPayload: true }) const params = { ...Helper.putByTypeIdRequest.params, SubId: 'SubId' } - const expectedCallbackEnpointType = Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_SUB_ID_PUT_ERROR + const expectedCallbackEnpointType = Enum.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_SUB_ID_PUT_ERROR // Act await partiesDomain.putPartiesErrorByTypeAndID(Helper.putByTypeIdRequest.headers, params, payload) @@ -677,5 +910,31 @@ describe('Parties Tests', () => { const sendErrorCallArgs = participant.sendErrorToParticipant.getCall(0).args expect(sendErrorCallArgs[1]).toBe(expectedCallbackEnpointType) }) + + it('should handle notValidPayeeIdentifier case, and delete partyId from oracle', async () => { + Config.PROXY_CACHE_CONFIG.enabled = true + const errorCode = MojaloopApiErrorCodes.PAYEE_IDENTIFIER_NOT_VALID.code + const payload = fixtures.errorCallbackResponseDto({ errorCode }) + const source = `source-${Date.now()}` + const proxy = `proxy-${Date.now()}` + const headers = fixtures.partiesCallHeadersDto({ source, proxy }) + const { params } = Helper.putByTypeIdRequest + participant.sendRequest = sandbox.stub().resolves() + participant.sendErrorToParticipant = sandbox.stub().resolves() + oracleEndpointCached.getOracleEndpointByType = sandbox.stub().resolves([ + { value: 'http://oracle.endpoint' } + ]) + oracle.oracleRequest = sandbox.stub().resolves() + + await partiesDomain.putPartiesErrorByTypeAndID(headers, params, payload, '', null, null, proxyCache) + + expect(participant.sendRequest.callCount).toBe(0) + expect(oracle.oracleRequest.callCount).toBe(1) + const [, method] = oracle.oracleRequest.getCall(0).args + expect(method).toBe(RestMethods.DELETE) + // todo: think, how to stub getPartiesByTypeAndID call + // expect(partiesDomain.getPartiesByTypeAndID.callCount).toBe(1) + // expect(participant.sendErrorToParticipant.callCount).toBe(0) + }) }) }) diff --git a/test/unit/domain/timeout/index.test.js b/test/unit/domain/timeout/index.test.js new file mode 100644 index 00000000..bdbdb5cf --- /dev/null +++ b/test/unit/domain/timeout/index.test.js @@ -0,0 +1,81 @@ +/***** + License + -------------- + Copyright © 2020-2024 Mojaloop Foundation + + The Mojaloop files are made available by the Mojaloop Foundation under the Apache License, Version 2.0 + (the "License") and you may not use these files except in compliance with the [License](http://www.apache.org/licenses/LICENSE-2.0). + + You may obtain a copy of the License at [http://www.apache.org/licenses/LICENSE-2.0](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](http://www.apache.org/licenses/LICENSE-2.0). + + 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 + - Steven Oderayi + -------------- +******/ + +'use strict' + +const Participant = require('../../../../src/models/participantEndpoint/facade') +const TimeoutDomain = require('../../../../src/domain/timeout') + +describe('Timeout Domain', () => { + beforeEach(() => { + jest.clearAllMocks() + jest.spyOn(Participant, 'validateParticipant').mockResolvedValue({}) + jest.spyOn(Participant, 'sendErrorToParticipant').mockResolvedValue({}) + }) + + afterEach(() => { + jest.restoreAllMocks() + }) + + describe('timeoutInterschemePartiesLookups', () => { + describe('timeoutInterschemePartiesLookups', () => { + it('should process expired ALS keys', async () => { + const mockCache = { processExpiredAlsKeys: jest.fn() } + await TimeoutDomain.timeoutInterschemePartiesLookups({ proxyCache: mockCache, batchSize: 10 }) + expect(mockCache.processExpiredAlsKeys).toHaveBeenCalledWith(TimeoutDomain.sendTimeoutCallback, 10) + }) + }) + + describe('sendTimeoutCallback', () => { + it('should send error to participant', async () => { + jest.spyOn(Participant, 'validateParticipant').mockResolvedValue({ fspId: 'sourceId' }) + + const cacheKey = 'als:sourceId:2:3:expiresAt' + const [, destination] = cacheKey.split(':') + + await TimeoutDomain.sendTimeoutCallback(cacheKey) + + expect(Participant.validateParticipant).toHaveBeenCalledWith('sourceId') + expect(Participant.sendErrorToParticipant).toHaveBeenCalledWith( + destination, + 'FSPIOP_CALLBACK_URL_PARTIES_PUT_ERROR', + { errorInformation: { errorCode: '3300', errorDescription: 'Generic expired error' } }, + expect.any(Object), expect.any(Object), undefined, expect.any(Object) + ) + }) + + it('should throw error if participant validation fails', async () => { + Participant.validateParticipant.mockResolvedValue(null) + await expect(TimeoutDomain.sendTimeoutCallback('als:sourceId:2:3:expiresAt')).rejects.toThrow() + }) + }) + }) +}) diff --git a/test/unit/handlers/TimeoutHandler.test.js b/test/unit/handlers/TimeoutHandler.test.js new file mode 100644 index 00000000..60e5d2c4 --- /dev/null +++ b/test/unit/handlers/TimeoutHandler.test.js @@ -0,0 +1,125 @@ +/***** + License + -------------- + Copyright © 2020-2024 Mojaloop Foundation + + The Mojaloop files are made available by the Mojaloop Foundation under the Apache License, Version 2.0 + (the "License") and you may not use these files except in compliance with the [License](http://www.apache.org/licenses/LICENSE-2.0). + + You may obtain a copy of the License at [http://www.apache.org/licenses/LICENSE-2.0](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](http://www.apache.org/licenses/LICENSE-2.0). + + 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 + - Steven Oderayi + -------------- + ******/ + +'use strict' + +const CronJob = require('cron').CronJob +const TimeoutHandler = require('../../../src/handlers/TimeoutHandler') +const TimeoutService = require('../../../src/domain/timeout') +const Config = require('../../../src/lib/config') +const { logger } = require('../../../src/lib') +const DefaultConfig = { ...Config } + +const wait = (ms) => new Promise(resolve => setTimeout(resolve, ms)) + +describe('TimeoutHandler', () => { + let mockOptions + + beforeEach(() => { + jest.spyOn(CronJob, 'from').mockReturnValue({ + start: jest.fn(), + stop: jest.fn() + }) + const mockProxyCache = { processExpiredAlsKeys: jest.fn() } + mockOptions = { proxyCache: mockProxyCache, batchSize: 10, logger } + }) + + afterEach(async () => { + jest.clearAllMocks() + }) + + describe('timeout', () => { + it('should execute timout service', async () => { + jest.spyOn(TimeoutService, 'timeoutInterschemePartiesLookups').mockResolvedValue() + await expect(TimeoutHandler.timeout(mockOptions)).resolves.toBeUndefined() + expect(TimeoutService.timeoutInterschemePartiesLookups).toHaveBeenCalled() + }) + + it('should not run if isRunning is true', async () => { + jest.spyOn(TimeoutService, 'timeoutInterschemePartiesLookups').mockImplementation(async () => { + await wait(1000) + }) + await Promise.all([ + TimeoutHandler.timeout(mockOptions), + TimeoutHandler.timeout(mockOptions) + ]) + expect(TimeoutService.timeoutInterschemePartiesLookups).toHaveBeenCalledTimes(1) + }) + }) + + describe('register', () => { + it('should register handler', async () => { + jest.spyOn(TimeoutHandler, 'stop') + const result = await TimeoutHandler.register(mockOptions) + + expect(result).toBe(true) + expect(TimeoutHandler.stop).not.toHaveBeenCalled() + expect(CronJob.from).toHaveBeenCalledWith({ + start: false, + onTick: expect.any(Function), + cronTime: Config.HANDLERS_TIMEOUT_TIMEXP, + timeZone: Config.HANDLERS_TIMEOUT_TIMEZONE + }) + expect(CronJob.from().start).toHaveBeenCalled() + + await TimeoutHandler.stop() + }) + + it('should not register handler if HANDLERS_TIMEOUT_DISABLED is true', async () => { + Config.HANDLERS_TIMEOUT_DISABLED = true + + const result = await TimeoutHandler.register(mockOptions) + + expect(result).toBe(false) + expect(CronJob.from).not.toHaveBeenCalled() + Config.HANDLERS_TIMEOUT_DISABLED = DefaultConfig.HANDLERS_TIMEOUT_DISABLED + }) + }) + + describe('stop', () => { + it('should stop handler', async () => { + jest.spyOn(TimeoutHandler, 'register') + await TimeoutHandler.register(mockOptions) + await TimeoutHandler.stop() + + expect(CronJob.from().stop).toHaveBeenCalled() + expect(TimeoutHandler.register).toHaveBeenCalled() + }) + + it('should not stop if not registered', async () => { + jest.spyOn(TimeoutHandler, 'register') + await TimeoutHandler.stop() + + expect(CronJob.from().stop).not.toHaveBeenCalled() + expect(TimeoutHandler.register).not.toHaveBeenCalled() + }) + }) +}) diff --git a/test/unit/handlers/index.test.js b/test/unit/handlers/index.test.js new file mode 100644 index 00000000..fd0d8140 --- /dev/null +++ b/test/unit/handlers/index.test.js @@ -0,0 +1,56 @@ +/***** + License + -------------- + Copyright © 2020-2024 Mojaloop Foundation + + The Mojaloop files are made available by the Mojaloop Foundation under the Apache License, Version 2.0 + (the "License") and you may not use these files except in compliance with the [License](http://www.apache.org/licenses/LICENSE-2.0). + + You may obtain a copy of the License at [http://www.apache.org/licenses/LICENSE-2.0](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](http://www.apache.org/licenses/LICENSE-2.0). + + 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 + - Steven Oderayi + -------------- + ******/ + +'use strict' + +const Server = require('../../../src/server') +const { HANDLER_TYPES } = require('../../../src/constants') +const Config = require('../../../src/lib/config') + +describe('Handlers Index', () => { + beforeEach(() => { + jest.clearAllMocks() + jest.spyOn(Server, 'initializeHandlers').mockImplementation(() => {}) + }) + + it('should start specified handlers in args', async () => { + process.argv = ['node', 'index.js', 'handlers', '--timeout'] + require('../../../src/handlers/index') + const expectedLogger = { info: expect.any(Function), error: expect.any(Function) } + expect(Server.initializeHandlers).toHaveBeenCalledWith([HANDLER_TYPES.TIMEOUT], Config, expect.objectContaining(expectedLogger)) + }) + + it('should not start any handlers if none are specified', async () => { + process.argv = ['node', 'index.js', 'handlers'] + require('../../../src/handlers/index') + expect(Server.initializeHandlers).not.toHaveBeenCalled() + }) +}) diff --git a/test/unit/handlers/register.test.js b/test/unit/handlers/register.test.js new file mode 100644 index 00000000..b6f7317f --- /dev/null +++ b/test/unit/handlers/register.test.js @@ -0,0 +1,90 @@ +/***** + License + -------------- + Copyright © 2020-2024 Mojaloop Foundation + + The Mojaloop files are made available by the Mojaloop Foundation under the Apache License, Version 2.0 + (the "License") and you may not use these files except in compliance with the [License](http://www.apache.org/licenses/LICENSE-2.0). + + You may obtain a copy of the License at [http://www.apache.org/licenses/LICENSE-2.0](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](http://www.apache.org/licenses/LICENSE-2.0). + + 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 + - Steven Oderayi + -------------- + ******/ + +'use strict' + +const TimeoutHandler = require('../../../src/handlers/TimeoutHandler') +const { HANDLER_TYPES } = require('../../../src/constants') +const { registerHandlers, registerAllHandlers, stopAllHandlers } = require('../../../src/handlers/register') +const Monitoring = require('../../../src/handlers/monitoring') +const { logger } = require('../../../src/lib') + +describe('RegisterHandlers', () => { + let mockOptions + + beforeEach(() => { + jest.clearAllMocks() + jest.spyOn(logger, 'debug') + jest.spyOn(logger, 'warn') + TimeoutHandler.register = jest.fn() + Monitoring.start = jest.fn() + Monitoring.stop = jest.fn() + const mockProxyCache = { processExpiredAlsKeys: jest.fn() } + mockOptions = { proxyCache: mockProxyCache, batchSize: 10, logger } + }) + + describe('registerHandlers', () => { + it('should register all handlers', async () => { + const handlers = [HANDLER_TYPES.TIMEOUT] + await registerHandlers(handlers, mockOptions) + + expect(logger.debug).toHaveBeenCalledWith('Registering Timeout Handler') + expect(TimeoutHandler.register).toHaveBeenCalled() + }) + + it('should not register unknown handlers', async () => { + const handlers = ['unknown'] + await registerHandlers(handlers, mockOptions) + + expect(logger.warn).toHaveBeenCalledWith('Handler unknown not found') + }) + }) + + describe('registerAllHandlers', () => { + it('should register all handlers', async () => { + await registerAllHandlers(mockOptions) + + expect(logger.debug).toHaveBeenCalledWith('Registering all handlers') + expect(TimeoutHandler.register).toHaveBeenCalled() + }) + }) + + describe('stopAllHandlers', () => { + it('should stop all handlers', async () => { + jest.spyOn(TimeoutHandler, 'stop') + + await stopAllHandlers({ logger }) + + expect(logger.debug).toHaveBeenCalledWith('Stopping all handlers') + expect(TimeoutHandler.stop).toHaveBeenCalled() + }) + }) +}) diff --git a/test/unit/index.test.js b/test/unit/index.test.js index b6d0f686..29ec1531 100644 --- a/test/unit/index.test.js +++ b/test/unit/index.test.js @@ -34,6 +34,7 @@ let Sinon let Command let sandbox +const Config = require('../../src/lib/config') describe('Base Tests', () => { beforeEach(() => { @@ -109,7 +110,7 @@ describe('Base Tests', () => { // When starting with default args, both the admin and api servers get startec expect(mockInitStub.callCount).toBe(1) const initStubArgs = mockInitStub.getCall(0).args - expect(initStubArgs[0]).toBe(4002) // true is API + expect(initStubArgs[0]).toStrictEqual(Config) }) it('should start the server with the --admin config', () => { @@ -133,6 +134,6 @@ describe('Base Tests', () => { // When starting with default args, both the admin and api servers get startec expect(mockInitStub.callCount).toBe(1) const initStubArgs = mockInitStub.getCall(0).args - expect(initStubArgs[0]).toBe(4001) // false is admin + expect(initStubArgs[0]).toStrictEqual(Config) }) }) diff --git a/test/unit/lib/requestLogger.test.js b/test/unit/lib/requestLogger.test.js index 6bb60a99..e1a57198 100644 --- a/test/unit/lib/requestLogger.test.js +++ b/test/unit/lib/requestLogger.test.js @@ -27,41 +27,33 @@ 'use strict' const Sinon = require('sinon') -const Util = require('util') -const Uuid = require('uuid4') +const ErrorHandler = require('@mojaloop/central-services-error-handling') const requestLogger = require('../../../src/lib/requestLogger') -const Logger = require('@mojaloop/central-services-logger') -const ErrorHandler = require('@mojaloop/central-services-error-handling') +const { logger } = require('../../../src/lib') +const fixtures = require('../../fixtures') let sandbox -let currentLoggerIsDebugEnabled describe('requestLogger', () => { beforeEach(() => { sandbox = Sinon.createSandbox() - currentLoggerIsDebugEnabled = Logger.isDebugEnabled }) afterEach(() => { sandbox.restore() - Logger.isDebugEnabled = currentLoggerIsDebugEnabled }) describe('logRequest', () => { it('prints the request.payload if it exists', async () => { // Arrange - const debugSpy = sandbox.spy(Logger, 'debug') - Logger.isDebugEnabled = true + const infoSpy = sandbox.spy(logger.mlLogger, 'info') const req = { - method: 'GET', + ...fixtures.mockHapiRequestDto(), url: { path: '/123/456' }, query: {}, - headers: { - traceid: Uuid() - }, payload: { itemA: 123, itemB: 456 @@ -72,111 +64,52 @@ describe('requestLogger', () => { requestLogger.logRequest(req) // Assert - expect(debugSpy.calledThrice).toBe(true) + expect(infoSpy.calledOnce).toBe(true) + const logLine = infoSpy.firstCall.args[0] + expect(logLine).toContain(JSON.stringify(req.headers)) + expect(logLine).toContain(JSON.stringify(req.query)) + expect(logLine).toContain(JSON.stringify(req.payload)) }) }) describe('logResponse', () => { - it('handles deseralizing invalid JSON', async () => { + it('should log response statusCode', async () => { // Arrange - const infoSpy = sandbox.spy(Logger, 'debug') - Logger.isDebugEnabled = true + const infoSpy = sandbox.spy(logger.mlLogger, 'info') const req = { - headers: { - traceid: Uuid() - }, + ...fixtures.mockHapiRequestDto(), response: { - source: { - itemA: true - }, statusCode: 500 } } - /* Make some circular JSON to break JSON.stringify() */ - const inner = {} - const outer = { - inner - } - inner[outer] = outer - req.response.source = outer // Act requestLogger.logResponse(req) - const response = Util.inspect(req.response) - // Assert - const result = infoSpy.calledWith(`ALS-Trace=${req.headers.traceid} - Response: ${response} Status: ${req.response.statusCode}, Stack: ${req.response.stack}`) - expect(result).toBe(true) - }) - - it('handles valid json', async () => { - // Arrange - const infoSpy = sandbox.spy(Logger, 'debug') - Logger.isDebugEnabled = true - const req = { - headers: { - traceid: Uuid() - }, - response: { - source: { - itemA: true - }, - statusCode: 500 - } - } - - // Act - requestLogger.logResponse(req) - - const response = JSON.stringify(req.response, null, 2) - // Assert - // const result = infoSpy.calledWith(`ALS-Trace - Response: ${JSON.stringify(req.response.source)} Status: ${req.response.statusCode}`) - const result = infoSpy.calledWith(`ALS-Trace=${req.headers.traceid} - Response: ${response} Status: ${req.response.statusCode}, Stack: ${req.response.stack}`) - expect(result).toBe(true) + const logLine = infoSpy.firstCall.args[0] + expect(logLine).toContain(JSON.stringify(req.response.statusCode)) }) it('handles valid json error response', async () => { // Arrange - const infoSpy = sandbox.spy(Logger, 'debug') - Logger.isDebugEnabled = true - const req = { - headers: { - traceid: Uuid() - }, - response: ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.VALIDATION_ERROR, 'Invalid currency code') + const infoSpy = sandbox.spy(logger.mlLogger, 'info') + const response = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.VALIDATION_ERROR, 'Invalid currency code') + const statusCode = 123 + response.output = { + statusCode } - - // Act - requestLogger.logResponse(req) - - const response = JSON.stringify(req.response, null, 2) - // Assert - // const result = infoSpy.calledWith(`ALS-Trace - Response: ${JSON.stringify(req.response.source)} Status: ${req.response.statusCode}`) - const result = infoSpy.calledWith(`ALS-Trace=${req.headers.traceid} - Response: ${response} Status: ${req.response.httpStatusCode}, Stack: ${req.response.stack}`) - expect(result).toBe(true) - }) - - it('handles if response is null or undefined after JSON stringifying', async () => { - // Arrange - const infoSpy = sandbox.spy(Logger, 'debug') - Logger.isDebugEnabled = true const req = { - headers: { - traceid: Uuid() - }, - response: { - statusCode: 500 - } + ...fixtures.mockHapiRequestDto(), + response } + // Act requestLogger.logResponse(req) - const response = JSON.stringify(req.response, null, 2) // Assert - // const result = infoSpy.calledWith('ALS-Trace - Response: [object Object]') - const result = infoSpy.calledWith(`ALS-Trace=${req.headers.traceid} - Response: ${response} Status: ${req.response.statusCode}, Stack: ${req.response.stack}`) - expect(result).toBe(true) + const logLine = infoSpy.firstCall.args[0] + expect(logLine).toContain(JSON.stringify(statusCode)) }) }) }) diff --git a/test/unit/mocks.js b/test/unit/mocks.js new file mode 100644 index 00000000..222e04e9 --- /dev/null +++ b/test/unit/mocks.js @@ -0,0 +1,66 @@ +/***** + 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 + + * Eugen Klymniuk + -------------- + **********/ + +const RedisMock = require('ioredis-mock') + +/* + ioredis-mock doesn't provide a status-field, so we need to override it here + */ +class MockIoRedis extends RedisMock { + connected = false + /** + @param opts RedisOptions + */ + constructor (opts) { + super(opts) + this.lazyConnect = Boolean(opts?.lazyConnect) + } + + get status () { + return this.connected ? 'ready' : this.lazyConnect ? 'wait' : 'end' + } +} + +class IoRedisMockCluster extends MockIoRedis { + /** + @param nodesList BasicConnectionConfig[] + @param redisOptions RedisClusterOptions + */ + constructor (nodesList, redisOptions) { + super(redisOptions) + this._nodes = [] + nodesList.forEach((connOpts) => this._nodes.push(new MockIoRedis({ ...connOpts, ...redisOptions }))) + } + + nodes () { + return this._nodes + } +} + +MockIoRedis.Cluster = IoRedisMockCluster + +module.exports = { + MockIoRedis +} diff --git a/test/unit/models/participantEndpoint/facade.test.js b/test/unit/models/participantEndpoint/facade.test.js index c2892fd5..6b36705d 100644 --- a/test/unit/models/participantEndpoint/facade.test.js +++ b/test/unit/models/participantEndpoint/facade.test.js @@ -27,12 +27,14 @@ 'use strict' +const mockHubName = require('../../../util/testConfig').HUB_NAME + const mockGetEndpoint = jest.fn() const mockGetParticipant = jest.fn() const mockSendRequest = jest.fn() const mockEnums = { Http: { - Headers: { FSPIOP: { DESTINATION: 'fsp1', SOURCE: 'fsp2', SWITCH: { value: 'switch' } } }, + Headers: { FSPIOP: { DESTINATION: 'fsp1', SOURCE: 'fsp2', SWITCH: { value: mockHubName } } }, RestMethods: { PUT: 'PUT' }, ResponseTypes: { JSON: 'json' }, HeaderResources: { PARTICIPANTS: 'value' } @@ -47,11 +49,14 @@ jest.mock('@mojaloop/central-services-shared', () => ({ Endpoints: { getEndpoint: mockGetEndpoint }, Participants: { getParticipant: mockGetParticipant }, Request: { sendRequest: mockSendRequest }, - Http: { SwitchDefaultHeaders: jest.fn() } + Http: { SwitchDefaultHeaders: jest.fn() }, + HeaderValidation: { getHubNameRegex: jest.fn().mockReturnValue(new RegExp(mockHubName)) } }, Enum: mockEnums })) + const Logger = require('@mojaloop/central-services-logger') +const fixtures = require('../../../fixtures') Logger.isDebugEnabled = jest.fn(() => true) Logger.isErrorEnabled = jest.fn(() => true) @@ -69,22 +74,8 @@ describe('participantEndpoint Facade', () => { // Arrange const mockedConfig = { JWS_SIGN: false, - FSPIOP_SOURCE_TO_SIGN: 'switch', - PROTOCOL_VERSIONS: { - CONTENT: { - DEFAULT: '2.1', - VALIDATELIST: [ - '2.1' - ] - }, - ACCEPT: { - DEFAULT: '2', - VALIDATELIST: [ - '2', - '2.1' - ] - } - } + FSPIOP_SOURCE_TO_SIGN: mockHubName, + PROTOCOL_VERSIONS: fixtures.protocolVersionsDto() } jest.mock('../../../../src/lib/config', () => (mockedConfig)) @@ -102,7 +93,7 @@ describe('participantEndpoint Facade', () => { // Assert expect(result).toBe(true) - expect(mockSendRequest.mock.calls[0][9]).toMatchObject({ + expect(mockSendRequest.mock.calls[0][0].protocolVersions).toMatchObject({ accept: mockedConfig.PROTOCOL_VERSIONS.ACCEPT.DEFAULT, content: mockedConfig.PROTOCOL_VERSIONS.CONTENT.DEFAULT }) @@ -112,7 +103,7 @@ describe('participantEndpoint Facade', () => { // Arrange const mockedConfig = { JWS_SIGN: false, - FSPIOP_SOURCE_TO_SIGN: 'switch', + FSPIOP_SOURCE_TO_SIGN: mockHubName, PROTOCOL_VERSIONS: { CONTENT: { DEFAULT: '2.1', @@ -145,7 +136,7 @@ describe('participantEndpoint Facade', () => { // Assert await expect(action()).rejects.toThrow('Request failed') - expect(mockSendRequest.mock.calls[0][9]).toMatchObject({ + expect(mockSendRequest.mock.calls[0][0].protocolVersions).toMatchObject({ accept: mockedConfig.PROTOCOL_VERSIONS.ACCEPT.DEFAULT, content: mockedConfig.PROTOCOL_VERSIONS.CONTENT.DEFAULT }) @@ -154,7 +145,7 @@ describe('participantEndpoint Facade', () => { it('should define jwsSigner and add fspiop-signature header', async () => { jest.mock('../../../../src/lib/config', () => ({ JWS_SIGN: true, - FSPIOP_SOURCE_TO_SIGN: 'switch', + FSPIOP_SOURCE_TO_SIGN: mockHubName, JWS_SIGNING_KEY_PATH: 'secrets/jwsSigningKey.key', JWS_SIGNING_KEY: 'somekey', PROTOCOL_VERSIONS: { @@ -171,16 +162,15 @@ describe('participantEndpoint Facade', () => { const participantName = 'fsp1' const headers = { [mockEnums.Http.Headers.FSPIOP.DESTINATION]: participantName, - [mockEnums.Http.Headers.FSPIOP.SOURCE]: 'switch', - 'fspiop-source': 'switch' + [mockEnums.Http.Headers.FSPIOP.SOURCE]: mockHubName, + 'fspiop-source': mockHubName } const endpointType = 'URL' const method = 'PUT' const payload = {} await participantFacade.sendRequest(headers, participantName, endpointType, method, payload) - const jwsSigner = mockSendRequest.mock.lastCall.at(-2) // the last but one argument - expect(jwsSigner).toBeTruthy() + expect(mockSendRequest.mock.lastCall[0].jwsSigner).toBeTruthy() }) }) @@ -200,29 +190,17 @@ describe('participantEndpoint Facade', () => { }) describe('sendErrorToParticipant', () => { + const mockConfigDto = ({ jwsSign = false } = {}) => ({ + JWS_SIGN: jwsSign, + FSPIOP_SOURCE_TO_SIGN: mockHubName, + JWS_SIGNING_KEY_PATH: 'secrets/jwsSigningKey.key', + JWS_SIGNING_KEY: 'somekey', + PROTOCOL_VERSIONS: fixtures.protocolVersionsDto() + }) + it('throws an error when the request fails', async () => { // Arrange - jest.mock('../../../../src/lib/config', () => ({ - JWS_SIGN: false, - FSPIOP_SOURCE_TO_SIGN: 'switch', - JWS_SIGNING_KEY_PATH: 'secrets/jwsSigningKey.key', - JWS_SIGNING_KEY: 'somekey', - PROTOCOL_VERSIONS: { - CONTENT: { - DEFAULT: '2.1', - VALIDATELIST: [ - '2.1' - ] - }, - ACCEPT: { - DEFAULT: '2', - VALIDATELIST: [ - '2', - '2.1' - ] - } - } - })) + jest.mock('../../../../src/lib/config', () => mockConfigDto()) mockGetEndpoint.mockImplementation(() => 'https://example.com/12345') mockSendRequest.mockImplementation(() => { throw new Error('Request failed') }) @@ -246,28 +224,8 @@ describe('participantEndpoint Facade', () => { it('Success without JWS', async () => { // Arrange - const mockedConfig = { - JWS_SIGN: false, - FSPIOP_SOURCE_TO_SIGN: 'switch', - JWS_SIGNING_KEY_PATH: 'secrets/jwsSigningKey.key', - JWS_SIGNING_KEY: 'somekey', - PROTOCOL_VERSIONS: { - CONTENT: { - DEFAULT: '2.1', - VALIDATELIST: [ - '2.1' - ] - }, - ACCEPT: { - DEFAULT: '2', - VALIDATELIST: [ - '2', - '2.1' - ] - } - } - } - jest.mock('../../../../src/lib/config', () => (mockedConfig)) + const mockedConfig = mockConfigDto() + jest.mock('../../../../src/lib/config', () => mockedConfig) mockGetEndpoint.mockImplementation(() => 'https://example.com/12345') mockSendRequest.mockImplementation(() => Promise.resolve(true)) @@ -290,8 +248,9 @@ describe('participantEndpoint Facade', () => { // Assert expect(spy).toHaveBeenCalled() - expect(mockSendRequest.mock.calls[0][8]).toBe(null) - expect(mockSendRequest.mock.calls[0][9]).toMatchObject({ + const { jwsSigner, protocolVersions } = mockSendRequest.mock.calls[0][0] + expect(jwsSigner).toBe(null) + expect(protocolVersions).toMatchObject({ accept: mockedConfig.PROTOCOL_VERSIONS.ACCEPT.DEFAULT, content: mockedConfig.PROTOCOL_VERSIONS.CONTENT.DEFAULT }) @@ -300,29 +259,8 @@ describe('participantEndpoint Facade', () => { it('adds jws signature when enabled', async () => { // Arrange - const mockedConfig = { - JWS_SIGN: true, - FSPIOP_SOURCE_TO_SIGN: 'switch', - JWS_SIGNING_KEY_PATH: 'secrets/jwsSigningKey.key', - JWS_SIGNING_KEY: 'somekey', - PROTOCOL_VERSIONS: { - CONTENT: { - DEFAULT: '2.1', - VALIDATELIST: [ - '2.1' - ] - }, - ACCEPT: { - DEFAULT: '2', - VALIDATELIST: [ - '2', - '2.1' - ] - } - } - } - - jest.mock('../../../../src/lib/config', () => (mockedConfig)) + const mockedConfig = mockConfigDto({ jwsSign: true }) + jest.mock('../../../../src/lib/config', () => mockedConfig) mockGetEndpoint.mockImplementation(() => 'https://example.com/parties/MSISDN12345') mockSendRequest.mockImplementation(() => Promise.resolve(true)) @@ -337,7 +275,7 @@ describe('participantEndpoint Facade', () => { } const headers = {} headers[mockEnums.Http.Headers.FSPIOP.DESTINATION] = 'fsp1' - headers[mockEnums.Http.Headers.FSPIOP.SOURCE] = 'switch' + headers[mockEnums.Http.Headers.FSPIOP.SOURCE] = mockHubName // Act const action = async () => ParticipantFacade.sendErrorToParticipant(participantName, endpointType, errorInformation, headers) @@ -345,8 +283,9 @@ describe('participantEndpoint Facade', () => { // Assert expect(spy).toHaveBeenCalled() - expect(typeof (mockSendRequest.mock.calls[0][8])).toBe('object') - expect(mockSendRequest.mock.calls[0][9]).toMatchObject({ + const { jwsSigner, protocolVersions } = mockSendRequest.mock.calls[0][0] + expect(jwsSigner).toBeTruthy() + expect(protocolVersions).toMatchObject({ accept: mockedConfig.PROTOCOL_VERSIONS.ACCEPT.DEFAULT, content: mockedConfig.PROTOCOL_VERSIONS.CONTENT.DEFAULT }) diff --git a/test/unit/plugins.test.js b/test/unit/plugins.test.js index de141fcf..ef748a1b 100644 --- a/test/unit/plugins.test.js +++ b/test/unit/plugins.test.js @@ -33,7 +33,7 @@ const OpenapiBackend = require('@mojaloop/central-services-shared').Util.Openapi const { registerPlugins } = require('../../src/plugins') const Config = require('../../src/lib/config') -const Handlers = require('../../src/handlers') +const Handlers = require('../../src/api') let sandbox diff --git a/test/unit/setup.js b/test/unit/setup.js new file mode 100644 index 00000000..64c9cfbf --- /dev/null +++ b/test/unit/setup.js @@ -0,0 +1,7 @@ +const { MockIoRedis } = require('./mocks') +jest.mock('ioredis', () => MockIoRedis) + +Object.assign(process.env, { + LOG_LEVEL: 'debug' + // override any other env vars needed for unit-tests +}) diff --git a/test/util/apiClients/AlsApiClient.js b/test/util/apiClients/AlsApiClient.js new file mode 100644 index 00000000..7b9eee96 --- /dev/null +++ b/test/util/apiClients/AlsApiClient.js @@ -0,0 +1,44 @@ +const config = require('../../../src/lib/config') +const BasicApiClient = require('./BasicApiClient') + +const baseURL = `http://localhost:${config.API_PORT}` + +class AlsProxyApiClient extends BasicApiClient { + constructor (deps) { + super({ ...deps, baseURL }) + } + + async getPartyByIdAndType ({ partyId, partyIdType, source, destination, proxy = '' }) { + return this.sendPartyRequest({ + partyId, partyIdType, source, destination, proxy + }) + } + + async putPartiesSuccess ({ partyId, partyIdType, source, destination, proxy = '', body = {} }) { + return this.sendPartyRequest({ + partyId, partyIdType, source, destination, proxy, body + }) + } + + async putPartiesError ({ partyId, partyIdType, source, destination, proxy = '', body = {} }) { + const isError = true + return this.sendPartyRequest({ + partyId, partyIdType, source, destination, proxy, body, isError + }) + } + + async sendPartyRequest ({ partyId, partyIdType, source, destination, proxy = '', body = null, isError = false }) { + const method = body ? 'PUT' : 'GET' + const url = `/parties/${partyIdType}/${partyId}${isError ? '/error' : ''}` + const headers = this.fixtures.partiesCallHeadersDto({ source, destination, proxy }) + + return this.sendRequest({ + method, + url, + headers, + body + }) + } +} + +module.exports = AlsProxyApiClient diff --git a/test/util/apiClients/BasicApiClient.js b/test/util/apiClients/BasicApiClient.js new file mode 100644 index 00000000..907de9a6 --- /dev/null +++ b/test/util/apiClients/BasicApiClient.js @@ -0,0 +1,34 @@ +const axiosLib = require('axios') +const { loggerFactory } = require('../../../src/lib') +const fixtures = require('../../fixtures') + +class BasicApiClient { + constructor ({ + baseURL, + axios = axiosLib.create({ baseURL }), + logger = loggerFactory(this.constructor.name) + } = {}) { + this.baseURL = baseURL + this.axios = axios + this.logger = logger + this.fixtures = fixtures + } + + async sendRequest ({ url, method = 'GET', headers = {}, body = null }) { + try { + const { data, status } = await this.axios.request({ + method, + url, + headers, + data: body + }) + this.logger.info('sendRequest is done:', { method, url, body, headers, response: { status, data } }) + return { data, status } + } catch (err) { + this.logger.error('error in sendRequest: ', err) + throw err + } + } +} + +module.exports = BasicApiClient diff --git a/test/util/apiClients/ProxyApiClient.js b/test/util/apiClients/ProxyApiClient.js new file mode 100644 index 00000000..dace736d --- /dev/null +++ b/test/util/apiClients/ProxyApiClient.js @@ -0,0 +1,25 @@ +const { PROXY_PORT } = require('../../integration/constants') +const BasicApiClient = require('./BasicApiClient') + +const baseURL = `http://localhost:${PROXY_PORT}` + +class ProxyApiClient extends BasicApiClient { + constructor (deps) { + super({ ...deps, baseURL }) + } + + async getHistory () { + const { data } = await this.sendRequest({ url: '/history' }) + return data.history + } + + async deleteHistory () { + const { data } = await this.sendRequest({ + url: '/history', + method: 'DELETE' + }) + return data.history + } +} + +module.exports = ProxyApiClient diff --git a/test/util/apiClients/index.js b/test/util/apiClients/index.js new file mode 100644 index 00000000..147d7e8a --- /dev/null +++ b/test/util/apiClients/index.js @@ -0,0 +1,7 @@ +const AlsApiClient = require('./AlsApiClient') +const ProxyApiClient = require('./ProxyApiClient') + +module.exports = { + AlsApiClient, + ProxyApiClient +} diff --git a/test/util/helper.js b/test/util/helper.js index 88828511..cd2b5ef0 100644 --- a/test/util/helper.js +++ b/test/util/helper.js @@ -33,7 +33,7 @@ const payerfsp = 'payerfsp' const payeefsp = 'payeefsp' const validatePayerFspUri = Mustache.render(Config.SWITCH_ENDPOINT + Enums.EndPoints.FspEndpointTemplates.PARTICIPANTS_GET, { fsp: payerfsp }) const validatePayeeFspUri = Mustache.render(Config.SWITCH_ENDPOINT + Enums.EndPoints.FspEndpointTemplates.PARTICIPANTS_GET, { fsp: payeefsp }) -const defaultSwitchHeaders = defaultHeaders(Enums.Http.HeaderResources.SWITCH, Enums.Http.HeaderResources.PARTICIPANTS, Enums.Http.HeaderResources.SWITCH) +const defaultSwitchHeaders = defaultHeaders(Config.HUB_NAME, Enums.Http.HeaderResources.PARTICIPANTS, Config.HUB_NAME) const defaultStandardHeaders = (resource = Enums.Http.HeaderResources.PARTICIPANTS) => defaultHeaders(payerfsp, resource, payeefsp) const getPayerfspEndpointsUri = Mustache.render(Config.SWITCH_ENDPOINT + Enums.EndPoints.FspEndpointTemplates.PARTICIPANT_ENDPOINTS_GET, { fsp: payerfsp }) const getPayeefspEndpointsUri = Mustache.render(Config.SWITCH_ENDPOINT + Enums.EndPoints.FspEndpointTemplates.PARTICIPANT_ENDPOINTS_GET, { fsp: payeefsp }) diff --git a/test/util/index.js b/test/util/index.js new file mode 100644 index 00000000..f23b5ad1 --- /dev/null +++ b/test/util/index.js @@ -0,0 +1,11 @@ +const onboarding = require('./onboarding') +const { + AlsApiClient, + ProxyApiClient +} = require('./apiClients') + +module.exports = { + onboarding, + AlsApiClient, + ProxyApiClient +} diff --git a/test/util/onboarding.js b/test/util/onboarding.js new file mode 100644 index 00000000..a435ce8e --- /dev/null +++ b/test/util/onboarding.js @@ -0,0 +1,129 @@ +const axios = require('axios') +const axiosRetry = require('axios-retry').default +const { FspEndpointTypes } = require('@mojaloop/central-services-shared').Enum.EndPoints +const Logger = require('@mojaloop/central-services-logger') +const config = require('../../src/lib/config') +const fixtures = require('../fixtures') +const { CL_PORT, PROXY_HOST, PROXY_PORT, PARTY_ID_TYPE } = require('../integration/constants') + +axiosRetry(axios, { retries: 5 }) + +const alsAdminUrl = `http://localhost:${config.ADMIN_PORT}` +const clUrl = `http://localhost:${CL_PORT}` +const proxyUrl = `http://${PROXY_HOST}:${PROXY_PORT}` + +const headers = { + 'Cache-Control': 'no-cache', + 'Content-Type': 'application/json', + 'FSPIOP-Source': 'util.createTestParticipant' +} + +const pause = async (ms = 1000) => new Promise(resolve => setTimeout(resolve, ms)) + +const createHubAccounts = async ({ + currency = 'EUR', + hubName = config.HUB_NAME +} = {}) => { + const opts = { headers } + + const accTypes = ['HUB_RECONCILIATION', 'HUB_MULTILATERAL_SETTLEMENT'] + const accCreating = accTypes.map(type => axios.post(`${clUrl}/participants/${hubName}/accounts`, { + currency, type + }, opts)) + const accounts = await Promise.all(accCreating) + + const settlModel = await axios.post(`${clUrl}/settlementModels`, { + name: 'DEFERREDNET', + settlementGranularity: 'NET', + settlementInterchange: 'MULTILATERAL', + settlementDelay: 'DEFERRED', + requireLiquidityCheck: true, + ledgerAccountType: 'POSITION', + autoPositionReset: true, + currency, + settlementAccountType: 'SETTLEMENT' + }, opts) + + Logger.info('createHubAccounts is finished') + return { + accounts, + settlModel + } +} + +const endpoints = [ + { + type: FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_PUT, + value: `${proxyUrl}/participants/{{partyIdType}}/{{partyIdentifier}}` + }, + { + type: FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_PUT_ERROR, + value: `${proxyUrl}/participants/{{partyIdType}}/{{partyIdentifier}}/error` + }, + { + type: FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_GET, + value: `${proxyUrl}/parties/{{partyIdType}}/{{partyIdentifier}}` + }, + { + type: FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_PUT, + value: `${proxyUrl}/parties/{{partyIdType}}/{{partyIdentifier}}` + }, + { + type: FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_PUT_ERROR, + value: `${proxyUrl}/parties/{{partyIdType}}/{{partyIdentifier}}/error` + } +] + +const createTestParticipant = async ({ + name = `testDfsp-${Date.now()}`, + currency = 'EUR', + isProxy = false +} = {}) => { + const opts = { headers } + + const participantCreated = await axios.post(`${clUrl}/participants`, { + name, currency, isProxy + }, opts) + await pause() + + const createEpUrl = `${clUrl}/participants/${name}/endpoints` + const addedEPs = [] + + for (const ep of endpoints) { + addedEPs.push(await axios.post(createEpUrl, ep, opts)) + } + + Logger.info(`createTestParticipant ${name} is finished`) + return { + participantCreated, + addedEPs + } +} + +const createOracle = async ({ + oracleIdType = PARTY_ID_TYPE, + currency = 'EUR', + endpointValue = `${proxyUrl}/oracle`, + isDefault = true +} = {}) => { + const headers = fixtures.participantsCallHeadersDto() + const body = { + oracleIdType, + endpoint: { + value: endpointValue, + endpointType: 'URL' + }, + currency, + isDefault + } + const oracle = await axios.post(`${alsAdminUrl}/oracles`, body, { headers }) + + Logger.info('createOracle is finished') + return oracle +} + +module.exports = { + createHubAccounts, + createTestParticipant, + createOracle +}