From 135ae5203d4e87891dde2c1d1cd343a2f01736f3 Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Sun, 3 Mar 2024 10:56:26 +0000 Subject: [PATCH 01/67] [PRMT-4565] store a working version of dynamodb localstack setup. --- docker-compose-itest.yml | 36 +- package-lock.json | 15955 +++++++++------- package.json | 5 +- scripts/create-dynamodb-table.sh | 11 + scripts/ehr-transfer-tracker-db.json | 59 + .../__tests__/dynamo-transfer-tracker.js | 15 + .../database/dynamo-transfer-tracker.js | 55 + 7 files changed, 9662 insertions(+), 6474 deletions(-) create mode 100755 scripts/create-dynamodb-table.sh create mode 100644 scripts/ehr-transfer-tracker-db.json create mode 100644 src/services/database/__tests__/dynamo-transfer-tracker.js create mode 100644 src/services/database/dynamo-transfer-tracker.js diff --git a/docker-compose-itest.yml b/docker-compose-itest.yml index a3153555..0d433fc8 100644 --- a/docker-compose-itest.yml +++ b/docker-compose-itest.yml @@ -3,7 +3,8 @@ services: default: links: - db:db - - localstack:localstack +# - localstack:localstack + - localstack-dynamo:localstack-dynamo ports: - "3000:3000" db: @@ -15,16 +16,27 @@ services: LC_ALL: C ports: - "5432:5432" - localstack: - image: localstack/localstack:0.10.9 +# localstack: +# image: localstack/localstack:0.10.9 +# environment: +# - SERVICES=s3:4572,dynamodb +# - S3_BUCKET_NAME=test-bucket +# - LOCALSTACK_URL=http://localstack:4572 +# ports: +# - "4572:4572" +# volumes: +# - ./scripts/create-bucket.sh:/docker-entrypoint-initaws.d/create-bucket.sh +# - /var/run/docker.sock:/var/run/docker.sock +# logging: +# driver: none + localstack-dynamo: + image: localstack/localstack environment: - - SERVICES=s3:4572 - - S3_BUCKET_NAME=test-bucket - - LOCALSTACK_URL=http://localstack:4572 - ports: - - "4572:4572" + - SERVICES=dynamodb + - DEBUG=1 + - DYNAMODB_SHARE_DB=1 volumes: - - ./scripts/create-bucket.sh:/docker-entrypoint-initaws.d/create-bucket.sh - - /var/run/docker.sock:/var/run/docker.sock - logging: - driver: none \ No newline at end of file + - ./scripts/ehr-transfer-tracker-db.json:/etc/localstack/init/ready.d/ehr-transfer-tracker-db.json + - ./scripts/create-dynamodb-table.sh:/etc/localstack/init/ready.d/create-dynamodb-table.sh + ports: + - "4573:4566" \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 67080cd0..5f9d5f77 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,8 @@ "name": "prm-deductions-ehr-repository", "version": "1.0.0", "dependencies": { + "@aws-sdk/client-dynamodb": "^3.525.0", + "@aws-sdk/lib-dynamodb": "^3.525.0", "@babel/runtime": "^7.17.8", "async-local-storage": "^2.3.1", "aws-sdk": "^2.1358.0", @@ -18,6 +20,7 @@ "helmet": "^6.0.0", "lodash.clonedeep": "^4.5.0", "lodash.merge": "^4.6.2", + "moment": "^2.30.1", "pg": "^8.7.3", "pg-hstore": "^2.3.4", "sequelize": "^6.33.0", @@ -71,6 +74,788 @@ "node": ">=6.0.0" } }, + "node_modules/@aws-crypto/crc32": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-3.0.0.tgz", + "integrity": "sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==", + "dependencies": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/ie11-detection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-3.0.0.tgz", + "integrity": "sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==", + "dependencies": { + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/sha256-browser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-3.0.0.tgz", + "integrity": "sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==", + "dependencies": { + "@aws-crypto/ie11-detection": "^3.0.0", + "@aws-crypto/sha256-js": "^3.0.0", + "@aws-crypto/supports-web-crypto": "^3.0.0", + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/sha256-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-3.0.0.tgz", + "integrity": "sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==", + "dependencies": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-3.0.0.tgz", + "integrity": "sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==", + "dependencies": { + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/util": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-3.0.0.tgz", + "integrity": "sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-sdk/client-dynamodb": { + "version": "3.525.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-dynamodb/-/client-dynamodb-3.525.0.tgz", + "integrity": "sha512-leL9TP4W8pjzNFf2/yqyphpEvBxVScXu8OB6fKdONiya6TNuR8tU+fcrcacNQvKs3hok7jKdfa8PaQpZ/bv3Ug==", + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/client-sts": "3.525.0", + "@aws-sdk/core": "3.525.0", + "@aws-sdk/credential-provider-node": "3.525.0", + "@aws-sdk/middleware-endpoint-discovery": "3.525.0", + "@aws-sdk/middleware-host-header": "3.523.0", + "@aws-sdk/middleware-logger": "3.523.0", + "@aws-sdk/middleware-recursion-detection": "3.523.0", + "@aws-sdk/middleware-user-agent": "3.525.0", + "@aws-sdk/region-config-resolver": "3.525.0", + "@aws-sdk/types": "3.523.0", + "@aws-sdk/util-endpoints": "3.525.0", + "@aws-sdk/util-user-agent-browser": "3.523.0", + "@aws-sdk/util-user-agent-node": "3.525.0", + "@smithy/config-resolver": "^2.1.4", + "@smithy/core": "^1.3.5", + "@smithy/fetch-http-handler": "^2.4.3", + "@smithy/hash-node": "^2.1.3", + "@smithy/invalid-dependency": "^2.1.3", + "@smithy/middleware-content-length": "^2.1.3", + "@smithy/middleware-endpoint": "^2.4.4", + "@smithy/middleware-retry": "^2.1.4", + "@smithy/middleware-serde": "^2.1.3", + "@smithy/middleware-stack": "^2.1.3", + "@smithy/node-config-provider": "^2.2.4", + "@smithy/node-http-handler": "^2.4.1", + "@smithy/protocol-http": "^3.2.1", + "@smithy/smithy-client": "^2.4.2", + "@smithy/types": "^2.10.1", + "@smithy/url-parser": "^2.1.3", + "@smithy/util-base64": "^2.1.1", + "@smithy/util-body-length-browser": "^2.1.1", + "@smithy/util-body-length-node": "^2.2.1", + "@smithy/util-defaults-mode-browser": "^2.1.4", + "@smithy/util-defaults-mode-node": "^2.2.3", + "@smithy/util-endpoints": "^1.1.4", + "@smithy/util-middleware": "^2.1.3", + "@smithy/util-retry": "^2.1.3", + "@smithy/util-utf8": "^2.1.1", + "@smithy/util-waiter": "^2.1.3", + "tslib": "^2.5.0", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-dynamodb/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@aws-sdk/client-dynamodb/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@aws-sdk/client-sso": { + "version": "3.525.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.525.0.tgz", + "integrity": "sha512-6KwGQWFoNLH1UupdWPFdKPfTgjSz1kN8/r8aCzuvvXBe4Pz+iDUZ6FEJzGWNc9AapjvZDNO1hs23slomM9rTaA==", + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/core": "3.525.0", + "@aws-sdk/middleware-host-header": "3.523.0", + "@aws-sdk/middleware-logger": "3.523.0", + "@aws-sdk/middleware-recursion-detection": "3.523.0", + "@aws-sdk/middleware-user-agent": "3.525.0", + "@aws-sdk/region-config-resolver": "3.525.0", + "@aws-sdk/types": "3.523.0", + "@aws-sdk/util-endpoints": "3.525.0", + "@aws-sdk/util-user-agent-browser": "3.523.0", + "@aws-sdk/util-user-agent-node": "3.525.0", + "@smithy/config-resolver": "^2.1.4", + "@smithy/core": "^1.3.5", + "@smithy/fetch-http-handler": "^2.4.3", + "@smithy/hash-node": "^2.1.3", + "@smithy/invalid-dependency": "^2.1.3", + "@smithy/middleware-content-length": "^2.1.3", + "@smithy/middleware-endpoint": "^2.4.4", + "@smithy/middleware-retry": "^2.1.4", + "@smithy/middleware-serde": "^2.1.3", + "@smithy/middleware-stack": "^2.1.3", + "@smithy/node-config-provider": "^2.2.4", + "@smithy/node-http-handler": "^2.4.1", + "@smithy/protocol-http": "^3.2.1", + "@smithy/smithy-client": "^2.4.2", + "@smithy/types": "^2.10.1", + "@smithy/url-parser": "^2.1.3", + "@smithy/util-base64": "^2.1.1", + "@smithy/util-body-length-browser": "^2.1.1", + "@smithy/util-body-length-node": "^2.2.1", + "@smithy/util-defaults-mode-browser": "^2.1.4", + "@smithy/util-defaults-mode-node": "^2.2.3", + "@smithy/util-endpoints": "^1.1.4", + "@smithy/util-middleware": "^2.1.3", + "@smithy/util-retry": "^2.1.3", + "@smithy/util-utf8": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.525.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.525.0.tgz", + "integrity": "sha512-zz13k/6RkjPSLmReSeGxd8wzGiiZa4Odr2Tv3wTcxClM4wOjD+zOgGv4Fe32b9AMqaueiCdjbvdu7AKcYxFA4A==", + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/client-sts": "3.525.0", + "@aws-sdk/core": "3.525.0", + "@aws-sdk/middleware-host-header": "3.523.0", + "@aws-sdk/middleware-logger": "3.523.0", + "@aws-sdk/middleware-recursion-detection": "3.523.0", + "@aws-sdk/middleware-user-agent": "3.525.0", + "@aws-sdk/region-config-resolver": "3.525.0", + "@aws-sdk/types": "3.523.0", + "@aws-sdk/util-endpoints": "3.525.0", + "@aws-sdk/util-user-agent-browser": "3.523.0", + "@aws-sdk/util-user-agent-node": "3.525.0", + "@smithy/config-resolver": "^2.1.4", + "@smithy/core": "^1.3.5", + "@smithy/fetch-http-handler": "^2.4.3", + "@smithy/hash-node": "^2.1.3", + "@smithy/invalid-dependency": "^2.1.3", + "@smithy/middleware-content-length": "^2.1.3", + "@smithy/middleware-endpoint": "^2.4.4", + "@smithy/middleware-retry": "^2.1.4", + "@smithy/middleware-serde": "^2.1.3", + "@smithy/middleware-stack": "^2.1.3", + "@smithy/node-config-provider": "^2.2.4", + "@smithy/node-http-handler": "^2.4.1", + "@smithy/protocol-http": "^3.2.1", + "@smithy/smithy-client": "^2.4.2", + "@smithy/types": "^2.10.1", + "@smithy/url-parser": "^2.1.3", + "@smithy/util-base64": "^2.1.1", + "@smithy/util-body-length-browser": "^2.1.1", + "@smithy/util-body-length-node": "^2.2.1", + "@smithy/util-defaults-mode-browser": "^2.1.4", + "@smithy/util-defaults-mode-node": "^2.2.3", + "@smithy/util-endpoints": "^1.1.4", + "@smithy/util-middleware": "^2.1.3", + "@smithy/util-retry": "^2.1.3", + "@smithy/util-utf8": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "@aws-sdk/credential-provider-node": "^3.525.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@aws-sdk/client-sso/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@aws-sdk/client-sts": { + "version": "3.525.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.525.0.tgz", + "integrity": "sha512-a8NUGRvO6rkfTZCbMaCsjDjLbERCwIUU9dIywFYcRgbFhkupJ7fSaZz3Het98U51M9ZbTEpaTa3fz0HaJv8VJw==", + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/core": "3.525.0", + "@aws-sdk/middleware-host-header": "3.523.0", + "@aws-sdk/middleware-logger": "3.523.0", + "@aws-sdk/middleware-recursion-detection": "3.523.0", + "@aws-sdk/middleware-user-agent": "3.525.0", + "@aws-sdk/region-config-resolver": "3.525.0", + "@aws-sdk/types": "3.523.0", + "@aws-sdk/util-endpoints": "3.525.0", + "@aws-sdk/util-user-agent-browser": "3.523.0", + "@aws-sdk/util-user-agent-node": "3.525.0", + "@smithy/config-resolver": "^2.1.4", + "@smithy/core": "^1.3.5", + "@smithy/fetch-http-handler": "^2.4.3", + "@smithy/hash-node": "^2.1.3", + "@smithy/invalid-dependency": "^2.1.3", + "@smithy/middleware-content-length": "^2.1.3", + "@smithy/middleware-endpoint": "^2.4.4", + "@smithy/middleware-retry": "^2.1.4", + "@smithy/middleware-serde": "^2.1.3", + "@smithy/middleware-stack": "^2.1.3", + "@smithy/node-config-provider": "^2.2.4", + "@smithy/node-http-handler": "^2.4.1", + "@smithy/protocol-http": "^3.2.1", + "@smithy/smithy-client": "^2.4.2", + "@smithy/types": "^2.10.1", + "@smithy/url-parser": "^2.1.3", + "@smithy/util-base64": "^2.1.1", + "@smithy/util-body-length-browser": "^2.1.1", + "@smithy/util-body-length-node": "^2.2.1", + "@smithy/util-defaults-mode-browser": "^2.1.4", + "@smithy/util-defaults-mode-node": "^2.2.3", + "@smithy/util-endpoints": "^1.1.4", + "@smithy/util-middleware": "^2.1.3", + "@smithy/util-retry": "^2.1.3", + "@smithy/util-utf8": "^2.1.1", + "fast-xml-parser": "4.2.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "@aws-sdk/credential-provider-node": "^3.525.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@aws-sdk/core": { + "version": "3.525.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.525.0.tgz", + "integrity": "sha512-E3LtEtMWCriQOFZpVKpLYzbdw/v2PAOEAMhn2VRRZ1g0/g1TXzQrfhEU2yd8l/vQEJaCJ82ooGGg7YECviBUxA==", + "dependencies": { + "@smithy/core": "^1.3.5", + "@smithy/protocol-http": "^3.2.1", + "@smithy/signature-v4": "^2.1.3", + "@smithy/smithy-client": "^2.4.2", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/core/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.523.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.523.0.tgz", + "integrity": "sha512-Y6DWdH6/OuMDoNKVzZlNeBc6f1Yjk1lYMjANKpIhMbkRCvLJw/PYZKOZa8WpXbTYdgg9XLjKybnLIb3ww3uuzA==", + "dependencies": { + "@aws-sdk/types": "3.523.0", + "@smithy/property-provider": "^2.1.3", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.525.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.525.0.tgz", + "integrity": "sha512-RNWQGuSBQZhl3iqklOslUEfQ4br1V3DCPboMpeqFtddUWJV3m2u2extFur9/4Uy+1EHVF120IwZUKtd8dF+ibw==", + "dependencies": { + "@aws-sdk/types": "3.523.0", + "@smithy/fetch-http-handler": "^2.4.3", + "@smithy/node-http-handler": "^2.4.1", + "@smithy/property-provider": "^2.1.3", + "@smithy/protocol-http": "^3.2.1", + "@smithy/smithy-client": "^2.4.2", + "@smithy/types": "^2.10.1", + "@smithy/util-stream": "^2.1.3", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.525.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.525.0.tgz", + "integrity": "sha512-JDnccfK5JRb9jcgpc9lirL9PyCwGIqY0nKdw3LlX5WL5vTpTG4E1q7rLAlpNh7/tFD1n66Itarfv2tsyHMIqCw==", + "dependencies": { + "@aws-sdk/client-sts": "3.525.0", + "@aws-sdk/credential-provider-env": "3.523.0", + "@aws-sdk/credential-provider-process": "3.523.0", + "@aws-sdk/credential-provider-sso": "3.525.0", + "@aws-sdk/credential-provider-web-identity": "3.525.0", + "@aws-sdk/types": "3.523.0", + "@smithy/credential-provider-imds": "^2.2.3", + "@smithy/property-provider": "^2.1.3", + "@smithy/shared-ini-file-loader": "^2.3.3", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.525.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.525.0.tgz", + "integrity": "sha512-RJXlO8goGXpnoHQAyrCcJ0QtWEOFa34LSbfdqBIjQX/fwnjUuEmiGdXTV3AZmwYQ7juk49tfBneHbtOP3AGqsQ==", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.523.0", + "@aws-sdk/credential-provider-http": "3.525.0", + "@aws-sdk/credential-provider-ini": "3.525.0", + "@aws-sdk/credential-provider-process": "3.523.0", + "@aws-sdk/credential-provider-sso": "3.525.0", + "@aws-sdk/credential-provider-web-identity": "3.525.0", + "@aws-sdk/types": "3.523.0", + "@smithy/credential-provider-imds": "^2.2.3", + "@smithy/property-provider": "^2.1.3", + "@smithy/shared-ini-file-loader": "^2.3.3", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.523.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.523.0.tgz", + "integrity": "sha512-f0LP9KlFmMvPWdKeUKYlZ6FkQAECUeZMmISsv6NKtvPCI9e4O4cLTeR09telwDK8P0HrgcRuZfXM7E30m8re0Q==", + "dependencies": { + "@aws-sdk/types": "3.523.0", + "@smithy/property-provider": "^2.1.3", + "@smithy/shared-ini-file-loader": "^2.3.3", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.525.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.525.0.tgz", + "integrity": "sha512-7V7ybtufxdD3plxeIeB6aqHZeFIUlAyPphXIUgXrGY10iNcosL970rQPBeggsohe4gCM6UvY2TfMeEcr+ZE8FA==", + "dependencies": { + "@aws-sdk/client-sso": "3.525.0", + "@aws-sdk/token-providers": "3.525.0", + "@aws-sdk/types": "3.523.0", + "@smithy/property-provider": "^2.1.3", + "@smithy/shared-ini-file-loader": "^2.3.3", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.525.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.525.0.tgz", + "integrity": "sha512-sAukOjR1oKb2JXG4nPpuBFpSwGUhrrY17PG/xbTy8NAoLLhrqRwnErcLfdTfmj6tH+3094k6ws/Sh8a35ae7fA==", + "dependencies": { + "@aws-sdk/client-sts": "3.525.0", + "@aws-sdk/types": "3.523.0", + "@smithy/property-provider": "^2.1.3", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@aws-sdk/endpoint-cache": { + "version": "3.495.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/endpoint-cache/-/endpoint-cache-3.495.0.tgz", + "integrity": "sha512-XCDrpiS50WaPzPzp7FwsChPHtX9PQQUU4nRzcn2N7IkUtpcFCUx8m1PAZe086VQr6hrbdeE4Z4j8hUPNwVdJGQ==", + "dependencies": { + "mnemonist": "0.38.3", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/endpoint-cache/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@aws-sdk/lib-dynamodb": { + "version": "3.525.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/lib-dynamodb/-/lib-dynamodb-3.525.0.tgz", + "integrity": "sha512-ED1YDzon/aAdXy4eUwFApXABxI+QM6THY5BSNrsGBQPxvYjMNQ2PGKMwbFPuMbNjZi48c6YpQyIF2cD7cEXG/A==", + "dependencies": { + "@aws-sdk/util-dynamodb": "3.525.0", + "@smithy/smithy-client": "^2.4.2", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-dynamodb": "^3.0.0" + } + }, + "node_modules/@aws-sdk/lib-dynamodb/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@aws-sdk/middleware-endpoint-discovery": { + "version": "3.525.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-endpoint-discovery/-/middleware-endpoint-discovery-3.525.0.tgz", + "integrity": "sha512-nT/XYP3RDRWPFCTEOZQbOC3HWmUkxB0fDuobmH8WzL92MCBGz9gBG/q9XBxiw9pHk9Dky/MIkLV50BlGB3kM7g==", + "dependencies": { + "@aws-sdk/endpoint-cache": "3.495.0", + "@aws-sdk/types": "3.523.0", + "@smithy/node-config-provider": "^2.2.4", + "@smithy/protocol-http": "^3.2.1", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-endpoint-discovery/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.523.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.523.0.tgz", + "integrity": "sha512-4g3q7Ta9sdD9TMUuohBAkbx/e3I/juTqfKi7TPgP+8jxcYX72MOsgemAMHuP6CX27eyj4dpvjH+w4SIVDiDSmg==", + "dependencies": { + "@aws-sdk/types": "3.523.0", + "@smithy/protocol-http": "^3.2.1", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-host-header/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.523.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.523.0.tgz", + "integrity": "sha512-PeDNJNhfiaZx54LBaLTXzUaJ9LXFwDFFIksipjqjvxMafnoVcQwKbkoPUWLe5ytT4nnL1LogD3s55mERFUsnwg==", + "dependencies": { + "@aws-sdk/types": "3.523.0", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-logger/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.523.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.523.0.tgz", + "integrity": "sha512-nZ3Vt7ehfSDYnrcg/aAfjjvpdE+61B3Zk68i6/hSUIegT3IH9H1vSW67NDKVp+50hcEfzWwM2HMPXxlzuyFyrw==", + "dependencies": { + "@aws-sdk/types": "3.523.0", + "@smithy/protocol-http": "^3.2.1", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-recursion-detection/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.525.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.525.0.tgz", + "integrity": "sha512-4al/6uO+t/QIYXK2OgqzDKQzzLAYJza1vWFS+S0lJ3jLNGyLB5BMU5KqWjDzevYZ4eCnz2Nn7z0FveUTNz8YdQ==", + "dependencies": { + "@aws-sdk/types": "3.523.0", + "@aws-sdk/util-endpoints": "3.525.0", + "@smithy/protocol-http": "^3.2.1", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-user-agent/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@aws-sdk/region-config-resolver": { + "version": "3.525.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.525.0.tgz", + "integrity": "sha512-8kFqXk6UyKgTMi7N7QlhA6qM4pGPWbiUXqEY2RgUWngtxqNFGeM9JTexZeuavQI+qLLe09VPShPNX71fEDcM6w==", + "dependencies": { + "@aws-sdk/types": "3.523.0", + "@smithy/node-config-provider": "^2.2.4", + "@smithy/types": "^2.10.1", + "@smithy/util-config-provider": "^2.2.1", + "@smithy/util-middleware": "^2.1.3", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/region-config-resolver/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.525.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.525.0.tgz", + "integrity": "sha512-puVjbxuK0Dq7PTQ2HdddHy2eQjOH8GZbump74yWJa6JVpRW84LlOcNmP+79x4Kscvz2ldWB8XDFw/pcCiSDe5A==", + "dependencies": { + "@aws-sdk/client-sso-oidc": "3.525.0", + "@aws-sdk/types": "3.523.0", + "@smithy/property-provider": "^2.1.3", + "@smithy/shared-ini-file-loader": "^2.3.3", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/token-providers/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@aws-sdk/types": { + "version": "3.523.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.523.0.tgz", + "integrity": "sha512-AqGIu4u+SxPiUuNBp2acCVcq80KDUFjxe6e3cMTvKWTzCbrVk1AXv0dAaJnCmdkWIha6zJDWxpIk/aL4EGhZ9A==", + "dependencies": { + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/types/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@aws-sdk/util-dynamodb": { + "version": "3.525.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-dynamodb/-/util-dynamodb-3.525.0.tgz", + "integrity": "sha512-2PXpWcMfD65d34y4Dd2OGpLmUYW6vegaITUOk95QwZOwMBUPJRrQB6AyGMf1z39Ej7utj7yJSqQKgqyw2i2U+A==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-dynamodb": "^3.0.0" + } + }, + "node_modules/@aws-sdk/util-dynamodb/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.525.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.525.0.tgz", + "integrity": "sha512-DIW7WWU5tIGkeeKX6NJUyrEIdWMiqjLQG3XBzaUj+ufIENwNjdAHhlD8l2vX7Yr3JZRT6yN/84wBCj7Tw1xd1g==", + "dependencies": { + "@aws-sdk/types": "3.523.0", + "@smithy/types": "^2.10.1", + "@smithy/util-endpoints": "^1.1.4", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-endpoints/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.495.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.495.0.tgz", + "integrity": "sha512-MfaPXT0kLX2tQaR90saBT9fWQq2DHqSSJRzW+MZWsmF+y5LGCOhO22ac/2o6TKSQm7h0HRc2GaADqYYYor62yg==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-locate-window/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.523.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.523.0.tgz", + "integrity": "sha512-6ZRNdGHX6+HQFqTbIA5+i8RWzxFyxsZv8D3soRfpdyWIKkzhSz8IyRKXRciwKBJDaC7OX2jzGE90wxRQft27nA==", + "dependencies": { + "@aws-sdk/types": "3.523.0", + "@smithy/types": "^2.10.1", + "bowser": "^2.11.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-browser/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.525.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.525.0.tgz", + "integrity": "sha512-88Wjt4efyUSBGcyIuh1dvoMqY1k15jpJc5A/3yi67clBQEFsu9QCodQCQPqmRjV3VRcMtBOk+jeCTiUzTY5dRQ==", + "dependencies": { + "@aws-sdk/types": "3.523.0", + "@smithy/node-config-provider": "^2.2.4", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/util-user-agent-node/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@aws-sdk/util-utf8-browser": { + "version": "3.259.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz", + "integrity": "sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==", + "dependencies": { + "tslib": "^2.3.1" + } + }, + "node_modules/@aws-sdk/util-utf8-browser/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, "node_modules/@babel/cli": { "version": "7.23.4", "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.23.4.tgz", @@ -2915,3218 +3700,3213 @@ "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz", "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==" }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, + "node_modules/@smithy/abort-controller": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-2.1.3.tgz", + "integrity": "sha512-c2aYH2Wu1RVE3rLlVgg2kQOBJGM0WbjReQi5DnPTm2Zb7F0gk7J2aeQeaX2u/lQZoHl6gv8Oac7mt9alU3+f4A==", "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" } }, - "node_modules/@types/babel__generator": { - "version": "7.6.7", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.7.tgz", - "integrity": "sha512-6Sfsq+EaaLrw4RmdFWE9Onp63TOUue71AWb4Gpa6JxzgTYtimbM086WnYTy2U67AofR++QKCo08ZP6pwx8YFHQ==", - "dev": true, - "dependencies": { - "@babel/types": "^7.0.0" - } + "node_modules/@smithy/abort-controller/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, + "node_modules/@smithy/config-resolver": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-2.1.4.tgz", + "integrity": "sha512-AW2WUZmBAzgO3V3ovKtsUbI3aBNMeQKFDumoqkNxaVDWF/xfnxAWqBKDr/NuG7c06N2Rm4xeZLPiJH/d+na0HA==", "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" + "@smithy/node-config-provider": "^2.2.4", + "@smithy/types": "^2.10.1", + "@smithy/util-config-provider": "^2.2.1", + "@smithy/util-middleware": "^2.1.3", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" } }, - "node_modules/@types/babel__traverse": { - "version": "7.20.4", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.4.tgz", - "integrity": "sha512-mSM/iKUk5fDDrEV/e83qY+Cr3I1+Q3qqTuEn++HAWYjEa1+NxZr6CNrcJGf2ZTnq4HoFGC3zaTPZTobCzCFukA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.20.7" - } + "node_modules/@smithy/config-resolver/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, - "node_modules/@types/debug": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", - "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "node_modules/@smithy/core": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-1.3.5.tgz", + "integrity": "sha512-Rrc+e2Jj6Gu7Xbn0jvrzZlSiP2CZocIOfZ9aNUA82+1sa6GBnxqL9+iZ9EKHeD9aqD1nU8EK4+oN2EiFpSv7Yw==", "dependencies": { - "@types/ms": "*" + "@smithy/middleware-endpoint": "^2.4.4", + "@smithy/middleware-retry": "^2.1.4", + "@smithy/middleware-serde": "^2.1.3", + "@smithy/protocol-http": "^3.2.1", + "@smithy/smithy-client": "^2.4.2", + "@smithy/types": "^2.10.1", + "@smithy/util-middleware": "^2.1.3", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" } }, - "node_modules/@types/graceful-fs": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", - "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", - "dev": true, + "node_modules/@smithy/core/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@smithy/credential-provider-imds": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-2.2.4.tgz", + "integrity": "sha512-DdatjmBZQnhGe1FhI8gO98f7NmvQFSDiZTwC3WMvLTCKQUY+Y1SVkhJqIuLu50Eb7pTheoXQmK+hKYUgpUWsNA==", "dependencies": { - "@types/node": "*" + "@smithy/node-config-provider": "^2.2.4", + "@smithy/property-provider": "^2.1.3", + "@smithy/types": "^2.10.1", + "@smithy/url-parser": "^2.1.3", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" } }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "dev": true + "node_modules/@smithy/credential-provider-imds/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", - "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", - "dev": true, + "node_modules/@smithy/eventstream-codec": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-2.1.3.tgz", + "integrity": "sha512-rGlCVuwSDv6qfKH4/lRxFjcZQnIE0LZ3D4lkMHg7ZSltK9rA74r0VuGSvWVQ4N/d70VZPaniFhp4Z14QYZsa+A==", "dependencies": { - "@types/istanbul-lib-coverage": "*" + "@aws-crypto/crc32": "3.0.0", + "@smithy/types": "^2.10.1", + "@smithy/util-hex-encoding": "^2.1.1", + "tslib": "^2.5.0" } }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", - "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", - "dev": true, + "node_modules/@smithy/eventstream-codec/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@smithy/fetch-http-handler": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-2.4.3.tgz", + "integrity": "sha512-Fn/KYJFo6L5I4YPG8WQb2hOmExgRmNpVH5IK2zU3JKrY5FKW7y9ar5e0BexiIC9DhSKqKX+HeWq/Y18fq7Dkpw==", "dependencies": { - "@types/istanbul-lib-report": "*" + "@smithy/protocol-http": "^3.2.1", + "@smithy/querystring-builder": "^2.1.3", + "@smithy/types": "^2.10.1", + "@smithy/util-base64": "^2.1.1", + "tslib": "^2.5.0" } }, - "node_modules/@types/json-schema": { - "version": "7.0.10", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/ms": { - "version": "0.7.34", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", - "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==" - }, - "node_modules/@types/node": { - "version": "17.0.23", - "license": "MIT" - }, - "node_modules/@types/prettier": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", - "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", - "dev": true + "node_modules/@smithy/fetch-http-handler/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, - "node_modules/@types/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", - "dev": true + "node_modules/@smithy/hash-node": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-2.1.3.tgz", + "integrity": "sha512-FsAPCUj7VNJIdHbSxMd5uiZiF20G2zdSDgrgrDrHqIs/VMxK85Vqk5kMVNNDMCZmMezp6UKnac0B4nAyx7HJ9g==", + "dependencies": { + "@smithy/types": "^2.10.1", + "@smithy/util-buffer-from": "^2.1.1", + "@smithy/util-utf8": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } }, - "node_modules/@types/validator": { - "version": "13.11.7", - "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.11.7.tgz", - "integrity": "sha512-q0JomTsJ2I5Mv7dhHhQLGjMvX0JJm5dyZ1DXQySIUzU1UlwzB8bt+R6+LODUbz0UDIOvEzGc28tk27gBJw2N8Q==" + "node_modules/@smithy/hash-node/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, - "node_modules/@types/yargs": { - "version": "17.0.32", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", - "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", - "dev": true, + "node_modules/@smithy/invalid-dependency": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-2.1.3.tgz", + "integrity": "sha512-wkra7d/G4CbngV4xsjYyAYOvdAhahQje/WymuQdVEnXFExJopEu7fbL5AEAlBPgWHXwu94VnCSG00gVzRfExyg==", "dependencies": { - "@types/yargs-parser": "*" + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" } }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "dev": true + "node_modules/@smithy/invalid-dependency/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "5.16.0", - "dev": true, - "license": "MIT", + "node_modules/@smithy/is-array-buffer": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.1.1.tgz", + "integrity": "sha512-xozSQrcUinPpNPNPds4S7z/FakDTh1MZWtRP/2vQtYB/u3HYrX2UXuZs+VhaKBd6Vc7g2XPr2ZtwGBNDN6fNKQ==", "dependencies": { - "@typescript-eslint/types": "5.16.0", - "@typescript-eslint/visitor-keys": "5.16.0" + "tslib": "^2.5.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=14.0.0" } }, - "node_modules/@typescript-eslint/types": { - "version": "5.16.0", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node_modules/@smithy/is-array-buffer/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@smithy/middleware-content-length": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-2.1.3.tgz", + "integrity": "sha512-aJduhkC+dcXxdnv5ZpM3uMmtGmVFKx412R1gbeykS5HXDmRU6oSsyy2SoHENCkfOGKAQOjVE2WVqDJibC0d21g==", + "dependencies": { + "@smithy/protocol-http": "^3.2.1", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "engines": { + "node": ">=14.0.0" } }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.16.0", - "dev": true, - "license": "BSD-2-Clause", + "node_modules/@smithy/middleware-content-length/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@smithy/middleware-endpoint": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-2.4.4.tgz", + "integrity": "sha512-4yjHyHK2Jul4JUDBo2sTsWY9UshYUnXeb/TAK/MTaPEb8XQvDmpwSFnfIRDU45RY1a6iC9LCnmJNg/yHyfxqkw==", "dependencies": { - "@typescript-eslint/types": "5.16.0", - "@typescript-eslint/visitor-keys": "5.16.0", - "debug": "^4.3.2", - "globby": "^11.0.4", - "is-glob": "^4.0.3", - "semver": "^7.3.5", - "tsutils": "^3.21.0" + "@smithy/middleware-serde": "^2.1.3", + "@smithy/node-config-provider": "^2.2.4", + "@smithy/shared-ini-file-loader": "^2.3.4", + "@smithy/types": "^2.10.1", + "@smithy/url-parser": "^2.1.3", + "@smithy/util-middleware": "^2.1.3", + "tslib": "^2.5.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "node": ">=14.0.0" } }, - "node_modules/@typescript-eslint/utils": { - "version": "5.16.0", - "dev": true, - "license": "MIT", + "node_modules/@smithy/middleware-endpoint/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@smithy/middleware-retry": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-2.1.4.tgz", + "integrity": "sha512-Cyolv9YckZTPli1EkkaS39UklonxMd08VskiuMhURDjC0HHa/AD6aK/YoD21CHv9s0QLg0WMLvk9YeLTKkXaFQ==", "dependencies": { - "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.16.0", - "@typescript-eslint/types": "5.16.0", - "@typescript-eslint/typescript-estree": "5.16.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0" + "@smithy/node-config-provider": "^2.2.4", + "@smithy/protocol-http": "^3.2.1", + "@smithy/service-error-classification": "^2.1.3", + "@smithy/smithy-client": "^2.4.2", + "@smithy/types": "^2.10.1", + "@smithy/util-middleware": "^2.1.3", + "@smithy/util-retry": "^2.1.3", + "tslib": "^2.5.0", + "uuid": "^8.3.2" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "node": ">=14.0.0" } }, - "node_modules/@typescript-eslint/utils/node_modules/eslint-scope": { - "version": "5.1.1", - "dev": true, - "license": "BSD-2-Clause", + "node_modules/@smithy/middleware-retry/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@smithy/middleware-serde": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-2.1.3.tgz", + "integrity": "sha512-s76LId+TwASrHhUa9QS4k/zeXDUAuNuddKklQzRgumbzge5BftVXHXIqL4wQxKGLocPwfgAOXWx+HdWhQk9hTg==", "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" }, "engines": { - "node": ">=8.0.0" + "node": ">=14.0.0" } }, - "node_modules/@typescript-eslint/utils/node_modules/estraverse": { - "version": "4.3.0", - "dev": true, - "license": "BSD-2-Clause", + "node_modules/@smithy/middleware-serde/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@smithy/middleware-stack": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-2.1.3.tgz", + "integrity": "sha512-opMFufVQgvBSld/b7mD7OOEBxF6STyraVr1xel1j0abVILM8ALJvRoFbqSWHGmaDlRGIiV9Q5cGbWi0sdiEaLQ==", + "dependencies": { + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, "engines": { - "node": ">=4.0" + "node": ">=14.0.0" } }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.16.0", - "dev": true, - "license": "MIT", + "node_modules/@smithy/middleware-stack/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@smithy/node-config-provider": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-2.2.4.tgz", + "integrity": "sha512-nqazHCp8r4KHSFhRQ+T0VEkeqvA0U+RhehBSr1gunUuNW3X7j0uDrWBxB2gE9eutzy6kE3Y7L+Dov/UXT871vg==", "dependencies": { - "@typescript-eslint/types": "5.16.0", - "eslint-visitor-keys": "^3.0.0" + "@smithy/property-provider": "^2.1.3", + "@smithy/shared-ini-file-loader": "^2.3.4", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=14.0.0" } }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true - }, - "node_modules/abbrev": { - "version": "1.1.1", - "dev": true, - "license": "ISC" + "node_modules/@smithy/node-config-provider/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, - "node_modules/accepts": { - "version": "1.3.8", - "license": "MIT", + "node_modules/@smithy/node-http-handler": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-2.4.1.tgz", + "integrity": "sha512-HCkb94soYhJMxPCa61wGKgmeKpJ3Gftx1XD6bcWEB2wMV1L9/SkQu/6/ysKBnbOzWRE01FGzwrTxucHypZ8rdg==", "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" + "@smithy/abort-controller": "^2.1.3", + "@smithy/protocol-http": "^3.2.1", + "@smithy/querystring-builder": "^2.1.3", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" }, "engines": { - "node": ">= 0.6" + "node": ">=14.0.0" } }, - "node_modules/acorn": { - "version": "8.11.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", - "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", - "dev": true, - "bin": { - "acorn": "bin/acorn" + "node_modules/@smithy/node-http-handler/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@smithy/property-provider": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-2.1.3.tgz", + "integrity": "sha512-bMz3se+ySKWNrgm7eIiQMa2HO/0fl2D0HvLAdg9pTMcpgp4SqOAh6bz7Ik6y7uQqSrk4rLjIKgbQ6yzYgGehCQ==", + "dependencies": { + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" }, "engines": { - "node": ">=0.4.0" + "node": ">=14.0.0" } }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } + "node_modules/@smithy/property-provider/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, + "node_modules/@smithy/protocol-http": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-3.2.1.tgz", + "integrity": "sha512-KLrQkEw4yJCeAmAH7hctE8g9KwA7+H2nSJwxgwIxchbp/L0B5exTdOQi9D5HinPLlothoervGmhpYKelZ6AxIA==", "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" + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "engines": { + "node": ">=14.0.0" } }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, + "node_modules/@smithy/protocol-http/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@smithy/querystring-builder": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-2.1.3.tgz", + "integrity": "sha512-kFD3PnNqKELe6m9GRHQw/ftFFSZpnSeQD4qvgDB6BQN6hREHELSosVFUMPN4M3MDKN2jAwk35vXHLoDrNfKu0A==", "dependencies": { - "type-fest": "^0.21.3" + "@smithy/types": "^2.10.1", + "@smithy/util-uri-escape": "^2.1.1", + "tslib": "^2.5.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=14.0.0" } }, - "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==", - "dev": true, + "node_modules/@smithy/querystring-builder/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@smithy/querystring-parser": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-2.1.3.tgz", + "integrity": "sha512-3+CWJoAqcBMR+yvz6D+Fc5VdoGFtfenW6wqSWATWajrRMGVwJGPT3Vy2eb2bnMktJc4HU4bpjeovFa566P3knQ==", + "dependencies": { + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, "engines": { - "node": ">=8" + "node": ">=14.0.0" } }, - "node_modules/ansi-styles": { - "version": "3.2.1", - "license": "MIT", + "node_modules/@smithy/querystring-parser/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@smithy/service-error-classification": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-2.1.3.tgz", + "integrity": "sha512-iUrpSsem97bbXHHT/v3s7vaq8IIeMo6P6cXdeYHrx0wOJpMeBGQF7CB0mbJSiTm3//iq3L55JiEm8rA7CTVI8A==", "dependencies": { - "color-convert": "^1.9.0" + "@smithy/types": "^2.10.1" }, "engines": { - "node": ">=4" + "node": ">=14.0.0" } }, - "node_modules/anymatch": { - "version": "3.1.2", - "dev": true, - "license": "ISC", + "node_modules/@smithy/shared-ini-file-loader": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-2.3.4.tgz", + "integrity": "sha512-CiZmPg9GeDKbKmJGEFvJBsJcFnh0AQRzOtQAzj1XEa8N/0/uSN/v1LYzgO7ry8hhO8+9KB7+DhSW0weqBra4Aw==", "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" }, "engines": { - "node": ">= 8" + "node": ">=14.0.0" } }, - "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/array-flatten": { - "version": "1.1.1", - "license": "MIT" + "node_modules/@smithy/shared-ini-file-loader/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, - "node_modules/array-union": { - "version": "2.1.0", - "dev": true, - "license": "MIT", + "node_modules/@smithy/signature-v4": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-2.1.3.tgz", + "integrity": "sha512-Jq4iPPdCmJojZTsPePn4r1ULShh6ONkokLuxp1Lnk4Sq7r7rJp4HlA1LbPBq4bD64TIzQezIpr1X+eh5NYkNxw==", + "dependencies": { + "@smithy/eventstream-codec": "^2.1.3", + "@smithy/is-array-buffer": "^2.1.1", + "@smithy/types": "^2.10.1", + "@smithy/util-hex-encoding": "^2.1.1", + "@smithy/util-middleware": "^2.1.3", + "@smithy/util-uri-escape": "^2.1.1", + "@smithy/util-utf8": "^2.1.1", + "tslib": "^2.5.0" + }, "engines": { - "node": ">=8" + "node": ">=14.0.0" } }, - "node_modules/asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "dev": true - }, - "node_modules/async": { - "version": "3.2.3", - "license": "MIT" + "node_modules/@smithy/signature-v4/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, - "node_modules/async-local-storage": { - "version": "2.3.1", - "license": "MIT", + "node_modules/@smithy/smithy-client": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-2.4.2.tgz", + "integrity": "sha512-ntAFYN51zu3N3mCd95YFcFi/8rmvm//uX+HnK24CRbI6k5Rjackn0JhgKz5zOx/tbNvOpgQIwhSX+1EvEsBLbA==", "dependencies": { - "nano-seconds": "^1.2.2" + "@smithy/middleware-endpoint": "^2.4.4", + "@smithy/middleware-stack": "^2.1.3", + "@smithy/protocol-http": "^3.2.1", + "@smithy/types": "^2.10.1", + "@smithy/util-stream": "^2.1.3", + "tslib": "^2.5.0" }, "engines": { - "node": ">=8.0.0" + "node": ">=14.0.0" } }, - "node_modules/asynckit": { - "version": "0.4.0", - "dev": true, - "license": "MIT" + "node_modules/@smithy/smithy-client/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, - "node_modules/at-least-node": { - "version": "1.0.0", - "dev": true, - "license": "ISC", + "node_modules/@smithy/types": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.10.1.tgz", + "integrity": "sha512-hjQO+4ru4cQ58FluQvKKiyMsFg0A6iRpGm2kqdH8fniyNd2WyanoOsYJfMX/IFLuLxEoW6gnRkNZy1y6fUUhtA==", + "dependencies": { + "tslib": "^2.5.0" + }, "engines": { - "node": ">= 4.0.0" + "node": ">=14.0.0" } }, - "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/@smithy/types/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, - "node_modules/audit-resolve-core/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/@smithy/url-parser": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-2.1.3.tgz", + "integrity": "sha512-X1NRA4WzK/ihgyzTpeGvI9Wn45y8HmqF4AZ/FazwAv8V203Ex+4lXqcYI70naX9ETqbqKVzFk88W6WJJzCggTQ==", + "dependencies": { + "@smithy/querystring-parser": "^2.1.3", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" } }, - "node_modules/available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "node_modules/@smithy/url-parser/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, - "node_modules/aws-sdk": { - "version": "2.1358.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1358.0.tgz", - "integrity": "sha512-ZolqFlnm0mDNgub7FGrVi7r5A1rw+58zZziKhlis3IxOtIpHdx4BQU5pH4htAMuD0Ct557p/dC/wmnZH/1Rc9Q==", + "node_modules/@smithy/util-base64": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-2.1.1.tgz", + "integrity": "sha512-UfHVpY7qfF/MrgndI5PexSKVTxSZIdz9InghTFa49QOvuu9I52zLPLUHXvHpNuMb1iD2vmc6R+zbv/bdMipR/g==", "dependencies": { - "buffer": "4.9.2", - "events": "1.1.1", - "ieee754": "1.1.13", - "jmespath": "0.16.0", - "querystring": "0.2.0", - "sax": "1.2.1", - "url": "0.10.3", - "util": "^0.12.4", - "uuid": "8.0.0", - "xml2js": "0.5.0" + "@smithy/util-buffer-from": "^2.1.1", + "tslib": "^2.5.0" }, "engines": { - "node": ">= 10.0.0" + "node": ">=14.0.0" } }, - "node_modules/aws-sdk/node_modules/uuid": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", - "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==", - "bin": { - "uuid": "dist/bin/uuid" - } + "node_modules/@smithy/util-base64/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, - "node_modules/axios": { - "version": "0.26.1", - "dev": true, - "license": "MIT", + "node_modules/@smithy/util-body-length-browser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-2.1.1.tgz", + "integrity": "sha512-ekOGBLvs1VS2d1zM2ER4JEeBWAvIOUKeaFch29UjjJsxmZ/f0L3K3x0dEETgh3Q9bkZNHgT+rkdl/J/VUqSRag==", "dependencies": { - "follow-redirects": "^1.14.8" + "tslib": "^2.5.0" } }, - "node_modules/babel-jest": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-28.1.3.tgz", - "integrity": "sha512-epUaPOEWMk3cWX0M/sPvCHHCe9fMFAa/9hXEgKP8nFfNl/jlGkE9ucq9NqkZGXLDduCJYS0UvSlPUwC0S+rH6Q==", - "dev": true, + "node_modules/@smithy/util-body-length-browser/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@smithy/util-body-length-node": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-2.2.1.tgz", + "integrity": "sha512-/ggJG+ta3IDtpNVq4ktmEUtOkH1LW64RHB5B0hcr5ZaWBmo96UX2cIOVbjCqqDickTXqBWZ4ZO0APuaPrD7Abg==", "dependencies": { - "@jest/transform": "^28.1.3", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^28.1.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" + "tslib": "^2.5.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" + "node": ">=14.0.0" } }, - "node_modules/babel-jest/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, + "node_modules/@smithy/util-body-length-node/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@smithy/util-buffer-from": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.1.1.tgz", + "integrity": "sha512-clhNjbyfqIv9Md2Mg6FffGVrJxw7bgK7s3Iax36xnfVj6cg0fUG7I4RH0XgXJF8bxi+saY5HR21g2UPKSxVCXg==", "dependencies": { - "color-convert": "^2.0.1" + "@smithy/is-array-buffer": "^2.1.1", + "tslib": "^2.5.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=14.0.0" } }, - "node_modules/babel-jest/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, + "node_modules/@smithy/util-buffer-from/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@smithy/util-config-provider": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-2.2.1.tgz", + "integrity": "sha512-50VL/tx9oYYcjJn/qKqNy7sCtpD0+s8XEBamIFo4mFFTclKMNp+rsnymD796uybjiIquB7VCB/DeafduL0y2kw==", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "tslib": "^2.5.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=14.0.0" } }, - "node_modules/babel-jest/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, + "node_modules/@smithy/util-config-provider/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@smithy/util-defaults-mode-browser": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-2.1.4.tgz", + "integrity": "sha512-J6XAVY+/g7jf03QMnvqPyU+8jqGrrtXoKWFVOS+n1sz0Lg8HjHJ1ANqaDN+KTTKZRZlvG8nU5ZrJOUL6VdwgcQ==", "dependencies": { - "color-name": "~1.1.4" + "@smithy/property-provider": "^2.1.3", + "@smithy/smithy-client": "^2.4.2", + "@smithy/types": "^2.10.1", + "bowser": "^2.11.0", + "tslib": "^2.5.0" }, "engines": { - "node": ">=7.0.0" + "node": ">= 10.0.0" } }, - "node_modules/babel-jest/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "node_modules/@smithy/util-defaults-mode-browser/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, - "node_modules/babel-jest/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, + "node_modules/@smithy/util-defaults-mode-node": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-2.2.3.tgz", + "integrity": "sha512-ttUISrv1uVOjTlDa3nznX33f0pthoUlP+4grhTvOzcLhzArx8qHB94/untGACOG3nlf8vU20nI2iWImfzoLkYA==", + "dependencies": { + "@smithy/config-resolver": "^2.1.4", + "@smithy/credential-provider-imds": "^2.2.4", + "@smithy/node-config-provider": "^2.2.4", + "@smithy/property-provider": "^2.1.3", + "@smithy/smithy-client": "^2.4.2", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, "engines": { - "node": ">=8" + "node": ">= 10.0.0" } }, - "node_modules/babel-jest/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } + "node_modules/@smithy/util-defaults-mode-node/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, - "node_modules/babel-jest/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, + "node_modules/@smithy/util-endpoints": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-1.1.4.tgz", + "integrity": "sha512-/qAeHmK5l4yQ4/bCIJ9p49wDe9rwWtOzhPHblu386fwPNT3pxmodgcs9jDCV52yK9b4rB8o9Sj31P/7Vzka1cg==", "dependencies": { - "has-flag": "^4.0.0" + "@smithy/node-config-provider": "^2.2.4", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" }, "engines": { - "node": ">=8" + "node": ">= 14.0.0" } }, - "node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, + "node_modules/@smithy/util-endpoints/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@smithy/util-hex-encoding": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-2.1.1.tgz", + "integrity": "sha512-3UNdP2pkYUUBGEXzQI9ODTDK+Tcu1BlCyDBaRHwyxhA+8xLP8agEKQq4MGmpjqb4VQAjq9TwlCQX0kP6XDKYLg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" + "tslib": "^2.5.0" }, "engines": { - "node": ">=8" + "node": ">=14.0.0" } }, - "node_modules/babel-plugin-jest-hoist": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-28.1.3.tgz", - "integrity": "sha512-Ys3tUKAmfnkRUpPdpa98eYrAR0nV+sSFUZZEGuQ2EbFd1y4SOLtD5QDNHAq+bb9a+bbXvYQC4b+ID/THIMcU6Q==", - "dev": true, + "node_modules/@smithy/util-hex-encoding/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@smithy/util-middleware": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-2.1.3.tgz", + "integrity": "sha512-/+2fm7AZ2ozl5h8wM++ZP0ovE9/tiUUAHIbCfGfb3Zd3+Dyk17WODPKXBeJ/TnK5U+x743QmA0xHzlSm8I/qhw==", "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "node": ">=14.0.0" } }, - "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.7", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.7.tgz", - "integrity": "sha512-LidDk/tEGDfuHW2DWh/Hgo4rmnw3cduK6ZkOI1NPFceSK3n/yAGeOsNT7FLnSGHkXj3RHGSEVkN3FsCTY6w2CQ==", - "dev": true, + "node_modules/@smithy/util-middleware/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@smithy/util-retry": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-2.1.3.tgz", + "integrity": "sha512-Kbvd+GEMuozbNUU3B89mb99tbufwREcyx2BOX0X2+qHjq6Gvsah8xSDDgxISDwcOHoDqUWO425F0Uc/QIRhYkg==", "dependencies": { - "@babel/compat-data": "^7.22.6", - "@babel/helper-define-polyfill-provider": "^0.4.4", - "semver": "^6.3.1" + "@smithy/service-error-classification": "^2.1.3", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + "engines": { + "node": ">= 14.0.0" } }, - "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.8.7", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.7.tgz", - "integrity": "sha512-KyDvZYxAzkC0Aj2dAPyDzi2Ym15e5JKZSK+maI7NAwSqofvuFglbSsxE7wUOvTg9oFVnHMzVzBKcqEb4PJgtOA==", - "dev": true, + "node_modules/@smithy/util-retry/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@smithy/util-stream": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-2.1.3.tgz", + "integrity": "sha512-HvpEQbP8raTy9n86ZfXiAkf3ezp1c3qeeO//zGqwZdrfaoOpGKQgF2Sv1IqZp7wjhna7pvczWaGUHjcOPuQwKw==", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.4.4", - "core-js-compat": "^3.33.1" + "@smithy/fetch-http-handler": "^2.4.3", + "@smithy/node-http-handler": "^2.4.1", + "@smithy/types": "^2.10.1", + "@smithy/util-base64": "^2.1.1", + "@smithy/util-buffer-from": "^2.1.1", + "@smithy/util-hex-encoding": "^2.1.1", + "@smithy/util-utf8": "^2.1.1", + "tslib": "^2.5.0" }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + "engines": { + "node": ">=14.0.0" } }, - "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.4.tgz", - "integrity": "sha512-S/x2iOCvDaCASLYsOOgWOq4bCfKYVqvO/uxjkaYyZ3rVsVE3CeAI/c84NpyuBBymEgNvHgjEot3a9/Z/kXvqsg==", - "dev": true, + "node_modules/@smithy/util-stream/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@smithy/util-uri-escape": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-2.1.1.tgz", + "integrity": "sha512-saVzI1h6iRBUVSqtnlOnc9ssU09ypo7n+shdQ8hBTZno/9rZ3AuRYvoHInV57VF7Qn7B+pFJG7qTzFiHxWlWBw==", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.4.4" + "tslib": "^2.5.0" }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + "engines": { + "node": ">=14.0.0" } }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", - "dev": true, + "node_modules/@smithy/util-uri-escape/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@smithy/util-utf8": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.1.1.tgz", + "integrity": "sha512-BqTpzYEcUMDwAKr7/mVRUtHDhs6ZoXDi9NypMvMfOr/+u1NW7JgqodPDECiiLboEm6bobcPcECxzjtQh865e9A==", "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" + "@smithy/util-buffer-from": "^2.1.1", + "tslib": "^2.5.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "engines": { + "node": ">=14.0.0" } }, - "node_modules/babel-preset-jest": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-28.1.3.tgz", - "integrity": "sha512-L+fupJvlWAHbQfn74coNX3zf60LXMJsezNvvx8eIh7iOR1luJ1poxYgQk1F8PYtNq/6QODDHCqsSnTFSWC491A==", - "dev": true, + "node_modules/@smithy/util-utf8/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@smithy/util-waiter": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-2.1.3.tgz", + "integrity": "sha512-3R0wNFAQQoH9e4m+bVLDYNOst2qNxtxFgq03WoNHWTBOqQT3jFnOBRj1W51Rf563xDA5kwqjziksxn6RKkHB+Q==", "dependencies": { - "babel-plugin-jest-hoist": "^28.1.3", - "babel-preset-current-node-syntax": "^1.0.0" + "@smithy/abort-controller": "^2.1.3", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "node": ">=14.0.0" } }, - "node_modules/balanced-match": { - "version": "1.0.2", - "dev": true, - "license": "MIT" - }, - "node_modules/base64-js": { - "version": "1.5.1", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" + "node_modules/@smithy/util-waiter/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, - "node_modules/binary-extensions": { - "version": "2.2.0", + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" } }, - "node_modules/bindings": { - "version": "1.5.0", - "license": "MIT", - "optional": true, - "peer": true, + "node_modules/@types/babel__generator": { + "version": "7.6.7", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.7.tgz", + "integrity": "sha512-6Sfsq+EaaLrw4RmdFWE9Onp63TOUue71AWb4Gpa6JxzgTYtimbM086WnYTy2U67AofR++QKCo08ZP6pwx8YFHQ==", + "dev": true, "dependencies": { - "file-uri-to-path": "1.0.0" + "@babel/types": "^7.0.0" } }, - "node_modules/bluebird": { - "version": "3.7.2", + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", "dev": true, - "license": "MIT" - }, - "node_modules/body-parser": { - "version": "1.19.2", - "license": "MIT", "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.8.1", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.9.7", - "raw-body": "2.4.3", - "type-is": "~1.6.18" - }, - "engines": { - "node": ">= 0.8" + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" } }, - "node_modules/body-parser/node_modules/debug": { - "version": "2.6.9", - "license": "MIT", + "node_modules/@types/babel__traverse": { + "version": "7.20.4", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.4.tgz", + "integrity": "sha512-mSM/iKUk5fDDrEV/e83qY+Cr3I1+Q3qqTuEn++HAWYjEa1+NxZr6CNrcJGf2ZTnq4HoFGC3zaTPZTobCzCFukA==", + "dev": true, "dependencies": { - "ms": "2.0.0" + "@babel/types": "^7.20.7" } }, - "node_modules/body-parser/node_modules/ms": { - "version": "2.0.0", - "license": "MIT" - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "dev": true, - "license": "MIT", + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "@types/ms": "*" } }, - "node_modules/braces": { - "version": "3.0.2", + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", "dev": true, - "license": "MIT", "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" + "@types/node": "*" } }, - "node_modules/browserslist": { - "version": "4.22.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz", - "integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "caniuse-lite": "^1.0.30001565", - "electron-to-chromium": "^1.4.601", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", "dev": true, "dependencies": { - "node-int64": "^0.4.0" + "@types/istanbul-lib-coverage": "*" } }, - "node_modules/buffer": { - "version": "4.9.2", - "license": "MIT", + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, "dependencies": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" + "@types/istanbul-lib-report": "*" } }, - "node_modules/buffer-from": { - "version": "1.1.2", + "node_modules/@types/json-schema": { + "version": "7.0.10", "dev": true, "license": "MIT" }, - "node_modules/buffer-writer": { - "version": "2.0.0", - "license": "MIT", - "engines": { - "node": ">=4" + "node_modules/@types/ms": { + "version": "0.7.34", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", + "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==" + }, + "node_modules/@types/node": { + "version": "17.0.23", + "license": "MIT" + }, + "node_modules/@types/prettier": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", + "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", + "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", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true + }, + "node_modules/@types/validator": { + "version": "13.11.7", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.11.7.tgz", + "integrity": "sha512-q0JomTsJ2I5Mv7dhHhQLGjMvX0JJm5dyZ1DXQySIUzU1UlwzB8bt+R6+LODUbz0UDIOvEzGc28tk27gBJw2N8Q==" + }, + "node_modules/@types/yargs": { + "version": "17.0.32", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", + "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" } }, - "node_modules/bytes": { - "version": "3.1.2", + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.16.0", + "dev": true, "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "5.16.0", + "@typescript-eslint/visitor-keys": "5.16.0" + }, "engines": { - "node": ">= 0.8" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/call-bind": { - "version": "1.0.2", + "node_modules/@typescript-eslint/types": { + "version": "5.16.0", + "dev": true, "license": "MIT", - "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.16.0", "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "5.16.0", + "@typescript-eslint/visitor-keys": "5.16.0", + "debug": "^4.3.2", + "globby": "^11.0.4", + "is-glob": "^4.0.3", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, "engines": { - "node": ">=6" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/camelcase": { - "version": "5.3.1", + "node_modules/@typescript-eslint/utils": { + "version": "5.16.0", "dev": true, "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.16.0", + "@typescript-eslint/types": "5.16.0", + "@typescript-eslint/typescript-estree": "5.16.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + }, "engines": { - "node": ">=6" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/caniuse-lite": { - "version": "1.0.30001570", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001570.tgz", - "integrity": "sha512-+3e0ASu4sw1SWaoCtvPeyXp+5PsjigkSt8OXZbF9StH5pQWbxEjLAZE3n8Aup5udop1uRiKA7a4utUk/uoSpUw==", + "node_modules/@typescript-eslint/utils/node_modules/eslint-scope": { + "version": "5.1.1", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ] - }, - "node_modules/chalk": { - "version": "2.4.2", - "license": "MIT", + "license": "BSD-2-Clause", "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" }, "engines": { - "node": ">=4" + "node": ">=8.0.0" } }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "node_modules/@typescript-eslint/utils/node_modules/estraverse": { + "version": "4.3.0", "dev": true, + "license": "BSD-2-Clause", "engines": { - "node": ">=10" + "node": ">=4.0" } }, - "node_modules/chokidar": { - "version": "3.5.3", + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.16.0", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], "license": "MIT", "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" + "@typescript-eslint/types": "5.16.0", + "eslint-visitor-keys": "^3.0.0" }, "engines": { - "node": ">= 8.10.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, - "optionalDependencies": { - "fsevents": "~2.3.2" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "node_modules/abbrev": { + "version": "1.1.1", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], + "license": "ISC" + }, + "node_modules/accepts": { + "version": "1.3.8", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, "engines": { - "node": ">=8" + "node": ">= 0.6" } }, - "node_modules/cjs-module-lexer": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", - "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", - "dev": true - }, - "node_modules/cli-color": { - "version": "2.0.1", + "node_modules/acorn": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", + "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", "dev": true, - "license": "ISC", - "dependencies": { - "d": "^1.0.1", - "es5-ext": "^0.10.53", - "es6-iterator": "^2.0.3", - "memoizee": "^0.4.15", - "timers-ext": "^0.1.7" + "bin": { + "acorn": "bin/acorn" }, "engines": { - "node": ">=0.10" + "node": ">=0.4.0" } }, - "node_modules/cliui": { - "version": "7.0.4", + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, - "license": "ISC", "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" + "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/clone-deep": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", - "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "dev": true, "dependencies": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" + "type-fest": "^0.21.3" }, "engines": { - "node": ">=6" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "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==", "dev": true, "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" + "node": ">=8" } }, - "node_modules/collect-v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", - "dev": true - }, - "node_modules/color": { + "node_modules/ansi-styles": { "version": "3.2.1", "license": "MIT", "dependencies": { - "color-convert": "^1.9.3", - "color-string": "^1.6.0" + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" } }, - "node_modules/color-convert": { - "version": "1.9.3", - "license": "MIT", + "node_modules/anymatch": { + "version": "3.1.2", + "dev": true, + "license": "ISC", "dependencies": { - "color-name": "1.1.3" + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" } }, - "node_modules/color-name": { - "version": "1.1.3", + "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/array-flatten": { + "version": "1.1.1", "license": "MIT" }, - "node_modules/color-string": { - "version": "1.9.0", + "node_modules/array-union": { + "version": "2.1.0", + "dev": true, "license": "MIT", - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" + "engines": { + "node": ">=8" } }, - "node_modules/colorspace": { - "version": "1.1.4", - "license": "MIT", - "dependencies": { - "color": "^3.1.3", - "text-hex": "1.0.x" - } + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true }, - "node_modules/combined-stream": { - "version": "1.0.8", - "dev": true, + "node_modules/async": { + "version": "3.2.3", + "license": "MIT" + }, + "node_modules/async-local-storage": { + "version": "2.3.1", "license": "MIT", "dependencies": { - "delayed-stream": "~1.0.0" + "nano-seconds": "^1.2.2" }, "engines": { - "node": ">= 0.8" - } - }, - "node_modules/commander": { - "version": "4.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" + "node": ">=8.0.0" } }, - "node_modules/commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", - "dev": true - }, - "node_modules/component-emitter": { - "version": "1.3.0", + "node_modules/asynckit": { + "version": "0.4.0", "dev": true, "license": "MIT" }, - "node_modules/concat-map": { - "version": "0.0.1", + "node_modules/at-least-node": { + "version": "1.0.0", "dev": true, - "license": "MIT" + "license": "ISC", + "engines": { + "node": ">= 4.0.0" + } }, - "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==", + "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, - "engines": [ - "node >= 6.0" - ], "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.0.2", - "typedarray": "^0.0.6" + "debug": "^4.3.1", + "djv": "^2.1.4", + "yargs-parser": "^21.0.0" } }, - "node_modules/config-chain": { - "version": "1.1.13", + "node_modules/audit-resolve-core/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, - "license": "MIT", - "dependencies": { - "ini": "^1.3.4", - "proto-list": "~1.2.1" + "engines": { + "node": ">=12" } }, - "node_modules/config-chain/node_modules/ini": { - "version": "1.3.8", - "dev": true, - "license": "ISC" + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/content-disposition": { - "version": "0.5.4", - "license": "MIT", + "node_modules/aws-sdk": { + "version": "2.1358.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1358.0.tgz", + "integrity": "sha512-ZolqFlnm0mDNgub7FGrVi7r5A1rw+58zZziKhlis3IxOtIpHdx4BQU5pH4htAMuD0Ct557p/dC/wmnZH/1Rc9Q==", "dependencies": { - "safe-buffer": "5.2.1" + "buffer": "4.9.2", + "events": "1.1.1", + "ieee754": "1.1.13", + "jmespath": "0.16.0", + "querystring": "0.2.0", + "sax": "1.2.1", + "url": "0.10.3", + "util": "^0.12.4", + "uuid": "8.0.0", + "xml2js": "0.5.0" }, "engines": { - "node": ">= 0.6" + "node": ">= 10.0.0" } }, - "node_modules/content-disposition/node_modules/safe-buffer": { - "version": "5.2.1", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/content-type": { - "version": "1.0.4", - "license": "MIT", - "engines": { - "node": ">= 0.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/cookie": { - "version": "0.4.2", - "license": "MIT", - "engines": { - "node": ">= 0.6" + "node_modules/aws-sdk/node_modules/uuid": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", + "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==", + "bin": { + "uuid": "dist/bin/uuid" } }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "license": "MIT" - }, - "node_modules/cookiejar": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", - "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", - "dev": true - }, - "node_modules/core-js": { - "version": "3.34.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.34.0.tgz", - "integrity": "sha512-aDdvlDder8QmY91H88GzNi9EtQi2TjvQhpCX6B1v/dAZHU1AuLgHvRh54RiOerpEhEW46Tkf+vgAViB/CWC0ag==", + "node_modules/axios": { + "version": "0.26.1", "dev": true, - "hasInstallScript": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.14.8" } }, - "node_modules/core-js-compat": { - "version": "3.34.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.34.0.tgz", - "integrity": "sha512-4ZIyeNbW/Cn1wkMMDy+mvrRUxrwFNjKwbhCfQpDd+eLgYipDqp8oGFGtLmhh18EDPKA0g3VUBYOxQGGwvWLVpA==", + "node_modules/babel-jest": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-28.1.3.tgz", + "integrity": "sha512-epUaPOEWMk3cWX0M/sPvCHHCe9fMFAa/9hXEgKP8nFfNl/jlGkE9ucq9NqkZGXLDduCJYS0UvSlPUwC0S+rH6Q==", "dev": true, "dependencies": { - "browserslist": "^4.22.2" + "@jest/transform": "^28.1.3", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^28.1.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" } }, - "node_modules/core-util-is": { - "version": "1.0.2", - "license": "MIT", - "optional": true, - "peer": true - }, - "node_modules/cross-spawn": { - "version": "7.0.3", + "node_modules/babel-jest/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "license": "MIT", "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "color-convert": "^2.0.1" }, "engines": { - "node": ">= 8" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/d": { - "version": "1.0.1", + "node_modules/babel-jest/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "license": "ISC", - "dependencies": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - } - }, - "node_modules/dayjs": { - "version": "1.11.0", - "license": "MIT" - }, - "node_modules/debug": { - "version": "4.3.4", - "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=6.0" + "node": ">=10" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/decamelize": { - "version": "1.2.0", + "node_modules/babel-jest/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, "engines": { - "node": ">=0.10.0" + "node": ">=7.0.0" } }, - "node_modules/dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", - "dev": true - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "node_modules/babel-jest/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "node_modules/babel-jest/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "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==", + "node_modules/babel-jest/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/define-properties": { - "version": "1.1.3", + "node_modules/babel-jest/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "license": "MIT", "dependencies": { - "object-keys": "^1.0.12" + "has-flag": "^4.0.0" }, "engines": { - "node": ">= 0.4" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/depd": { - "version": "1.1.2", - "license": "MIT", - "engines": { - "node": ">= 0.6" + "node": ">=8" } }, - "node_modules/destroy": { - "version": "1.0.4", - "license": "MIT" - }, - "node_modules/detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, "engines": { "node": ">=8" } }, - "node_modules/dezalgo": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", - "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", + "node_modules/babel-plugin-jest-hoist": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-28.1.3.tgz", + "integrity": "sha512-Ys3tUKAmfnkRUpPdpa98eYrAR0nV+sSFUZZEGuQ2EbFd1y4SOLtD5QDNHAq+bb9a+bbXvYQC4b+ID/THIMcU6Q==", "dev": true, "dependencies": { - "asap": "^2.0.0", - "wrappy": "1" - } - }, - "node_modules/diff": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", - "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, "engines": { - "node": ">=0.3.1" + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/diff-sequences": { - "version": "28.1.1", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-28.1.1.tgz", - "integrity": "sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==", - "dev": true, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/dir-glob": { - "version": "3.0.1", + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.7.tgz", + "integrity": "sha512-LidDk/tEGDfuHW2DWh/Hgo4rmnw3cduK6ZkOI1NPFceSK3n/yAGeOsNT7FLnSGHkXj3RHGSEVkN3FsCTY6w2CQ==", "dev": true, - "license": "MIT", "dependencies": { - "path-type": "^4.0.0" + "@babel/compat-data": "^7.22.6", + "@babel/helper-define-polyfill-provider": "^0.4.4", + "semver": "^6.3.1" }, - "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" + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, - "node_modules/doctrine": { - "version": "3.0.0", + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.8.7", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.7.tgz", + "integrity": "sha512-KyDvZYxAzkC0Aj2dAPyDzi2Ym15e5JKZSK+maI7NAwSqofvuFglbSsxE7wUOvTg9oFVnHMzVzBKcqEb4PJgtOA==", "dev": true, - "license": "Apache-2.0", "dependencies": { - "esutils": "^2.0.2" + "@babel/helper-define-polyfill-provider": "^0.4.4", + "core-js-compat": "^3.33.1" }, - "engines": { - "node": ">=6.0.0" + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, - "node_modules/dotenv": { - "version": "16.0.0", + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.4.tgz", + "integrity": "sha512-S/x2iOCvDaCASLYsOOgWOq4bCfKYVqvO/uxjkaYyZ3rVsVE3CeAI/c84NpyuBBymEgNvHgjEot3a9/Z/kXvqsg==", "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.4.4" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, - "node_modules/dottie": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.6.tgz", - "integrity": "sha512-iGCHkfUc5kFekGiqhe8B/mdaurD+lakO9txNnTvKtA6PISrw86LgqHvRzWYPyoE2Ph5aMIrCw9/uko6XHTKCwA==" - }, - "node_modules/editorconfig": { - "version": "0.15.3", + "node_modules/babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", "dev": true, - "license": "MIT", "dependencies": { - "commander": "^2.19.0", - "lru-cache": "^4.1.5", - "semver": "^5.6.0", - "sigmund": "^1.0.1" + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" }, - "bin": { - "editorconfig": "bin/editorconfig" + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/editorconfig/node_modules/commander": { - "version": "2.20.3", - "dev": true, - "license": "MIT" - }, - "node_modules/editorconfig/node_modules/lru-cache": { - "version": "4.1.5", + "node_modules/babel-preset-jest": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-28.1.3.tgz", + "integrity": "sha512-L+fupJvlWAHbQfn74coNX3zf60LXMJsezNvvx8eIh7iOR1luJ1poxYgQk1F8PYtNq/6QODDHCqsSnTFSWC491A==", "dev": true, - "license": "ISC", "dependencies": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" + "babel-plugin-jest-hoist": "^28.1.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/editorconfig/node_modules/yallist": { - "version": "2.1.2", + "node_modules/balanced-match": { + "version": "1.0.2", "dev": true, - "license": "ISC" - }, - "node_modules/ee-first": { - "version": "1.1.1", "license": "MIT" }, - "node_modules/electron-to-chromium": { - "version": "1.4.613", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.613.tgz", - "integrity": "sha512-r4x5+FowKG6q+/Wj0W9nidx7QO31BJwmR2uEo+Qh3YLGQ8SbBAFuDFpTxzly/I2gsbrFwBuIjrMp423L3O5U3w==", - "dev": true + "node_modules/base64-js": { + "version": "1.5.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" }, - "node_modules/emittery": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.2.tgz", - "integrity": "sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==", + "node_modules/binary-extensions": { + "version": "2.2.0", "dev": true, + "license": "MIT", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" + "node": ">=8" } }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "dev": true, - "license": "MIT" + "node_modules/bindings": { + "version": "1.5.0", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "file-uri-to-path": "1.0.0" + } }, - "node_modules/enabled": { - "version": "2.0.0", + "node_modules/bluebird": { + "version": "3.7.2", + "dev": true, "license": "MIT" }, - "node_modules/encodeurl": { - "version": "1.0.2", + "node_modules/body-parser": { + "version": "1.19.2", "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.8.1", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.9.7", + "raw-body": "2.4.3", + "type-is": "~1.6.18" + }, "engines": { "node": ">= 0.8" } }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "license": "MIT", "dependencies": { - "is-arrayish": "^0.2.1" + "ms": "2.0.0" } }, - "node_modules/error-ex/node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "license": "MIT" }, - "node_modules/es-abstract": { - "version": "1.19.1", + "node_modules/bowser": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", + "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "get-intrinsic": "^1.1.1", - "get-symbol-description": "^1.0.0", - "has": "^1.0.3", - "has-symbols": "^1.0.2", - "internal-slot": "^1.0.3", - "is-callable": "^1.2.4", - "is-negative-zero": "^2.0.1", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.1", - "is-string": "^1.0.7", - "is-weakref": "^1.0.1", - "object-inspect": "^1.11.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "string.prototype.trimend": "^1.0.4", - "string.prototype.trimstart": "^1.0.4", - "unbox-primitive": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/es-to-primitive": { - "version": "1.2.1", + "node_modules/braces": { + "version": "3.0.2", "dev": true, "license": "MIT", "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" + "fill-range": "^7.0.1" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/es5-ext": { - "version": "0.10.59", - "dev": true, - "hasInstallScript": true, - "license": "ISC", + "node_modules/browserslist": { + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz", + "integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "dependencies": { - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.3", - "next-tick": "^1.1.0" + "caniuse-lite": "^1.0.30001565", + "electron-to-chromium": "^1.4.601", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" }, "engines": { - "node": ">=0.10" + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/es6-iterator": { - "version": "2.0.3", + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", "dev": true, - "license": "MIT", "dependencies": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" + "node-int64": "^0.4.0" } }, - "node_modules/es6-symbol": { - "version": "3.1.3", - "dev": true, - "license": "ISC", + "node_modules/buffer": { + "version": "4.9.2", + "license": "MIT", "dependencies": { - "d": "^1.0.1", - "ext": "^1.1.2" + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" } }, - "node_modules/es6-weak-map": { - "version": "2.0.3", + "node_modules/buffer-from": { + "version": "1.1.2", "dev": true, - "license": "ISC", - "dependencies": { - "d": "1", - "es5-ext": "^0.10.46", - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.1" - } + "license": "MIT" }, - "node_modules/escalade": { - "version": "3.1.1", - "dev": true, + "node_modules/buffer-writer": { + "version": "2.0.0", "license": "MIT", "engines": { - "node": ">=6" + "node": ">=4" } }, - "node_modules/escape-html": { - "version": "1.0.3", - "license": "MIT" - }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", + "node_modules/bytes": { + "version": "3.1.2", "license": "MIT", "engines": { - "node": ">=0.8.0" + "node": ">= 0.8" } }, - "node_modules/eslint": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.55.0.tgz", - "integrity": "sha512-iyUUAM0PCKj5QpwGfmCAG9XXbZCWsqP/eWAWrG/W0umvjuLRBECwSFdt+rCntju0xEH7teIABPwXpahftIaTdA==", - "dev": true, + "node_modules/call-bind": { + "version": "1.0.2", + "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.55.0", - "@humanwhocodes/config-array": "^0.11.13", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint-config-prettier": { - "version": "8.5.0", + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, - "license": "MIT", - "bin": { - "eslint-config-prettier": "bin/cli.js" - }, - "peerDependencies": { - "eslint": ">=7.0.0" + "engines": { + "node": ">=6" } }, - "node_modules/eslint-plugin-jest": { - "version": "26.1.3", + "node_modules/camelcase": { + "version": "5.3.1", "dev": true, "license": "MIT", - "dependencies": { - "@typescript-eslint/utils": "^5.10.0" - }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "@typescript-eslint/eslint-plugin": "^5.0.0", - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "@typescript-eslint/eslint-plugin": { - "optional": true - }, - "jest": { - "optional": true - } + "node": ">=6" } }, - "node_modules/eslint-plugin-prettier": { - "version": "4.0.0", + "node_modules/caniuse-lite": { + "version": "1.0.30001570", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001570.tgz", + "integrity": "sha512-+3e0ASu4sw1SWaoCtvPeyXp+5PsjigkSt8OXZbF9StH5pQWbxEjLAZE3n8Aup5udop1uRiKA7a4utUk/uoSpUw==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chalk": { + "version": "2.4.2", "license": "MIT", "dependencies": { - "prettier-linter-helpers": "^1.0.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" }, "engines": { - "node": ">=6.0.0" - }, - "peerDependencies": { - "eslint": ">=7.28.0", - "prettier": ">=2.0.0" - }, - "peerDependenciesMeta": { - "eslint-config-prettier": { - "optional": true - } + "node": ">=4" } }, - "node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=10" } }, - "node_modules/eslint-utils": { - "version": "3.0.0", + "node_modules/chokidar": { + "version": "3.5.3", "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], "license": "MIT", "dependencies": { - "eslint-visitor-keys": "^2.0.0" + "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": "^10.0.0 || ^12.0.0 || >= 14.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" + "node": ">= 8.10.0" }, - "peerDependencies": { - "eslint": ">=5" + "optionalDependencies": { + "fsevents": "~2.3.2" } }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "2.1.0", + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", "dev": true, - "license": "Apache-2.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], "engines": { - "node": ">=10" + "node": ">=8" } }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } + "node_modules/cjs-module-lexer": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", + "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", + "dev": true }, - "node_modules/eslint/node_modules/ansi-styles": { - "version": "4.3.0", + "node_modules/cli-color": { + "version": "2.0.1", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "color-convert": "^2.0.1" + "d": "^1.0.1", + "es5-ext": "^0.10.53", + "es6-iterator": "^2.0.3", + "memoizee": "^0.4.15", + "timers-ext": "^0.1.7" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=0.10" } }, - "node_modules/eslint/node_modules/chalk": { - "version": "4.1.2", + "node_modules/cliui": { + "version": "7.0.4", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" } }, - "node_modules/eslint/node_modules/color-convert": { - "version": "2.0.1", + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", "dev": true, - "license": "MIT", "dependencies": { - "color-name": "~1.1.4" + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" }, "engines": { - "node": ">=7.0.0" + "node": ">=6" } }, - "node_modules/eslint/node_modules/color-name": { - "version": "1.1.4", + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", "dev": true, - "license": "MIT" + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } }, - "node_modules/eslint/node_modules/escape-string-regexp": { - "version": "4.0.0", - "dev": true, + "node_modules/collect-v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "dev": true + }, + "node_modules/color": { + "version": "3.2.1", "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" } }, - "node_modules/eslint/node_modules/glob-parent": { - "version": "6.0.2", - "dev": true, - "license": "ISC", + "node_modules/color-convert": { + "version": "1.9.3", + "license": "MIT", "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" + "color-name": "1.1.3" } }, - "node_modules/eslint/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, + "node_modules/color-name": { + "version": "1.1.3", + "license": "MIT" + }, + "node_modules/color-string": { + "version": "1.9.0", + "license": "MIT", "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" } }, - "node_modules/eslint/node_modules/has-flag": { - "version": "4.0.0", - "dev": true, + "node_modules/colorspace": { + "version": "1.1.4", "license": "MIT", - "engines": { - "node": ">=8" + "dependencies": { + "color": "^3.1.3", + "text-hex": "1.0.x" } }, - "node_modules/eslint/node_modules/supports-color": { - "version": "7.2.0", + "node_modules/combined-stream": { + "version": "1.0.8", "dev": true, "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "delayed-stream": "~1.0.0" }, "engines": { - "node": ">=8" + "node": ">= 0.8" } }, - "node_modules/eslint/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "node_modules/commander": { + "version": "4.1.1", "dev": true, + "license": "MIT", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 6" } }, - "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true + }, + "node_modules/component-emitter": { + "version": "1.3.0", "dev": true, - "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } + "license": "MIT" }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "node_modules/concat-map": { + "version": "0.0.1", "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" + "license": "MIT" + }, + "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/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "node_modules/config-chain": { + "version": "1.1.13", "dev": true, + "license": "MIT", "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" + "ini": "^1.3.4", + "proto-list": "~1.2.1" } }, - "node_modules/esrecurse": { - "version": "4.3.0", + "node_modules/config-chain/node_modules/ini": { + "version": "1.3.8", "dev": true, - "license": "BSD-2-Clause", + "license": "ISC" + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "license": "MIT", "dependencies": { - "estraverse": "^5.2.0" + "safe-buffer": "5.2.1" }, "engines": { - "node": ">=4.0" + "node": ">= 0.6" } }, - "node_modules/estraverse": { - "version": "5.3.0", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } + "node_modules/content-disposition/node_modules/safe-buffer": { + "version": "5.2.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" }, - "node_modules/esutils": { - "version": "2.0.3", - "dev": true, - "license": "BSD-2-Clause", + "node_modules/content-type": { + "version": "1.0.4", + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">= 0.6" } }, - "node_modules/etag": { - "version": "1.8.1", + "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/cookie": { + "version": "0.4.2", "license": "MIT", "engines": { "node": ">= 0.6" } }, - "node_modules/event-emitter": { - "version": "0.3.5", + "node_modules/cookie-signature": { + "version": "1.0.6", + "license": "MIT" + }, + "node_modules/cookiejar": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", + "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", + "dev": true + }, + "node_modules/core-js": { + "version": "3.34.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.34.0.tgz", + "integrity": "sha512-aDdvlDder8QmY91H88GzNi9EtQi2TjvQhpCX6B1v/dAZHU1AuLgHvRh54RiOerpEhEW46Tkf+vgAViB/CWC0ag==", + "dev": true, + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-compat": { + "version": "3.34.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.34.0.tgz", + "integrity": "sha512-4ZIyeNbW/Cn1wkMMDy+mvrRUxrwFNjKwbhCfQpDd+eLgYipDqp8oGFGtLmhh18EDPKA0g3VUBYOxQGGwvWLVpA==", "dev": true, - "license": "MIT", "dependencies": { - "d": "1", - "es5-ext": "~0.10.14" + "browserslist": "^4.22.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" } }, - "node_modules/events": { - "version": "1.1.1", + "node_modules/core-util-is": { + "version": "1.0.2", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "dev": true, "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, "engines": { - "node": ">=0.4.x" + "node": ">= 8" } }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "node_modules/d": { + "version": "1.0.1", "dev": true, + "license": "ISC", "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "node_modules/dayjs": { + "version": "1.11.0", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.3.4", + "license": "MIT", + "dependencies": { + "ms": "2.1.2" }, "engines": { - "node": ">=10" + "node": ">=6.0" }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "node_modules/decamelize": { + "version": "1.2.0", "dev": true, + "license": "MIT", "engines": { - "node": ">= 0.8.0" + "node": ">=0.10.0" } }, - "node_modules/expect": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/expect/-/expect-28.1.3.tgz", - "integrity": "sha512-eEh0xn8HlsuOBxFgIss+2mX85VAS4Qy3OSkjV7rlBWljtA4oWH37glVGyOZSZvErDT/yBywZdPGwCXuTvSG85g==", + "node_modules/dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", + "dev": true + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", "dev": true, - "dependencies": { - "@jest/expect-utils": "^28.1.3", - "jest-get-type": "^28.0.2", - "jest-matcher-utils": "^28.1.3", - "jest-message-util": "^28.1.3", - "jest-util": "^28.1.3" - }, "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "node": ">=0.10.0" } }, - "node_modules/express": { - "version": "4.17.3", - "license": "MIT", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.19.2", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.4.2", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.1.2", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.9.7", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.17.2", - "serve-static": "1.14.2", - "setprototypeof": "1.2.0", - "statuses": "~1.5.0", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, + "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": ">= 0.10.0" + "node": ">=4" } }, - "node_modules/express-validator": { - "version": "6.14.0", + "node_modules/define-properties": { + "version": "1.1.3", + "dev": true, "license": "MIT", "dependencies": { - "lodash": "^4.17.21", - "validator": "^13.7.0" + "object-keys": "^1.0.12" }, "engines": { - "node": ">= 8.0.0" + "node": ">= 0.4" } }, - "node_modules/express-winston": { - "version": "4.2.0", + "node_modules/delayed-stream": { + "version": "1.0.0", + "dev": true, "license": "MIT", - "dependencies": { - "chalk": "^2.4.2", - "lodash": "^4.17.21" - }, "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "winston": ">=3.x <4" + "node": ">=0.4.0" } }, - "node_modules/express/node_modules/debug": { - "version": "2.6.9", + "node_modules/depd": { + "version": "1.1.2", "license": "MIT", - "dependencies": { - "ms": "2.0.0" + "engines": { + "node": ">= 0.6" } }, - "node_modules/express/node_modules/ms": { - "version": "2.0.0", + "node_modules/destroy": { + "version": "1.0.4", "license": "MIT" }, - "node_modules/express/node_modules/safe-buffer": { - "version": "5.2.1", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/ext": { - "version": "1.6.0", + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", "dev": true, - "license": "ISC", - "dependencies": { - "type": "^2.5.0" + "engines": { + "node": ">=8" } }, - "node_modules/ext/node_modules/type": { - "version": "2.6.0", + "node_modules/dezalgo": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", + "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", "dev": true, - "license": "ISC" + "dependencies": { + "asap": "^2.0.0", + "wrappy": "1" + } }, - "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==", - "dev": true + "node_modules/diff": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", + "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", + "engines": { + "node": ">=0.3.1" + } }, - "node_modules/fast-diff": { - "version": "1.2.0", + "node_modules/diff-sequences": { + "version": "28.1.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-28.1.1.tgz", + "integrity": "sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==", "dev": true, - "license": "Apache-2.0" + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } }, - "node_modules/fast-glob": { - "version": "3.2.11", + "node_modules/dir-glob": { + "version": "3.0.1", "dev": true, "license": "MIT", "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" + "path-type": "^4.0.0" }, "engines": { - "node": ">=8.6.0" + "node": ">=8" } }, - "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==", - "dev": true - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "node_modules/fast-safe-stringify": { - "version": "2.1.1", - "dev": true, - "license": "MIT" - }, - "node_modules/fastq": { - "version": "1.13.0", + "node_modules/djv": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/djv/-/djv-2.1.4.tgz", + "integrity": "sha512-giDn+BVbtLlwtkvtcsZjbjzpALHB77skiv3FIu6Wp8b5j8BunDcVJYH0cGUaexp6s0Sb7IkquXXjsLBJhXwQpA==", "dev": true, - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" + "optionalDependencies": { + "@korzio/djv-draft-04": "^2.0.1" } }, - "node_modules/fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "node_modules/doctrine": { + "version": "3.0.0", "dev": true, + "license": "Apache-2.0", "dependencies": { - "bser": "2.1.1" + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" } }, - "node_modules/fecha": { - "version": "4.2.1", - "license": "MIT" - }, - "node_modules/file-entry-cache": { - "version": "6.0.1", + "node_modules/dotenv": { + "version": "16.0.0", "dev": true, - "license": "MIT", - "dependencies": { - "flat-cache": "^3.0.4" - }, + "license": "BSD-2-Clause", "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=12" } }, - "node_modules/file-uri-to-path": { - "version": "1.0.0", - "license": "MIT", - "optional": true, - "peer": true + "node_modules/dottie": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.6.tgz", + "integrity": "sha512-iGCHkfUc5kFekGiqhe8B/mdaurD+lakO9txNnTvKtA6PISrw86LgqHvRzWYPyoE2Ph5aMIrCw9/uko6XHTKCwA==" }, - "node_modules/fill-range": { - "version": "7.0.1", + "node_modules/editorconfig": { + "version": "0.15.3", "dev": true, "license": "MIT", "dependencies": { - "to-regex-range": "^5.0.1" + "commander": "^2.19.0", + "lru-cache": "^4.1.5", + "semver": "^5.6.0", + "sigmund": "^1.0.1" }, - "engines": { - "node": ">=8" + "bin": { + "editorconfig": "bin/editorconfig" } }, - "node_modules/finalhandler": { - "version": "1.1.2", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } + "node_modules/editorconfig/node_modules/commander": { + "version": "2.20.3", + "dev": true, + "license": "MIT" }, - "node_modules/finalhandler/node_modules/debug": { - "version": "2.6.9", - "license": "MIT", + "node_modules/editorconfig/node_modules/lru-cache": { + "version": "4.1.5", + "dev": true, + "license": "ISC", "dependencies": { - "ms": "2.0.0" + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" } }, - "node_modules/finalhandler/node_modules/ms": { - "version": "2.0.0", + "node_modules/editorconfig/node_modules/yallist": { + "version": "2.1.2", + "dev": true, + "license": "ISC" + }, + "node_modules/ee-first": { + "version": "1.1.1", "license": "MIT" }, - "node_modules/find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", - "dev": true, - "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" - }, - "engines": { - "node": ">=6" - } + "node_modules/electron-to-chromium": { + "version": "1.4.613", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.613.tgz", + "integrity": "sha512-r4x5+FowKG6q+/Wj0W9nidx7QO31BJwmR2uEo+Qh3YLGQ8SbBAFuDFpTxzly/I2gsbrFwBuIjrMp423L3O5U3w==", + "dev": true }, - "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==", + "node_modules/emittery": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.2.tgz", + "integrity": "sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==", "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sindresorhus/emittery?sponsor=1" } }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "node_modules/emoji-regex": { + "version": "8.0.0", "dev": true, - "bin": { - "flat": "cli.js" - } + "license": "MIT" }, - "node_modules/flat-cache": { - "version": "3.0.4", - "dev": true, + "node_modules/enabled": { + "version": "2.0.0", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "1.0.2", "license": "MIT", - "dependencies": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">= 0.8" } }, - "node_modules/flatted": { - "version": "3.2.5", + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", "dev": true, - "license": "ISC" + "dependencies": { + "is-arrayish": "^0.2.1" + } }, - "node_modules/fn.name": { - "version": "1.1.0", - "license": "MIT" + "node_modules/error-ex/node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true }, - "node_modules/follow-redirects": { - "version": "1.14.9", + "node_modules/es-abstract": { + "version": "1.19.1", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dependencies": { - "is-callable": "^1.1.3" - } - }, - "node_modules/formidable": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz", - "integrity": "sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==", - "dev": true, "dependencies": { - "dezalgo": "^1.0.4", - "hexoid": "^1.0.0", - "once": "^1.4.0", - "qs": "^6.11.0" + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-symbols": "^1.0.2", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.1", + "is-string": "^1.0.7", + "is-weakref": "^1.0.1", + "object-inspect": "^1.11.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { - "url": "https://ko-fi.com/tunnckoCore/commissions" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/formidable/node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "node_modules/es-to-primitive": { + "version": "1.2.1", "dev": true, + "license": "MIT", "dependencies": { - "side-channel": "^1.0.4" + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" }, "engines": { - "node": ">=0.6" + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/forwarded": { - "version": "0.2.0", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fs-extra": { - "version": "9.1.0", + "node_modules/es5-ext": { + "version": "0.10.59", "dev": true, - "license": "MIT", + "hasInstallScript": true, + "license": "ISC", "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "next-tick": "^1.1.0" }, "engines": { - "node": ">=10" + "node": ">=0.10" } }, - "node_modules/fs-extra/node_modules/universalify": { - "version": "2.0.0", + "node_modules/es6-iterator": { + "version": "2.0.3", "dev": true, "license": "MIT", - "engines": { - "node": ">= 10.0.0" + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" } }, - "node_modules/fs-readdir-recursive": { - "version": "1.1.0", + "node_modules/es6-symbol": { + "version": "3.1.3", "dev": true, - "license": "MIT" + "license": "ISC", + "dependencies": { + "d": "^1.0.1", + "ext": "^1.1.2" + } }, - "node_modules/fs.realpath": { - "version": "1.0.0", + "node_modules/es6-weak-map": { + "version": "2.0.3", "dev": true, - "license": "ISC" + "license": "ISC", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.1" + } }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "node_modules/escalade": { + "version": "3.1.1", "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], + "license": "MIT", "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + "node": ">=6" } }, - "node_modules/function-bind": { - "version": "1.1.1", + "node_modules/escape-html": { + "version": "1.0.3", "license": "MIT" }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "dev": true, + "node_modules/escape-string-regexp": { + "version": "1.0.5", "license": "MIT", "engines": { - "node": ">=6.9.0" + "node": ">=0.8.0" } }, - "node_modules/get-caller-file": { - "version": "2.0.5", + "node_modules/eslint": { + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.55.0.tgz", + "integrity": "sha512-iyUUAM0PCKj5QpwGfmCAG9XXbZCWsqP/eWAWrG/W0umvjuLRBECwSFdt+rCntju0xEH7teIABPwXpahftIaTdA==", "dev": true, - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", - "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.55.0", + "@humanwhocodes/config-array": "^0.11.13", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, "engines": { - "node": ">=8.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "node_modules/eslint-config-prettier": { + "version": "8.5.0", "dev": true, - "engines": { - "node": ">=10" + "license": "MIT", + "bin": { + "eslint-config-prettier": "bin/cli.js" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "eslint": ">=7.0.0" } }, - "node_modules/get-symbol-description": { - "version": "1.0.0", + "node_modules/eslint-plugin-jest": { + "version": "26.1.3", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" + "@typescript-eslint/utils": "^5.10.0" }, "engines": { - "node": ">= 0.4" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + }, + "jest": { + "optional": true + } } }, - "node_modules/glob": { - "version": "7.2.0", + "node_modules/eslint-plugin-prettier": { + "version": "4.0.0", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "prettier-linter-helpers": "^1.0.0" }, "engines": { - "node": "*" + "node": ">=6.0.0" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "peerDependencies": { + "eslint": ">=7.28.0", + "prettier": ">=2.0.0" + }, + "peerDependenciesMeta": { + "eslint-config-prettier": { + "optional": true + } } }, - "node_modules/glob-parent": { - "version": "5.1.2", + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, - "license": "ISC", "dependencies": { - "is-glob": "^4.0.1" + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" }, "engines": { - "node": ">= 6" - } - }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "engines": { - "node": ">=4" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/globby": { - "version": "11.1.0", + "node_modules/eslint-utils": { + "version": "3.0.0", "dev": true, "license": "MIT", "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" + "eslint-visitor-keys": "^2.0.0" }, "engines": { - "node": ">=10" + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" } }, - "node_modules/globby/node_modules/slash": { - "version": "3.0.0", + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "2.1.0", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "engines": { - "node": ">=8" + "node": ">=10" } }, - "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" + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://opencollective.com/eslint" } }, - "node_modules/graceful-fs": { - "version": "4.2.9", + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", "dev": true, - "license": "ISC" - }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true - }, - "node_modules/has": { - "version": "1.0.3", "license": "MIT", "dependencies": { - "function-bind": "^1.1.1" + "color-convert": "^2.0.1" }, "engines": { - "node": ">= 0.4.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/has-bigints": { - "version": "1.0.1", + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", "dev": true, "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/has-flag": { - "version": "3.0.0", + "node_modules/eslint/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, "engines": { - "node": ">=4" + "node": ">=7.0.0" } }, - "node_modules/has-symbols": { - "version": "1.0.3", + "node_modules/eslint/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "dev": true, "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/has-tostringtag": { - "version": "1.0.0", - "license": "MIT", + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "dev": true, + "license": "ISC", "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" + "is-glob": "^4.0.3" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/helmet": { - "version": "6.0.0", - "license": "MIT", "engines": { - "node": ">=14.0.0" + "node": ">=10.13.0" } }, - "node_modules/hexoid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", - "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", + "node_modules/eslint/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/homedir-polyfill": { - "version": "1.0.3", + "node_modules/eslint/node_modules/has-flag": { + "version": "4.0.0", "dev": true, "license": "MIT", - "dependencies": { - "parse-passwd": "^1.0.0" - }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "node_modules/http-errors": { - "version": "1.8.1", + "node_modules/eslint/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, "license": "MIT", "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.1" + "has-flag": "^4.0.0" }, "engines": { - "node": ">= 0.6" + "node": ">=8" } }, - "node_modules/http-errors/node_modules/inherits": { - "version": "2.0.4", - "license": "ISC" - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "node_modules/eslint/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==", "dev": true, "engines": { - "node": ">=10.17.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "license": "MIT", + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" }, "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ieee754": { - "version": "1.1.13", - "license": "BSD-3-Clause" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } }, - "node_modules/ignore": { - "version": "5.2.0", + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true, - "license": "MIT", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, "engines": { - "node": ">= 4" + "node": ">=4" } }, - "node_modules/ignore-by-default": { - "version": "1.0.1", - "dev": true, - "license": "ISC" - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "dev": true, "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" + "estraverse": "^5.1.0" }, "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10" } }, - "node_modules/import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "node_modules/esrecurse": { + "version": "4.3.0", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" + "estraverse": "^5.2.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=4.0" } }, - "node_modules/import-local/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==", + "node_modules/estraverse": { + "version": "5.3.0", "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, + "license": "BSD-2-Clause", "engines": { - "node": ">=8" + "node": ">=4.0" } }, - "node_modules/import-local/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==", + "node_modules/esutils": { + "version": "2.0.3", "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, + "license": "BSD-2-Clause", "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/import-local/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" - }, + "node_modules/etag": { + "version": "1.8.1", + "license": "MIT", "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.6" } }, - "node_modules/import-local/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==", + "node_modules/event-emitter": { + "version": "0.3.5", "dev": true, + "license": "MIT", "dependencies": { - "p-limit": "^2.2.0" - }, + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "node_modules/events": { + "version": "1.1.1", + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=0.4.x" } }, - "node_modules/import-local/node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", "dev": true, "dependencies": { - "find-up": "^4.0.0" + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/imurmurhash": { - "version": "0.1.4", + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", "dev": true, - "license": "MIT", "engines": { - "node": ">=0.8.19" + "node": ">= 0.8.0" } }, - "node_modules/inflection": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.13.4.tgz", - "integrity": "sha512-6I/HUDeYFfuNCVS3td055BaXBwKYuzw7K3ExVMStBowKo9oOAMJIXIHvdyR3iboTCp1b+1i5DSkIZTcwIktuDw==", - "engines": [ - "node >= 0.4.0" - ] - }, - "node_modules/inflight": { - "version": "1.0.6", + "node_modules/expect": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/expect/-/expect-28.1.3.tgz", + "integrity": "sha512-eEh0xn8HlsuOBxFgIss+2mX85VAS4Qy3OSkjV7rlBWljtA4oWH37glVGyOZSZvErDT/yBywZdPGwCXuTvSG85g==", "dev": true, - "license": "ISC", "dependencies": { - "once": "^1.3.0", - "wrappy": "1" + "@jest/expect-utils": "^28.1.3", + "jest-get-type": "^28.0.2", + "jest-matcher-utils": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-util": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/inherits": { - "version": "2.0.3", - "license": "ISC" - }, - "node_modules/internal-slot": { - "version": "1.0.3", - "dev": true, + "node_modules/express": { + "version": "4.17.3", "license": "MIT", "dependencies": { - "get-intrinsic": "^1.1.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.19.2", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.4.2", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.9.7", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.17.2", + "serve-static": "1.14.2", + "setprototypeof": "1.2.0", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" }, "engines": { - "node": ">= 0.4" + "node": ">= 0.10.0" } }, - "node_modules/ipaddr.js": { - "version": "1.9.1", + "node_modules/express-validator": { + "version": "6.14.0", "license": "MIT", + "dependencies": { + "lodash": "^4.17.21", + "validator": "^13.7.0" + }, "engines": { - "node": ">= 0.10" + "node": ">= 8.0.0" } }, - "node_modules/is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "node_modules/express-winston": { + "version": "4.2.0", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "chalk": "^2.4.2", + "lodash": "^4.17.21" }, "engines": { - "node": ">= 0.4" + "node": ">= 6" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "winston": ">=3.x <4" } }, - "node_modules/is-arrayish": { - "version": "0.3.2", - "license": "MIT" - }, - "node_modules/is-bigint": { - "version": "1.0.4", - "dev": true, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", "license": "MIT", "dependencies": { - "has-bigints": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "ms": "2.0.0" } }, - "node_modules/is-binary-path": { - "version": "2.1.0", + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "license": "MIT" + }, + "node_modules/express/node_modules/safe-buffer": { + "version": "5.2.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/ext": { + "version": "1.6.0", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" + "type": "^2.5.0" } }, - "node_modules/is-boolean-object": { - "version": "1.1.2", + "node_modules/ext/node_modules/type": { + "version": "2.6.0", + "dev": true, + "license": "ISC" + }, + "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==", + "dev": true + }, + "node_modules/fast-diff": { + "version": "1.2.0", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/fast-glob": { + "version": "3.2.11", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "@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": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8.6.0" } }, - "node_modules/is-callable": { - "version": "1.2.4", - "license": "MIT", - "engines": { - "node": ">= 0.4" + "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==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-xml-parser": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz", + "integrity": "sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==", + "funding": [ + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + }, + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "dependencies": { + "strnum": "^1.0.5" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "bin": { + "fxparser": "src/cli/cli.js" } }, - "node_modules/is-core-module": { - "version": "2.8.1", + "node_modules/fastq": { + "version": "1.13.0", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "reusify": "^1.0.4" } }, - "node_modules/is-date-object": { - "version": "1.0.5", + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", "dev": true, - "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "bser": "2.1.1" } }, - "node_modules/is-extglob": { - "version": "2.1.1", + "node_modules/fecha": { + "version": "4.2.1", + "license": "MIT" + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", "dev": true, "license": "MIT", + "dependencies": { + "flat-cache": "^3.0.4" + }, "engines": { - "node": ">=0.10.0" + "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/fill-range": { + "version": "7.0.1", "dev": true, "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, "engines": { "node": ">=8" } }, - "node_modules/is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true, + "node_modules/finalhandler": { + "version": "1.1.2", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, "engines": { - "node": ">=6" + "node": ">= 0.8" } }, - "node_modules/is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "ms": "2.0.0" } }, - "node_modules/is-glob": { - "version": "4.0.3", + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "license": "MIT" + }, + "node_modules/find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", "dev": true, - "license": "MIT", "dependencies": { - "is-extglob": "^2.1.1" + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=6" } }, - "node_modules/is-negative-zero": { - "version": "2.0.2", + "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, - "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-number": { - "version": "7.0.0", + "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, - "license": "MIT", - "engines": { - "node": ">=0.12.0" + "bin": { + "flat": "cli.js" } }, - "node_modules/is-number-object": { - "version": "1.0.6", + "node_modules/flat-cache": { + "version": "3.0.4", "dev": true, "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "flatted": "^3.1.0", + "rimraf": "^3.0.2" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "node_modules/flatted": { + "version": "3.2.5", "dev": true, - "engines": { - "node": ">=8" - } + "license": "ISC" }, - "node_modules/is-plain-obj": { + "node_modules/fn.name": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "license": "MIT" + }, + "node_modules/follow-redirects": { + "version": "1.14.9", "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } } }, - "node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" + "is-callable": "^1.1.3" } }, - "node_modules/is-promise": { - "version": "2.2.2", + "node_modules/formidable": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz", + "integrity": "sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==", "dev": true, - "license": "MIT" + "dependencies": { + "dezalgo": "^1.0.4", + "hexoid": "^1.0.0", + "once": "^1.4.0", + "qs": "^6.11.0" + }, + "funding": { + "url": "https://ko-fi.com/tunnckoCore/commissions" + } }, - "node_modules/is-regex": { - "version": "1.1.4", + "node_modules/formidable/node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "side-channel": "^1.0.4" }, "engines": { - "node": ">= 0.4" + "node": ">=0.6" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.1", - "dev": true, + "node_modules/forwarded": { + "version": "0.2.0", "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">= 0.6" } }, - "node_modules/is-stream": { - "version": "2.0.1", + "node_modules/fresh": { + "version": "0.5.2", "license": "MIT", "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.6" } }, - "node_modules/is-string": { - "version": "1.0.7", + "node_modules/fs-extra": { + "version": "9.1.0", "dev": true, "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=10" } }, - "node_modules/is-symbol": { - "version": "1.0.4", + "node_modules/fs-extra/node_modules/universalify": { + "version": "2.0.0", "dev": true, "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", - "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 10.0.0" } }, - "node_modules/is-weakref": { - "version": "1.0.2", + "node_modules/fs-readdir-recursive": { + "version": "1.1.0", "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/isarray": { - "version": "1.0.0", "license": "MIT" }, - "node_modules/isexe": { - "version": "2.0.0", + "node_modules/fs.realpath": { + "version": "1.0.0", "dev": true, "license": "ISC" }, - "node_modules/isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=0.10.0" + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "node_modules/function-bind": { + "version": "1.1.1", + "license": "MIT" + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", "dev": true, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=6.9.0" } }, - "node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "node_modules/get-caller-file": { + "version": "2.0.5", "dev": true, - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, + "license": "ISC", "engines": { - "node": ">=8" + "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, + "node_modules/get-intrinsic": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" }, - "engines": { - "node": ">=10" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/istanbul-lib-report/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", "dev": true, "engines": { - "node": ">=8" + "node": ">=8.0.0" } }, - "node_modules/istanbul-lib-report/node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true, - "dependencies": { - "semver": "^7.5.3" - }, "engines": { "node": ">=10" }, @@ -6134,966 +6914,881 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/istanbul-lib-report/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/get-symbol-description": { + "version": "1.0.0", "dev": true, + "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" }, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "node_modules/glob": { + "version": "7.2.0", "dev": true, + "license": "ISC", "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-reports": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", - "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", - "dev": true, - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" }, "engines": { - "node": ">=8" + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/jest": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest/-/jest-28.1.3.tgz", - "integrity": "sha512-N4GT5on8UkZgH0O5LUavMRV1EDEhNTL0KEfRmDIeZHSV7p2XgLoY9t9VDUgL6o+yfdgYHVxuz81G8oB9VG5uyA==", + "node_modules/glob-parent": { + "version": "5.1.2", "dev": true, + "license": "ISC", "dependencies": { - "@jest/core": "^28.1.3", - "@jest/types": "^28.1.3", - "import-local": "^3.0.2", - "jest-cli": "^28.1.3" - }, - "bin": { - "jest": "bin/jest.js" + "is-glob": "^4.0.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } + "node": ">= 6" } }, - "node_modules/jest-changed-files": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-28.1.3.tgz", - "integrity": "sha512-esaOfUWJXk2nfZt9SPyC8gA1kNfdKLkQWyzsMlqq8msYSlNKfmZxfRgZn4Cd4MGVUF+7v6dBs0d5TOAKa7iIiA==", + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true, - "dependencies": { - "execa": "^5.0.0", - "p-limit": "^3.1.0" - }, "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "node": ">=4" } }, - "node_modules/jest-circus": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-28.1.3.tgz", - "integrity": "sha512-cZ+eS5zc79MBwt+IhQhiEp0OeBddpc1n8MBo1nMB8A7oPMKEO+Sre+wHaLJexQUj9Ya/8NOBY0RESUgYjB6fow==", + "node_modules/globby": { + "version": "11.1.0", "dev": true, + "license": "MIT", "dependencies": { - "@jest/environment": "^28.1.3", - "@jest/expect": "^28.1.3", - "@jest/test-result": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^0.7.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^28.1.3", - "jest-matcher-utils": "^28.1.3", - "jest-message-util": "^28.1.3", - "jest-runtime": "^28.1.3", - "jest-snapshot": "^28.1.3", - "jest-util": "^28.1.3", - "p-limit": "^3.1.0", - "pretty-format": "^28.1.3", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" + "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": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-circus/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==", + "node_modules/globby/node_modules/slash": { + "version": "3.0.0", "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, + "license": "MIT", "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-circus/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" + "get-intrinsic": "^1.1.3" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-circus/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==", + "node_modules/graceful-fs": { + "version": "4.2.9", "dev": true, + "license": "ISC" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/has": { + "version": "1.0.3", + "license": "MIT", "dependencies": { - "color-name": "~1.1.4" + "function-bind": "^1.1.1" }, "engines": { - "node": ">=7.0.0" + "node": ">= 0.4.0" } }, - "node_modules/jest-circus/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-circus/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/has-bigints": { + "version": "1.0.1", "dev": true, - "engines": { - "node": ">=8" + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-circus/node_modules/slash": { + "node_modules/has-flag": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/jest-circus/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, + "node_modules/has-symbols": { + "version": "1.0.3", + "license": "MIT", "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-cli": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-28.1.3.tgz", - "integrity": "sha512-roY3kvrv57Azn1yPgdTebPAXvdR2xfezaKKYzVxZ6It/5NCxzJym6tUI5P1zkdWhfUYkxEI9uZWcQdaFLo8mJQ==", - "dev": true, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "license": "MIT", "dependencies": { - "@jest/core": "^28.1.3", - "@jest/test-result": "^28.1.3", - "@jest/types": "^28.1.3", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "import-local": "^3.0.2", - "jest-config": "^28.1.3", - "jest-util": "^28.1.3", - "jest-validate": "^28.1.3", - "prompts": "^2.0.1", - "yargs": "^17.3.1" - }, - "bin": { - "jest": "bin/jest.js" + "has-symbols": "^1.0.2" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + "node": ">= 0.4" }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-cli/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==", + "node_modules/helmet": { + "version": "6.0.0", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/hexoid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", + "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-cli/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/homedir-polyfill": { + "version": "1.0.3", "dev": true, + "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "parse-passwd": "^1.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=0.10.0" } }, - "node_modules/jest-cli/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true }, - "node_modules/jest-cli/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, + "node_modules/http-errors": { + "version": "1.8.1", + "license": "MIT", "dependencies": { - "color-name": "~1.1.4" + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" }, "engines": { - "node": ">=7.0.0" + "node": ">= 0.6" } }, - "node_modules/jest-cli/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "node_modules/http-errors/node_modules/inherits": { + "version": "2.0.4", + "license": "ISC" }, - "node_modules/jest-cli/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", "dev": true, "engines": { - "node": ">=8" + "node": ">=10.17.0" } }, - "node_modules/jest-cli/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, + "node_modules/iconv-lite": { + "version": "0.4.24", + "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "safer-buffer": ">= 2.1.2 < 3" }, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/jest-cli/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } + "node_modules/ieee754": { + "version": "1.1.13", + "license": "BSD-3-Clause" }, - "node_modules/jest-cli/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==", + "node_modules/ignore": { + "version": "5.2.0", "dev": true, + "license": "MIT", "engines": { - "node": ">=12" + "node": ">= 4" } }, - "node_modules/jest-config": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-28.1.3.tgz", - "integrity": "sha512-MG3INjByJ0J4AsNBm7T3hsuxKQqFIiRo/AUqb1q9LRKI5UU6Aar9JHbr9Ivn1TVwfUD9KirRoM/T6u8XlcQPHQ==", + "node_modules/ignore-by-default": { + "version": "1.0.1", "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^28.1.3", - "@jest/types": "^28.1.3", - "babel-jest": "^28.1.3", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^28.1.3", - "jest-environment-node": "^28.1.3", - "jest-get-type": "^28.0.2", - "jest-regex-util": "^28.0.2", - "jest-resolve": "^28.1.3", - "jest-runner": "^28.1.3", - "jest-util": "^28.1.3", - "jest-validate": "^28.1.3", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^28.1.3", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "ts-node": { - "optional": true - } - } + "license": "ISC" }, - "node_modules/jest-config/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==", + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" }, "engines": { - "node": ">=8" + "node": ">=6" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-config/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" }, "engines": { - "node": ">=10" + "node": ">=8" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-config/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==", + "node_modules/import-local/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==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-config/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-config/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, "engines": { "node": ">=8" } }, - "node_modules/jest-config/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "node_modules/import-local/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==", "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, "engines": { "node": ">=8" } }, - "node_modules/jest-config/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/import-local/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": { - "has-flag": "^4.0.0" + "p-try": "^2.0.0" }, "engines": { - "node": ">=8" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-diff": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-28.1.3.tgz", - "integrity": "sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw==", + "node_modules/import-local/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==", "dev": true, "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^28.1.1", - "jest-get-type": "^28.0.2", - "pretty-format": "^28.1.3" + "p-limit": "^2.2.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "node": ">=8" } }, - "node_modules/jest-diff/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==", + "node_modules/import-local/node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "find-up": "^4.0.0" }, "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-diff/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/imurmurhash": { + "version": "0.1.4", "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, + "license": "MIT", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=0.8.19" } }, - "node_modules/jest-diff/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==", + "node_modules/inflection": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.13.4.tgz", + "integrity": "sha512-6I/HUDeYFfuNCVS3td055BaXBwKYuzw7K3ExVMStBowKo9oOAMJIXIHvdyR3iboTCp1b+1i5DSkIZTcwIktuDw==", + "engines": [ + "node >= 0.4.0" + ] + }, + "node_modules/inflight": { + "version": "1.0.6", "dev": true, + "license": "ISC", "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" + "once": "^1.3.0", + "wrappy": "1" } }, - "node_modules/jest-diff/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "node_modules/inherits": { + "version": "2.0.3", + "license": "ISC" }, - "node_modules/jest-diff/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/internal-slot": { + "version": "1.0.3", "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + }, "engines": { - "node": ">=8" + "node": ">= 0.4" } }, - "node_modules/jest-diff/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", "dependencies": { - "has-flag": "^4.0.0" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-docblock": { - "version": "28.1.1", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-28.1.1.tgz", - "integrity": "sha512-3wayBVNiOYx0cwAbl9rwm5kKFP8yHH3d/fkEaL02NPTkDojPtheGB7HZSFY4wzX+DxyrvhXz0KSCVksmCknCuA==", + "node_modules/is-arrayish": { + "version": "0.3.2", + "license": "MIT" + }, + "node_modules/is-bigint": { + "version": "1.0.4", "dev": true, + "license": "MIT", "dependencies": { - "detect-newline": "^3.0.0" + "has-bigints": "^1.0.1" }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-each": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-28.1.3.tgz", - "integrity": "sha512-arT1z4sg2yABU5uogObVPvSlSMQlDA48owx07BDPAiasW0yYpYHYOo4HHLz9q0BVzDVU4hILFjzJw0So9aCL/g==", + "node_modules/is-binary-path": { + "version": "2.1.0", "dev": true, + "license": "MIT", "dependencies": { - "@jest/types": "^28.1.3", - "chalk": "^4.0.0", - "jest-get-type": "^28.0.2", - "jest-util": "^28.1.3", - "pretty-format": "^28.1.3" + "binary-extensions": "^2.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "node": ">=8" } }, - "node_modules/jest-each/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==", + "node_modules/is-boolean-object": { + "version": "1.1.2", "dev": true, + "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">=8" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-each/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, + "node_modules/is-callable": { + "version": "1.2.4", + "license": "MIT", "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-each/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==", + "node_modules/is-core-module": { + "version": "2.8.1", "dev": true, + "license": "MIT", "dependencies": { - "color-name": "~1.1.4" + "has": "^1.0.3" }, - "engines": { - "node": ">=7.0.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-each/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-each/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/is-date-object": { + "version": "1.0.5", "dev": true, + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-each/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/is-extglob": { + "version": "2.1.1", "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/jest-environment-node": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-28.1.3.tgz", - "integrity": "sha512-ugP6XOhEpjAEhGYvp5Xj989ns5cB1K6ZdjBYuS30umT4CQEETaxSiPcZ/E1kFktX4GkrcM4qu07IIlDYX1gp+A==", + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", "dev": true, - "dependencies": { - "@jest/environment": "^28.1.3", - "@jest/fake-timers": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/node": "*", - "jest-mock": "^28.1.3", - "jest-util": "^28.1.3" - }, + "license": "MIT", "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "node": ">=8" } }, - "node_modules/jest-get-type": { - "version": "28.0.2", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-28.0.2.tgz", - "integrity": "sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==", + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", "dev": true, "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "node": ">=6" } }, - "node_modules/jest-haste-map": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-28.1.3.tgz", - "integrity": "sha512-3S+RQWDXccXDKSWnkHa/dPwt+2qwA8CJzR61w3FoYCvoo3Pn8tvGcysmMF0Bj0EX5RYvAI2EIvC57OmotfdtKA==", - "dev": true, + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", "dependencies": { - "@jest/types": "^28.1.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^28.0.2", - "jest-util": "^28.1.3", - "jest-worker": "^28.1.3", - "micromatch": "^4.0.4", - "walker": "^1.0.8" + "has-tostringtag": "^1.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "node": ">= 0.4" }, - "optionalDependencies": { - "fsevents": "^2.3.2" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-leak-detector": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-28.1.3.tgz", - "integrity": "sha512-WFVJhnQsiKtDEo5lG2mM0v40QWnBM+zMdHHyJs8AWZ7J0QZJS59MsyKeJHWhpBZBH32S48FOVvGyOFT1h0DlqA==", + "node_modules/is-glob": { + "version": "4.0.3", "dev": true, + "license": "MIT", "dependencies": { - "jest-get-type": "^28.0.2", - "pretty-format": "^28.1.3" + "is-extglob": "^2.1.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "node": ">=0.10.0" } }, - "node_modules/jest-matcher-utils": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz", - "integrity": "sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw==", + "node_modules/is-negative-zero": { + "version": "2.0.2", "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^28.1.3", - "jest-get-type": "^28.0.2", - "pretty-format": "^28.1.3" + "license": "MIT", + "engines": { + "node": ">= 0.4" }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "dev": true, + "license": "MIT", "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "node": ">=0.12.0" } }, - "node_modules/jest-matcher-utils/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==", + "node_modules/is-number-object": { + "version": "1.0.6", "dev": true, + "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">=8" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-matcher-utils/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=8" } }, - "node_modules/jest-matcher-utils/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==", + "node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "isobject": "^3.0.1" }, "engines": { - "node": ">=7.0.0" + "node": ">=0.10.0" } }, - "node_modules/jest-matcher-utils/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "node_modules/is-promise": { + "version": "2.2.2", + "dev": true, + "license": "MIT" }, - "node_modules/jest-matcher-utils/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/is-regex": { + "version": "1.1.4", "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-matcher-utils/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/is-shared-array-buffer": { + "version": "1.0.1", "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "license": "MIT", "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-message-util": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz", - "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==", + "node_modules/is-string": { + "version": "1.0.7", "dev": true, + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^28.1.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^28.1.3", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" + "has-tostringtag": "^1.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-message-util/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==", + "node_modules/is-symbol": { + "version": "1.0.4", "dev": true, + "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "has-symbols": "^1.0.2" }, "engines": { - "node": ">=8" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-message-util/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, + "node_modules/is-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", + "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-message-util/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==", + "node_modules/is-weakref": { + "version": "1.0.2", "dev": true, + "license": "MIT", "dependencies": { - "color-name": "~1.1.4" + "call-bind": "^1.0.2" }, - "engines": { - "node": ">=7.0.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-message-util/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "node_modules/isarray": { + "version": "1.0.0", + "license": "MIT" }, - "node_modules/jest-message-util/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/isexe": { + "version": "2.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", "dev": true, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/jest-message-util/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true, "engines": { "node": ">=8" } }, - "node_modules/jest-message-util/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" }, "engines": { "node": ">=8" } }, - "node_modules/jest-mock": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-28.1.3.tgz", - "integrity": "sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA==", + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, "dependencies": { - "@jest/types": "^28.1.3", - "@types/node": "*" + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "node": ">=10" } }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "node_modules/istanbul-lib-report/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "engines": { - "node": ">=6" + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" }, - "peerDependencies": { - "jest-resolve": "*" + "engines": { + "node": ">=10" }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-regex-util": { - "version": "28.0.2", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz", - "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==", + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", + "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest/-/jest-28.1.3.tgz", + "integrity": "sha512-N4GT5on8UkZgH0O5LUavMRV1EDEhNTL0KEfRmDIeZHSV7p2XgLoY9t9VDUgL6o+yfdgYHVxuz81G8oB9VG5uyA==", "dev": true, + "dependencies": { + "@jest/core": "^28.1.3", + "@jest/types": "^28.1.3", + "import-local": "^3.0.2", + "jest-cli": "^28.1.3" + }, + "bin": { + "jest": "bin/jest.js" + }, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "node_modules/jest-resolve": { + "node_modules/jest-changed-files": { "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-28.1.3.tgz", - "integrity": "sha512-Z1W3tTjE6QaNI90qo/BJpfnvpxtaFTFw5CDgwpyE/Kz8U/06N1Hjf4ia9quUhCh39qIGWF1ZuxFiBiJQwSEYKQ==", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-28.1.3.tgz", + "integrity": "sha512-esaOfUWJXk2nfZt9SPyC8gA1kNfdKLkQWyzsMlqq8msYSlNKfmZxfRgZn4Cd4MGVUF+7v6dBs0d5TOAKa7iIiA==", "dev": true, "dependencies": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^28.1.3", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^28.1.3", - "jest-validate": "^28.1.3", - "resolve": "^1.20.0", - "resolve.exports": "^1.1.0", - "slash": "^3.0.0" + "execa": "^5.0.0", + "p-limit": "^3.1.0" }, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/jest-resolve-dependencies": { + "node_modules/jest-circus": { "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-28.1.3.tgz", - "integrity": "sha512-qa0QO2Q0XzQoNPouMbCc7Bvtsem8eQgVPNkwn9LnS+R2n8DaVDPL/U1gngC0LTl1RYXJU0uJa2BMC2DbTfFrHA==", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-28.1.3.tgz", + "integrity": "sha512-cZ+eS5zc79MBwt+IhQhiEp0OeBddpc1n8MBo1nMB8A7oPMKEO+Sre+wHaLJexQUj9Ya/8NOBY0RESUgYjB6fow==", "dev": true, "dependencies": { - "jest-regex-util": "^28.0.2", - "jest-snapshot": "^28.1.3" + "@jest/environment": "^28.1.3", + "@jest/expect": "^28.1.3", + "@jest/test-result": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^0.7.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^28.1.3", + "jest-matcher-utils": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-runtime": "^28.1.3", + "jest-snapshot": "^28.1.3", + "jest-util": "^28.1.3", + "p-limit": "^3.1.0", + "pretty-format": "^28.1.3", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" }, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/jest-resolve/node_modules/ansi-styles": { + "node_modules/jest-circus/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==", @@ -7108,7 +7803,7 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-resolve/node_modules/chalk": { + "node_modules/jest-circus/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", @@ -7124,7 +7819,7 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-resolve/node_modules/color-convert": { + "node_modules/jest-circus/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==", @@ -7136,13 +7831,13 @@ "node": ">=7.0.0" } }, - "node_modules/jest-resolve/node_modules/color-name": { + "node_modules/jest-circus/node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/jest-resolve/node_modules/has-flag": { + "node_modules/jest-circus/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", @@ -7151,7 +7846,7 @@ "node": ">=8" } }, - "node_modules/jest-resolve/node_modules/slash": { + "node_modules/jest-circus/node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", @@ -7160,7 +7855,7 @@ "node": ">=8" } }, - "node_modules/jest-resolve/node_modules/supports-color": { + "node_modules/jest-circus/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", @@ -7172,39 +7867,41 @@ "node": ">=8" } }, - "node_modules/jest-runner": { + "node_modules/jest-cli": { "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-28.1.3.tgz", - "integrity": "sha512-GkMw4D/0USd62OVO0oEgjn23TM+YJa2U2Wu5zz9xsQB1MxWKDOlrnykPxnMsN0tnJllfLPinHTka61u0QhaxBA==", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-28.1.3.tgz", + "integrity": "sha512-roY3kvrv57Azn1yPgdTebPAXvdR2xfezaKKYzVxZ6It/5NCxzJym6tUI5P1zkdWhfUYkxEI9uZWcQdaFLo8mJQ==", "dev": true, "dependencies": { - "@jest/console": "^28.1.3", - "@jest/environment": "^28.1.3", + "@jest/core": "^28.1.3", "@jest/test-result": "^28.1.3", - "@jest/transform": "^28.1.3", "@jest/types": "^28.1.3", - "@types/node": "*", "chalk": "^4.0.0", - "emittery": "^0.10.2", + "exit": "^0.1.2", "graceful-fs": "^4.2.9", - "jest-docblock": "^28.1.1", - "jest-environment-node": "^28.1.3", - "jest-haste-map": "^28.1.3", - "jest-leak-detector": "^28.1.3", - "jest-message-util": "^28.1.3", - "jest-resolve": "^28.1.3", - "jest-runtime": "^28.1.3", + "import-local": "^3.0.2", + "jest-config": "^28.1.3", "jest-util": "^28.1.3", - "jest-watcher": "^28.1.3", - "jest-worker": "^28.1.3", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" + "jest-validate": "^28.1.3", + "prompts": "^2.0.1", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" }, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "node_modules/jest-runner/node_modules/ansi-styles": { + "node_modules/jest-cli/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==", @@ -7219,7 +7916,7 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-runner/node_modules/chalk": { + "node_modules/jest-cli/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", @@ -7235,7 +7932,21 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-runner/node_modules/color-convert": { + "node_modules/jest-cli/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/jest-cli/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==", @@ -7247,13 +7958,13 @@ "node": ">=7.0.0" } }, - "node_modules/jest-runner/node_modules/color-name": { + "node_modules/jest-cli/node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/jest-runner/node_modules/has-flag": { + "node_modules/jest-cli/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", @@ -7262,17 +7973,7 @@ "node": ">=8" } }, - "node_modules/jest-runner/node_modules/source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/jest-runner/node_modules/supports-color": { + "node_modules/jest-cli/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", @@ -7284,40 +7985,79 @@ "node": ">=8" } }, - "node_modules/jest-runtime": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-28.1.3.tgz", - "integrity": "sha512-NU+881ScBQQLc1JHG5eJGU7Ui3kLKrmwCPPtYsJtBykixrM2OhVQlpMmFWJjMyDfdkGgBMNjXCGB/ebzsgNGQw==", + "node_modules/jest-cli/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, "dependencies": { - "@jest/environment": "^28.1.3", - "@jest/fake-timers": "^28.1.3", - "@jest/globals": "^28.1.3", - "@jest/source-map": "^28.1.2", - "@jest/test-result": "^28.1.3", - "@jest/transform": "^28.1.3", - "@jest/types": "^28.1.3", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "execa": "^5.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^28.1.3", - "jest-message-util": "^28.1.3", - "jest-mock": "^28.1.3", - "jest-regex-util": "^28.0.2", - "jest-resolve": "^28.1.3", - "jest-snapshot": "^28.1.3", - "jest-util": "^28.1.3", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" + "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/jest-cli/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/jest-config": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-28.1.3.tgz", + "integrity": "sha512-MG3INjByJ0J4AsNBm7T3hsuxKQqFIiRo/AUqb1q9LRKI5UU6Aar9JHbr9Ivn1TVwfUD9KirRoM/T6u8XlcQPHQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^28.1.3", + "@jest/types": "^28.1.3", + "babel-jest": "^28.1.3", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^28.1.3", + "jest-environment-node": "^28.1.3", + "jest-get-type": "^28.0.2", + "jest-regex-util": "^28.0.2", + "jest-resolve": "^28.1.3", + "jest-runner": "^28.1.3", + "jest-util": "^28.1.3", + "jest-validate": "^28.1.3", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^28.1.3", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" }, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } } }, - "node_modules/jest-runtime/node_modules/ansi-styles": { + "node_modules/jest-config/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==", @@ -7332,7 +8072,7 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-runtime/node_modules/chalk": { + "node_modules/jest-config/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", @@ -7348,7 +8088,7 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-runtime/node_modules/color-convert": { + "node_modules/jest-config/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==", @@ -7360,13 +8100,13 @@ "node": ">=7.0.0" } }, - "node_modules/jest-runtime/node_modules/color-name": { + "node_modules/jest-config/node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/jest-runtime/node_modules/has-flag": { + "node_modules/jest-config/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", @@ -7375,7 +8115,7 @@ "node": ">=8" } }, - "node_modules/jest-runtime/node_modules/slash": { + "node_modules/jest-config/node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", @@ -7384,7 +8124,7 @@ "node": ">=8" } }, - "node_modules/jest-runtime/node_modules/supports-color": { + "node_modules/jest-config/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", @@ -7396,41 +8136,22 @@ "node": ">=8" } }, - "node_modules/jest-snapshot": { + "node_modules/jest-diff": { "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-28.1.3.tgz", - "integrity": "sha512-4lzMgtiNlc3DU/8lZfmqxN3AYD6GGLbl+72rdBpXvcV+whX7mDrREzkPdp2RnmfIiWBg1YbuFSkXduF2JcafJg==", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-28.1.3.tgz", + "integrity": "sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw==", "dev": true, "dependencies": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/traverse": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^28.1.3", - "@jest/transform": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/babel__traverse": "^7.0.6", - "@types/prettier": "^2.1.5", - "babel-preset-current-node-syntax": "^1.0.0", "chalk": "^4.0.0", - "expect": "^28.1.3", - "graceful-fs": "^4.2.9", - "jest-diff": "^28.1.3", + "diff-sequences": "^28.1.1", "jest-get-type": "^28.0.2", - "jest-haste-map": "^28.1.3", - "jest-matcher-utils": "^28.1.3", - "jest-message-util": "^28.1.3", - "jest-util": "^28.1.3", - "natural-compare": "^1.4.0", - "pretty-format": "^28.1.3", - "semver": "^7.3.5" + "pretty-format": "^28.1.3" }, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/jest-snapshot/node_modules/ansi-styles": { + "node_modules/jest-diff/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==", @@ -7445,7 +8166,7 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-snapshot/node_modules/chalk": { + "node_modules/jest-diff/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", @@ -7461,7 +8182,7 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-snapshot/node_modules/color-convert": { + "node_modules/jest-diff/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==", @@ -7473,13 +8194,13 @@ "node": ">=7.0.0" } }, - "node_modules/jest-snapshot/node_modules/color-name": { + "node_modules/jest-diff/node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/jest-snapshot/node_modules/has-flag": { + "node_modules/jest-diff/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", @@ -7488,7 +8209,7 @@ "node": ">=8" } }, - "node_modules/jest-snapshot/node_modules/supports-color": { + "node_modules/jest-diff/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", @@ -7500,24 +8221,35 @@ "node": ">=8" } }, - "node_modules/jest-util": { + "node_modules/jest-docblock": { + "version": "28.1.1", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-28.1.1.tgz", + "integrity": "sha512-3wayBVNiOYx0cwAbl9rwm5kKFP8yHH3d/fkEaL02NPTkDojPtheGB7HZSFY4wzX+DxyrvhXz0KSCVksmCknCuA==", + "dev": true, + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-each": { "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", - "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-28.1.3.tgz", + "integrity": "sha512-arT1z4sg2yABU5uogObVPvSlSMQlDA48owx07BDPAiasW0yYpYHYOo4HHLz9q0BVzDVU4hILFjzJw0So9aCL/g==", "dev": true, "dependencies": { "@jest/types": "^28.1.3", - "@types/node": "*", "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" + "jest-get-type": "^28.0.2", + "jest-util": "^28.1.3", + "pretty-format": "^28.1.3" }, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/jest-util/node_modules/ansi-styles": { + "node_modules/jest-each/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==", @@ -7532,7 +8264,7 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-util/node_modules/chalk": { + "node_modules/jest-each/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", @@ -7548,7 +8280,7 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-util/node_modules/color-convert": { + "node_modules/jest-each/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==", @@ -7560,13 +8292,13 @@ "node": ">=7.0.0" } }, - "node_modules/jest-util/node_modules/color-name": { + "node_modules/jest-each/node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/jest-util/node_modules/has-flag": { + "node_modules/jest-each/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", @@ -7575,7 +8307,7 @@ "node": ">=8" } }, - "node_modules/jest-util/node_modules/supports-color": { + "node_modules/jest-each/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", @@ -7587,24 +8319,86 @@ "node": ">=8" } }, - "node_modules/jest-validate": { + "node_modules/jest-environment-node": { "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-28.1.3.tgz", - "integrity": "sha512-SZbOGBWEsaTxBGCOpsRWlXlvNkvTkY0XxRfh7zYmvd8uL5Qzyg0CHAXiXKROflh801quA6+/DsT4ODDthOC/OA==", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-28.1.3.tgz", + "integrity": "sha512-ugP6XOhEpjAEhGYvp5Xj989ns5cB1K6ZdjBYuS30umT4CQEETaxSiPcZ/E1kFktX4GkrcM4qu07IIlDYX1gp+A==", "dev": true, "dependencies": { + "@jest/environment": "^28.1.3", + "@jest/fake-timers": "^28.1.3", "@jest/types": "^28.1.3", - "camelcase": "^6.2.0", + "@types/node": "*", + "jest-mock": "^28.1.3", + "jest-util": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "28.0.2", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-28.0.2.tgz", + "integrity": "sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==", + "dev": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-28.1.3.tgz", + "integrity": "sha512-3S+RQWDXccXDKSWnkHa/dPwt+2qwA8CJzR61w3FoYCvoo3Pn8tvGcysmMF0Bj0EX5RYvAI2EIvC57OmotfdtKA==", + "dev": true, + "dependencies": { + "@jest/types": "^28.1.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^28.0.2", + "jest-util": "^28.1.3", + "jest-worker": "^28.1.3", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-28.1.3.tgz", + "integrity": "sha512-WFVJhnQsiKtDEo5lG2mM0v40QWnBM+zMdHHyJs8AWZ7J0QZJS59MsyKeJHWhpBZBH32S48FOVvGyOFT1h0DlqA==", + "dev": true, + "dependencies": { + "jest-get-type": "^28.0.2", + "pretty-format": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz", + "integrity": "sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw==", + "dev": true, + "dependencies": { "chalk": "^4.0.0", + "jest-diff": "^28.1.3", "jest-get-type": "^28.0.2", - "leven": "^3.1.0", "pretty-format": "^28.1.3" }, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/jest-validate/node_modules/ansi-styles": { + "node_modules/jest-matcher-utils/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==", @@ -7619,19 +8413,7 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-validate/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/jest-validate/node_modules/chalk": { + "node_modules/jest-matcher-utils/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", @@ -7647,7 +8429,7 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-validate/node_modules/color-convert": { + "node_modules/jest-matcher-utils/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==", @@ -7659,13 +8441,13 @@ "node": ">=7.0.0" } }, - "node_modules/jest-validate/node_modules/color-name": { + "node_modules/jest-matcher-utils/node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/jest-validate/node_modules/has-flag": { + "node_modules/jest-matcher-utils/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", @@ -7674,7 +8456,7 @@ "node": ">=8" } }, - "node_modules/jest-validate/node_modules/supports-color": { + "node_modules/jest-matcher-utils/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", @@ -7686,26 +8468,27 @@ "node": ">=8" } }, - "node_modules/jest-watcher": { + "node_modules/jest-message-util": { "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-28.1.3.tgz", - "integrity": "sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g==", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz", + "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==", "dev": true, "dependencies": { - "@jest/test-result": "^28.1.3", + "@babel/code-frame": "^7.12.13", "@jest/types": "^28.1.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", + "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", - "emittery": "^0.10.2", - "jest-util": "^28.1.3", - "string-length": "^4.0.1" + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^28.1.3", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" }, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/jest-watcher/node_modules/ansi-styles": { + "node_modules/jest-message-util/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==", @@ -7720,7 +8503,7 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-watcher/node_modules/chalk": { + "node_modules/jest-message-util/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", @@ -7736,7 +8519,7 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-watcher/node_modules/color-convert": { + "node_modules/jest-message-util/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==", @@ -7748,13 +8531,13 @@ "node": ">=7.0.0" } }, - "node_modules/jest-watcher/node_modules/color-name": { + "node_modules/jest-message-util/node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/jest-watcher/node_modules/has-flag": { + "node_modules/jest-message-util/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", @@ -7763,7 +8546,16 @@ "node": ">=8" } }, - "node_modules/jest-watcher/node_modules/supports-color": { + "node_modules/jest-message-util/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-message-util/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", @@ -7775,2149 +8567,2045 @@ "node": ">=8" } }, - "node_modules/jest-when": { - "version": "3.5.1", - "dev": true, - "license": "MIT", - "peerDependencies": { - "jest": ">= 25" - } - }, - "node_modules/jest-worker": { + "node_modules/jest-mock": { "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.3.tgz", - "integrity": "sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-28.1.3.tgz", + "integrity": "sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA==", "dev": true, "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" + "@jest/types": "^28.1.3", + "@types/node": "*" }, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/jest-worker/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", "dev": true, "engines": { - "node": ">=8" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" + "node": ">=6" }, - "engines": { - "node": ">=10" + "peerDependencies": { + "jest-resolve": "*" }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } } }, - "node_modules/jmespath": { - "version": "0.16.0", - "license": "Apache-2.0", + "node_modules/jest-regex-util": { + "version": "28.0.2", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz", + "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==", + "dev": true, "engines": { - "node": ">= 0.6.0" + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/js-beautify": { - "version": "1.14.0", + "node_modules/jest-resolve": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-28.1.3.tgz", + "integrity": "sha512-Z1W3tTjE6QaNI90qo/BJpfnvpxtaFTFw5CDgwpyE/Kz8U/06N1Hjf4ia9quUhCh39qIGWF1ZuxFiBiJQwSEYKQ==", "dev": true, - "license": "MIT", "dependencies": { - "config-chain": "^1.1.12", - "editorconfig": "^0.15.3", - "glob": "^7.1.3", - "nopt": "^5.0.0" - }, - "bin": { - "css-beautify": "js/bin/css-beautify.js", - "html-beautify": "js/bin/html-beautify.js", - "js-beautify": "js/bin/js-beautify.js" + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^28.1.3", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^28.1.3", + "jest-validate": "^28.1.3", + "resolve": "^1.20.0", + "resolve.exports": "^1.1.0", + "slash": "^3.0.0" }, "engines": { - "node": ">=10" + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/js-beautify/node_modules/nopt": { - "version": "5.0.0", + "node_modules/jest-resolve-dependencies": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-28.1.3.tgz", + "integrity": "sha512-qa0QO2Q0XzQoNPouMbCc7Bvtsem8eQgVPNkwn9LnS+R2n8DaVDPL/U1gngC0LTl1RYXJU0uJa2BMC2DbTfFrHA==", "dev": true, - "license": "ISC", "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" + "jest-regex-util": "^28.0.2", + "jest-snapshot": "^28.1.3" }, "engines": { - "node": ">=6" + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "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==", - "dev": true - }, - "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==", + "node_modules/jest-resolve/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "dependencies": { - "argparse": "^2.0.1" + "color-convert": "^2.0.1" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "node_modules/jest-resolve/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "bin": { - "jsesc": "bin/jsesc" + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "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==", - "dev": true - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/jsonfile": { - "version": "6.1.0", + "node_modules/jest-resolve/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "license": "MIT", "dependencies": { - "universalify": "^2.0.0" + "color-name": "~1.1.4" }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/jsonfile/node_modules/universalify": { - "version": "2.0.0", - "dev": true, - "license": "MIT", "engines": { - "node": ">= 10.0.0" + "node": ">=7.0.0" } }, - "node_modules/jsonlines": { - "version": "0.1.1", - "dev": true, - "license": "MIT" - }, - "node_modules/just-extend": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", - "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==" + "node_modules/jest-resolve/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "node_modules/jest-resolve/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "node_modules/jest-resolve/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/kuler": { - "version": "2.0.0", - "license": "MIT" - }, - "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==", + "node_modules/jest-resolve/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "node_modules/jest-runner": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-28.1.3.tgz", + "integrity": "sha512-GkMw4D/0USd62OVO0oEgjn23TM+YJa2U2Wu5zz9xsQB1MxWKDOlrnykPxnMsN0tnJllfLPinHTka61u0QhaxBA==", "dev": true, "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" + "@jest/console": "^28.1.3", + "@jest/environment": "^28.1.3", + "@jest/test-result": "^28.1.3", + "@jest/transform": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.10.2", + "graceful-fs": "^4.2.9", + "jest-docblock": "^28.1.1", + "jest-environment-node": "^28.1.3", + "jest-haste-map": "^28.1.3", + "jest-leak-detector": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-resolve": "^28.1.3", + "jest-runtime": "^28.1.3", + "jest-util": "^28.1.3", + "jest-watcher": "^28.1.3", + "jest-worker": "^28.1.3", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" }, "engines": { - "node": ">= 0.8.0" + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/libpq": { - "version": "1.8.12", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "peer": true, + "node_modules/jest-runner/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "dependencies": { - "bindings": "1.5.0", - "nan": "^2.14.0" + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "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==", + "node_modules/jest-runner/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "dependencies": { - "p-locate": "^5.0.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/lodash": { - "version": "4.17.21", - "license": "MIT" - }, - "node_modules/lodash.clonedeep": { - "version": "4.5.0", - "license": "MIT" + "node_modules/jest-runner/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } }, - "node_modules/lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "node_modules/jest-runner/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==" - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "license": "MIT" - }, - "node_modules/logform": { - "version": "2.4.0", - "license": "MIT", - "dependencies": { - "@colors/colors": "1.5.0", - "fecha": "^4.2.0", - "ms": "^2.1.1", - "safe-stable-stringify": "^2.3.1", - "triple-beam": "^1.3.0" + "node_modules/jest-runner/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" } }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "node_modules/jest-runner/node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", "dev": true, "dependencies": { - "yallist": "^3.0.2" + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" } }, - "node_modules/lru-queue": { - "version": "0.1.0", + "node_modules/jest-runner/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "license": "MIT", "dependencies": { - "es5-ext": "~0.10.2" + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/make-dir": { - "version": "2.1.0", + "node_modules/jest-runtime": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-28.1.3.tgz", + "integrity": "sha512-NU+881ScBQQLc1JHG5eJGU7Ui3kLKrmwCPPtYsJtBykixrM2OhVQlpMmFWJjMyDfdkGgBMNjXCGB/ebzsgNGQw==", "dev": true, - "license": "MIT", "dependencies": { - "pify": "^4.0.1", - "semver": "^5.6.0" + "@jest/environment": "^28.1.3", + "@jest/fake-timers": "^28.1.3", + "@jest/globals": "^28.1.3", + "@jest/source-map": "^28.1.2", + "@jest/test-result": "^28.1.3", + "@jest/transform": "^28.1.3", + "@jest/types": "^28.1.3", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "execa": "^5.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-mock": "^28.1.3", + "jest-regex-util": "^28.0.2", + "jest-resolve": "^28.1.3", + "jest-snapshot": "^28.1.3", + "jest-util": "^28.1.3", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" }, "engines": { - "node": ">=6" + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "node_modules/jest-runtime/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "dependencies": { - "tmpl": "1.0.5" - } - }, - "node_modules/media-typer": { - "version": "0.3.0", - "license": "MIT", + "color-convert": "^2.0.1" + }, "engines": { - "node": ">= 0.6" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/memoizee": { - "version": "0.4.15", + "node_modules/jest-runtime/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "license": "ISC", "dependencies": { - "d": "^1.0.1", - "es5-ext": "^0.10.53", - "es6-weak-map": "^2.0.3", - "event-emitter": "^0.3.5", - "is-promise": "^2.2.2", - "lru-queue": "^0.1.0", - "next-tick": "^1.1.0", - "timers-ext": "^0.1.7" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/merge-descriptors": { - "version": "1.0.1", - "license": "MIT" - }, - "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==", + "node_modules/jest-runtime/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "dependencies": { - "is-plain-obj": "^1.1" + "color-name": "~1.1.4" }, "engines": { - "node": ">=4" + "node": ">=7.0.0" } }, - "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==", + "node_modules/jest-runtime/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/merge2": { - "version": "1.4.1", + "node_modules/jest-runtime/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "license": "MIT", "engines": { - "node": ">= 8" + "node": ">=8" } }, - "node_modules/methods": { - "version": "1.1.2", - "license": "MIT", + "node_modules/jest-runtime/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, "engines": { - "node": ">= 0.6" + "node": ">=8" } }, - "node_modules/micromatch": { - "version": "4.0.4", + "node_modules/jest-runtime/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "license": "MIT", "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=8.6" + "node": ">=8" } }, - "node_modules/mime": { - "version": "1.6.0", - "license": "MIT", - "bin": { - "mime": "cli.js" + "node_modules/jest-snapshot": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-28.1.3.tgz", + "integrity": "sha512-4lzMgtiNlc3DU/8lZfmqxN3AYD6GGLbl+72rdBpXvcV+whX7mDrREzkPdp2RnmfIiWBg1YbuFSkXduF2JcafJg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^28.1.3", + "@jest/transform": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/babel__traverse": "^7.0.6", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^28.1.3", + "graceful-fs": "^4.2.9", + "jest-diff": "^28.1.3", + "jest-get-type": "^28.0.2", + "jest-haste-map": "^28.1.3", + "jest-matcher-utils": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-util": "^28.1.3", + "natural-compare": "^1.4.0", + "pretty-format": "^28.1.3", + "semver": "^7.3.5" }, "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "license": "MIT", - "engines": { - "node": ">= 0.6" + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/mime-types": { - "version": "2.1.35", - "license": "MIT", + "node_modules/jest-snapshot/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "dependencies": { - "mime-db": "1.52.0" + "color-convert": "^2.0.1" }, "engines": { - "node": ">= 0.6" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "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==", + "node_modules/jest-snapshot/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, "engines": { - "node": ">=6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/minimatch": { - "version": "3.1.2", + "node_modules/jest-snapshot/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "license": "ISC", "dependencies": { - "brace-expansion": "^1.1.7" + "color-name": "~1.1.4" }, "engines": { - "node": "*" + "node": ">=7.0.0" } }, - "node_modules/moment": { - "version": "2.29.4", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", - "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "node_modules/jest-snapshot/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-snapshot/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, "engines": { - "node": "*" + "node": ">=8" } }, - "node_modules/moment-timezone": { - "version": "0.5.43", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.43.tgz", - "integrity": "sha512-72j3aNyuIsDxdF1i7CEgV2FfxM1r6aaqJyLB2vwb33mXYyoyLly+F1zbWqhA3/bVIoJ4szlUoMbUnVdid32NUQ==", + "node_modules/jest-snapshot/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "dependencies": { - "moment": "^2.29.4" + "has-flag": "^4.0.0" }, "engines": { - "node": "*" + "node": ">=8" } }, - "node_modules/ms": { - "version": "2.1.2", - "license": "MIT" - }, - "node_modules/mute-stream": { - "version": "0.0.8", + "node_modules/jest-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", + "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", "dev": true, - "license": "ISC" - }, - "node_modules/nan": { - "version": "2.16.0", - "license": "MIT", - "optional": true, - "peer": true - }, - "node_modules/nano-seconds": { - "version": "1.2.2", - "license": "MIT", + "dependencies": { + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, "engines": { - "node": ">=4.0.0" + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/natural-compare": { - "version": "1.4.0", + "node_modules/jest-util/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "license": "MIT" - }, - "node_modules/negotiator": { - "version": "0.6.3", - "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, "engines": { - "node": ">= 0.6" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/next-tick": { - "version": "1.1.0", + "node_modules/jest-util/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "license": "ISC" - }, - "node_modules/nise": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.5.tgz", - "integrity": "sha512-VJuPIfUFaXNRzETTQEEItTOP8Y171ijr+JLq42wHes3DiryR8vT+1TXQW/Rx8JNUhyYYWyIvjXTU6dOhJcs9Nw==", "dependencies": { - "@sinonjs/commons": "^2.0.0", - "@sinonjs/fake-timers": "^10.0.2", - "@sinonjs/text-encoding": "^0.7.1", - "just-extend": "^4.0.2", - "path-to-regexp": "^1.7.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/nise/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/jest-util/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "dependencies": { - "type-detect": "4.0.8" + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "node_modules/nise/node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "dependencies": { - "@sinonjs/commons": "^3.0.0" + "node_modules/jest-util/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-util/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" } }, - "node_modules/nise/node_modules/@sinonjs/fake-timers/node_modules/@sinonjs/commons": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", - "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", + "node_modules/jest-util/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "dependencies": { - "type-detect": "4.0.8" + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/nise/node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" - }, - "node_modules/nise/node_modules/path-to-regexp": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", - "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", - "dependencies": { - "isarray": "0.0.1" - } - }, - "node_modules/node-environment-flags": { - "version": "1.0.6", + "node_modules/jest-validate": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-28.1.3.tgz", + "integrity": "sha512-SZbOGBWEsaTxBGCOpsRWlXlvNkvTkY0XxRfh7zYmvd8uL5Qzyg0CHAXiXKROflh801quA6+/DsT4ODDthOC/OA==", "dev": true, - "license": "Apache-2.0", "dependencies": { - "object.getownpropertydescriptors": "^2.0.3", - "semver": "^5.7.0" + "@jest/types": "^28.1.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^28.0.2", + "leven": "^3.1.0", + "pretty-format": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.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-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", - "dev": true - }, - "node_modules/nodemon": { - "version": "2.0.19", + "node_modules/jest-validate/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "hasInstallScript": true, - "license": "MIT", "dependencies": { - "chokidar": "^3.5.2", - "debug": "^3.2.7", - "ignore-by-default": "^1.0.1", - "minimatch": "^3.0.4", - "pstree.remy": "^1.1.8", - "semver": "^5.7.1", - "simple-update-notifier": "^1.0.7", - "supports-color": "^5.5.0", - "touch": "^3.1.0", - "undefsafe": "^2.0.5" + "color-convert": "^2.0.1" }, - "bin": { - "nodemon": "bin/nodemon.js" + "engines": { + "node": ">=8" }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-validate/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": ">=8.10.0" + "node": ">=10" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/nodemon" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/nodemon/node_modules/debug": { - "version": "3.2.7", + "node_modules/jest-validate/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "license": "MIT", "dependencies": { - "ms": "^2.1.1" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/nopt": { - "version": "1.0.10", + "node_modules/jest-validate/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "license": "MIT", "dependencies": { - "abbrev": "1" + "color-name": "~1.1.4" }, - "bin": { - "nopt": "bin/nopt.js" + "engines": { + "node": ">=7.0.0" } }, - "node_modules/normalize-path": { - "version": "3.0.0", + "node_modules/jest-validate/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-validate/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/npm-audit-resolver": { - "version": "3.0.0-7", - "resolved": "https://registry.npmjs.org/npm-audit-resolver/-/npm-audit-resolver-3.0.0-7.tgz", - "integrity": "sha512-6mXix6DOeiJxZ/vJKA5xkyPPO0ocRj8ZFPdockd0vO7woQbRdAnBdK5JXCpnGxrTW/ND7X6XcbYrWvWdxYoVng==", + "node_modules/jest-validate/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "dependencies": { - "audit-resolve-core": "^3.0.0-3", - "chalk": "^2.4.2", - "concat-stream": "^2.0.0", - "djv": "^2.1.4", - "jsonlines": "^0.1.1", - "read": "^1.0.7", - "spawn-shell": "^2.1.0", - "yargs-parser": "^18.1.3", - "yargs-unparser": "^2.0.0" + "has-flag": "^4.0.0" }, - "bin": { - "check-audit": "check.js", - "resolve-audit": "resolve.js" + "engines": { + "node": ">=8" } }, - "node_modules/npm-audit-resolver/node_modules/yargs-parser": { - "version": "18.1.3", + "node_modules/jest-watcher": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-28.1.3.tgz", + "integrity": "sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g==", "dev": true, - "license": "ISC", "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" + "@jest/test-result": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.10.2", + "jest-util": "^28.1.3", + "string-length": "^4.0.1" }, "engines": { - "node": ">=6" + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "node_modules/jest-watcher/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "dependencies": { - "path-key": "^3.0.0" + "color-convert": "^2.0.1" }, "engines": { "node": ">=8" - } - }, - "node_modules/object-inspect": { - "version": "1.12.0", - "dev": true, - "license": "MIT", + }, "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/object.assign": { + "node_modules/jest-watcher/node_modules/chalk": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/object.getownpropertydescriptors": { - "version": "2.1.3", + "node_modules/jest-watcher/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" + "color-name": "~1.1.4" }, "engines": { - "node": ">= 0.8" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=7.0.0" } }, - "node_modules/on-finished": { - "version": "2.3.0", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, + "node_modules/jest-watcher/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-watcher/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, "engines": { - "node": ">= 0.8" + "node": ">=8" } }, - "node_modules/once": { - "version": "1.4.0", + "node_modules/jest-watcher/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "license": "ISC", "dependencies": { - "wrappy": "1" + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/one-time": { - "version": "1.0.0", + "node_modules/jest-when": { + "version": "3.5.1", + "dev": true, "license": "MIT", - "dependencies": { - "fn.name": "1.x.x" + "peerDependencies": { + "jest": ">= 25" } }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "node_modules/jest-worker": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.3.tgz", + "integrity": "sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==", "dev": true, "dependencies": { - "mimic-fn": "^2.1.0" + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" }, "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "node_modules/jest-worker/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "dependencies": { - "@aashutoshrathi/word-wrap": "^1.2.3", - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" - }, "engines": { - "node": ">= 0.8.0" + "node": ">=8" } }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "dependencies": { - "yocto-queue": "^0.1.0" + "has-flag": "^4.0.0" }, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "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==", + "node_modules/jmespath": { + "version": "0.16.0", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/js-beautify": { + "version": "1.14.0", "dev": true, + "license": "MIT", "dependencies": { - "p-limit": "^3.0.2" + "config-chain": "^1.1.12", + "editorconfig": "^0.15.3", + "glob": "^7.1.3", + "nopt": "^5.0.0" + }, + "bin": { + "css-beautify": "js/bin/css-beautify.js", + "html-beautify": "js/bin/html-beautify.js", + "js-beautify": "js/bin/js-beautify.js" }, "engines": { "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "node_modules/js-beautify/node_modules/nopt": { + "version": "5.0.0", "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, "engines": { "node": ">=6" } }, - "node_modules/packet-reader": { - "version": "1.0.0", - "license": "MIT" + "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==", + "dev": true }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "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": { - "callsites": "^3.0.0" + "argparse": "^2.0.1" }, - "engines": { - "node": ">=6" + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" + "bin": { + "jsesc": "bin/jsesc" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=4" } }, - "node_modules/parse-passwd": { - "version": "1.0.0", + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "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==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonfile": { + "version": "6.1.0", "dev": true, "license": "MIT", - "engines": { - "node": ">=0.10.0" + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" } }, - "node_modules/parseurl": { - "version": "1.3.3", + "node_modules/jsonfile/node_modules/universalify": { + "version": "2.0.0", + "dev": true, "license": "MIT", "engines": { - "node": ">= 0.8" + "node": ">= 10.0.0" } }, - "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==", + "node_modules/jsonlines": { + "version": "0.1.1", "dev": true, - "engines": { - "node": ">=8" - } + "license": "MIT" }, - "node_modules/path-is-absolute": { - "version": "1.0.1", + "node_modules/just-extend": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", + "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==" + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/path-key": { - "version": "3.1.1", + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", "dev": true, - "license": "MIT", "engines": { - "node": ">=8" + "node": ">=6" } }, - "node_modules/path-parse": { - "version": "1.0.7", - "dev": true, - "license": "MIT" - }, - "node_modules/path-to-regexp": { - "version": "0.1.7", + "node_modules/kuler": { + "version": "2.0.0", "license": "MIT" }, - "node_modules/path-type": { - "version": "4.0.0", + "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==", "dev": true, - "license": "MIT", "engines": { - "node": ">=8" + "node": ">=6" } }, - "node_modules/pg": { - "version": "8.7.3", - "license": "MIT", + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, "dependencies": { - "buffer-writer": "2.0.0", - "packet-reader": "1.0.0", - "pg-connection-string": "^2.5.0", - "pg-pool": "^3.5.1", - "pg-protocol": "^1.5.0", - "pg-types": "^2.1.0", - "pgpass": "1.x" + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" }, "engines": { - "node": ">= 8.0.0" - }, - "peerDependencies": { - "pg-native": ">=2.0.0" - }, - "peerDependenciesMeta": { - "pg-native": { - "optional": true - } + "node": ">= 0.8.0" } }, - "node_modules/pg-connection-string": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", - "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==" - }, - "node_modules/pg-hstore": { - "version": "2.3.4", - "license": "MIT", - "dependencies": { - "underscore": "^1.13.1" - }, - "engines": { - "node": ">= 0.8.x" - } - }, - "node_modules/pg-int8": { - "version": "1.0.1", - "license": "ISC", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/pg-native": { - "version": "3.0.1", + "node_modules/libpq": { + "version": "1.8.12", + "hasInstallScript": true, "license": "MIT", "optional": true, "peer": true, "dependencies": { - "libpq": "^1.8.10", - "pg-types": "^1.12.1", - "readable-stream": "1.0.31" + "bindings": "1.5.0", + "nan": "^2.14.0" } }, - "node_modules/pg-native/node_modules/isarray": { - "version": "0.0.1", - "license": "MIT", - "optional": true, - "peer": true + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true }, - "node_modules/pg-native/node_modules/pg-types": { - "version": "1.13.0", - "license": "MIT", - "optional": true, - "peer": true, + "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": { - "pg-int8": "1.0.1", - "postgres-array": "~1.0.0", - "postgres-bytea": "~1.0.0", - "postgres-date": "~1.0.0", - "postgres-interval": "^1.1.0" - } - }, - "node_modules/pg-native/node_modules/postgres-array": { - "version": "1.0.3", - "license": "MIT", - "optional": true, - "peer": true, + "p-locate": "^5.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/pg-native/node_modules/readable-stream": { - "version": "1.0.31", - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } + "node_modules/lodash": { + "version": "4.17.21", + "license": "MIT" }, - "node_modules/pg-native/node_modules/string_decoder": { - "version": "0.10.31", - "license": "MIT", - "optional": true, - "peer": true + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "license": "MIT" }, - "node_modules/pg-pool": { - "version": "3.5.1", - "license": "MIT", - "peerDependencies": { - "pg": ">=8.0" - } + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "dev": true }, - "node_modules/pg-protocol": { - "version": "1.5.0", + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", "license": "MIT" }, - "node_modules/pg-types": { - "version": "2.2.0", + "node_modules/logform": { + "version": "2.4.0", "license": "MIT", "dependencies": { - "pg-int8": "1.0.1", - "postgres-array": "~2.0.0", - "postgres-bytea": "~1.0.0", - "postgres-date": "~1.0.4", - "postgres-interval": "^1.1.0" - }, - "engines": { - "node": ">=4" + "@colors/colors": "1.5.0", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" } }, - "node_modules/pgpass": { - "version": "1.0.5", - "license": "MIT", + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, "dependencies": { - "split2": "^4.1.0" + "yallist": "^3.0.2" } }, - "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "node_modules/picomatch": { - "version": "2.3.1", + "node_modules/lru-queue": { + "version": "0.1.0", "dev": true, "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "dependencies": { + "es5-ext": "~0.10.2" } }, - "node_modules/pify": { - "version": "4.0.1", + "node_modules/make-dir": { + "version": "2.1.0", "dev": true, "license": "MIT", + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, "engines": { "node": ">=6" } }, - "node_modules/pirates": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", "dev": true, + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "license": "MIT", "engines": { - "node": ">= 6" + "node": ">= 0.6" } }, - "node_modules/pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "node_modules/memoizee": { + "version": "0.4.15", "dev": true, + "license": "ISC", "dependencies": { - "find-up": "^3.0.0" - }, - "engines": { - "node": ">=6" + "d": "^1.0.1", + "es5-ext": "^0.10.53", + "es6-weak-map": "^2.0.3", + "event-emitter": "^0.3.5", + "is-promise": "^2.2.2", + "lru-queue": "^0.1.0", + "next-tick": "^1.1.0", + "timers-ext": "^0.1.7" } }, - "node_modules/pkg-dir/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==", + "node_modules/merge-descriptors": { + "version": "1.0.1", + "license": "MIT" + }, + "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": { - "locate-path": "^3.0.0" + "is-plain-obj": "^1.1" }, "engines": { - "node": ">=6" + "node": ">=4" } }, - "node_modules/pkg-dir/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==", + "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", "dev": true, - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, + "license": "MIT", "engines": { - "node": ">=6" + "node": ">= 8" } }, - "node_modules/pkg-dir/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" - }, + "node_modules/methods": { + "version": "1.1.2", + "license": "MIT", "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.6" } }, - "node_modules/pkg-dir/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==", + "node_modules/micromatch": { + "version": "4.0.4", "dev": true, + "license": "MIT", "dependencies": { - "p-limit": "^2.0.0" + "braces": "^3.0.1", + "picomatch": "^2.2.3" }, "engines": { - "node": ">=6" + "node": ">=8.6" } }, - "node_modules/pkg-dir/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==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/postgres-array": { - "version": "2.0.0", + "node_modules/mime": { + "version": "1.6.0", "license": "MIT", + "bin": { + "mime": "cli.js" + }, "engines": { "node": ">=4" } }, - "node_modules/postgres-bytea": { - "version": "1.0.0", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postgres-date": { - "version": "1.0.7", + "node_modules/mime-db": { + "version": "1.52.0", "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">= 0.6" } }, - "node_modules/postgres-interval": { - "version": "1.2.0", + "node_modules/mime-types": { + "version": "2.1.35", "license": "MIT", "dependencies": { - "xtend": "^4.0.0" + "mime-db": "1.52.0" }, "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "engines": { - "node": ">= 0.8.0" + "node": ">= 0.6" } }, - "node_modules/prettier": { - "version": "2.6.0", + "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==", "dev": true, - "license": "MIT", - "bin": { - "prettier": "bin-prettier.js" - }, "engines": { - "node": ">=10.13.0" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" + "node": ">=6" } }, - "node_modules/prettier-linter-helpers": { - "version": "1.0.0", + "node_modules/minimatch": { + "version": "3.1.2", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "fast-diff": "^1.1.2" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=6.0.0" + "node": "*" } }, - "node_modules/pretty-format": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", - "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", - "dev": true, + "node_modules/mnemonist": { + "version": "0.38.3", + "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.38.3.tgz", + "integrity": "sha512-2K9QYubXx/NAjv4VLq1d1Ly8pWNC5L3BrixtdkyTegXWJIqY+zLNDhhX/A+ZwWt70tB1S8H4BE8FLYEFyNoOBw==", "dependencies": { - "@jest/schemas": "^28.1.3", - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "obliterator": "^1.6.1" } }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, + "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==", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": "*" } }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, + "node_modules/moment-timezone": { + "version": "0.5.43", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.43.tgz", + "integrity": "sha512-72j3aNyuIsDxdF1i7CEgV2FfxM1r6aaqJyLB2vwb33mXYyoyLly+F1zbWqhA3/bVIoJ4szlUoMbUnVdid32NUQ==", "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" + "moment": "^2.29.4" }, "engines": { - "node": ">= 6" + "node": "*" } }, - "node_modules/proto-list": { - "version": "1.2.4", - "dev": true, - "license": "ISC" - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "license": "MIT", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } + "node_modules/ms": { + "version": "2.1.2", + "license": "MIT" }, - "node_modules/pseudomap": { - "version": "1.0.2", + "node_modules/mute-stream": { + "version": "0.0.8", "dev": true, "license": "ISC" }, - "node_modules/pstree.remy": { - "version": "1.1.8", - "dev": true, - "license": "MIT" - }, - "node_modules/punycode": { - "version": "1.3.2", - "license": "MIT" - }, - "node_modules/qs": { - "version": "6.9.7", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "node_modules/nan": { + "version": "2.16.0", + "license": "MIT", + "optional": true, + "peer": true }, - "node_modules/querystring": { - "version": "0.2.0", + "node_modules/nano-seconds": { + "version": "1.2.2", + "license": "MIT", "engines": { - "node": ">=0.4.x" + "node": ">=4.0.0" } }, - "node_modules/queue-microtask": { - "version": "1.2.3", + "node_modules/natural-compare": { + "version": "1.4.0", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], "license": "MIT" }, - "node_modules/range-parser": { - "version": "1.2.1", + "node_modules/negotiator": { + "version": "0.6.3", "license": "MIT", "engines": { "node": ">= 0.6" } }, - "node_modules/raw-body": { - "version": "2.4.3", - "license": "MIT", + "node_modules/next-tick": { + "version": "1.1.0", + "dev": true, + "license": "ISC" + }, + "node_modules/nise": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.5.tgz", + "integrity": "sha512-VJuPIfUFaXNRzETTQEEItTOP8Y171ijr+JLq42wHes3DiryR8vT+1TXQW/Rx8JNUhyYYWyIvjXTU6dOhJcs9Nw==", "dependencies": { - "bytes": "3.1.2", - "http-errors": "1.8.1", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" + "@sinonjs/commons": "^2.0.0", + "@sinonjs/fake-timers": "^10.0.2", + "@sinonjs/text-encoding": "^0.7.1", + "just-extend": "^4.0.2", + "path-to-regexp": "^1.7.0" } }, - "node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true - }, - "node_modules/read": { - "version": "1.0.7", - "dev": true, - "license": "ISC", + "node_modules/nise/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==", "dependencies": { - "mute-stream": "~0.0.4" - }, - "engines": { - "node": ">=0.8" + "type-detect": "4.0.8" } }, - "node_modules/readable-stream": { - "version": "3.6.0", - "license": "MIT", + "node_modules/nise/node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" + "@sinonjs/commons": "^3.0.0" } }, - "node_modules/readdirp": { - "version": "3.6.0", - "dev": true, - "license": "MIT", + "node_modules/nise/node_modules/@sinonjs/fake-timers/node_modules/@sinonjs/commons": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", + "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" + "type-detect": "4.0.8" } }, - "node_modules/regenerate": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", - "dev": true + "node_modules/nise/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" }, - "node_modules/regenerate-unicode-properties": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz", - "integrity": "sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==", - "dev": true, + "node_modules/nise/node_modules/path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", "dependencies": { - "regenerate": "^1.4.2" - }, - "engines": { - "node": ">=4" + "isarray": "0.0.1" } }, - "node_modules/regenerator-runtime": { - "version": "0.13.9", - "license": "MIT" - }, - "node_modules/regenerator-transform": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", - "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", + "node_modules/node-environment-flags": { + "version": "1.0.6", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@babel/runtime": "^7.8.4" + "object.getownpropertydescriptors": "^2.0.3", + "semver": "^5.7.0" } }, - "node_modules/regexpu-core": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", - "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", + "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-releases": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", + "dev": true + }, + "node_modules/nodemon": { + "version": "2.0.19", "dev": true, + "hasInstallScript": true, + "license": "MIT", "dependencies": { - "@babel/regjsgen": "^0.8.0", - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.1.0", - "regjsparser": "^0.9.1", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.1.0" + "chokidar": "^3.5.2", + "debug": "^3.2.7", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.0.4", + "pstree.remy": "^1.1.8", + "semver": "^5.7.1", + "simple-update-notifier": "^1.0.7", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" }, "engines": { - "node": ">=4" + "node": ">=8.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" } }, - "node_modules/regjsparser": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", - "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", + "node_modules/nodemon/node_modules/debug": { + "version": "3.2.7", "dev": true, + "license": "MIT", "dependencies": { - "jsesc": "~0.5.0" - }, - "bin": { - "regjsparser": "bin/parser" + "ms": "^2.1.1" } }, - "node_modules/regjsparser/node_modules/jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "node_modules/nopt": { + "version": "1.0.10", "dev": true, + "license": "MIT", + "dependencies": { + "abbrev": "1" + }, "bin": { - "jsesc": "bin/jsesc" + "nopt": "bin/nopt.js" } }, - "node_modules/require-directory": { - "version": "2.1.1", + "node_modules/normalize-path": { + "version": "3.0.0", "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/resolve": { - "version": "1.22.0", + "node_modules/npm-audit-resolver": { + "version": "3.0.0-7", + "resolved": "https://registry.npmjs.org/npm-audit-resolver/-/npm-audit-resolver-3.0.0-7.tgz", + "integrity": "sha512-6mXix6DOeiJxZ/vJKA5xkyPPO0ocRj8ZFPdockd0vO7woQbRdAnBdK5JXCpnGxrTW/ND7X6XcbYrWvWdxYoVng==", "dev": true, - "license": "MIT", "dependencies": { - "is-core-module": "^2.8.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" + "audit-resolve-core": "^3.0.0-3", + "chalk": "^2.4.2", + "concat-stream": "^2.0.0", + "djv": "^2.1.4", + "jsonlines": "^0.1.1", + "read": "^1.0.7", + "spawn-shell": "^2.1.0", + "yargs-parser": "^18.1.3", + "yargs-unparser": "^2.0.0" }, "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "check-audit": "check.js", + "resolve-audit": "resolve.js" } }, - "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==", + "node_modules/npm-audit-resolver/node_modules/yargs-parser": { + "version": "18.1.3", "dev": true, + "license": "ISC", "dependencies": { - "resolve-from": "^5.0.0" + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" }, "engines": { - "node": ">=8" + "node": ">=6" } }, - "node_modules/resolve-cwd/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==", + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, "engines": { "node": ">=8" } }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "node_modules/object-inspect": { + "version": "1.12.0", "dev": true, - "engines": { - "node": ">=4" + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/resolve.exports": { + "node_modules/object-keys": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.1.tgz", - "integrity": "sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/retry-as-promised": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-7.0.4.tgz", - "integrity": "sha512-XgmCoxKWkDofwH8WddD0w85ZfqYz+ZHlr5yo+3YUCfycWawU56T5ckWXsScsj5B8tqUcIG67DxXByo3VUgiAdA==" - }, - "node_modules/reusify": { - "version": "1.0.4", "dev": true, "license": "MIT", "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" + "node": ">= 0.4" } }, - "node_modules/rimraf": { - "version": "3.0.2", + "node_modules/object.assign": { + "version": "4.1.2", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "glob": "^7.1.3" + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" }, - "bin": { - "rimraf": "bin.js" + "engines": { + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/run-parallel": { - "version": "1.2.0", + "node_modules/object.getownpropertydescriptors": { + "version": "2.1.3", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], "license": "MIT", "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/safe-stable-stringify": { - "version": "2.3.1", - "license": "MIT", + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + }, "engines": { - "node": ">=10" + "node": ">= 0.8" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "license": "MIT" - }, - "node_modules/sax": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", - "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==" + "node_modules/obliterator": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-1.6.1.tgz", + "integrity": "sha512-9WXswnqINnnhOG/5SLimUlzuU1hFJUc8zkwyD59Sd+dPOMf05PmnYG/d6Q7HZ+KmgkZJa1PxRso6QdM3sTNHig==" }, - "node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "node_modules/on-finished": { + "version": "2.3.0", + "license": "MIT", "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" + "ee-first": "1.1.1" }, "engines": { - "node": ">=10" + "node": ">= 0.8" } }, - "node_modules/semver/node_modules/lru-cache": { - "version": "6.0.0", + "node_modules/once": { + "version": "1.4.0", + "dev": true, "license": "ISC", "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" + "wrappy": "1" } }, - "node_modules/semver/node_modules/yallist": { - "version": "4.0.0", - "license": "ISC" - }, - "node_modules/send": { - "version": "0.17.2", + "node_modules/one-time": { + "version": "1.0.0", "license": "MIT", "dependencies": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "1.8.1", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "~2.3.0", - "range-parser": "~1.2.1", - "statuses": "~1.5.0" + "fn.name": "1.x.x" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" }, "engines": { - "node": ">= 0.8.0" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/send/node_modules/debug": { - "version": "2.6.9", - "license": "MIT", + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, "dependencies": { - "ms": "2.0.0" + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" } }, - "node_modules/send/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "license": "MIT" - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "license": "MIT" - }, - "node_modules/sequelize": { - "version": "6.35.2", - "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.35.2.tgz", - "integrity": "sha512-EdzLaw2kK4/aOnWQ7ed/qh3B6/g+1DvmeXr66RwbcqSm/+QRS9X0LDI5INBibsy4eNJHWIRPo3+QK0zL+IPBHg==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/sequelize" - } - ], + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, "dependencies": { - "@types/debug": "^4.1.8", - "@types/validator": "^13.7.17", - "debug": "^4.3.4", - "dottie": "^2.0.6", - "inflection": "^1.13.4", - "lodash": "^4.17.21", - "moment": "^2.29.4", - "moment-timezone": "^0.5.43", - "pg-connection-string": "^2.6.1", - "retry-as-promised": "^7.0.4", - "semver": "^7.5.4", - "sequelize-pool": "^7.1.0", - "toposort-class": "^1.0.1", - "uuid": "^8.3.2", - "validator": "^13.9.0", - "wkx": "^0.5.0" + "yocto-queue": "^0.1.0" }, "engines": { - "node": ">=10.0.0" + "node": ">=10" }, - "peerDependenciesMeta": { - "ibm_db": { - "optional": true - }, - "mariadb": { - "optional": true - }, - "mysql2": { - "optional": true - }, - "oracledb": { - "optional": true - }, - "pg": { - "optional": true - }, - "pg-hstore": { - "optional": true - }, - "snowflake-sdk": { - "optional": true - }, - "sqlite3": { - "optional": true - }, - "tedious": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/sequelize-cli": { - "version": "6.4.1", + "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, - "license": "MIT", "dependencies": { - "cli-color": "^2.0.1", - "fs-extra": "^9.1.0", - "js-beautify": "^1.14.0", - "lodash": "^4.17.21", - "resolve": "^1.20.0", - "umzug": "^2.3.0", - "yargs": "^16.2.0" - }, - "bin": { - "sequelize": "lib/sequelize", - "sequelize-cli": "lib/sequelize" + "p-limit": "^3.0.2" }, "engines": { - "node": ">=10.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/sequelize-pool": { - "version": "7.1.0", - "license": "MIT", + "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==", + "dev": true, "engines": { - "node": ">= 10.0.0" + "node": ">=6" } }, - "node_modules/serve-static": { - "version": "1.14.2", - "license": "MIT", + "node_modules/packet-reader": { + "version": "1.0.0", + "license": "MIT" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, "dependencies": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.17.2" + "callsites": "^3.0.0" }, "engines": { - "node": ">= 0.8.0" + "node": ">=6" } }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "license": "ISC" - }, - "node_modules/shallow-clone": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", - "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "dev": true, "dependencies": { - "kind-of": "^6.0.2" + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" }, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/shebang-command": { - "version": "2.0.0", + "node_modules/parse-passwd": { + "version": "1.0.0", "dev": true, "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "dev": true, + "node_modules/parseurl": { + "version": "1.3.3", "license": "MIT", "engines": { - "node": ">=8" + "node": ">= 0.8" } }, - "node_modules/side-channel": { - "version": "1.0.4", + "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==", "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=8" } }, - "node_modules/sigmund": { + "node_modules/path-is-absolute": { "version": "1.0.1", "dev": true, - "license": "ISC" - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "node_modules/simple-swizzle": { - "version": "0.2.2", "license": "MIT", - "dependencies": { - "is-arrayish": "^0.3.1" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/simple-update-notifier": { + "node_modules/path-key": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { "version": "1.0.7", "dev": true, + "license": "MIT" + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "license": "MIT" + }, + "node_modules/path-type": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pg": { + "version": "8.7.3", "license": "MIT", "dependencies": { - "semver": "~7.0.0" + "buffer-writer": "2.0.0", + "packet-reader": "1.0.0", + "pg-connection-string": "^2.5.0", + "pg-pool": "^3.5.1", + "pg-protocol": "^1.5.0", + "pg-types": "^2.1.0", + "pgpass": "1.x" }, "engines": { - "node": ">=8.10.0" + "node": ">= 8.0.0" + }, + "peerDependencies": { + "pg-native": ">=2.0.0" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } } }, - "node_modules/sinon": { - "version": "15.2.0", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-15.2.0.tgz", - "integrity": "sha512-nPS85arNqwBXaIsFCkolHjGIkFo+Oxu9vbgmBJizLAhqe6P2o3Qmj3KCUoRkfhHtvgDhZdWD3risLHAUJ8npjw==", - "deprecated": "16.1.1", + "node_modules/pg-connection-string": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", + "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==" + }, + "node_modules/pg-hstore": { + "version": "2.3.4", + "license": "MIT", "dependencies": { - "@sinonjs/commons": "^3.0.0", - "@sinonjs/fake-timers": "^10.3.0", - "@sinonjs/samsam": "^8.0.0", - "diff": "^5.1.0", - "nise": "^5.1.4", - "supports-color": "^7.2.0" + "underscore": "^1.13.1" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/sinon" + "engines": { + "node": ">= 0.8.x" } }, - "node_modules/sinon/node_modules/@sinonjs/commons": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", - "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", + "node_modules/pg-int8": { + "version": "1.0.1", + "license": "ISC", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-native": { + "version": "3.0.1", + "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "type-detect": "4.0.8" + "libpq": "^1.8.10", + "pg-types": "^1.12.1", + "readable-stream": "1.0.31" } }, - "node_modules/sinon/node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "node_modules/pg-native/node_modules/isarray": { + "version": "0.0.1", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/pg-native/node_modules/pg-types": { + "version": "1.13.0", + "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "@sinonjs/commons": "^3.0.0" + "pg-int8": "1.0.1", + "postgres-array": "~1.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.0", + "postgres-interval": "^1.1.0" } }, - "node_modules/sinon/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/pg-native/node_modules/postgres-array": { + "version": "1.0.3", + "license": "MIT", + "optional": true, + "peer": true, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/sinon/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/pg-native/node_modules/readable-stream": { + "version": "1.0.31", + "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "has-flag": "^4.0.0" + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/pg-native/node_modules/string_decoder": { + "version": "0.10.31", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/pg-pool": { + "version": "3.5.1", + "license": "MIT", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.5.0", + "license": "MIT" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "license": "MIT", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" }, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/sisteransi": { + "node_modules/pgpass": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "license": "MIT", + "dependencies": { + "split2": "^4.1.0" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", "dev": true }, - "node_modules/slash": { - "version": "2.0.0", + "node_modules/picomatch": { + "version": "2.3.1", "dev": true, "license": "MIT", "engines": { - "node": ">=6" + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "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==", + "node_modules/pify": { + "version": "4.0.1", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=6" } }, - "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==", + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" + "engines": { + "node": ">= 6" } }, - "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==", + "node_modules/pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", "dev": true, "dependencies": { - "default-shell": "^1.0.1", - "merge-options": "~1.0.1", - "npm-run-path": "^2.0.2" + "find-up": "^3.0.0" }, "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==", + "node_modules/pkg-dir/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==", "dev": true, "dependencies": { - "path-key": "^2.0.0" + "locate-path": "^3.0.0" }, "engines": { - "node": ">=4" + "node": ">=6" } }, - "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==", + "node_modules/pkg-dir/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==", "dev": true, + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, "engines": { - "node": ">=4" - } - }, - "node_modules/split2": { - "version": "4.1.0", - "license": "ISC", - "engines": { - "node": ">= 10.x" + "node": ">=6" } }, - "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/stack-trace": { - "version": "0.0.10", - "license": "MIT", + "node_modules/pkg-dir/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": "*" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "node_modules/pkg-dir/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==", "dev": true, "dependencies": { - "escape-string-regexp": "^2.0.0" + "p-limit": "^2.0.0" }, "engines": { - "node": ">=10" + "node": ">=6" } }, - "node_modules/stack-utils/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "node_modules/pkg-dir/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==", "dev": true, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/statuses": { - "version": "1.5.0", + "node_modules/postgres-array": { + "version": "2.0.0", "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">=4" } }, - "node_modules/string_decoder": { - "version": "1.3.0", + "node_modules/postgres-bytea": { + "version": "1.0.0", "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.2.1", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, + "node_modules/postgres-date": { + "version": "1.0.7", + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=0.10.0" } }, - "node_modules/string-width": { - "version": "4.2.3", - "dev": true, + "node_modules/postgres-interval": { + "version": "1.2.0", "license": "MIT", "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "xtend": "^4.0.0" }, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/string.prototype.trimend": { - "version": "1.0.4", + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">= 0.8.0" } }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.4", + "node_modules/prettier": { + "version": "2.6.0", "dev": true, "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/strip-ansi": { - "version": "6.0.1", + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", "dev": true, "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.1" + "fast-diff": "^1.1.2" }, "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, - "engines": { - "node": ">=8" + "node": ">=6.0.0" } }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "node_modules/pretty-format": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", + "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", "dev": true, + "dependencies": { + "@jest/schemas": "^28.1.3", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, "engines": { - "node": ">=6" + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "engines": { - "node": ">=8" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/superagent": { - "version": "8.0.8", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-8.0.8.tgz", - "integrity": "sha512-OpxPrqqWKOjmuomLq5pCm4LWCSFdgAQ11XVkMl7t4ie13WxWuLkdJ83ZgG2jOQeLXKwMR2p9k30hLrKGAzkPaA==", + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", "dev": true, "dependencies": { - "component-emitter": "^1.3.0", - "cookiejar": "^2.1.4", - "debug": "^4.3.4", - "fast-safe-stringify": "^2.1.1", - "form-data": "^4.0.0", - "formidable": "^2.1.2", - "methods": "^1.1.2", - "mime": "2.6.0", - "qs": "^6.11.0", - "semver": "^7.3.8" + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" }, "engines": { - "node": ">=6.4.0 <13 || >=14" + "node": ">= 6" } }, - "node_modules/superagent/node_modules/form-data": { - "version": "4.0.0", + "node_modules/proto-list": { + "version": "1.2.4", "dev": true, + "license": "ISC" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", "license": "MIT", "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" }, "engines": { - "node": ">= 6" + "node": ">= 0.10" } }, - "node_modules/superagent/node_modules/mime": { - "version": "2.6.0", + "node_modules/pseudomap": { + "version": "1.0.2", "dev": true, - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4.0.0" - } + "license": "ISC" }, - "node_modules/superagent/node_modules/qs": { - "version": "6.11.0", + "node_modules/pstree.remy": { + "version": "1.1.8", "dev": true, + "license": "MIT" + }, + "node_modules/punycode": { + "version": "1.3.2", + "license": "MIT" + }, + "node_modules/qs": { + "version": "6.9.7", "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.0.4" - }, "engines": { "node": ">=0.6" }, @@ -9925,2034 +10613,3642 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/supertest": { - "version": "6.3.3", - "dev": true, - "license": "MIT", - "dependencies": { - "methods": "^1.1.2", - "superagent": "^8.0.5" - }, + "node_modules/querystring": { + "version": "0.2.0", "engines": { - "node": ">=6.4.0" + "node": ">=0.4.x" } }, - "node_modules/supports-color": { - "version": "5.5.0", - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/supports-hyperlinks": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", - "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", + "node_modules/queue-microtask": { + "version": "1.2.3", "dev": true, - "dependencies": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - }, - "engines": { - "node": ">=8" - } + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" }, - "node_modules/supports-hyperlinks/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, + "node_modules/range-parser": { + "version": "1.2.1", + "license": "MIT", "engines": { - "node": ">=8" + "node": ">= 0.6" } }, - "node_modules/supports-hyperlinks/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, + "node_modules/raw-body": { + "version": "2.4.3", + "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "bytes": "3.1.2", + "http-errors": "1.8.1", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" }, "engines": { - "node": ">=8" + "node": ">= 0.8" } }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", + "node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/read": { + "version": "1.0.7", "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" + "license": "ISC", + "dependencies": { + "mute-stream": "~0.0.4" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=0.8" } }, - "node_modules/swagger-ui-dist": { - "version": "4.8.1", - "license": "Apache-2.0" - }, - "node_modules/swagger-ui-express": { - "version": "4.3.0", + "node_modules/readable-stream": { + "version": "3.6.0", "license": "MIT", "dependencies": { - "swagger-ui-dist": ">=4.1.3" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" }, "engines": { - "node": ">= v0.10.32" - }, - "peerDependencies": { - "express": ">=4.0.0" + "node": ">= 6" } }, - "node_modules/terminal-link": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", - "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", + "node_modules/readdirp": { + "version": "3.6.0", "dev": true, + "license": "MIT", "dependencies": { - "ansi-escapes": "^4.2.1", - "supports-hyperlinks": "^2.0.0" + "picomatch": "^2.2.1" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8.10.0" } }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz", + "integrity": "sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==", "dev": true, "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" + "regenerate": "^1.4.2" }, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/text-hex": { - "version": "1.0.0", - "license": "MIT" - }, - "node_modules/text-table": { - "version": "0.2.0", - "dev": true, + "node_modules/regenerator-runtime": { + "version": "0.13.9", "license": "MIT" }, - "node_modules/timers-ext": { - "version": "0.1.7", + "node_modules/regenerator-transform": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", + "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", "dev": true, - "license": "ISC", "dependencies": { - "es5-ext": "~0.10.46", - "next-tick": "1" + "@babel/runtime": "^7.8.4" } }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "node_modules/regexpu-core": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", + "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", "dev": true, + "dependencies": { + "@babel/regjsgen": "^0.8.0", + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.1.0", + "regjsparser": "^0.9.1", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" + }, "engines": { "node": ">=4" } }, - "node_modules/to-regex-range": { - "version": "5.0.1", + "node_modules/regjsparser": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", + "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", "dev": true, - "license": "MIT", "dependencies": { - "is-number": "^7.0.0" + "jsesc": "~0.5.0" }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "license": "MIT", - "engines": { - "node": ">=0.6" + "bin": { + "regjsparser": "bin/parser" } }, - "node_modules/toposort-class": { - "version": "1.0.1", - "license": "MIT" - }, - "node_modules/touch": { - "version": "3.1.0", + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", "dev": true, - "license": "ISC", - "dependencies": { - "nopt": "~1.0.10" - }, "bin": { - "nodetouch": "bin/nodetouch.js" + "jsesc": "bin/jsesc" } }, - "node_modules/traverse": { - "version": "0.6.6", - "license": "MIT" - }, - "node_modules/triple-beam": { - "version": "1.3.0", - "license": "MIT" - }, - "node_modules/tslib": { - "version": "1.14.1", + "node_modules/require-directory": { + "version": "2.1.1", "dev": true, - "license": "0BSD" + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } }, - "node_modules/tsutils": { - "version": "3.21.0", + "node_modules/resolve": { + "version": "1.22.0", "dev": true, "license": "MIT", "dependencies": { - "tslib": "^1.8.1" + "is-core-module": "^2.8.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" }, - "engines": { - "node": ">= 6" + "bin": { + "resolve": "bin/resolve" }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/type": { - "version": "1.2.0", - "dev": true, - "license": "ISC" - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "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": { - "prelude-ls": "^1.2.1" + "resolve-from": "^5.0.0" }, "engines": { - "node": ">= 0.8.0" + "node": ">=8" } }, - "node_modules/type-detect": { - "version": "4.0.8", - "license": "MIT", + "node_modules/resolve-cwd/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, "engines": { "node": ">=4" } }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "node_modules/resolve.exports": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.1.tgz", + "integrity": "sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ==", "dev": true, "engines": { "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/type-is": { - "version": "1.6.18", + "node_modules/retry-as-promised": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-7.0.4.tgz", + "integrity": "sha512-XgmCoxKWkDofwH8WddD0w85ZfqYz+ZHlr5yo+3YUCfycWawU56T5ckWXsScsj5B8tqUcIG67DxXByo3VUgiAdA==" + }, + "node_modules/reusify": { + "version": "1.0.4", + "dev": true, "license": "MIT", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, "engines": { - "node": ">= 0.6" + "iojs": ">=1.0.0", + "node": ">=0.10.0" } }, - "node_modules/typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", - "dev": true - }, - "node_modules/typescript": { - "version": "4.6.2", + "node_modules/rimraf": { + "version": "3.0.2", "dev": true, - "license": "Apache-2.0", + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" + "rimraf": "bin.js" }, - "engines": { - "node": ">=4.2.0" + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/umzug": { - "version": "2.3.0", + "node_modules/run-parallel": { + "version": "1.2.0", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], "license": "MIT", "dependencies": { - "bluebird": "^3.7.2" - }, - "engines": { - "node": ">=6.0.0" + "queue-microtask": "^1.2.2" } }, - "node_modules/unbox-primitive": { - "version": "1.0.1", - "dev": true, + "node_modules/safe-stable-stringify": { + "version": "2.3.1", "license": "MIT", - "dependencies": { - "function-bind": "^1.1.1", - "has-bigints": "^1.0.1", - "has-symbols": "^1.0.2", - "which-boxed-primitive": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=10" } }, - "node_modules/undefsafe": { - "version": "2.0.5", - "dev": true, + "node_modules/safer-buffer": { + "version": "2.1.2", "license": "MIT" }, - "node_modules/underscore": { - "version": "1.13.1", - "license": "MIT" + "node_modules/sax": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", + "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==" }, - "node_modules/unicode-canonical-property-names-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", - "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", - "dev": true, + "node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, "engines": { - "node": ">=4" + "node": ">=10" } }, - "node_modules/unicode-match-property-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", - "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", - "dev": true, + "node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "license": "ISC", "dependencies": { - "unicode-canonical-property-names-ecmascript": "^2.0.0", - "unicode-property-aliases-ecmascript": "^2.0.0" + "yallist": "^4.0.0" }, "engines": { - "node": ">=4" + "node": ">=10" } }, - "node_modules/unicode-match-property-value-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", - "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", - "dev": true, - "engines": { - "node": ">=4" - } + "node_modules/semver/node_modules/yallist": { + "version": "4.0.0", + "license": "ISC" }, - "node_modules/unicode-property-aliases-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", - "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", - "dev": true, + "node_modules/send": { + "version": "0.17.2", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "1.8.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, "engines": { - "node": ">=4" + "node": ">= 0.8.0" } }, - "node_modules/unpipe": { - "version": "1.0.0", + "node_modules/send/node_modules/debug": { + "version": "2.6.9", "license": "MIT", - "engines": { - "node": ">= 0.8" + "dependencies": { + "ms": "2.0.0" } }, - "node_modules/update-browserslist-db": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", - "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", - "dev": true, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "license": "MIT" + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "license": "MIT" + }, + "node_modules/sequelize": { + "version": "6.35.2", + "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.35.2.tgz", + "integrity": "sha512-EdzLaw2kK4/aOnWQ7ed/qh3B6/g+1DvmeXr66RwbcqSm/+QRS9X0LDI5INBibsy4eNJHWIRPo3+QK0zL+IPBHg==", "funding": [ { "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" + "url": "https://opencollective.com/sequelize" } ], "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - }, - "bin": { - "update-browserslist-db": "cli.js" + "@types/debug": "^4.1.8", + "@types/validator": "^13.7.17", + "debug": "^4.3.4", + "dottie": "^2.0.6", + "inflection": "^1.13.4", + "lodash": "^4.17.21", + "moment": "^2.29.4", + "moment-timezone": "^0.5.43", + "pg-connection-string": "^2.6.1", + "retry-as-promised": "^7.0.4", + "semver": "^7.5.4", + "sequelize-pool": "^7.1.0", + "toposort-class": "^1.0.1", + "uuid": "^8.3.2", + "validator": "^13.9.0", + "wkx": "^0.5.0" }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/uri-js/node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, "engines": { - "node": ">=6" + "node": ">=10.0.0" + }, + "peerDependenciesMeta": { + "ibm_db": { + "optional": true + }, + "mariadb": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "oracledb": { + "optional": true + }, + "pg": { + "optional": true + }, + "pg-hstore": { + "optional": true + }, + "snowflake-sdk": { + "optional": true + }, + "sqlite3": { + "optional": true + }, + "tedious": { + "optional": true + } } }, - "node_modules/url": { - "version": "0.10.3", + "node_modules/sequelize-cli": { + "version": "6.4.1", + "dev": true, "license": "MIT", "dependencies": { - "punycode": "1.3.2", - "querystring": "0.2.0" - } - }, - "node_modules/util": { - "version": "0.12.5", - "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", - "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", - "dependencies": { - "inherits": "^2.0.3", - "is-arguments": "^1.0.4", - "is-generator-function": "^1.0.7", - "is-typed-array": "^1.1.3", - "which-typed-array": "^1.1.2" + "cli-color": "^2.0.1", + "fs-extra": "^9.1.0", + "js-beautify": "^1.14.0", + "lodash": "^4.17.21", + "resolve": "^1.20.0", + "umzug": "^2.3.0", + "yargs": "^16.2.0" + }, + "bin": { + "sequelize": "lib/sequelize", + "sequelize-cli": "lib/sequelize" + }, + "engines": { + "node": ">=10.0.0" } }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "license": "MIT" - }, - "node_modules/utils-merge": { - "version": "1.0.1", + "node_modules/sequelize-pool": { + "version": "7.1.0", "license": "MIT", "engines": { - "node": ">= 0.4.0" + "node": ">= 10.0.0" } }, - "node_modules/uuid": { - "version": "8.3.2", + "node_modules/serve-static": { + "version": "1.14.2", "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.2" + }, + "engines": { + "node": ">= 0.8.0" } }, - "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==", + "node_modules/setprototypeof": { + "version": "1.2.0", + "license": "ISC" + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", "dev": true, "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" + "kind-of": "^6.0.2" }, "engines": { - "node": ">=10.12.0" + "node": ">=8" } }, - "node_modules/v8flags": { - "version": "3.2.0", + "node_modules/shebang-command": { + "version": "2.0.0", "dev": true, "license": "MIT", "dependencies": { - "homedir-polyfill": "^1.0.1" + "shebang-regex": "^3.0.0" }, "engines": { - "node": ">= 0.10" + "node": ">=8" } }, - "node_modules/validator": { - "version": "13.11.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.11.0.tgz", - "integrity": "sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==", + "node_modules/shebang-regex": { + "version": "3.0.0", + "dev": true, + "license": "MIT", "engines": { - "node": ">= 0.10" + "node": ">=8" } }, - "node_modules/vary": { - "version": "1.1.2", + "node_modules/side-channel": { + "version": "1.0.4", + "dev": true, "license": "MIT", - "engines": { - "node": ">= 0.8" + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "node_modules/sigmund": { + "version": "1.0.1", "dev": true, + "license": "ISC" + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "license": "MIT", "dependencies": { - "makeerror": "1.0.12" + "is-arrayish": "^0.3.1" } }, - "node_modules/which": { - "version": "2.0.2", + "node_modules/simple-update-notifier": { + "version": "1.0.7", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" + "semver": "~7.0.0" }, "engines": { - "node": ">= 8" + "node": ">=8.10.0" } }, - "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "dev": true, - "license": "MIT", + "node_modules/sinon": { + "version": "15.2.0", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-15.2.0.tgz", + "integrity": "sha512-nPS85arNqwBXaIsFCkolHjGIkFo+Oxu9vbgmBJizLAhqe6P2o3Qmj3KCUoRkfhHtvgDhZdWD3risLHAUJ8npjw==", + "deprecated": "16.1.1", "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" + "@sinonjs/commons": "^3.0.0", + "@sinonjs/fake-timers": "^10.3.0", + "@sinonjs/samsam": "^8.0.0", + "diff": "^5.1.0", + "nise": "^5.1.4", + "supports-color": "^7.2.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/sinon" } }, - "node_modules/which-typed-array": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", - "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "node_modules/sinon/node_modules/@sinonjs/commons": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", + "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.10" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "type-detect": "4.0.8" } }, - "node_modules/winston": { - "version": "3.6.0", - "license": "MIT", + "node_modules/sinon/node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", "dependencies": { - "@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.5.0" - }, + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/sinon/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "engines": { - "node": ">= 12.0.0" + "node": ">=8" } }, - "node_modules/winston-transport": { - "version": "4.5.0", - "license": "MIT", + "node_modules/sinon/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dependencies": { - "logform": "^2.3.2", - "readable-stream": "^3.6.0", - "triple-beam": "^1.3.0" + "has-flag": "^4.0.0" }, "engines": { - "node": ">= 6.4.0" + "node": ">=8" } }, - "node_modules/wkx": { - "version": "0.5.0", + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "node_modules/slash": { + "version": "2.0.0", + "dev": true, "license": "MIT", - "dependencies": { - "@types/node": "*" + "engines": { + "node": ">=6" } }, - "node_modules/wrap-ansi": { - "version": "7.0.0", + "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==", "dev": true, - "license": "MIT", - "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": ">=0.10.0" } }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "4.3.0", + "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, - "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" } }, - "node_modules/wrap-ansi/node_modules/color-convert": { - "version": "2.0.1", + "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, - "license": "MIT", "dependencies": { - "color-name": "~1.1.4" + "default-shell": "^1.0.1", + "merge-options": "~1.0.1", + "npm-run-path": "^2.0.2" }, "engines": { - "node": ">=7.0.0" + "node": ">=6" } }, - "node_modules/wrap-ansi/node_modules/color-name": { - "version": "1.1.4", - "dev": true, - "license": "MIT" - }, - "node_modules/wrappy": { - "version": "1.0.2", - "dev": true, - "license": "ISC" - }, - "node_modules/write-file-atomic": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "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": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" + "path-key": "^2.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=4" } }, - "node_modules/xml2js": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", - "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", - "dependencies": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - }, + "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.0.0" + "node": ">=4" } }, - "node_modules/xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "node_modules/split2": { + "version": "4.1.0", + "license": "ISC", "engines": { - "node": ">=4.0" + "node": ">= 10.x" } }, - "node_modules/xtend": { - "version": "4.0.2", + "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/stack-trace": { + "version": "0.0.10", "license": "MIT", "engines": { - "node": ">=0.4" + "node": "*" } }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, "engines": { "node": ">=10" } }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, - "node_modules/yargs": { - "version": "16.2.0", + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/statuses": { + "version": "1.5.0", "license": "MIT", - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, "engines": { - "node": ">=10" + "node": ">= 0.6" } }, - "node_modules/yargs-parser": { - "version": "20.2.9", + "node_modules/string_decoder": { + "version": "1.3.0", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.2.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", "dev": true, - "license": "ISC", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, "engines": { "node": ">=10" } }, - "node_modules/yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "node_modules/string-width": { + "version": "4.2.3", "dev": true, + "license": "MIT", "dependencies": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">=10" + "node": ">=8" } }, - "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==", + "node_modules/string.prototype.trimend": { + "version": "1.0.4", "dev": true, - "engines": { - "node": ">=10" + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "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==", + "node_modules/string.prototype.trimstart": { + "version": "1.0.4", "dev": true, - "engines": { - "node": ">=10" + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "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==", + "node_modules/strip-ansi": { + "version": "6.0.1", "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, "engines": { "node": ">=8" } }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", "dev": true, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } - } - }, - "dependencies": { - "@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "dev": true }, - "@ampproject/remapping": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", - "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", "dev": true, - "requires": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" + "engines": { + "node": ">=6" } }, - "@babel/cli": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.23.4.tgz", - "integrity": "sha512-j3luA9xGKCXVyCa5R7lJvOMM+Kc2JEnAEIgz2ggtjQ/j5YUVgfsg/WsG95bbsgq7YLHuiCOzMnoSasuY16qiCw==", + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.17", - "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents.3", - "chokidar": "^3.4.0", - "commander": "^4.0.1", - "convert-source-map": "^2.0.0", - "fs-readdir-recursive": "^1.1.0", - "glob": "^7.2.0", - "make-dir": "^2.1.0", - "slash": "^2.0.0" + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "@babel/code-frame": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", - "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", + "node_modules/strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==" + }, + "node_modules/superagent": { + "version": "8.0.8", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-8.0.8.tgz", + "integrity": "sha512-OpxPrqqWKOjmuomLq5pCm4LWCSFdgAQ11XVkMl7t4ie13WxWuLkdJ83ZgG2jOQeLXKwMR2p9k30hLrKGAzkPaA==", "dev": true, - "requires": { - "@babel/highlight": "^7.23.4", - "chalk": "^2.4.2" + "dependencies": { + "component-emitter": "^1.3.0", + "cookiejar": "^2.1.4", + "debug": "^4.3.4", + "fast-safe-stringify": "^2.1.1", + "form-data": "^4.0.0", + "formidable": "^2.1.2", + "methods": "^1.1.2", + "mime": "2.6.0", + "qs": "^6.11.0", + "semver": "^7.3.8" + }, + "engines": { + "node": ">=6.4.0 <13 || >=14" } }, - "@babel/compat-data": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", - "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==", - "dev": true - }, - "@babel/core": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.6.tgz", - "integrity": "sha512-FxpRyGjrMJXh7X3wGLGhNDCRiwpWEF74sKjTLDJSG5Kyvow3QZaG0Adbqzi9ZrVjTWpsX+2cxWXD71NMg93kdw==", + "node_modules/superagent/node_modules/form-data": { + "version": "4.0.0", "dev": true, - "requires": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.6", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helpers": "^7.23.6", - "@babel/parser": "^7.23.6", - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.6", - "@babel/types": "^7.23.6", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "2.2.2", - "semver": "^7.5.2" - }, + "license": "MIT", "dependencies": { - "json5": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.2.tgz", - "integrity": "sha512-46Tk9JiOL2z7ytNQWFLpj99RZkVgeHf87yGQKsIkaPz1qSH9UczKH1rO7K3wgRselo0tYMUNfecYpm/p1vC7tQ==", - "dev": true - } + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" } }, - "@babel/generator": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", - "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", + "node_modules/superagent/node_modules/mime": { + "version": "2.6.0", "dev": true, - "requires": { - "@babel/types": "^7.23.6", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" } }, - "@babel/helper-annotate-as-pure": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", - "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", + "node_modules/superagent/node_modules/qs": { + "version": "6.11.0", "dev": true, - "requires": { - "@babel/types": "^7.22.5" + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz", - "integrity": "sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==", + "node_modules/supertest": { + "version": "6.3.3", "dev": true, - "requires": { - "@babel/types": "^7.22.15" + "license": "MIT", + "dependencies": { + "methods": "^1.1.2", + "superagent": "^8.0.5" + }, + "engines": { + "node": ">=6.4.0" } }, - "@babel/helper-compilation-targets": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", - "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.23.5", - "@babel/helper-validator-option": "^7.23.5", - "browserslist": "^4.22.2", - "lru-cache": "^5.1.1", - "semver": "^7.5.2" + "node_modules/supports-color": { + "version": "5.5.0", + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" } }, - "@babel/helper-create-class-features-plugin": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.23.6.tgz", - "integrity": "sha512-cBXU1vZni/CpGF29iTu4YRbOZt3Wat6zCoMDxRF1MayiEc4URxOj31tT65HUM0CRpMowA3HCJaAOVOUnMf96cw==", + "node_modules/supports-hyperlinks": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", + "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-member-expression-to-functions": "^7.23.0", - "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.20", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "semver": "^7.5.2" + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=8" } }, - "@babel/helper-create-regexp-features-plugin": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz", - "integrity": "sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==", + "node_modules/supports-hyperlinks/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "regexpu-core": "^5.3.1", - "semver": "^7.5.2" + "engines": { + "node": ">=8" } }, - "@babel/helper-define-polyfill-provider": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.4.tgz", - "integrity": "sha512-QcJMILQCu2jm5TFPGA3lCpJJTeEP+mqeXooG/NZbg/h5FTFi6V0+99ahlRsW8/kRLyb24LZVCCiclDedhLKcBA==", + "node_modules/supports-hyperlinks/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "requires": { - "@babel/helper-compilation-targets": "^7.22.6", - "@babel/helper-plugin-utils": "^7.22.5", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2" + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", - "dev": true - }, - "@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", "dev": true, - "requires": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" + "node_modules/swagger-ui-dist": { + "version": "4.8.1", + "license": "Apache-2.0" + }, + "node_modules/swagger-ui-express": { + "version": "4.3.0", + "license": "MIT", + "dependencies": { + "swagger-ui-dist": ">=4.1.3" + }, + "engines": { + "node": ">= v0.10.32" + }, + "peerDependencies": { + "express": ">=4.0.0" } }, - "@babel/helper-member-expression-to-functions": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz", - "integrity": "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==", + "node_modules/terminal-link": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", + "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", "dev": true, - "requires": { - "@babel/types": "^7.23.0" + "dependencies": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "@babel/helper-module-imports": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", - "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", "dev": true, - "requires": { - "@babel/types": "^7.22.15" + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" } }, - "@babel/helper-module-transforms": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", - "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", + "node_modules/text-hex": { + "version": "1.0.0", + "license": "MIT" + }, + "node_modules/text-table": { + "version": "0.2.0", "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.20" - } + "license": "MIT" }, - "@babel/helper-optimise-call-expression": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", - "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==", + "node_modules/timers-ext": { + "version": "0.1.7", "dev": true, - "requires": { - "@babel/types": "^7.22.5" + "license": "ISC", + "dependencies": { + "es5-ext": "~0.10.46", + "next-tick": "1" } }, - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", "dev": true }, - "@babel/helper-remap-async-to-generator": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz", - "integrity": "sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==", + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-wrap-function": "^7.22.20" + "engines": { + "node": ">=4" } }, - "@babel/helper-replace-supers": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz", - "integrity": "sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==", + "node_modules/to-regex-range": { + "version": "5.0.1", "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-member-expression-to-functions": "^7.22.15", - "@babel/helper-optimise-call-expression": "^7.22.5" + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" } }, - "@babel/helper-simple-access": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", - "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" + "node_modules/toidentifier": { + "version": "1.0.1", + "license": "MIT", + "engines": { + "node": ">=0.6" } }, - "@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz", - "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } + "node_modules/toposort-class": { + "version": "1.0.1", + "license": "MIT" }, - "@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "node_modules/touch": { + "version": "3.1.0", "dev": true, - "requires": { - "@babel/types": "^7.22.5" + "license": "ISC", + "dependencies": { + "nopt": "~1.0.10" + }, + "bin": { + "nodetouch": "bin/nodetouch.js" } }, - "@babel/helper-string-parser": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", - "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", - "dev": true + "node_modules/traverse": { + "version": "0.6.6", + "license": "MIT" }, - "@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", - "dev": true + "node_modules/triple-beam": { + "version": "1.3.0", + "license": "MIT" }, - "@babel/helper-validator-option": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", - "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", - "dev": true + "node_modules/tslib": { + "version": "1.14.1", + "license": "0BSD" }, - "@babel/helper-wrap-function": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.20.tgz", - "integrity": "sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw==", + "node_modules/tsutils": { + "version": "3.21.0", "dev": true, - "requires": { - "@babel/helper-function-name": "^7.22.5", - "@babel/template": "^7.22.15", - "@babel/types": "^7.22.19" + "license": "MIT", + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" } }, - "@babel/helpers": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.6.tgz", - "integrity": "sha512-wCfsbN4nBidDRhpDhvcKlzHWCTlgJYUUdSJfzXb2NuBssDSIjc3xcb+znA7l+zYsFljAcGM0aFkN40cR3lXiGA==", + "node_modules/type": { + "version": "1.2.0", "dev": true, - "requires": { - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.6", - "@babel/types": "^7.23.6" - } + "license": "ISC" }, - "@babel/highlight": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", - "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" } }, - "@babel/node": { - "version": "7.22.19", - "resolved": "https://registry.npmjs.org/@babel/node/-/node-7.22.19.tgz", - "integrity": "sha512-VsKSO9aEHdO16NdtqkJfrXZ9Sxlna1BVnBbToWr1KGdI3cyIk6KqOoa8mWvpK280lJDOwJqxvnl994KmLhq1Yw==", - "dev": true, - "requires": { - "@babel/register": "^7.22.15", - "commander": "^4.0.1", - "core-js": "^3.30.2", - "node-environment-flags": "^1.0.5", - "regenerator-runtime": "^0.14.0", - "v8flags": "^3.1.1" - }, - "dependencies": { - "regenerator-runtime": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", - "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==", - "dev": true - } - } - }, - "@babel/parser": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.6.tgz", - "integrity": "sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==", - "dev": true - }, - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.23.3.tgz", - "integrity": "sha512-iRkKcCqb7iGnq9+3G6rZ+Ciz5VywC4XNRHe57lKM+jOeYAoR0lVqdeeDRfh0tQcTfw/+vBhHn926FmQhLtlFLQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "node_modules/type-detect": { + "version": "4.0.8", + "license": "MIT", + "engines": { + "node": ">=4" } }, - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.23.3.tgz", - "integrity": "sha512-WwlxbfMNdVEpQjZmK5mhm7oSwD3dS6eU+Iwsi4Knl9wAletWem7kaRsGOG+8UEbRyqxY4SS5zvtfXwX+jMxUwQ==", + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/plugin-transform-optional-chaining": "^7.23.3" + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.23.3.tgz", - "integrity": "sha512-XaJak1qcityzrX0/IU5nKHb34VaibwP3saKqG6a/tppelgllOH13LUann4ZCIBcVOeE6H18K4Vx9QKkVww3z/w==", - "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-plugin-utils": "^7.22.5" + "node_modules/type-is": { + "version": "1.6.18", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" } }, - "@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.0-placeholder-for-preset-env.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", - "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", - "dev": true, - "requires": {} + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "dev": true }, - "@babel/plugin-syntax-async-generators": { - "version": "7.8.4", + "node_modules/typescript": { + "version": "4.6.2", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" } }, - "@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "node_modules/umzug": { + "version": "2.3.0", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" + "license": "MIT", + "dependencies": { + "bluebird": "^3.7.2" + }, + "engines": { + "node": ">=6.0.0" } }, - "@babel/plugin-syntax-class-properties": { - "version": "7.12.13", + "node_modules/unbox-primitive": { + "version": "1.0.1", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "node_modules/undefsafe": { + "version": "2.0.5", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } + "license": "MIT" }, - "@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "node_modules/underscore": { + "version": "1.13.1", + "license": "MIT" + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" + "engines": { + "node": ">=4" } }, - "@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", - "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" } }, - "@babel/plugin-syntax-import-assertions": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.23.3.tgz", - "integrity": "sha512-lPgDSU+SJLK3xmFDTV2ZRQAiM7UuUjGidwBywFavObCiZc1BeAAcMtHJKUya92hPHO+at63JJPLygilZard8jw==", + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", + "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "engines": { + "node": ">=4" } }, - "@babel/plugin-syntax-import-attributes": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.23.3.tgz", - "integrity": "sha512-pawnE0P9g10xgoP7yKr6CK63K2FMsTE+FZidZO/1PwRdzmAPVs+HS1mAURUsgaoxammTJvULUdIkEK0gOcU2tA==", + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "engines": { + "node": ">=4" } }, - "@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "node_modules/unpipe": { + "version": "1.0.0", + "license": "MIT", + "engines": { + "node": ">= 0.8" } }, - "@babel/plugin-syntax-json-strings": { - "version": "7.8.3", + "node_modules/update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" } }, - "@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "dependencies": { + "punycode": "^2.1.0" } }, - "@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", + "node_modules/uri-js/node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" + "engines": { + "node": ">=6" } }, - "@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "node_modules/url": { + "version": "0.10.3", + "license": "MIT", + "dependencies": { + "punycode": "1.3.2", + "querystring": "0.2.0" } }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" + "node_modules/util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" } }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" + "node_modules/util-deprecate": { + "version": "1.0.2", + "license": "MIT" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" } }, - "@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" + "node_modules/uuid": { + "version": "8.3.2", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" } }, - "@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "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==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" } }, - "@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", + "node_modules/v8flags": { + "version": "3.2.0", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" + "license": "MIT", + "dependencies": { + "homedir-polyfill": "^1.0.1" + }, + "engines": { + "node": ">= 0.10" } }, - "@babel/plugin-syntax-typescript": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz", - "integrity": "sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "node_modules/validator": { + "version": "13.11.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.11.0.tgz", + "integrity": "sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==", + "engines": { + "node": ">= 0.10" } }, - "@babel/plugin-syntax-unicode-sets-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", - "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "node_modules/vary": { + "version": "1.1.2", + "license": "MIT", + "engines": { + "node": ">= 0.8" } }, - "@babel/plugin-transform-arrow-functions": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.23.3.tgz", - "integrity": "sha512-NzQcQrzaQPkaEwoTm4Mhyl8jI1huEL/WWIEvudjTCMJ9aBZNpsJbMASx7EQECtQQPS/DcnFpo0FIh3LvEO9cxQ==", + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "dependencies": { + "makeerror": "1.0.12" } }, - "@babel/plugin-transform-async-generator-functions": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.4.tgz", - "integrity": "sha512-efdkfPhHYTtn0G6n2ddrESE91fgXxjlqLsnUtPWnJs4a4mZIbUaK7ffqKIIUKXSHwcDvaCVX6GXkaJJFqtX7jw==", + "node_modules/which": { + "version": "2.0.2", "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-remap-async-to-generator": "^7.22.20", - "@babel/plugin-syntax-async-generators": "^7.8.4" + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" } }, - "@babel/plugin-transform-async-to-generator": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.23.3.tgz", - "integrity": "sha512-A7LFsKi4U4fomjqXJlZg/u0ft/n8/7n7lpffUP/ZULx/DtV9SGlNKZolHH6PE8Xl1ngCc0M11OaeZptXVkfKSw==", + "node_modules/which-boxed-primitive": { + "version": "1.0.2", "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-remap-async-to-generator": "^7.22.20" + "license": "MIT", + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "@babel/plugin-transform-block-scoped-functions": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.23.3.tgz", - "integrity": "sha512-vI+0sIaPIO6CNuM9Kk5VmXcMVRiOpDh7w2zZt9GXzmE/9KD70CUEVhvPR/etAeNK/FAEkhxQtXOzVF3EuRL41A==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "node_modules/which-typed-array": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", + "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "@babel/plugin-transform-block-scoping": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.23.4.tgz", - "integrity": "sha512-0QqbP6B6HOh7/8iNR4CQU2Th/bbRtBp4KS9vcaZd1fZ0wSh5Fyssg0UCIHwxh+ka+pNDREbVLQnHCMHKZfPwfw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "node_modules/winston": { + "version": "3.6.0", + "license": "MIT", + "dependencies": { + "@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.5.0" + }, + "engines": { + "node": ">= 12.0.0" } }, - "@babel/plugin-transform-class-properties": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.23.3.tgz", - "integrity": "sha512-uM+AN8yCIjDPccsKGlw271xjJtGii+xQIF/uMPS8H15L12jZTsLfF4o5vNO7d/oUguOyfdikHGc/yi9ge4SGIg==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" + "node_modules/winston-transport": { + "version": "4.5.0", + "license": "MIT", + "dependencies": { + "logform": "^2.3.2", + "readable-stream": "^3.6.0", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 6.4.0" } }, - "@babel/plugin-transform-class-static-block": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.23.4.tgz", - "integrity": "sha512-nsWu/1M+ggti1SOALj3hfx5FXzAY06fwPJsUZD4/A5e1bWi46VUIWtD+kOX6/IdhXGsXBWllLFDSnqSCdUNydQ==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-class-static-block": "^7.14.5" + "node_modules/wkx": { + "version": "0.5.0", + "license": "MIT", + "dependencies": { + "@types/node": "*" } }, - "@babel/plugin-transform-classes": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.23.5.tgz", - "integrity": "sha512-jvOTR4nicqYC9yzOHIhXG5emiFEOpappSJAl73SDSEDcybD+Puuze8Tnpb9p9qEyYup24tq891gkaygIFvWDqg==", + "node_modules/wrap-ansi": { + "version": "7.0.0", "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.20", - "@babel/helper-split-export-declaration": "^7.22.6", - "globals": "^11.1.0" + "license": "MIT", + "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" } }, - "@babel/plugin-transform-computed-properties": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.23.3.tgz", - "integrity": "sha512-dTj83UVTLw/+nbiHqQSFdwO9CbTtwq1DsDqm3CUEtDrZNET5rT5E6bIdTlOftDTDLMYxvxHNEYO4B9SLl8SLZw==", + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/template": "^7.22.15" + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "@babel/plugin-transform-destructuring": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.23.3.tgz", - "integrity": "sha512-n225npDqjDIr967cMScVKHXJs7rout1q+tt50inyBCPkyZ8KxeI6d+GIbSBTT/w/9WdlWDOej3V9HE5Lgk57gw==", + "node_modules/wrap-ansi/node_modules/color-convert": { + "version": "2.0.1", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "@babel/plugin-transform-dotall-regex": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.23.3.tgz", - "integrity": "sha512-vgnFYDHAKzFaTVp+mneDsIEbnJ2Np/9ng9iviHw3P/KVcgONxpNULEW/51Z/BaFojG2GI2GwwXck5uV1+1NOYQ==", + "node_modules/wrap-ansi/node_modules/color-name": { + "version": "1.1.4", "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" + "license": "MIT" + }, + "node_modules/wrappy": { + "version": "1.0.2", + "dev": true, + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "@babel/plugin-transform-duplicate-keys": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.23.3.tgz", - "integrity": "sha512-RrqQ+BQmU3Oyav3J+7/myfvRCq7Tbz+kKLLshUmMwNlDHExbGL7ARhajvoBJEvc+fCguPPu887N+3RRXBVKZUA==", + "node_modules/xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "engines": { + "node": ">=10" } }, - "@babel/plugin-transform-dynamic-import": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.23.4.tgz", - "integrity": "sha512-V6jIbLhdJK86MaLh4Jpghi8ho5fGzt3imHOBu/x0jlBaPYqDoWz4RDXjmMOfnh+JWNaQleEAByZLV0QzBT4YQQ==", + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yargs": { + "version": "16.2.0", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" } }, - "@babel/plugin-transform-exponentiation-operator": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.23.3.tgz", - "integrity": "sha512-5fhCsl1odX96u7ILKHBj4/Y8vipoqwsJMh4csSA8qFfxrZDEA4Ssku2DyNvMJSmZNOEBT750LfFPbtrnTP90BQ==", + "node_modules/yargs-parser": { + "version": "20.2.9", "dev": true, - "requires": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" + "license": "ISC", + "engines": { + "node": ">=10" } }, - "@babel/plugin-transform-export-namespace-from": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.23.4.tgz", - "integrity": "sha512-GzuSBcKkx62dGzZI1WVgTWvkkz84FZO5TC5T8dl/Tht/rAla6Dg/Mz9Yhypg+ezVACf/rgDuQt3kbWEv7LdUDQ==", + "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, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" } }, - "@babel/plugin-transform-for-of": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.23.6.tgz", - "integrity": "sha512-aYH4ytZ0qSuBbpfhuofbg/e96oQ7U2w1Aw/UQmKT+1l39uEhUPoFS3fHevDc1G0OvewyDudfMKY1OulczHzWIw==", + "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, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "@babel/plugin-transform-function-name": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.23.3.tgz", - "integrity": "sha512-I1QXp1LxIvt8yLaib49dRW5Okt7Q4oaxao6tFVKS/anCdEOMtYwWVKoiOA1p34GOWIZjUK0E+zCp7+l1pfQyiw==", + "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, - "requires": { - "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-plugin-utils": "^7.22.5" + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "@babel/plugin-transform-json-strings": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.23.4.tgz", - "integrity": "sha512-81nTOqM1dMwZ/aRXQ59zVubN9wHGqk6UtqRK+/q+ciXmRy8fSolhGVvG09HHRGo4l6fr/c4ZhXUQH0uFW7PZbg==", + "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, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-json-strings": "^7.8.3" + "engines": { + "node": ">=8" } }, - "@babel/plugin-transform-literals": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.23.3.tgz", - "integrity": "sha512-wZ0PIXRxnwZvl9AYpqNUxpZ5BiTGrYt7kueGQ+N5FiQ7RCOD4cm8iShd6S6ggfVIWaJf2EMk8eRzAh52RfP4rQ==", + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } + } + }, + "dependencies": { + "@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true }, - "@babel/plugin-transform-logical-assignment-operators": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.23.4.tgz", - "integrity": "sha512-Mc/ALf1rmZTP4JKKEhUwiORU+vcfarFVLfcFiolKUo6sewoxSEgl36ak5t+4WamRsNr6nzjZXQjM35WsU+9vbg==", + "@ampproject/remapping": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" } }, - "@babel/plugin-transform-member-expression-literals": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.23.3.tgz", - "integrity": "sha512-sC3LdDBDi5x96LA+Ytekz2ZPk8i/Ck+DEuDbRAll5rknJ5XRTSaPKEYwomLcs1AA8wg9b3KjIQRsnApj+q51Ag==", - "dev": true, + "@aws-crypto/crc32": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-3.0.0.tgz", + "integrity": "sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" } }, - "@babel/plugin-transform-modules-amd": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.23.3.tgz", - "integrity": "sha512-vJYQGxeKM4t8hYCKVBlZX/gtIY2I7mRGFNcm85sgXGMTBcoV3QdVtdpbcWEbzbfUIUZKwvgFT82mRvaQIebZzw==", - "dev": true, + "@aws-crypto/ie11-detection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-3.0.0.tgz", + "integrity": "sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==", "requires": { - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.22.5" + "tslib": "^1.11.1" } }, - "@babel/plugin-transform-modules-commonjs": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.3.tgz", - "integrity": "sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA==", - "dev": true, + "@aws-crypto/sha256-browser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-3.0.0.tgz", + "integrity": "sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==", "requires": { - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-simple-access": "^7.22.5" + "@aws-crypto/ie11-detection": "^3.0.0", + "@aws-crypto/sha256-js": "^3.0.0", + "@aws-crypto/supports-web-crypto": "^3.0.0", + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" } }, - "@babel/plugin-transform-modules-systemjs": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.23.3.tgz", - "integrity": "sha512-ZxyKGTkF9xT9YJuKQRo19ewf3pXpopuYQd8cDXqNzc3mUNbOME0RKMoZxviQk74hwzfQsEe66dE92MaZbdHKNQ==", - "dev": true, + "@aws-crypto/sha256-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-3.0.0.tgz", + "integrity": "sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==", "requires": { - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.20" + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" } }, - "@babel/plugin-transform-modules-umd": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.23.3.tgz", - "integrity": "sha512-zHsy9iXX2nIsCBFPud3jKn1IRPWg3Ing1qOZgeKV39m1ZgIdpJqvlWVeiHBZC6ITRG0MfskhYe9cLgntfSFPIg==", - "dev": true, + "@aws-crypto/supports-web-crypto": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-3.0.0.tgz", + "integrity": "sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==", "requires": { - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.22.5" + "tslib": "^1.11.1" } }, - "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz", - "integrity": "sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" + "@aws-crypto/util": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-3.0.0.tgz", + "integrity": "sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==", + "requires": { + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "@aws-sdk/client-dynamodb": { + "version": "3.525.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-dynamodb/-/client-dynamodb-3.525.0.tgz", + "integrity": "sha512-leL9TP4W8pjzNFf2/yqyphpEvBxVScXu8OB6fKdONiya6TNuR8tU+fcrcacNQvKs3hok7jKdfa8PaQpZ/bv3Ug==", + "requires": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/client-sts": "3.525.0", + "@aws-sdk/core": "3.525.0", + "@aws-sdk/credential-provider-node": "3.525.0", + "@aws-sdk/middleware-endpoint-discovery": "3.525.0", + "@aws-sdk/middleware-host-header": "3.523.0", + "@aws-sdk/middleware-logger": "3.523.0", + "@aws-sdk/middleware-recursion-detection": "3.523.0", + "@aws-sdk/middleware-user-agent": "3.525.0", + "@aws-sdk/region-config-resolver": "3.525.0", + "@aws-sdk/types": "3.523.0", + "@aws-sdk/util-endpoints": "3.525.0", + "@aws-sdk/util-user-agent-browser": "3.523.0", + "@aws-sdk/util-user-agent-node": "3.525.0", + "@smithy/config-resolver": "^2.1.4", + "@smithy/core": "^1.3.5", + "@smithy/fetch-http-handler": "^2.4.3", + "@smithy/hash-node": "^2.1.3", + "@smithy/invalid-dependency": "^2.1.3", + "@smithy/middleware-content-length": "^2.1.3", + "@smithy/middleware-endpoint": "^2.4.4", + "@smithy/middleware-retry": "^2.1.4", + "@smithy/middleware-serde": "^2.1.3", + "@smithy/middleware-stack": "^2.1.3", + "@smithy/node-config-provider": "^2.2.4", + "@smithy/node-http-handler": "^2.4.1", + "@smithy/protocol-http": "^3.2.1", + "@smithy/smithy-client": "^2.4.2", + "@smithy/types": "^2.10.1", + "@smithy/url-parser": "^2.1.3", + "@smithy/util-base64": "^2.1.1", + "@smithy/util-body-length-browser": "^2.1.1", + "@smithy/util-body-length-node": "^2.2.1", + "@smithy/util-defaults-mode-browser": "^2.1.4", + "@smithy/util-defaults-mode-node": "^2.2.3", + "@smithy/util-endpoints": "^1.1.4", + "@smithy/util-middleware": "^2.1.3", + "@smithy/util-retry": "^2.1.3", + "@smithy/util-utf8": "^2.1.1", + "@smithy/util-waiter": "^2.1.3", + "tslib": "^2.5.0", + "uuid": "^9.0.1" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==" + } } }, - "@babel/plugin-transform-new-target": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.23.3.tgz", - "integrity": "sha512-YJ3xKqtJMAT5/TIZnpAR3I+K+WaDowYbN3xyxI8zxx/Gsypwf9B9h0VB+1Nh6ACAAPRS5NSRje0uVv5i79HYGQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@aws-sdk/client-sso": { + "version": "3.525.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.525.0.tgz", + "integrity": "sha512-6KwGQWFoNLH1UupdWPFdKPfTgjSz1kN8/r8aCzuvvXBe4Pz+iDUZ6FEJzGWNc9AapjvZDNO1hs23slomM9rTaA==", + "requires": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/core": "3.525.0", + "@aws-sdk/middleware-host-header": "3.523.0", + "@aws-sdk/middleware-logger": "3.523.0", + "@aws-sdk/middleware-recursion-detection": "3.523.0", + "@aws-sdk/middleware-user-agent": "3.525.0", + "@aws-sdk/region-config-resolver": "3.525.0", + "@aws-sdk/types": "3.523.0", + "@aws-sdk/util-endpoints": "3.525.0", + "@aws-sdk/util-user-agent-browser": "3.523.0", + "@aws-sdk/util-user-agent-node": "3.525.0", + "@smithy/config-resolver": "^2.1.4", + "@smithy/core": "^1.3.5", + "@smithy/fetch-http-handler": "^2.4.3", + "@smithy/hash-node": "^2.1.3", + "@smithy/invalid-dependency": "^2.1.3", + "@smithy/middleware-content-length": "^2.1.3", + "@smithy/middleware-endpoint": "^2.4.4", + "@smithy/middleware-retry": "^2.1.4", + "@smithy/middleware-serde": "^2.1.3", + "@smithy/middleware-stack": "^2.1.3", + "@smithy/node-config-provider": "^2.2.4", + "@smithy/node-http-handler": "^2.4.1", + "@smithy/protocol-http": "^3.2.1", + "@smithy/smithy-client": "^2.4.2", + "@smithy/types": "^2.10.1", + "@smithy/url-parser": "^2.1.3", + "@smithy/util-base64": "^2.1.1", + "@smithy/util-body-length-browser": "^2.1.1", + "@smithy/util-body-length-node": "^2.2.1", + "@smithy/util-defaults-mode-browser": "^2.1.4", + "@smithy/util-defaults-mode-node": "^2.2.3", + "@smithy/util-endpoints": "^1.1.4", + "@smithy/util-middleware": "^2.1.3", + "@smithy/util-retry": "^2.1.3", + "@smithy/util-utf8": "^2.1.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } } }, - "@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.23.4.tgz", - "integrity": "sha512-jHE9EVVqHKAQx+VePv5LLGHjmHSJR76vawFPTdlxR/LVJPfOEGxREQwQfjuZEOPTwG92X3LINSh3M40Rv4zpVA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + "@aws-sdk/client-sso-oidc": { + "version": "3.525.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.525.0.tgz", + "integrity": "sha512-zz13k/6RkjPSLmReSeGxd8wzGiiZa4Odr2Tv3wTcxClM4wOjD+zOgGv4Fe32b9AMqaueiCdjbvdu7AKcYxFA4A==", + "requires": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/client-sts": "3.525.0", + "@aws-sdk/core": "3.525.0", + "@aws-sdk/middleware-host-header": "3.523.0", + "@aws-sdk/middleware-logger": "3.523.0", + "@aws-sdk/middleware-recursion-detection": "3.523.0", + "@aws-sdk/middleware-user-agent": "3.525.0", + "@aws-sdk/region-config-resolver": "3.525.0", + "@aws-sdk/types": "3.523.0", + "@aws-sdk/util-endpoints": "3.525.0", + "@aws-sdk/util-user-agent-browser": "3.523.0", + "@aws-sdk/util-user-agent-node": "3.525.0", + "@smithy/config-resolver": "^2.1.4", + "@smithy/core": "^1.3.5", + "@smithy/fetch-http-handler": "^2.4.3", + "@smithy/hash-node": "^2.1.3", + "@smithy/invalid-dependency": "^2.1.3", + "@smithy/middleware-content-length": "^2.1.3", + "@smithy/middleware-endpoint": "^2.4.4", + "@smithy/middleware-retry": "^2.1.4", + "@smithy/middleware-serde": "^2.1.3", + "@smithy/middleware-stack": "^2.1.3", + "@smithy/node-config-provider": "^2.2.4", + "@smithy/node-http-handler": "^2.4.1", + "@smithy/protocol-http": "^3.2.1", + "@smithy/smithy-client": "^2.4.2", + "@smithy/types": "^2.10.1", + "@smithy/url-parser": "^2.1.3", + "@smithy/util-base64": "^2.1.1", + "@smithy/util-body-length-browser": "^2.1.1", + "@smithy/util-body-length-node": "^2.2.1", + "@smithy/util-defaults-mode-browser": "^2.1.4", + "@smithy/util-defaults-mode-node": "^2.2.3", + "@smithy/util-endpoints": "^1.1.4", + "@smithy/util-middleware": "^2.1.3", + "@smithy/util-retry": "^2.1.3", + "@smithy/util-utf8": "^2.1.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } } }, - "@babel/plugin-transform-numeric-separator": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.23.4.tgz", - "integrity": "sha512-mps6auzgwjRrwKEZA05cOwuDc9FAzoyFS4ZsG/8F43bTLf/TgkJg7QXOrPO1JO599iA3qgK9MXdMGOEC8O1h6Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" + "@aws-sdk/client-sts": { + "version": "3.525.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.525.0.tgz", + "integrity": "sha512-a8NUGRvO6rkfTZCbMaCsjDjLbERCwIUU9dIywFYcRgbFhkupJ7fSaZz3Het98U51M9ZbTEpaTa3fz0HaJv8VJw==", + "requires": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/core": "3.525.0", + "@aws-sdk/middleware-host-header": "3.523.0", + "@aws-sdk/middleware-logger": "3.523.0", + "@aws-sdk/middleware-recursion-detection": "3.523.0", + "@aws-sdk/middleware-user-agent": "3.525.0", + "@aws-sdk/region-config-resolver": "3.525.0", + "@aws-sdk/types": "3.523.0", + "@aws-sdk/util-endpoints": "3.525.0", + "@aws-sdk/util-user-agent-browser": "3.523.0", + "@aws-sdk/util-user-agent-node": "3.525.0", + "@smithy/config-resolver": "^2.1.4", + "@smithy/core": "^1.3.5", + "@smithy/fetch-http-handler": "^2.4.3", + "@smithy/hash-node": "^2.1.3", + "@smithy/invalid-dependency": "^2.1.3", + "@smithy/middleware-content-length": "^2.1.3", + "@smithy/middleware-endpoint": "^2.4.4", + "@smithy/middleware-retry": "^2.1.4", + "@smithy/middleware-serde": "^2.1.3", + "@smithy/middleware-stack": "^2.1.3", + "@smithy/node-config-provider": "^2.2.4", + "@smithy/node-http-handler": "^2.4.1", + "@smithy/protocol-http": "^3.2.1", + "@smithy/smithy-client": "^2.4.2", + "@smithy/types": "^2.10.1", + "@smithy/url-parser": "^2.1.3", + "@smithy/util-base64": "^2.1.1", + "@smithy/util-body-length-browser": "^2.1.1", + "@smithy/util-body-length-node": "^2.2.1", + "@smithy/util-defaults-mode-browser": "^2.1.4", + "@smithy/util-defaults-mode-node": "^2.2.3", + "@smithy/util-endpoints": "^1.1.4", + "@smithy/util-middleware": "^2.1.3", + "@smithy/util-retry": "^2.1.3", + "@smithy/util-utf8": "^2.1.1", + "fast-xml-parser": "4.2.5", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } } }, - "@babel/plugin-transform-object-rest-spread": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.23.4.tgz", - "integrity": "sha512-9x9K1YyeQVw0iOXJlIzwm8ltobIIv7j2iLyP2jIhEbqPRQ7ScNgwQufU2I0Gq11VjyG4gI4yMXt2VFags+1N3g==", - "dev": true, + "@aws-sdk/core": { + "version": "3.525.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.525.0.tgz", + "integrity": "sha512-E3LtEtMWCriQOFZpVKpLYzbdw/v2PAOEAMhn2VRRZ1g0/g1TXzQrfhEU2yd8l/vQEJaCJ82ooGGg7YECviBUxA==", "requires": { - "@babel/compat-data": "^7.23.3", - "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.23.3" + "@smithy/core": "^1.3.5", + "@smithy/protocol-http": "^3.2.1", + "@smithy/signature-v4": "^2.1.3", + "@smithy/smithy-client": "^2.4.2", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } } }, - "@babel/plugin-transform-object-super": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.23.3.tgz", - "integrity": "sha512-BwQ8q0x2JG+3lxCVFohg+KbQM7plfpBwThdW9A6TMtWwLsbDA01Ek2Zb/AgDN39BiZsExm4qrXxjk+P1/fzGrA==", - "dev": true, + "@aws-sdk/credential-provider-env": { + "version": "3.523.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.523.0.tgz", + "integrity": "sha512-Y6DWdH6/OuMDoNKVzZlNeBc6f1Yjk1lYMjANKpIhMbkRCvLJw/PYZKOZa8WpXbTYdgg9XLjKybnLIb3ww3uuzA==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.20" + "@aws-sdk/types": "3.523.0", + "@smithy/property-provider": "^2.1.3", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } } }, - "@babel/plugin-transform-optional-catch-binding": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.23.4.tgz", - "integrity": "sha512-XIq8t0rJPHf6Wvmbn9nFxU6ao4c7WhghTR5WyV8SrJfUFzyxhCm4nhC+iAp3HFhbAKLfYpgzhJ6t4XCtVwqO5A==", - "dev": true, + "@aws-sdk/credential-provider-http": { + "version": "3.525.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.525.0.tgz", + "integrity": "sha512-RNWQGuSBQZhl3iqklOslUEfQ4br1V3DCPboMpeqFtddUWJV3m2u2extFur9/4Uy+1EHVF120IwZUKtd8dF+ibw==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + "@aws-sdk/types": "3.523.0", + "@smithy/fetch-http-handler": "^2.4.3", + "@smithy/node-http-handler": "^2.4.1", + "@smithy/property-provider": "^2.1.3", + "@smithy/protocol-http": "^3.2.1", + "@smithy/smithy-client": "^2.4.2", + "@smithy/types": "^2.10.1", + "@smithy/util-stream": "^2.1.3", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } } }, - "@babel/plugin-transform-optional-chaining": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.23.4.tgz", - "integrity": "sha512-ZU8y5zWOfjM5vZ+asjgAPwDaBjJzgufjES89Rs4Lpq63O300R/kOz30WCLo6BxxX6QVEilwSlpClnG5cZaikTA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" + "@aws-sdk/credential-provider-ini": { + "version": "3.525.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.525.0.tgz", + "integrity": "sha512-JDnccfK5JRb9jcgpc9lirL9PyCwGIqY0nKdw3LlX5WL5vTpTG4E1q7rLAlpNh7/tFD1n66Itarfv2tsyHMIqCw==", + "requires": { + "@aws-sdk/client-sts": "3.525.0", + "@aws-sdk/credential-provider-env": "3.523.0", + "@aws-sdk/credential-provider-process": "3.523.0", + "@aws-sdk/credential-provider-sso": "3.525.0", + "@aws-sdk/credential-provider-web-identity": "3.525.0", + "@aws-sdk/types": "3.523.0", + "@smithy/credential-provider-imds": "^2.2.3", + "@smithy/property-provider": "^2.1.3", + "@smithy/shared-ini-file-loader": "^2.3.3", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } } }, - "@babel/plugin-transform-parameters": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.23.3.tgz", - "integrity": "sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@aws-sdk/credential-provider-node": { + "version": "3.525.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.525.0.tgz", + "integrity": "sha512-RJXlO8goGXpnoHQAyrCcJ0QtWEOFa34LSbfdqBIjQX/fwnjUuEmiGdXTV3AZmwYQ7juk49tfBneHbtOP3AGqsQ==", + "requires": { + "@aws-sdk/credential-provider-env": "3.523.0", + "@aws-sdk/credential-provider-http": "3.525.0", + "@aws-sdk/credential-provider-ini": "3.525.0", + "@aws-sdk/credential-provider-process": "3.523.0", + "@aws-sdk/credential-provider-sso": "3.525.0", + "@aws-sdk/credential-provider-web-identity": "3.525.0", + "@aws-sdk/types": "3.523.0", + "@smithy/credential-provider-imds": "^2.2.3", + "@smithy/property-provider": "^2.1.3", + "@smithy/shared-ini-file-loader": "^2.3.3", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } } }, - "@babel/plugin-transform-private-methods": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.23.3.tgz", - "integrity": "sha512-UzqRcRtWsDMTLrRWFvUBDwmw06tCQH9Rl1uAjfh6ijMSmGYQ+fpdB+cnqRC8EMh5tuuxSv0/TejGL+7vyj+50g==", - "dev": true, + "@aws-sdk/credential-provider-process": { + "version": "3.523.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.523.0.tgz", + "integrity": "sha512-f0LP9KlFmMvPWdKeUKYlZ6FkQAECUeZMmISsv6NKtvPCI9e4O4cLTeR09telwDK8P0HrgcRuZfXM7E30m8re0Q==", "requires": { - "@babel/helper-create-class-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" + "@aws-sdk/types": "3.523.0", + "@smithy/property-provider": "^2.1.3", + "@smithy/shared-ini-file-loader": "^2.3.3", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } } }, - "@babel/plugin-transform-private-property-in-object": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.23.4.tgz", - "integrity": "sha512-9G3K1YqTq3F4Vt88Djx1UZ79PDyj+yKRnUy7cZGSMe+a7jkwD259uKKuUzQlPkGam7R+8RJwh5z4xO27fA1o2A==", - "dev": true, + "@aws-sdk/credential-provider-sso": { + "version": "3.525.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.525.0.tgz", + "integrity": "sha512-7V7ybtufxdD3plxeIeB6aqHZeFIUlAyPphXIUgXrGY10iNcosL970rQPBeggsohe4gCM6UvY2TfMeEcr+ZE8FA==", "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + "@aws-sdk/client-sso": "3.525.0", + "@aws-sdk/token-providers": "3.525.0", + "@aws-sdk/types": "3.523.0", + "@smithy/property-provider": "^2.1.3", + "@smithy/shared-ini-file-loader": "^2.3.3", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } } }, - "@babel/plugin-transform-property-literals": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.23.3.tgz", - "integrity": "sha512-jR3Jn3y7cZp4oEWPFAlRsSWjxKe4PZILGBSd4nis1TsC5qeSpb+nrtihJuDhNI7QHiVbUaiXa0X2RZY3/TI6Nw==", - "dev": true, + "@aws-sdk/credential-provider-web-identity": { + "version": "3.525.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.525.0.tgz", + "integrity": "sha512-sAukOjR1oKb2JXG4nPpuBFpSwGUhrrY17PG/xbTy8NAoLLhrqRwnErcLfdTfmj6tH+3094k6ws/Sh8a35ae7fA==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@aws-sdk/client-sts": "3.525.0", + "@aws-sdk/types": "3.523.0", + "@smithy/property-provider": "^2.1.3", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } } }, - "@babel/plugin-transform-regenerator": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.23.3.tgz", - "integrity": "sha512-KP+75h0KghBMcVpuKisx3XTu9Ncut8Q8TuvGO4IhY+9D5DFEckQefOuIsB/gQ2tG71lCke4NMrtIPS8pOj18BQ==", - "dev": true, + "@aws-sdk/endpoint-cache": { + "version": "3.495.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/endpoint-cache/-/endpoint-cache-3.495.0.tgz", + "integrity": "sha512-XCDrpiS50WaPzPzp7FwsChPHtX9PQQUU4nRzcn2N7IkUtpcFCUx8m1PAZe086VQr6hrbdeE4Z4j8hUPNwVdJGQ==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "regenerator-transform": "^0.15.2" + "mnemonist": "0.38.3", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } } }, - "@babel/plugin-transform-reserved-words": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.23.3.tgz", - "integrity": "sha512-QnNTazY54YqgGxwIexMZva9gqbPa15t/x9VS+0fsEFWplwVpXYZivtgl43Z1vMpc1bdPP2PP8siFeVcnFvA3Cg==", - "dev": true, + "@aws-sdk/lib-dynamodb": { + "version": "3.525.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/lib-dynamodb/-/lib-dynamodb-3.525.0.tgz", + "integrity": "sha512-ED1YDzon/aAdXy4eUwFApXABxI+QM6THY5BSNrsGBQPxvYjMNQ2PGKMwbFPuMbNjZi48c6YpQyIF2cD7cEXG/A==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@aws-sdk/util-dynamodb": "3.525.0", + "@smithy/smithy-client": "^2.4.2", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } } }, - "@babel/plugin-transform-runtime": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.23.6.tgz", - "integrity": "sha512-kF1Zg62aPseQ11orDhFRw+aPG/eynNQtI+TyY+m33qJa2cJ5EEvza2P2BNTIA9E5MyqFABHEyY6CPHwgdy9aNg==", - "dev": true, + "@aws-sdk/middleware-endpoint-discovery": { + "version": "3.525.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-endpoint-discovery/-/middleware-endpoint-discovery-3.525.0.tgz", + "integrity": "sha512-nT/XYP3RDRWPFCTEOZQbOC3HWmUkxB0fDuobmH8WzL92MCBGz9gBG/q9XBxiw9pHk9Dky/MIkLV50BlGB3kM7g==", "requires": { - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "babel-plugin-polyfill-corejs2": "^0.4.6", - "babel-plugin-polyfill-corejs3": "^0.8.5", - "babel-plugin-polyfill-regenerator": "^0.5.3", - "semver": "^7.5.2" + "@aws-sdk/endpoint-cache": "3.495.0", + "@aws-sdk/types": "3.523.0", + "@smithy/node-config-provider": "^2.2.4", + "@smithy/protocol-http": "^3.2.1", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } } }, - "@babel/plugin-transform-shorthand-properties": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.23.3.tgz", - "integrity": "sha512-ED2fgqZLmexWiN+YNFX26fx4gh5qHDhn1O2gvEhreLW2iI63Sqm4llRLCXALKrCnbN4Jy0VcMQZl/SAzqug/jg==", - "dev": true, + "@aws-sdk/middleware-host-header": { + "version": "3.523.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.523.0.tgz", + "integrity": "sha512-4g3q7Ta9sdD9TMUuohBAkbx/e3I/juTqfKi7TPgP+8jxcYX72MOsgemAMHuP6CX27eyj4dpvjH+w4SIVDiDSmg==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@aws-sdk/types": "3.523.0", + "@smithy/protocol-http": "^3.2.1", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } } }, - "@babel/plugin-transform-spread": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.23.3.tgz", - "integrity": "sha512-VvfVYlrlBVu+77xVTOAoxQ6mZbnIq5FM0aGBSFEcIh03qHf+zNqA4DC/3XMUozTg7bZV3e3mZQ0i13VB6v5yUg==", - "dev": true, + "@aws-sdk/middleware-logger": { + "version": "3.523.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.523.0.tgz", + "integrity": "sha512-PeDNJNhfiaZx54LBaLTXzUaJ9LXFwDFFIksipjqjvxMafnoVcQwKbkoPUWLe5ytT4nnL1LogD3s55mERFUsnwg==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" + "@aws-sdk/types": "3.523.0", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } } }, - "@babel/plugin-transform-sticky-regex": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.23.3.tgz", - "integrity": "sha512-HZOyN9g+rtvnOU3Yh7kSxXrKbzgrm5X4GncPY1QOquu7epga5MxKHVpYu2hvQnry/H+JjckSYRb93iNfsioAGg==", - "dev": true, + "@aws-sdk/middleware-recursion-detection": { + "version": "3.523.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.523.0.tgz", + "integrity": "sha512-nZ3Vt7ehfSDYnrcg/aAfjjvpdE+61B3Zk68i6/hSUIegT3IH9H1vSW67NDKVp+50hcEfzWwM2HMPXxlzuyFyrw==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@aws-sdk/types": "3.523.0", + "@smithy/protocol-http": "^3.2.1", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } } }, - "@babel/plugin-transform-template-literals": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.23.3.tgz", - "integrity": "sha512-Flok06AYNp7GV2oJPZZcP9vZdszev6vPBkHLwxwSpaIqx75wn6mUd3UFWsSsA0l8nXAKkyCmL/sR02m8RYGeHg==", - "dev": true, + "@aws-sdk/middleware-user-agent": { + "version": "3.525.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.525.0.tgz", + "integrity": "sha512-4al/6uO+t/QIYXK2OgqzDKQzzLAYJza1vWFS+S0lJ3jLNGyLB5BMU5KqWjDzevYZ4eCnz2Nn7z0FveUTNz8YdQ==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@aws-sdk/types": "3.523.0", + "@aws-sdk/util-endpoints": "3.525.0", + "@smithy/protocol-http": "^3.2.1", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } } }, - "@babel/plugin-transform-typeof-symbol": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.23.3.tgz", - "integrity": "sha512-4t15ViVnaFdrPC74be1gXBSMzXk3B4Us9lP7uLRQHTFpV5Dvt33pn+2MyyNxmN3VTTm3oTrZVMUmuw3oBnQ2oQ==", - "dev": true, + "@aws-sdk/region-config-resolver": { + "version": "3.525.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.525.0.tgz", + "integrity": "sha512-8kFqXk6UyKgTMi7N7QlhA6qM4pGPWbiUXqEY2RgUWngtxqNFGeM9JTexZeuavQI+qLLe09VPShPNX71fEDcM6w==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@aws-sdk/types": "3.523.0", + "@smithy/node-config-provider": "^2.2.4", + "@smithy/types": "^2.10.1", + "@smithy/util-config-provider": "^2.2.1", + "@smithy/util-middleware": "^2.1.3", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } } }, - "@babel/plugin-transform-unicode-escapes": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.23.3.tgz", - "integrity": "sha512-OMCUx/bU6ChE3r4+ZdylEqAjaQgHAgipgW8nsCfu5pGqDcFytVd91AwRvUJSBZDz0exPGgnjoqhgRYLRjFZc9Q==", - "dev": true, + "@aws-sdk/token-providers": { + "version": "3.525.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.525.0.tgz", + "integrity": "sha512-puVjbxuK0Dq7PTQ2HdddHy2eQjOH8GZbump74yWJa6JVpRW84LlOcNmP+79x4Kscvz2ldWB8XDFw/pcCiSDe5A==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@aws-sdk/client-sso-oidc": "3.525.0", + "@aws-sdk/types": "3.523.0", + "@smithy/property-provider": "^2.1.3", + "@smithy/shared-ini-file-loader": "^2.3.3", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } } }, - "@babel/plugin-transform-unicode-property-regex": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.23.3.tgz", - "integrity": "sha512-KcLIm+pDZkWZQAFJ9pdfmh89EwVfmNovFBcXko8szpBeF8z68kWIPeKlmSOkT9BXJxs2C0uk+5LxoxIv62MROA==", - "dev": true, + "@aws-sdk/types": { + "version": "3.523.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.523.0.tgz", + "integrity": "sha512-AqGIu4u+SxPiUuNBp2acCVcq80KDUFjxe6e3cMTvKWTzCbrVk1AXv0dAaJnCmdkWIha6zJDWxpIk/aL4EGhZ9A==", "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } } }, - "@babel/plugin-transform-unicode-regex": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.23.3.tgz", - "integrity": "sha512-wMHpNA4x2cIA32b/ci3AfwNgheiva2W0WUKWTK7vBHBhDKfPsc5cFGNWm69WBqpwd86u1qwZ9PWevKqm1A3yAw==", - "dev": true, + "@aws-sdk/util-dynamodb": { + "version": "3.525.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-dynamodb/-/util-dynamodb-3.525.0.tgz", + "integrity": "sha512-2PXpWcMfD65d34y4Dd2OGpLmUYW6vegaITUOk95QwZOwMBUPJRrQB6AyGMf1z39Ej7utj7yJSqQKgqyw2i2U+A==", "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } } }, - "@babel/plugin-transform-unicode-sets-regex": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.23.3.tgz", - "integrity": "sha512-W7lliA/v9bNR83Qc3q1ip9CQMZ09CcHDbHfbLRDNuAhn1Mvkr1ZNF7hPmztMQvtTGVLJ9m8IZqWsTkXOml8dbw==", - "dev": true, + "@aws-sdk/util-endpoints": { + "version": "3.525.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.525.0.tgz", + "integrity": "sha512-DIW7WWU5tIGkeeKX6NJUyrEIdWMiqjLQG3XBzaUj+ufIENwNjdAHhlD8l2vX7Yr3JZRT6yN/84wBCj7Tw1xd1g==", "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" + "@aws-sdk/types": "3.523.0", + "@smithy/types": "^2.10.1", + "@smithy/util-endpoints": "^1.1.4", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } } }, - "@babel/preset-env": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.23.6.tgz", - "integrity": "sha512-2XPn/BqKkZCpzYhUUNZ1ssXw7DcXfKQEjv/uXZUXgaebCMYmkEsfZ2yY+vv+xtXv50WmL5SGhyB6/xsWxIvvOQ==", - "dev": true, + "@aws-sdk/util-locate-window": { + "version": "3.495.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.495.0.tgz", + "integrity": "sha512-MfaPXT0kLX2tQaR90saBT9fWQq2DHqSSJRzW+MZWsmF+y5LGCOhO22ac/2o6TKSQm7h0HRc2GaADqYYYor62yg==", "requires": { - "@babel/compat-data": "^7.23.5", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-option": "^7.23.5", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.23.3", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.23.3", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.23.3", - "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.23.3", - "@babel/plugin-syntax-import-attributes": "^7.23.3", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5", - "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.23.3", - "@babel/plugin-transform-async-generator-functions": "^7.23.4", - "@babel/plugin-transform-async-to-generator": "^7.23.3", - "@babel/plugin-transform-block-scoped-functions": "^7.23.3", - "@babel/plugin-transform-block-scoping": "^7.23.4", - "@babel/plugin-transform-class-properties": "^7.23.3", - "@babel/plugin-transform-class-static-block": "^7.23.4", - "@babel/plugin-transform-classes": "^7.23.5", - "@babel/plugin-transform-computed-properties": "^7.23.3", - "@babel/plugin-transform-destructuring": "^7.23.3", - "@babel/plugin-transform-dotall-regex": "^7.23.3", - "@babel/plugin-transform-duplicate-keys": "^7.23.3", - "@babel/plugin-transform-dynamic-import": "^7.23.4", - "@babel/plugin-transform-exponentiation-operator": "^7.23.3", - "@babel/plugin-transform-export-namespace-from": "^7.23.4", - "@babel/plugin-transform-for-of": "^7.23.6", - "@babel/plugin-transform-function-name": "^7.23.3", - "@babel/plugin-transform-json-strings": "^7.23.4", - "@babel/plugin-transform-literals": "^7.23.3", - "@babel/plugin-transform-logical-assignment-operators": "^7.23.4", - "@babel/plugin-transform-member-expression-literals": "^7.23.3", - "@babel/plugin-transform-modules-amd": "^7.23.3", - "@babel/plugin-transform-modules-commonjs": "^7.23.3", - "@babel/plugin-transform-modules-systemjs": "^7.23.3", - "@babel/plugin-transform-modules-umd": "^7.23.3", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", - "@babel/plugin-transform-new-target": "^7.23.3", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.23.4", - "@babel/plugin-transform-numeric-separator": "^7.23.4", - "@babel/plugin-transform-object-rest-spread": "^7.23.4", - "@babel/plugin-transform-object-super": "^7.23.3", - "@babel/plugin-transform-optional-catch-binding": "^7.23.4", - "@babel/plugin-transform-optional-chaining": "^7.23.4", - "@babel/plugin-transform-parameters": "^7.23.3", - "@babel/plugin-transform-private-methods": "^7.23.3", - "@babel/plugin-transform-private-property-in-object": "^7.23.4", - "@babel/plugin-transform-property-literals": "^7.23.3", - "@babel/plugin-transform-regenerator": "^7.23.3", - "@babel/plugin-transform-reserved-words": "^7.23.3", - "@babel/plugin-transform-shorthand-properties": "^7.23.3", - "@babel/plugin-transform-spread": "^7.23.3", - "@babel/plugin-transform-sticky-regex": "^7.23.3", - "@babel/plugin-transform-template-literals": "^7.23.3", - "@babel/plugin-transform-typeof-symbol": "^7.23.3", - "@babel/plugin-transform-unicode-escapes": "^7.23.3", - "@babel/plugin-transform-unicode-property-regex": "^7.23.3", - "@babel/plugin-transform-unicode-regex": "^7.23.3", - "@babel/plugin-transform-unicode-sets-regex": "^7.23.3", - "@babel/preset-modules": "0.1.6-no-external-plugins", - "babel-plugin-polyfill-corejs2": "^0.4.6", - "babel-plugin-polyfill-corejs3": "^0.8.5", - "babel-plugin-polyfill-regenerator": "^0.5.3", - "core-js-compat": "^3.31.0", - "semver": "^7.5.2" + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } } }, - "@babel/preset-modules": { - "version": "0.1.6-no-external-plugins", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", - "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", - "dev": true, + "@aws-sdk/util-user-agent-browser": { + "version": "3.523.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.523.0.tgz", + "integrity": "sha512-6ZRNdGHX6+HQFqTbIA5+i8RWzxFyxsZv8D3soRfpdyWIKkzhSz8IyRKXRciwKBJDaC7OX2jzGE90wxRQft27nA==", "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" + "@aws-sdk/types": "3.523.0", + "@smithy/types": "^2.10.1", + "bowser": "^2.11.0", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } } }, - "@babel/register": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.22.15.tgz", - "integrity": "sha512-V3Q3EqoQdn65RCgTLwauZaTfd1ShhwPmbBv+1dkZV/HpCGMKVyn6oFcRlI7RaKqiDQjX2Qd3AuoEguBgdjIKlg==", - "dev": true, + "@aws-sdk/util-user-agent-node": { + "version": "3.525.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.525.0.tgz", + "integrity": "sha512-88Wjt4efyUSBGcyIuh1dvoMqY1k15jpJc5A/3yi67clBQEFsu9QCodQCQPqmRjV3VRcMtBOk+jeCTiUzTY5dRQ==", "requires": { - "clone-deep": "^4.0.1", - "find-cache-dir": "^2.0.0", - "make-dir": "^2.1.0", - "pirates": "^4.0.5", - "source-map-support": "^0.5.16" + "@aws-sdk/types": "3.523.0", + "@smithy/node-config-provider": "^2.2.4", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } } }, - "@babel/regjsgen": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==", - "dev": true + "@aws-sdk/util-utf8-browser": { + "version": "3.259.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz", + "integrity": "sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==", + "requires": { + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } }, - "@babel/runtime": { - "version": "7.17.8", + "@babel/cli": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.23.4.tgz", + "integrity": "sha512-j3luA9xGKCXVyCa5R7lJvOMM+Kc2JEnAEIgz2ggtjQ/j5YUVgfsg/WsG95bbsgq7YLHuiCOzMnoSasuY16qiCw==", + "dev": true, "requires": { - "regenerator-runtime": "^0.13.4" + "@jridgewell/trace-mapping": "^0.3.17", + "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents.3", + "chokidar": "^3.4.0", + "commander": "^4.0.1", + "convert-source-map": "^2.0.0", + "fs-readdir-recursive": "^1.1.0", + "glob": "^7.2.0", + "make-dir": "^2.1.0", + "slash": "^2.0.0" } }, - "@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "@babel/code-frame": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", "dev": true, "requires": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" + "@babel/highlight": "^7.23.4", + "chalk": "^2.4.2" } }, - "@babel/traverse": { + "@babel/compat-data": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", + "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==", + "dev": true + }, + "@babel/core": { "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.6.tgz", - "integrity": "sha512-czastdK1e8YByZqezMPFiZ8ahwVMh/ESl9vPgvgdB9AmFMGP5jfpFax74AQgl5zj4XHzqeYAg2l8PuUeRS1MgQ==", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.6.tgz", + "integrity": "sha512-FxpRyGjrMJXh7X3wGLGhNDCRiwpWEF74sKjTLDJSG5Kyvow3QZaG0Adbqzi9ZrVjTWpsX+2cxWXD71NMg93kdw==", "dev": true, "requires": { + "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.23.5", "@babel/generator": "^7.23.6", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.23.6", "@babel/parser": "^7.23.6", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.6", "@babel/types": "^7.23.6", - "debug": "^4.3.1", - "globals": "^11.1.0" + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "2.2.2", + "semver": "^7.5.2" + }, + "dependencies": { + "json5": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.2.tgz", + "integrity": "sha512-46Tk9JiOL2z7ytNQWFLpj99RZkVgeHf87yGQKsIkaPz1qSH9UczKH1rO7K3wgRselo0tYMUNfecYpm/p1vC7tQ==", + "dev": true + } } }, - "@babel/types": { + "@babel/generator": { "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.6.tgz", - "integrity": "sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", + "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", "dev": true, "requires": { - "@babel/helper-string-parser": "^7.23.4", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" + "@babel/types": "^7.23.6", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" } }, - "@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "@colors/colors": { - "version": "1.5.0" - }, - "@dabh/diagnostics": { - "version": "2.0.3", + "@babel/helper-annotate-as-pure": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", + "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", + "dev": true, "requires": { - "colorspace": "1.1.x", - "enabled": "2.0.x", - "kuler": "^2.0.0" + "@babel/types": "^7.22.5" } }, - "@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz", + "integrity": "sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==", "dev": true, "requires": { - "eslint-visitor-keys": "^3.3.0" + "@babel/types": "^7.22.15" } }, - "@eslint-community/regexpp": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", - "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", - "dev": true + "@babel/helper-compilation-targets": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", + "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.23.5", + "@babel/helper-validator-option": "^7.23.5", + "browserslist": "^4.22.2", + "lru-cache": "^5.1.1", + "semver": "^7.5.2" + } }, - "@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "@babel/helper-create-class-features-plugin": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.23.6.tgz", + "integrity": "sha512-cBXU1vZni/CpGF29iTu4YRbOZt3Wat6zCoMDxRF1MayiEc4URxOj31tT65HUM0CRpMowA3HCJaAOVOUnMf96cw==", "dev": true, "requires": { - "ajv": "^6.12.4", + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-member-expression-to-functions": "^7.23.0", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.20", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "semver": "^7.5.2" + } + }, + "@babel/helper-create-regexp-features-plugin": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz", + "integrity": "sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "regexpu-core": "^5.3.1", + "semver": "^7.5.2" + } + }, + "@babel/helper-define-polyfill-provider": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.4.tgz", + "integrity": "sha512-QcJMILQCu2jm5TFPGA3lCpJJTeEP+mqeXooG/NZbg/h5FTFi6V0+99ahlRsW8/kRLyb24LZVCCiclDedhLKcBA==", + "dev": true, + "requires": { + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-plugin-utils": "^7.22.5", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2" + } + }, + "@babel/helper-environment-visitor": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "dev": true + }, + "@babel/helper-function-name": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "dev": true, + "requires": { + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "dev": true, + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz", + "integrity": "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==", + "dev": true, + "requires": { + "@babel/types": "^7.23.0" + } + }, + "@babel/helper-module-imports": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", + "dev": true, + "requires": { + "@babel/types": "^7.22.15" + } + }, + "@babel/helper-module-transforms": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", + "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.20" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", + "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==", + "dev": true, + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", + "dev": true + }, + "@babel/helper-remap-async-to-generator": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz", + "integrity": "sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-wrap-function": "^7.22.20" + } + }, + "@babel/helper-replace-supers": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz", + "integrity": "sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-member-expression-to-functions": "^7.22.15", + "@babel/helper-optimise-call-expression": "^7.22.5" + } + }, + "@babel/helper-simple-access": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "dev": true, + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz", + "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==", + "dev": true, + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "dev": true, + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-string-parser": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", + "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", + "dev": true + }, + "@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "dev": true + }, + "@babel/helper-validator-option": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", + "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", + "dev": true + }, + "@babel/helper-wrap-function": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.20.tgz", + "integrity": "sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.22.5", + "@babel/template": "^7.22.15", + "@babel/types": "^7.22.19" + } + }, + "@babel/helpers": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.6.tgz", + "integrity": "sha512-wCfsbN4nBidDRhpDhvcKlzHWCTlgJYUUdSJfzXb2NuBssDSIjc3xcb+znA7l+zYsFljAcGM0aFkN40cR3lXiGA==", + "dev": true, + "requires": { + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.6", + "@babel/types": "^7.23.6" + } + }, + "@babel/highlight": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + } + }, + "@babel/node": { + "version": "7.22.19", + "resolved": "https://registry.npmjs.org/@babel/node/-/node-7.22.19.tgz", + "integrity": "sha512-VsKSO9aEHdO16NdtqkJfrXZ9Sxlna1BVnBbToWr1KGdI3cyIk6KqOoa8mWvpK280lJDOwJqxvnl994KmLhq1Yw==", + "dev": true, + "requires": { + "@babel/register": "^7.22.15", + "commander": "^4.0.1", + "core-js": "^3.30.2", + "node-environment-flags": "^1.0.5", + "regenerator-runtime": "^0.14.0", + "v8flags": "^3.1.1" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", + "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==", + "dev": true + } + } + }, + "@babel/parser": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.6.tgz", + "integrity": "sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==", + "dev": true + }, + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.23.3.tgz", + "integrity": "sha512-iRkKcCqb7iGnq9+3G6rZ+Ciz5VywC4XNRHe57lKM+jOeYAoR0lVqdeeDRfh0tQcTfw/+vBhHn926FmQhLtlFLQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.23.3.tgz", + "integrity": "sha512-WwlxbfMNdVEpQjZmK5mhm7oSwD3dS6eU+Iwsi4Knl9wAletWem7kaRsGOG+8UEbRyqxY4SS5zvtfXwX+jMxUwQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/plugin-transform-optional-chaining": "^7.23.3" + } + }, + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.23.3.tgz", + "integrity": "sha512-XaJak1qcityzrX0/IU5nKHb34VaibwP3saKqG6a/tppelgllOH13LUann4ZCIBcVOeE6H18K4Vx9QKkVww3z/w==", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "dev": true, + "requires": {} + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-syntax-import-assertions": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.23.3.tgz", + "integrity": "sha512-lPgDSU+SJLK3xmFDTV2ZRQAiM7UuUjGidwBywFavObCiZc1BeAAcMtHJKUya92hPHO+at63JJPLygilZard8jw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-syntax-import-attributes": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.23.3.tgz", + "integrity": "sha512-pawnE0P9g10xgoP7yKr6CK63K2FMsTE+FZidZO/1PwRdzmAPVs+HS1mAURUsgaoxammTJvULUdIkEK0gOcU2tA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-typescript": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz", + "integrity": "sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-syntax-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-arrow-functions": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.23.3.tgz", + "integrity": "sha512-NzQcQrzaQPkaEwoTm4Mhyl8jI1huEL/WWIEvudjTCMJ9aBZNpsJbMASx7EQECtQQPS/DcnFpo0FIh3LvEO9cxQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-async-generator-functions": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.4.tgz", + "integrity": "sha512-efdkfPhHYTtn0G6n2ddrESE91fgXxjlqLsnUtPWnJs4a4mZIbUaK7ffqKIIUKXSHwcDvaCVX6GXkaJJFqtX7jw==", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-remap-async-to-generator": "^7.22.20", + "@babel/plugin-syntax-async-generators": "^7.8.4" + } + }, + "@babel/plugin-transform-async-to-generator": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.23.3.tgz", + "integrity": "sha512-A7LFsKi4U4fomjqXJlZg/u0ft/n8/7n7lpffUP/ZULx/DtV9SGlNKZolHH6PE8Xl1ngCc0M11OaeZptXVkfKSw==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-remap-async-to-generator": "^7.22.20" + } + }, + "@babel/plugin-transform-block-scoped-functions": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.23.3.tgz", + "integrity": "sha512-vI+0sIaPIO6CNuM9Kk5VmXcMVRiOpDh7w2zZt9GXzmE/9KD70CUEVhvPR/etAeNK/FAEkhxQtXOzVF3EuRL41A==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-block-scoping": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.23.4.tgz", + "integrity": "sha512-0QqbP6B6HOh7/8iNR4CQU2Th/bbRtBp4KS9vcaZd1fZ0wSh5Fyssg0UCIHwxh+ka+pNDREbVLQnHCMHKZfPwfw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-class-properties": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.23.3.tgz", + "integrity": "sha512-uM+AN8yCIjDPccsKGlw271xjJtGii+xQIF/uMPS8H15L12jZTsLfF4o5vNO7d/oUguOyfdikHGc/yi9ge4SGIg==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-class-static-block": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.23.4.tgz", + "integrity": "sha512-nsWu/1M+ggti1SOALj3hfx5FXzAY06fwPJsUZD4/A5e1bWi46VUIWtD+kOX6/IdhXGsXBWllLFDSnqSCdUNydQ==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + } + }, + "@babel/plugin-transform-classes": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.23.5.tgz", + "integrity": "sha512-jvOTR4nicqYC9yzOHIhXG5emiFEOpappSJAl73SDSEDcybD+Puuze8Tnpb9p9qEyYup24tq891gkaygIFvWDqg==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.20", + "@babel/helper-split-export-declaration": "^7.22.6", + "globals": "^11.1.0" + } + }, + "@babel/plugin-transform-computed-properties": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.23.3.tgz", + "integrity": "sha512-dTj83UVTLw/+nbiHqQSFdwO9CbTtwq1DsDqm3CUEtDrZNET5rT5E6bIdTlOftDTDLMYxvxHNEYO4B9SLl8SLZw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/template": "^7.22.15" + } + }, + "@babel/plugin-transform-destructuring": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.23.3.tgz", + "integrity": "sha512-n225npDqjDIr967cMScVKHXJs7rout1q+tt50inyBCPkyZ8KxeI6d+GIbSBTT/w/9WdlWDOej3V9HE5Lgk57gw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-dotall-regex": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.23.3.tgz", + "integrity": "sha512-vgnFYDHAKzFaTVp+mneDsIEbnJ2Np/9ng9iviHw3P/KVcgONxpNULEW/51Z/BaFojG2GI2GwwXck5uV1+1NOYQ==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-duplicate-keys": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.23.3.tgz", + "integrity": "sha512-RrqQ+BQmU3Oyav3J+7/myfvRCq7Tbz+kKLLshUmMwNlDHExbGL7ARhajvoBJEvc+fCguPPu887N+3RRXBVKZUA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-dynamic-import": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.23.4.tgz", + "integrity": "sha512-V6jIbLhdJK86MaLh4Jpghi8ho5fGzt3imHOBu/x0jlBaPYqDoWz4RDXjmMOfnh+JWNaQleEAByZLV0QzBT4YQQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + } + }, + "@babel/plugin-transform-exponentiation-operator": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.23.3.tgz", + "integrity": "sha512-5fhCsl1odX96u7ILKHBj4/Y8vipoqwsJMh4csSA8qFfxrZDEA4Ssku2DyNvMJSmZNOEBT750LfFPbtrnTP90BQ==", + "dev": true, + "requires": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-export-namespace-from": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.23.4.tgz", + "integrity": "sha512-GzuSBcKkx62dGzZI1WVgTWvkkz84FZO5TC5T8dl/Tht/rAla6Dg/Mz9Yhypg+ezVACf/rgDuQt3kbWEv7LdUDQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + } + }, + "@babel/plugin-transform-for-of": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.23.6.tgz", + "integrity": "sha512-aYH4ytZ0qSuBbpfhuofbg/e96oQ7U2w1Aw/UQmKT+1l39uEhUPoFS3fHevDc1G0OvewyDudfMKY1OulczHzWIw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" + } + }, + "@babel/plugin-transform-function-name": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.23.3.tgz", + "integrity": "sha512-I1QXp1LxIvt8yLaib49dRW5Okt7Q4oaxao6tFVKS/anCdEOMtYwWVKoiOA1p34GOWIZjUK0E+zCp7+l1pfQyiw==", + "dev": true, + "requires": { + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-json-strings": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.23.4.tgz", + "integrity": "sha512-81nTOqM1dMwZ/aRXQ59zVubN9wHGqk6UtqRK+/q+ciXmRy8fSolhGVvG09HHRGo4l6fr/c4ZhXUQH0uFW7PZbg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-json-strings": "^7.8.3" + } + }, + "@babel/plugin-transform-literals": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.23.3.tgz", + "integrity": "sha512-wZ0PIXRxnwZvl9AYpqNUxpZ5BiTGrYt7kueGQ+N5FiQ7RCOD4cm8iShd6S6ggfVIWaJf2EMk8eRzAh52RfP4rQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-logical-assignment-operators": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.23.4.tgz", + "integrity": "sha512-Mc/ALf1rmZTP4JKKEhUwiORU+vcfarFVLfcFiolKUo6sewoxSEgl36ak5t+4WamRsNr6nzjZXQjM35WsU+9vbg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + } + }, + "@babel/plugin-transform-member-expression-literals": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.23.3.tgz", + "integrity": "sha512-sC3LdDBDi5x96LA+Ytekz2ZPk8i/Ck+DEuDbRAll5rknJ5XRTSaPKEYwomLcs1AA8wg9b3KjIQRsnApj+q51Ag==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-modules-amd": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.23.3.tgz", + "integrity": "sha512-vJYQGxeKM4t8hYCKVBlZX/gtIY2I7mRGFNcm85sgXGMTBcoV3QdVtdpbcWEbzbfUIUZKwvgFT82mRvaQIebZzw==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-modules-commonjs": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.3.tgz", + "integrity": "sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-simple-access": "^7.22.5" + } + }, + "@babel/plugin-transform-modules-systemjs": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.23.3.tgz", + "integrity": "sha512-ZxyKGTkF9xT9YJuKQRo19ewf3pXpopuYQd8cDXqNzc3mUNbOME0RKMoZxviQk74hwzfQsEe66dE92MaZbdHKNQ==", + "dev": true, + "requires": { + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20" + } + }, + "@babel/plugin-transform-modules-umd": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.23.3.tgz", + "integrity": "sha512-zHsy9iXX2nIsCBFPud3jKn1IRPWg3Ing1qOZgeKV39m1ZgIdpJqvlWVeiHBZC6ITRG0MfskhYe9cLgntfSFPIg==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz", + "integrity": "sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-new-target": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.23.3.tgz", + "integrity": "sha512-YJ3xKqtJMAT5/TIZnpAR3I+K+WaDowYbN3xyxI8zxx/Gsypwf9B9h0VB+1Nh6ACAAPRS5NSRje0uVv5i79HYGQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.23.4.tgz", + "integrity": "sha512-jHE9EVVqHKAQx+VePv5LLGHjmHSJR76vawFPTdlxR/LVJPfOEGxREQwQfjuZEOPTwG92X3LINSh3M40Rv4zpVA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + } + }, + "@babel/plugin-transform-numeric-separator": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.23.4.tgz", + "integrity": "sha512-mps6auzgwjRrwKEZA05cOwuDc9FAzoyFS4ZsG/8F43bTLf/TgkJg7QXOrPO1JO599iA3qgK9MXdMGOEC8O1h6Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + } + }, + "@babel/plugin-transform-object-rest-spread": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.23.4.tgz", + "integrity": "sha512-9x9K1YyeQVw0iOXJlIzwm8ltobIIv7j2iLyP2jIhEbqPRQ7ScNgwQufU2I0Gq11VjyG4gI4yMXt2VFags+1N3g==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.23.3", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.23.3" + } + }, + "@babel/plugin-transform-object-super": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.23.3.tgz", + "integrity": "sha512-BwQ8q0x2JG+3lxCVFohg+KbQM7plfpBwThdW9A6TMtWwLsbDA01Ek2Zb/AgDN39BiZsExm4qrXxjk+P1/fzGrA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.20" + } + }, + "@babel/plugin-transform-optional-catch-binding": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.23.4.tgz", + "integrity": "sha512-XIq8t0rJPHf6Wvmbn9nFxU6ao4c7WhghTR5WyV8SrJfUFzyxhCm4nhC+iAp3HFhbAKLfYpgzhJ6t4XCtVwqO5A==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + } + }, + "@babel/plugin-transform-optional-chaining": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.23.4.tgz", + "integrity": "sha512-ZU8y5zWOfjM5vZ+asjgAPwDaBjJzgufjES89Rs4Lpq63O300R/kOz30WCLo6BxxX6QVEilwSlpClnG5cZaikTA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + } + }, + "@babel/plugin-transform-parameters": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.23.3.tgz", + "integrity": "sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-private-methods": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.23.3.tgz", + "integrity": "sha512-UzqRcRtWsDMTLrRWFvUBDwmw06tCQH9Rl1uAjfh6ijMSmGYQ+fpdB+cnqRC8EMh5tuuxSv0/TejGL+7vyj+50g==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-private-property-in-object": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.23.4.tgz", + "integrity": "sha512-9G3K1YqTq3F4Vt88Djx1UZ79PDyj+yKRnUy7cZGSMe+a7jkwD259uKKuUzQlPkGam7R+8RJwh5z4xO27fA1o2A==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + } + }, + "@babel/plugin-transform-property-literals": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.23.3.tgz", + "integrity": "sha512-jR3Jn3y7cZp4oEWPFAlRsSWjxKe4PZILGBSd4nis1TsC5qeSpb+nrtihJuDhNI7QHiVbUaiXa0X2RZY3/TI6Nw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-regenerator": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.23.3.tgz", + "integrity": "sha512-KP+75h0KghBMcVpuKisx3XTu9Ncut8Q8TuvGO4IhY+9D5DFEckQefOuIsB/gQ2tG71lCke4NMrtIPS8pOj18BQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "regenerator-transform": "^0.15.2" + } + }, + "@babel/plugin-transform-reserved-words": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.23.3.tgz", + "integrity": "sha512-QnNTazY54YqgGxwIexMZva9gqbPa15t/x9VS+0fsEFWplwVpXYZivtgl43Z1vMpc1bdPP2PP8siFeVcnFvA3Cg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-runtime": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.23.6.tgz", + "integrity": "sha512-kF1Zg62aPseQ11orDhFRw+aPG/eynNQtI+TyY+m33qJa2cJ5EEvza2P2BNTIA9E5MyqFABHEyY6CPHwgdy9aNg==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "babel-plugin-polyfill-corejs2": "^0.4.6", + "babel-plugin-polyfill-corejs3": "^0.8.5", + "babel-plugin-polyfill-regenerator": "^0.5.3", + "semver": "^7.5.2" + } + }, + "@babel/plugin-transform-shorthand-properties": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.23.3.tgz", + "integrity": "sha512-ED2fgqZLmexWiN+YNFX26fx4gh5qHDhn1O2gvEhreLW2iI63Sqm4llRLCXALKrCnbN4Jy0VcMQZl/SAzqug/jg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-spread": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.23.3.tgz", + "integrity": "sha512-VvfVYlrlBVu+77xVTOAoxQ6mZbnIq5FM0aGBSFEcIh03qHf+zNqA4DC/3XMUozTg7bZV3e3mZQ0i13VB6v5yUg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" + } + }, + "@babel/plugin-transform-sticky-regex": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.23.3.tgz", + "integrity": "sha512-HZOyN9g+rtvnOU3Yh7kSxXrKbzgrm5X4GncPY1QOquu7epga5MxKHVpYu2hvQnry/H+JjckSYRb93iNfsioAGg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-template-literals": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.23.3.tgz", + "integrity": "sha512-Flok06AYNp7GV2oJPZZcP9vZdszev6vPBkHLwxwSpaIqx75wn6mUd3UFWsSsA0l8nXAKkyCmL/sR02m8RYGeHg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-typeof-symbol": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.23.3.tgz", + "integrity": "sha512-4t15ViVnaFdrPC74be1gXBSMzXk3B4Us9lP7uLRQHTFpV5Dvt33pn+2MyyNxmN3VTTm3oTrZVMUmuw3oBnQ2oQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-unicode-escapes": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.23.3.tgz", + "integrity": "sha512-OMCUx/bU6ChE3r4+ZdylEqAjaQgHAgipgW8nsCfu5pGqDcFytVd91AwRvUJSBZDz0exPGgnjoqhgRYLRjFZc9Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-unicode-property-regex": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.23.3.tgz", + "integrity": "sha512-KcLIm+pDZkWZQAFJ9pdfmh89EwVfmNovFBcXko8szpBeF8z68kWIPeKlmSOkT9BXJxs2C0uk+5LxoxIv62MROA==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-unicode-regex": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.23.3.tgz", + "integrity": "sha512-wMHpNA4x2cIA32b/ci3AfwNgheiva2W0WUKWTK7vBHBhDKfPsc5cFGNWm69WBqpwd86u1qwZ9PWevKqm1A3yAw==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-unicode-sets-regex": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.23.3.tgz", + "integrity": "sha512-W7lliA/v9bNR83Qc3q1ip9CQMZ09CcHDbHfbLRDNuAhn1Mvkr1ZNF7hPmztMQvtTGVLJ9m8IZqWsTkXOml8dbw==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/preset-env": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.23.6.tgz", + "integrity": "sha512-2XPn/BqKkZCpzYhUUNZ1ssXw7DcXfKQEjv/uXZUXgaebCMYmkEsfZ2yY+vv+xtXv50WmL5SGhyB6/xsWxIvvOQ==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.23.5", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-option": "^7.23.5", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.23.3", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.23.3", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.23.3", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-import-assertions": "^7.23.3", + "@babel/plugin-syntax-import-attributes": "^7.23.3", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.23.3", + "@babel/plugin-transform-async-generator-functions": "^7.23.4", + "@babel/plugin-transform-async-to-generator": "^7.23.3", + "@babel/plugin-transform-block-scoped-functions": "^7.23.3", + "@babel/plugin-transform-block-scoping": "^7.23.4", + "@babel/plugin-transform-class-properties": "^7.23.3", + "@babel/plugin-transform-class-static-block": "^7.23.4", + "@babel/plugin-transform-classes": "^7.23.5", + "@babel/plugin-transform-computed-properties": "^7.23.3", + "@babel/plugin-transform-destructuring": "^7.23.3", + "@babel/plugin-transform-dotall-regex": "^7.23.3", + "@babel/plugin-transform-duplicate-keys": "^7.23.3", + "@babel/plugin-transform-dynamic-import": "^7.23.4", + "@babel/plugin-transform-exponentiation-operator": "^7.23.3", + "@babel/plugin-transform-export-namespace-from": "^7.23.4", + "@babel/plugin-transform-for-of": "^7.23.6", + "@babel/plugin-transform-function-name": "^7.23.3", + "@babel/plugin-transform-json-strings": "^7.23.4", + "@babel/plugin-transform-literals": "^7.23.3", + "@babel/plugin-transform-logical-assignment-operators": "^7.23.4", + "@babel/plugin-transform-member-expression-literals": "^7.23.3", + "@babel/plugin-transform-modules-amd": "^7.23.3", + "@babel/plugin-transform-modules-commonjs": "^7.23.3", + "@babel/plugin-transform-modules-systemjs": "^7.23.3", + "@babel/plugin-transform-modules-umd": "^7.23.3", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", + "@babel/plugin-transform-new-target": "^7.23.3", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.23.4", + "@babel/plugin-transform-numeric-separator": "^7.23.4", + "@babel/plugin-transform-object-rest-spread": "^7.23.4", + "@babel/plugin-transform-object-super": "^7.23.3", + "@babel/plugin-transform-optional-catch-binding": "^7.23.4", + "@babel/plugin-transform-optional-chaining": "^7.23.4", + "@babel/plugin-transform-parameters": "^7.23.3", + "@babel/plugin-transform-private-methods": "^7.23.3", + "@babel/plugin-transform-private-property-in-object": "^7.23.4", + "@babel/plugin-transform-property-literals": "^7.23.3", + "@babel/plugin-transform-regenerator": "^7.23.3", + "@babel/plugin-transform-reserved-words": "^7.23.3", + "@babel/plugin-transform-shorthand-properties": "^7.23.3", + "@babel/plugin-transform-spread": "^7.23.3", + "@babel/plugin-transform-sticky-regex": "^7.23.3", + "@babel/plugin-transform-template-literals": "^7.23.3", + "@babel/plugin-transform-typeof-symbol": "^7.23.3", + "@babel/plugin-transform-unicode-escapes": "^7.23.3", + "@babel/plugin-transform-unicode-property-regex": "^7.23.3", + "@babel/plugin-transform-unicode-regex": "^7.23.3", + "@babel/plugin-transform-unicode-sets-regex": "^7.23.3", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "babel-plugin-polyfill-corejs2": "^0.4.6", + "babel-plugin-polyfill-corejs3": "^0.8.5", + "babel-plugin-polyfill-regenerator": "^0.5.3", + "core-js-compat": "^3.31.0", + "semver": "^7.5.2" + } + }, + "@babel/preset-modules": { + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + } + }, + "@babel/register": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.22.15.tgz", + "integrity": "sha512-V3Q3EqoQdn65RCgTLwauZaTfd1ShhwPmbBv+1dkZV/HpCGMKVyn6oFcRlI7RaKqiDQjX2Qd3AuoEguBgdjIKlg==", + "dev": true, + "requires": { + "clone-deep": "^4.0.1", + "find-cache-dir": "^2.0.0", + "make-dir": "^2.1.0", + "pirates": "^4.0.5", + "source-map-support": "^0.5.16" + } + }, + "@babel/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==", + "dev": true + }, + "@babel/runtime": { + "version": "7.17.8", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@babel/template": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" + } + }, + "@babel/traverse": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.6.tgz", + "integrity": "sha512-czastdK1e8YByZqezMPFiZ8ahwVMh/ESl9vPgvgdB9AmFMGP5jfpFax74AQgl5zj4XHzqeYAg2l8PuUeRS1MgQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.6", + "@babel/types": "^7.23.6", + "debug": "^4.3.1", + "globals": "^11.1.0" + } + }, + "@babel/types": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.6.tgz", + "integrity": "sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.23.4", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + } + }, + "@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "@colors/colors": { + "version": "1.5.0" + }, + "@dabh/diagnostics": { + "version": "2.0.3", + "requires": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, + "@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^3.3.0" + } + }, + "@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true + }, + "@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "requires": { + "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^9.6.0", "globals": "^13.19.0", @@ -11963,147 +14259,519 @@ "strip-json-comments": "^3.1.1" }, "dependencies": { - "globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "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==", + "dev": true + } + } + }, + "@eslint/js": { + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.55.0.tgz", + "integrity": "sha512-qQfo2mxH5yVom1kacMtZZJFVdW+E70mqHMJvVg6WTLo+VBuQJ4TojZlfWBjK0ve5BdEeNAVxOsl/nvNMpJOaJA==", + "dev": true + }, + "@humanwhocodes/config-array": { + "version": "0.11.13", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", + "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^2.0.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + } + }, + "@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true + }, + "@humanwhocodes/object-schema": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", + "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", + "dev": true + }, + "@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "dependencies": { + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, "requires": { - "type-fest": "^0.20.2" + "sprintf-js": "~1.0.2" } }, - "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==", + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "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, + "requires": { + "p-try": "^2.0.0" + } + }, + "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==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + } + } + }, + "@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true + }, + "@jest/console": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-28.1.3.tgz", + "integrity": "sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw==", + "dev": true, + "requires": { + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^28.1.3", + "jest-util": "^28.1.3", + "slash": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@jest/core": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-28.1.3.tgz", + "integrity": "sha512-CIKBrlaKOzA7YG19BEqCw3SLIsEwjZkeJzf5bdooVnW4bH5cktqe3JX+G2YV1aK5vP8N9na1IGWFzYaTp6k6NA==", + "dev": true, + "requires": { + "@jest/console": "^28.1.3", + "@jest/reporters": "^28.1.3", + "@jest/test-result": "^28.1.3", + "@jest/transform": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^28.1.3", + "jest-config": "^28.1.3", + "jest-haste-map": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-regex-util": "^28.0.2", + "jest-resolve": "^28.1.3", + "jest-resolve-dependencies": "^28.1.3", + "jest-runner": "^28.1.3", + "jest-runtime": "^28.1.3", + "jest-snapshot": "^28.1.3", + "jest-util": "^28.1.3", + "jest-validate": "^28.1.3", + "jest-watcher": "^28.1.3", + "micromatch": "^4.0.4", + "pretty-format": "^28.1.3", + "rimraf": "^3.0.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } } } }, - "@eslint/js": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.55.0.tgz", - "integrity": "sha512-qQfo2mxH5yVom1kacMtZZJFVdW+E70mqHMJvVg6WTLo+VBuQJ4TojZlfWBjK0ve5BdEeNAVxOsl/nvNMpJOaJA==", - "dev": true + "@jest/environment": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-28.1.3.tgz", + "integrity": "sha512-1bf40cMFTEkKyEf585R9Iz1WayDjHoHqvts0XFYEqyKM3cFWDpeMoqKKTAF9LSYQModPUlh8FKptoM2YcMWAXA==", + "dev": true, + "requires": { + "@jest/fake-timers": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "jest-mock": "^28.1.3" + } }, - "@humanwhocodes/config-array": { - "version": "0.11.13", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", - "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", + "@jest/expect": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-28.1.3.tgz", + "integrity": "sha512-lzc8CpUbSoE4dqT0U+g1qODQjBRHPpCPXissXD4mS9+sWQdmmpeJ9zSH1rS1HEkrsMN0fb7nKrJ9giAR1d3wBw==", "dev": true, "requires": { - "@humanwhocodes/object-schema": "^2.0.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" + "expect": "^28.1.3", + "jest-snapshot": "^28.1.3" } }, - "@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true + "@jest/expect-utils": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-28.1.3.tgz", + "integrity": "sha512-wvbi9LUrHJLn3NlDW6wF2hvIMtd4JUl2QNVrjq+IBSHirgfrR3o9RnVtxzdEGO2n9JyIWwHnLfby5KzqBGg2YA==", + "dev": true, + "requires": { + "jest-get-type": "^28.0.2" + } }, - "@humanwhocodes/object-schema": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", - "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", - "dev": true + "@jest/fake-timers": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-28.1.3.tgz", + "integrity": "sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw==", + "dev": true, + "requires": { + "@jest/types": "^28.1.3", + "@sinonjs/fake-timers": "^9.1.2", + "@types/node": "*", + "jest-message-util": "^28.1.3", + "jest-mock": "^28.1.3", + "jest-util": "^28.1.3" + } }, - "@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "@jest/globals": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-28.1.3.tgz", + "integrity": "sha512-XFU4P4phyryCXu1pbcqMO0GSQcYe1IsalYCDzRNyhetyeyxMcIxa11qPNDpVNLeretItNqEmYYQn1UYz/5x1NA==", "dev": true, "requires": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" + "@jest/environment": "^28.1.3", + "@jest/expect": "^28.1.3", + "@jest/types": "^28.1.3" + } + }, + "@jest/reporters": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-28.1.3.tgz", + "integrity": "sha512-JuAy7wkxQZVNU/V6g9xKzCGC5LVXx9FDcABKsSXp5MiKPEE2144a/vXTEDoyzjUpZKfVwp08Wqg5A4WfTMAzjg==", + "dev": true, + "requires": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^28.1.3", + "@jest/test-result": "^28.1.3", + "@jest/transform": "^28.1.3", + "@jest/types": "^28.1.3", + "@jridgewell/trace-mapping": "^0.3.13", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^28.1.3", + "jest-util": "^28.1.3", + "jest-worker": "^28.1.3", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^9.0.1" }, "dependencies": { - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "sprintf-js": "~1.0.2" + "color-convert": "^2.0.1" } }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" } }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "color-name": "~1.1.4" } }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, - "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, - "requires": { - "p-try": "^2.0.0" - } + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true }, - "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==", + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { - "p-limit": "^2.2.0" + "has-flag": "^4.0.0" } - }, - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true } } }, - "@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true + "@jest/schemas": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", + "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", + "dev": true, + "requires": { + "@sinclair/typebox": "^0.24.1" + } }, - "@jest/console": { + "@jest/source-map": { + "version": "28.1.2", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-28.1.2.tgz", + "integrity": "sha512-cV8Lx3BeStJb8ipPHnqVw/IM2VCMWO3crWZzYodSIkxXnRcXJipCdx1JCK0K5MsJJouZQTH73mzf4vgxRaH9ww==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.13", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + } + }, + "@jest/test-result": { "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-28.1.3.tgz", - "integrity": "sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw==", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-28.1.3.tgz", + "integrity": "sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg==", "dev": true, "requires": { + "@jest/console": "^28.1.3", "@jest/types": "^28.1.3", - "@types/node": "*", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + } + }, + "@jest/test-sequencer": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-28.1.3.tgz", + "integrity": "sha512-NIMPEqqa59MWnDi1kvXXpYbqsfQmSJsIbnd85mdVGkiDfQ9WQQTXOLsvISUfonmnBT+w85WEgneCigEEdHDFxw==", + "dev": true, + "requires": { + "@jest/test-result": "^28.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^28.1.3", + "slash": "^3.0.0" + }, + "dependencies": { + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + } + } + }, + "@jest/transform": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-28.1.3.tgz", + "integrity": "sha512-u5dT5di+oFI6hfcLOHGTAfmUxFRrjK+vnaP0kkVow9Md/M7V/MxqQMOz/VV25UZO8pzeA9PjfTpOu6BDuwSPQA==", + "dev": true, + "requires": { + "@babel/core": "^7.11.6", + "@jest/types": "^28.1.3", + "@jridgewell/trace-mapping": "^0.3.13", + "babel-plugin-istanbul": "^6.1.1", "chalk": "^4.0.0", - "jest-message-util": "^28.1.3", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^28.1.3", + "jest-regex-util": "^28.0.2", "jest-util": "^28.1.3", - "slash": "^3.0.0" + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.1" }, "dependencies": { "ansi-styles": { @@ -12140,6 +14808,12 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -12163,41 +14837,18 @@ } } }, - "@jest/core": { + "@jest/types": { "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-28.1.3.tgz", - "integrity": "sha512-CIKBrlaKOzA7YG19BEqCw3SLIsEwjZkeJzf5bdooVnW4bH5cktqe3JX+G2YV1aK5vP8N9na1IGWFzYaTp6k6NA==", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", + "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", "dev": true, "requires": { - "@jest/console": "^28.1.3", - "@jest/reporters": "^28.1.3", - "@jest/test-result": "^28.1.3", - "@jest/transform": "^28.1.3", - "@jest/types": "^28.1.3", + "@jest/schemas": "^28.1.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^28.1.3", - "jest-config": "^28.1.3", - "jest-haste-map": "^28.1.3", - "jest-message-util": "^28.1.3", - "jest-regex-util": "^28.0.2", - "jest-resolve": "^28.1.3", - "jest-resolve-dependencies": "^28.1.3", - "jest-runner": "^28.1.3", - "jest-runtime": "^28.1.3", - "jest-snapshot": "^28.1.3", - "jest-util": "^28.1.3", - "jest-validate": "^28.1.3", - "jest-watcher": "^28.1.3", - "micromatch": "^4.0.4", - "pretty-format": "^28.1.3", - "rimraf": "^3.0.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" }, "dependencies": { "ansi-styles": { @@ -12240,12 +14891,6 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -12257,474 +14902,832 @@ } } }, - "@jest/environment": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-28.1.3.tgz", - "integrity": "sha512-1bf40cMFTEkKyEf585R9Iz1WayDjHoHqvts0XFYEqyKM3cFWDpeMoqKKTAF9LSYQModPUlh8FKptoM2YcMWAXA==", + "@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", "dev": true, "requires": { - "@jest/fake-timers": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/node": "*", - "jest-mock": "^28.1.3" + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" } }, - "@jest/expect": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-28.1.3.tgz", - "integrity": "sha512-lzc8CpUbSoE4dqT0U+g1qODQjBRHPpCPXissXD4mS9+sWQdmmpeJ9zSH1rS1HEkrsMN0fb7nKrJ9giAR1d3wBw==", + "@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "dev": true + }, + "@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true + }, + "@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==", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", + "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", "dev": true, "requires": { - "expect": "^28.1.3", - "jest-snapshot": "^28.1.3" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "@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 + }, + "@nicolo-ribaudo/chokidar-2": { + "version": "2.1.8-no-fsevents.3", + "dev": true, + "optional": true + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@sinclair/typebox": { + "version": "0.24.51", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", + "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==", + "dev": true + }, + "@sinonjs/commons": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", + "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz", + "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.0" + } + }, + "@sinonjs/samsam": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.0.tgz", + "integrity": "sha512-Bp8KUVlLp8ibJZrnvq2foVhP0IVX2CIprMJPK0vqGqgrDa0OHVKeZyBykqskkrdxV6yKBPmGasO8LVjAKR3Gew==", + "requires": { + "@sinonjs/commons": "^2.0.0", + "lodash.get": "^4.4.2", + "type-detect": "^4.0.8" + }, + "dependencies": { + "@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "requires": { + "type-detect": "4.0.8" + } + } + } + }, + "@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==" + }, + "@smithy/abort-controller": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-2.1.3.tgz", + "integrity": "sha512-c2aYH2Wu1RVE3rLlVgg2kQOBJGM0WbjReQi5DnPTm2Zb7F0gk7J2aeQeaX2u/lQZoHl6gv8Oac7mt9alU3+f4A==", + "requires": { + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } + }, + "@smithy/config-resolver": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-2.1.4.tgz", + "integrity": "sha512-AW2WUZmBAzgO3V3ovKtsUbI3aBNMeQKFDumoqkNxaVDWF/xfnxAWqBKDr/NuG7c06N2Rm4xeZLPiJH/d+na0HA==", + "requires": { + "@smithy/node-config-provider": "^2.2.4", + "@smithy/types": "^2.10.1", + "@smithy/util-config-provider": "^2.2.1", + "@smithy/util-middleware": "^2.1.3", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } + }, + "@smithy/core": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-1.3.5.tgz", + "integrity": "sha512-Rrc+e2Jj6Gu7Xbn0jvrzZlSiP2CZocIOfZ9aNUA82+1sa6GBnxqL9+iZ9EKHeD9aqD1nU8EK4+oN2EiFpSv7Yw==", + "requires": { + "@smithy/middleware-endpoint": "^2.4.4", + "@smithy/middleware-retry": "^2.1.4", + "@smithy/middleware-serde": "^2.1.3", + "@smithy/protocol-http": "^3.2.1", + "@smithy/smithy-client": "^2.4.2", + "@smithy/types": "^2.10.1", + "@smithy/util-middleware": "^2.1.3", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } + }, + "@smithy/credential-provider-imds": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-2.2.4.tgz", + "integrity": "sha512-DdatjmBZQnhGe1FhI8gO98f7NmvQFSDiZTwC3WMvLTCKQUY+Y1SVkhJqIuLu50Eb7pTheoXQmK+hKYUgpUWsNA==", + "requires": { + "@smithy/node-config-provider": "^2.2.4", + "@smithy/property-provider": "^2.1.3", + "@smithy/types": "^2.10.1", + "@smithy/url-parser": "^2.1.3", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } + }, + "@smithy/eventstream-codec": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-2.1.3.tgz", + "integrity": "sha512-rGlCVuwSDv6qfKH4/lRxFjcZQnIE0LZ3D4lkMHg7ZSltK9rA74r0VuGSvWVQ4N/d70VZPaniFhp4Z14QYZsa+A==", + "requires": { + "@aws-crypto/crc32": "3.0.0", + "@smithy/types": "^2.10.1", + "@smithy/util-hex-encoding": "^2.1.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } + }, + "@smithy/fetch-http-handler": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-2.4.3.tgz", + "integrity": "sha512-Fn/KYJFo6L5I4YPG8WQb2hOmExgRmNpVH5IK2zU3JKrY5FKW7y9ar5e0BexiIC9DhSKqKX+HeWq/Y18fq7Dkpw==", + "requires": { + "@smithy/protocol-http": "^3.2.1", + "@smithy/querystring-builder": "^2.1.3", + "@smithy/types": "^2.10.1", + "@smithy/util-base64": "^2.1.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } + }, + "@smithy/hash-node": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-2.1.3.tgz", + "integrity": "sha512-FsAPCUj7VNJIdHbSxMd5uiZiF20G2zdSDgrgrDrHqIs/VMxK85Vqk5kMVNNDMCZmMezp6UKnac0B4nAyx7HJ9g==", + "requires": { + "@smithy/types": "^2.10.1", + "@smithy/util-buffer-from": "^2.1.1", + "@smithy/util-utf8": "^2.1.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } + }, + "@smithy/invalid-dependency": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-2.1.3.tgz", + "integrity": "sha512-wkra7d/G4CbngV4xsjYyAYOvdAhahQje/WymuQdVEnXFExJopEu7fbL5AEAlBPgWHXwu94VnCSG00gVzRfExyg==", + "requires": { + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } + }, + "@smithy/is-array-buffer": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.1.1.tgz", + "integrity": "sha512-xozSQrcUinPpNPNPds4S7z/FakDTh1MZWtRP/2vQtYB/u3HYrX2UXuZs+VhaKBd6Vc7g2XPr2ZtwGBNDN6fNKQ==", + "requires": { + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } + }, + "@smithy/middleware-content-length": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-2.1.3.tgz", + "integrity": "sha512-aJduhkC+dcXxdnv5ZpM3uMmtGmVFKx412R1gbeykS5HXDmRU6oSsyy2SoHENCkfOGKAQOjVE2WVqDJibC0d21g==", + "requires": { + "@smithy/protocol-http": "^3.2.1", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } } }, - "@jest/expect-utils": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-28.1.3.tgz", - "integrity": "sha512-wvbi9LUrHJLn3NlDW6wF2hvIMtd4JUl2QNVrjq+IBSHirgfrR3o9RnVtxzdEGO2n9JyIWwHnLfby5KzqBGg2YA==", - "dev": true, + "@smithy/middleware-endpoint": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-2.4.4.tgz", + "integrity": "sha512-4yjHyHK2Jul4JUDBo2sTsWY9UshYUnXeb/TAK/MTaPEb8XQvDmpwSFnfIRDU45RY1a6iC9LCnmJNg/yHyfxqkw==", "requires": { - "jest-get-type": "^28.0.2" + "@smithy/middleware-serde": "^2.1.3", + "@smithy/node-config-provider": "^2.2.4", + "@smithy/shared-ini-file-loader": "^2.3.4", + "@smithy/types": "^2.10.1", + "@smithy/url-parser": "^2.1.3", + "@smithy/util-middleware": "^2.1.3", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } } }, - "@jest/fake-timers": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-28.1.3.tgz", - "integrity": "sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw==", - "dev": true, + "@smithy/middleware-retry": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-2.1.4.tgz", + "integrity": "sha512-Cyolv9YckZTPli1EkkaS39UklonxMd08VskiuMhURDjC0HHa/AD6aK/YoD21CHv9s0QLg0WMLvk9YeLTKkXaFQ==", + "requires": { + "@smithy/node-config-provider": "^2.2.4", + "@smithy/protocol-http": "^3.2.1", + "@smithy/service-error-classification": "^2.1.3", + "@smithy/smithy-client": "^2.4.2", + "@smithy/types": "^2.10.1", + "@smithy/util-middleware": "^2.1.3", + "@smithy/util-retry": "^2.1.3", + "tslib": "^2.5.0", + "uuid": "^8.3.2" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } + }, + "@smithy/middleware-serde": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-2.1.3.tgz", + "integrity": "sha512-s76LId+TwASrHhUa9QS4k/zeXDUAuNuddKklQzRgumbzge5BftVXHXIqL4wQxKGLocPwfgAOXWx+HdWhQk9hTg==", "requires": { - "@jest/types": "^28.1.3", - "@sinonjs/fake-timers": "^9.1.2", - "@types/node": "*", - "jest-message-util": "^28.1.3", - "jest-mock": "^28.1.3", - "jest-util": "^28.1.3" + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } } }, - "@jest/globals": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-28.1.3.tgz", - "integrity": "sha512-XFU4P4phyryCXu1pbcqMO0GSQcYe1IsalYCDzRNyhetyeyxMcIxa11qPNDpVNLeretItNqEmYYQn1UYz/5x1NA==", - "dev": true, + "@smithy/middleware-stack": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-2.1.3.tgz", + "integrity": "sha512-opMFufVQgvBSld/b7mD7OOEBxF6STyraVr1xel1j0abVILM8ALJvRoFbqSWHGmaDlRGIiV9Q5cGbWi0sdiEaLQ==", "requires": { - "@jest/environment": "^28.1.3", - "@jest/expect": "^28.1.3", - "@jest/types": "^28.1.3" + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } } }, - "@jest/reporters": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-28.1.3.tgz", - "integrity": "sha512-JuAy7wkxQZVNU/V6g9xKzCGC5LVXx9FDcABKsSXp5MiKPEE2144a/vXTEDoyzjUpZKfVwp08Wqg5A4WfTMAzjg==", - "dev": true, + "@smithy/node-config-provider": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-2.2.4.tgz", + "integrity": "sha512-nqazHCp8r4KHSFhRQ+T0VEkeqvA0U+RhehBSr1gunUuNW3X7j0uDrWBxB2gE9eutzy6kE3Y7L+Dov/UXT871vg==", "requires": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^28.1.3", - "@jest/test-result": "^28.1.3", - "@jest/transform": "^28.1.3", - "@jest/types": "^28.1.3", - "@jridgewell/trace-mapping": "^0.3.13", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^5.1.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^28.1.3", - "jest-util": "^28.1.3", - "jest-worker": "^28.1.3", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "terminal-link": "^2.0.0", - "v8-to-istanbul": "^9.0.1" + "@smithy/property-provider": "^2.1.3", + "@smithy/shared-ini-file-loader": "^2.3.4", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" }, "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" } } }, - "@jest/schemas": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", - "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", - "dev": true, + "@smithy/node-http-handler": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-2.4.1.tgz", + "integrity": "sha512-HCkb94soYhJMxPCa61wGKgmeKpJ3Gftx1XD6bcWEB2wMV1L9/SkQu/6/ysKBnbOzWRE01FGzwrTxucHypZ8rdg==", "requires": { - "@sinclair/typebox": "^0.24.1" + "@smithy/abort-controller": "^2.1.3", + "@smithy/protocol-http": "^3.2.1", + "@smithy/querystring-builder": "^2.1.3", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } } }, - "@jest/source-map": { - "version": "28.1.2", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-28.1.2.tgz", - "integrity": "sha512-cV8Lx3BeStJb8ipPHnqVw/IM2VCMWO3crWZzYodSIkxXnRcXJipCdx1JCK0K5MsJJouZQTH73mzf4vgxRaH9ww==", - "dev": true, + "@smithy/property-provider": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-2.1.3.tgz", + "integrity": "sha512-bMz3se+ySKWNrgm7eIiQMa2HO/0fl2D0HvLAdg9pTMcpgp4SqOAh6bz7Ik6y7uQqSrk4rLjIKgbQ6yzYgGehCQ==", "requires": { - "@jridgewell/trace-mapping": "^0.3.13", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } } }, - "@jest/test-result": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-28.1.3.tgz", - "integrity": "sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg==", - "dev": true, + "@smithy/protocol-http": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-3.2.1.tgz", + "integrity": "sha512-KLrQkEw4yJCeAmAH7hctE8g9KwA7+H2nSJwxgwIxchbp/L0B5exTdOQi9D5HinPLlothoervGmhpYKelZ6AxIA==", "requires": { - "@jest/console": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } } }, - "@jest/test-sequencer": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-28.1.3.tgz", - "integrity": "sha512-NIMPEqqa59MWnDi1kvXXpYbqsfQmSJsIbnd85mdVGkiDfQ9WQQTXOLsvISUfonmnBT+w85WEgneCigEEdHDFxw==", - "dev": true, + "@smithy/querystring-builder": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-2.1.3.tgz", + "integrity": "sha512-kFD3PnNqKELe6m9GRHQw/ftFFSZpnSeQD4qvgDB6BQN6hREHELSosVFUMPN4M3MDKN2jAwk35vXHLoDrNfKu0A==", "requires": { - "@jest/test-result": "^28.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^28.1.3", - "slash": "^3.0.0" + "@smithy/types": "^2.10.1", + "@smithy/util-uri-escape": "^2.1.1", + "tslib": "^2.5.0" }, "dependencies": { - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" } } }, - "@jest/transform": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-28.1.3.tgz", - "integrity": "sha512-u5dT5di+oFI6hfcLOHGTAfmUxFRrjK+vnaP0kkVow9Md/M7V/MxqQMOz/VV25UZO8pzeA9PjfTpOu6BDuwSPQA==", - "dev": true, + "@smithy/querystring-parser": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-2.1.3.tgz", + "integrity": "sha512-3+CWJoAqcBMR+yvz6D+Fc5VdoGFtfenW6wqSWATWajrRMGVwJGPT3Vy2eb2bnMktJc4HU4bpjeovFa566P3knQ==", "requires": { - "@babel/core": "^7.11.6", - "@jest/types": "^28.1.3", - "@jridgewell/trace-mapping": "^0.3.13", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^1.4.0", - "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^28.1.3", - "jest-regex-util": "^28.0.2", - "jest-util": "^28.1.3", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.1" + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" }, "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } + }, + "@smithy/service-error-classification": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-2.1.3.tgz", + "integrity": "sha512-iUrpSsem97bbXHHT/v3s7vaq8IIeMo6P6cXdeYHrx0wOJpMeBGQF7CB0mbJSiTm3//iq3L55JiEm8rA7CTVI8A==", + "requires": { + "@smithy/types": "^2.10.1" + } + }, + "@smithy/shared-ini-file-loader": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-2.3.4.tgz", + "integrity": "sha512-CiZmPg9GeDKbKmJGEFvJBsJcFnh0AQRzOtQAzj1XEa8N/0/uSN/v1LYzgO7ry8hhO8+9KB7+DhSW0weqBra4Aw==", + "requires": { + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" } } }, - "@jest/types": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", - "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", - "dev": true, + "@smithy/signature-v4": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-2.1.3.tgz", + "integrity": "sha512-Jq4iPPdCmJojZTsPePn4r1ULShh6ONkokLuxp1Lnk4Sq7r7rJp4HlA1LbPBq4bD64TIzQezIpr1X+eh5NYkNxw==", + "requires": { + "@smithy/eventstream-codec": "^2.1.3", + "@smithy/is-array-buffer": "^2.1.1", + "@smithy/types": "^2.10.1", + "@smithy/util-hex-encoding": "^2.1.1", + "@smithy/util-middleware": "^2.1.3", + "@smithy/util-uri-escape": "^2.1.1", + "@smithy/util-utf8": "^2.1.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } + }, + "@smithy/smithy-client": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-2.4.2.tgz", + "integrity": "sha512-ntAFYN51zu3N3mCd95YFcFi/8rmvm//uX+HnK24CRbI6k5Rjackn0JhgKz5zOx/tbNvOpgQIwhSX+1EvEsBLbA==", "requires": { - "@jest/schemas": "^28.1.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" + "@smithy/middleware-endpoint": "^2.4.4", + "@smithy/middleware-stack": "^2.1.3", + "@smithy/protocol-http": "^3.2.1", + "@smithy/types": "^2.10.1", + "@smithy/util-stream": "^2.1.3", + "tslib": "^2.5.0" }, "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" } } }, - "@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", - "dev": true, + "@smithy/types": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.10.1.tgz", + "integrity": "sha512-hjQO+4ru4cQ58FluQvKKiyMsFg0A6iRpGm2kqdH8fniyNd2WyanoOsYJfMX/IFLuLxEoW6gnRkNZy1y6fUUhtA==", "requires": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } } }, - "@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", - "dev": true + "@smithy/url-parser": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-2.1.3.tgz", + "integrity": "sha512-X1NRA4WzK/ihgyzTpeGvI9Wn45y8HmqF4AZ/FazwAv8V203Ex+4lXqcYI70naX9ETqbqKVzFk88W6WJJzCggTQ==", + "requires": { + "@smithy/querystring-parser": "^2.1.3", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } }, - "@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "dev": true + "@smithy/util-base64": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-2.1.1.tgz", + "integrity": "sha512-UfHVpY7qfF/MrgndI5PexSKVTxSZIdz9InghTFa49QOvuu9I52zLPLUHXvHpNuMb1iD2vmc6R+zbv/bdMipR/g==", + "requires": { + "@smithy/util-buffer-from": "^2.1.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } }, - "@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==", - "dev": true + "@smithy/util-body-length-browser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-2.1.1.tgz", + "integrity": "sha512-ekOGBLvs1VS2d1zM2ER4JEeBWAvIOUKeaFch29UjjJsxmZ/f0L3K3x0dEETgh3Q9bkZNHgT+rkdl/J/VUqSRag==", + "requires": { + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } }, - "@jridgewell/trace-mapping": { - "version": "0.3.20", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", - "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", - "dev": true, + "@smithy/util-body-length-node": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-2.2.1.tgz", + "integrity": "sha512-/ggJG+ta3IDtpNVq4ktmEUtOkH1LW64RHB5B0hcr5ZaWBmo96UX2cIOVbjCqqDickTXqBWZ4ZO0APuaPrD7Abg==", "requires": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } } }, - "@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 + "@smithy/util-buffer-from": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.1.1.tgz", + "integrity": "sha512-clhNjbyfqIv9Md2Mg6FffGVrJxw7bgK7s3Iax36xnfVj6cg0fUG7I4RH0XgXJF8bxi+saY5HR21g2UPKSxVCXg==", + "requires": { + "@smithy/is-array-buffer": "^2.1.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } }, - "@nicolo-ribaudo/chokidar-2": { - "version": "2.1.8-no-fsevents.3", - "dev": true, - "optional": true + "@smithy/util-config-provider": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-2.2.1.tgz", + "integrity": "sha512-50VL/tx9oYYcjJn/qKqNy7sCtpD0+s8XEBamIFo4mFFTclKMNp+rsnymD796uybjiIquB7VCB/DeafduL0y2kw==", + "requires": { + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "dev": true, + "@smithy/util-defaults-mode-browser": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-2.1.4.tgz", + "integrity": "sha512-J6XAVY+/g7jf03QMnvqPyU+8jqGrrtXoKWFVOS+n1sz0Lg8HjHJ1ANqaDN+KTTKZRZlvG8nU5ZrJOUL6VdwgcQ==", "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" + "@smithy/property-provider": "^2.1.3", + "@smithy/smithy-client": "^2.4.2", + "@smithy/types": "^2.10.1", + "bowser": "^2.11.0", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } } }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "dev": true + "@smithy/util-defaults-mode-node": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-2.2.3.tgz", + "integrity": "sha512-ttUISrv1uVOjTlDa3nznX33f0pthoUlP+4grhTvOzcLhzArx8qHB94/untGACOG3nlf8vU20nI2iWImfzoLkYA==", + "requires": { + "@smithy/config-resolver": "^2.1.4", + "@smithy/credential-provider-imds": "^2.2.4", + "@smithy/node-config-provider": "^2.2.4", + "@smithy/property-provider": "^2.1.3", + "@smithy/smithy-client": "^2.4.2", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "dev": true, + "@smithy/util-endpoints": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-1.1.4.tgz", + "integrity": "sha512-/qAeHmK5l4yQ4/bCIJ9p49wDe9rwWtOzhPHblu386fwPNT3pxmodgcs9jDCV52yK9b4rB8o9Sj31P/7Vzka1cg==", "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" + "@smithy/node-config-provider": "^2.2.4", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } } }, - "@sinclair/typebox": { - "version": "0.24.51", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", - "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==", - "dev": true + "@smithy/util-hex-encoding": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-2.1.1.tgz", + "integrity": "sha512-3UNdP2pkYUUBGEXzQI9ODTDK+Tcu1BlCyDBaRHwyxhA+8xLP8agEKQq4MGmpjqb4VQAjq9TwlCQX0kP6XDKYLg==", + "requires": { + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } }, - "@sinonjs/commons": { - "version": "1.8.6", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", - "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", - "dev": true, + "@smithy/util-middleware": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-2.1.3.tgz", + "integrity": "sha512-/+2fm7AZ2ozl5h8wM++ZP0ovE9/tiUUAHIbCfGfb3Zd3+Dyk17WODPKXBeJ/TnK5U+x743QmA0xHzlSm8I/qhw==", "requires": { - "type-detect": "4.0.8" + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } } }, - "@sinonjs/fake-timers": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz", - "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==", - "dev": true, + "@smithy/util-retry": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-2.1.3.tgz", + "integrity": "sha512-Kbvd+GEMuozbNUU3B89mb99tbufwREcyx2BOX0X2+qHjq6Gvsah8xSDDgxISDwcOHoDqUWO425F0Uc/QIRhYkg==", "requires": { - "@sinonjs/commons": "^1.7.0" + "@smithy/service-error-classification": "^2.1.3", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } } }, - "@sinonjs/samsam": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.0.tgz", - "integrity": "sha512-Bp8KUVlLp8ibJZrnvq2foVhP0IVX2CIprMJPK0vqGqgrDa0OHVKeZyBykqskkrdxV6yKBPmGasO8LVjAKR3Gew==", + "@smithy/util-stream": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-2.1.3.tgz", + "integrity": "sha512-HvpEQbP8raTy9n86ZfXiAkf3ezp1c3qeeO//zGqwZdrfaoOpGKQgF2Sv1IqZp7wjhna7pvczWaGUHjcOPuQwKw==", + "requires": { + "@smithy/fetch-http-handler": "^2.4.3", + "@smithy/node-http-handler": "^2.4.1", + "@smithy/types": "^2.10.1", + "@smithy/util-base64": "^2.1.1", + "@smithy/util-buffer-from": "^2.1.1", + "@smithy/util-hex-encoding": "^2.1.1", + "@smithy/util-utf8": "^2.1.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } + }, + "@smithy/util-uri-escape": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-2.1.1.tgz", + "integrity": "sha512-saVzI1h6iRBUVSqtnlOnc9ssU09ypo7n+shdQ8hBTZno/9rZ3AuRYvoHInV57VF7Qn7B+pFJG7qTzFiHxWlWBw==", "requires": { - "@sinonjs/commons": "^2.0.0", - "lodash.get": "^4.4.2", - "type-detect": "^4.0.8" + "tslib": "^2.5.0" }, "dependencies": { - "@sinonjs/commons": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", - "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", - "requires": { - "type-detect": "4.0.8" - } + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" } } }, - "@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==" + "@smithy/util-utf8": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.1.1.tgz", + "integrity": "sha512-BqTpzYEcUMDwAKr7/mVRUtHDhs6ZoXDi9NypMvMfOr/+u1NW7JgqodPDECiiLboEm6bobcPcECxzjtQh865e9A==", + "requires": { + "@smithy/util-buffer-from": "^2.1.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } + }, + "@smithy/util-waiter": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-2.1.3.tgz", + "integrity": "sha512-3R0wNFAQQoH9e4m+bVLDYNOst2qNxtxFgq03WoNHWTBOqQT3jFnOBRj1W51Rf563xDA5kwqjziksxn6RKkHB+Q==", + "requires": { + "@smithy/abort-controller": "^2.1.3", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } }, "@types/babel__core": { "version": "7.20.5", @@ -13279,6 +16282,11 @@ } } }, + "bowser": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", + "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==" + }, "brace-expansion": { "version": "1.1.11", "dev": true, @@ -14216,6 +17224,14 @@ "version": "2.1.1", "dev": true }, + "fast-xml-parser": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz", + "integrity": "sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==", + "requires": { + "strnum": "^1.0.5" + } + }, "fastq": { "version": "1.13.0", "dev": true, @@ -16429,10 +19445,18 @@ "brace-expansion": "^1.1.7" } }, + "mnemonist": { + "version": "0.38.3", + "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.38.3.tgz", + "integrity": "sha512-2K9QYubXx/NAjv4VLq1d1Ly8pWNC5L3BrixtdkyTegXWJIqY+zLNDhhX/A+ZwWt70tB1S8H4BE8FLYEFyNoOBw==", + "requires": { + "obliterator": "^1.6.1" + } + }, "moment": { - "version": "2.29.4", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", - "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==" + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==" }, "moment-timezone": { "version": "0.5.43", @@ -16640,6 +19664,11 @@ "es-abstract": "^1.19.1" } }, + "obliterator": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-1.6.1.tgz", + "integrity": "sha512-9WXswnqINnnhOG/5SLimUlzuU1hFJUc8zkwyD59Sd+dPOMf05PmnYG/d6Q7HZ+KmgkZJa1PxRso6QdM3sTNHig==" + }, "on-finished": { "version": "2.3.0", "requires": { @@ -17560,6 +20589,11 @@ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true }, + "strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==" + }, "superagent": { "version": "8.0.8", "resolved": "https://registry.npmjs.org/superagent/-/superagent-8.0.8.tgz", @@ -17729,8 +20763,7 @@ "version": "1.3.0" }, "tslib": { - "version": "1.14.1", - "dev": true + "version": "1.14.1" }, "tsutils": { "version": "3.21.0", diff --git a/package.json b/package.json index bfcb9c4b..f7059dc7 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,8 @@ "typescript": "^4.6.2" }, "dependencies": { + "@aws-sdk/client-dynamodb": "^3.525.0", + "@aws-sdk/lib-dynamodb": "^3.525.0", "@babel/runtime": "^7.17.8", "async-local-storage": "^2.3.1", "aws-sdk": "^2.1358.0", @@ -55,6 +57,7 @@ "helmet": "^6.0.0", "lodash.clonedeep": "^4.5.0", "lodash.merge": "^4.6.2", + "moment": "^2.30.1", "pg": "^8.7.3", "pg-hstore": "^2.3.4", "sequelize": "^6.33.0", @@ -75,6 +78,6 @@ "overrides": { "json5": "2.2.2", "superagent": "8.0.8", - "semver": "^7.5.2" + "semver": "^7.5.2" } } diff --git a/scripts/create-dynamodb-table.sh b/scripts/create-dynamodb-table.sh new file mode 100755 index 00000000..0bb2dfcd --- /dev/null +++ b/scripts/create-dynamodb-table.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +export AWS_SECRET_ACCESS_KEY=test_key +export AWS_ACCESS_KEY_ID=test_id +export AWS_REGION=eu-west-2 + +set -e +echo Creating dynamodb table in localstack... +cd "$(dirname "$0")" +awslocal --region eu-west-2 dynamodb create-table --cli-input-json file://ehr-transfer-tracker-db.json +set +e \ No newline at end of file diff --git a/scripts/ehr-transfer-tracker-db.json b/scripts/ehr-transfer-tracker-db.json new file mode 100644 index 00000000..47b63442 --- /dev/null +++ b/scripts/ehr-transfer-tracker-db.json @@ -0,0 +1,59 @@ +{ + "TableName": "local-ehr-transfer-tracker", + "KeySchema": [ + { + "AttributeName": "InboundConversationId", + "KeyType": "HASH" + }, + { + "AttributeName": "Layer", + "KeyType": "RANGE" + } + ], + "AttributeDefinitions": [ + { + "AttributeName": "InboundConversationId", + "AttributeType": "S" + }, + { + "AttributeName": "Layer", + "AttributeType": "S" + }, + { + "AttributeName": "NhsNumber", + "AttributeType": "S" + }, + { + "AttributeName": "OutboundConversationId", + "AttributeType": "S" + } + ], + "GlobalSecondaryIndexes": [ + { + "IndexName": "OutboundConversationIdSecondaryIndex", + "KeySchema": [ + { + "AttributeName": "OutboundConversationId", + "KeyType": "HASH" + } + ], + "Projection": { + "ProjectionType": "ALL" + } + }, + { + "IndexName": "NhsNumberSecondaryIndex", + "KeySchema": [ + { + "AttributeName": "NhsNumber", + "KeyType": "HASH" + } + ], + "Projection": { + "ProjectionType": "ALL" + } + } + ], + "BillingMode": "PAY_PER_REQUEST" +} + diff --git a/src/services/database/__tests__/dynamo-transfer-tracker.js b/src/services/database/__tests__/dynamo-transfer-tracker.js new file mode 100644 index 00000000..38485be2 --- /dev/null +++ b/src/services/database/__tests__/dynamo-transfer-tracker.js @@ -0,0 +1,15 @@ +import { CreateTableCommand, DynamoDBClient } from "@aws-sdk/client-dynamodb"; +import { EhrTransferTracker } from "../dynamo-transfer-tracker"; +import { v4 as uuid } from "uuid"; + +describe("test", () => { + it("create a table in local dynamo", async () => { + const db = new EhrTransferTracker(); + const ehrExtract = { + conversationId: "test-conv-id", + messageId: uuid(), + nhsNumber: "9000000001" + }; + await db.createCore(ehrExtract); + }); +}); \ No newline at end of file diff --git a/src/services/database/dynamo-transfer-tracker.js b/src/services/database/dynamo-transfer-tracker.js new file mode 100644 index 00000000..32930b63 --- /dev/null +++ b/src/services/database/dynamo-transfer-tracker.js @@ -0,0 +1,55 @@ +import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; +import { DynamoDBDocumentClient, TransactWriteCommand } from "@aws-sdk/lib-dynamodb"; +import moment from "moment"; + + +export class EhrTransferTracker { + constructor() { + const baseClient = new DynamoDBClient({ + endpoint: "http://localhost:4573/" + }); + + this.tableName = "local-ehr-transfer-tracker"; + this.client = DynamoDBDocumentClient.from(baseClient); + } + + async createCore(ehrExtract) { + const { conversationId, messageId, nhsNumber } = ehrExtract; + const timestamp = moment().toISOString(); + const conversation = { + InboundConversationId: conversationId, + Layer: "Conversation", + NhsNumber: nhsNumber, + CreatedAt: timestamp, + LastUpdatedAt: timestamp + }; + const core = { + InboundConversationId: conversationId, + Layer: "Core", + InboundMessageId: messageId, + CreatedAt: timestamp, + LastUpdatedAt: timestamp + }; + + const command = new TransactWriteCommand({ + TransactItems: [ + { + Put: { + TableName: this.tableName, + Item: conversation + } + }, + { + Put: { + TableName: this.tableName, + Item: core + } + } + ] + } + ); + + + await this.client.send(command); + } +} \ No newline at end of file From b71df2e478ce28aba437a704473ea2212da391c9 Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Mon, 4 Mar 2024 12:22:57 +0000 Subject: [PATCH 02/67] [PRMT-4565] add basic test for dynamodb client --- docker-compose-itest.yml | 48 ++++---- ...o-ehr-transfer-tracker.integration.test.js | 22 ++++ .../__tests__/dynamo-transfer-tracker.js | 15 --- .../database/dynamo-ehr-transfer-tracker.js | 104 ++++++++++++++++++ .../database/dynamo-transfer-tracker.js | 55 --------- tasks | 13 +++ 6 files changed, 163 insertions(+), 94 deletions(-) create mode 100644 src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js delete mode 100644 src/services/database/__tests__/dynamo-transfer-tracker.js create mode 100644 src/services/database/dynamo-ehr-transfer-tracker.js delete mode 100644 src/services/database/dynamo-transfer-tracker.js diff --git a/docker-compose-itest.yml b/docker-compose-itest.yml index 0d433fc8..09fa151c 100644 --- a/docker-compose-itest.yml +++ b/docker-compose-itest.yml @@ -3,8 +3,8 @@ services: default: links: - db:db -# - localstack:localstack - - localstack-dynamo:localstack-dynamo + - localstack:localstack +# - localstack-dynamo:localstack-dynamo ports: - "3000:3000" db: @@ -16,27 +16,27 @@ services: LC_ALL: C ports: - "5432:5432" -# localstack: -# image: localstack/localstack:0.10.9 -# environment: -# - SERVICES=s3:4572,dynamodb -# - S3_BUCKET_NAME=test-bucket -# - LOCALSTACK_URL=http://localstack:4572 -# ports: -# - "4572:4572" -# volumes: -# - ./scripts/create-bucket.sh:/docker-entrypoint-initaws.d/create-bucket.sh -# - /var/run/docker.sock:/var/run/docker.sock -# logging: -# driver: none - localstack-dynamo: - image: localstack/localstack + localstack: + image: localstack/localstack:0.10.9 environment: - - SERVICES=dynamodb - - DEBUG=1 - - DYNAMODB_SHARE_DB=1 - volumes: - - ./scripts/ehr-transfer-tracker-db.json:/etc/localstack/init/ready.d/ehr-transfer-tracker-db.json - - ./scripts/create-dynamodb-table.sh:/etc/localstack/init/ready.d/create-dynamodb-table.sh + - SERVICES=s3:4572 + - S3_BUCKET_NAME=test-bucket + - LOCALSTACK_URL=http://localstack:4572 ports: - - "4573:4566" \ No newline at end of file + - "4572:4572" + volumes: + - ./scripts/create-bucket.sh:/docker-entrypoint-initaws.d/create-bucket.sh + - /var/run/docker.sock:/var/run/docker.sock + logging: + driver: none +# localstack-dynamo: +# image: localstack/localstack +# environment: +# - SERVICES=dynamodb +# - DEBUG=1 +# - DYNAMODB_SHARE_DB=1 +# volumes: +# - ./scripts/ehr-transfer-tracker-db.json:/etc/localstack/init/ready.d/ehr-transfer-tracker-db.json +# - ./scripts/create-dynamodb-table.sh:/etc/localstack/init/ready.d/create-dynamodb-table.sh +# ports: +# - "4573:4566" \ No newline at end of file diff --git a/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js b/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js new file mode 100644 index 00000000..c449ab27 --- /dev/null +++ b/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js @@ -0,0 +1,22 @@ +import { EhrTransferTracker } from "../dynamo-ehr-transfer-tracker"; +import { v4 as uuid } from "uuid"; + +describe("EhrTransferTracker", () => { + it("create and read an inbound conversation in dynamodb", async () => { + const db = EhrTransferTracker.getInstance(); + const testConversationId = uuid(); + const testMessageId = uuid(); + const testNhsNumber = "9000000001"; + + const ehrExtract = { + conversationId: testConversationId, + messageId: testMessageId, + nhsNumber: testNhsNumber + }; + await db.createCore(ehrExtract); + + const currentRecordId = await db.getCurrentHealthRecordIdForPatient(testNhsNumber); + + expect(currentRecordId).toEqual(testConversationId); + }); +}); \ No newline at end of file diff --git a/src/services/database/__tests__/dynamo-transfer-tracker.js b/src/services/database/__tests__/dynamo-transfer-tracker.js deleted file mode 100644 index 38485be2..00000000 --- a/src/services/database/__tests__/dynamo-transfer-tracker.js +++ /dev/null @@ -1,15 +0,0 @@ -import { CreateTableCommand, DynamoDBClient } from "@aws-sdk/client-dynamodb"; -import { EhrTransferTracker } from "../dynamo-transfer-tracker"; -import { v4 as uuid } from "uuid"; - -describe("test", () => { - it("create a table in local dynamo", async () => { - const db = new EhrTransferTracker(); - const ehrExtract = { - conversationId: "test-conv-id", - messageId: uuid(), - nhsNumber: "9000000001" - }; - await db.createCore(ehrExtract); - }); -}); \ No newline at end of file diff --git a/src/services/database/dynamo-ehr-transfer-tracker.js b/src/services/database/dynamo-ehr-transfer-tracker.js new file mode 100644 index 00000000..1e753337 --- /dev/null +++ b/src/services/database/dynamo-ehr-transfer-tracker.js @@ -0,0 +1,104 @@ +import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; +import { DynamoDBDocumentClient, TransactWriteCommand, QueryCommand } from "@aws-sdk/lib-dynamodb"; +import moment from "moment"; + + +export class EhrTransferTracker { + constructor() { + if (EhrTransferTracker._instance) { + throw new Error("Singleton classes can't be instantiated more than once."); + } + EhrTransferTracker._instance = this; + + // TOOD: read env var DYNAMODB_NAME from param store + this.tableName = process.env.DYNAMODB_NAME; + + const localDynamodbConfig = { + endpoint: process.env.DYNAMODB_ENDPOINT ?? "http://localhost:4573/" + }; + const isInLocal = process.env.nhsEnvironment === "local" || !process.env.nhsEnvironment; + const clientConfig = isInLocal ? localDynamodbConfig : {}; + + const baseClient = new DynamoDBClient(clientConfig); + this.client = DynamoDBDocumentClient.from(baseClient); + } + + static getInstance() { + if (this._instance) { + return this._instance; + } + return new this(); + } + + async createCore(ehrExtract) { + // to replace the existing `createEhrExtract` method + const { conversationId, messageId, nhsNumber, fragmentMessageIds } = ehrExtract; + const timestamp = moment().toISOString(); + const conversation = { + InboundConversationId: conversationId, + Layer: "Conversation", + NhsNumber: nhsNumber, + CreatedAt: timestamp, + LastUpdatedAt: timestamp + }; + const core = { + InboundConversationId: conversationId, + Layer: `Core#${messageId}`, + InboundMessageId: messageId, + CreatedAt: timestamp, + ReceivedAt: timestamp, + LastUpdatedAt: timestamp + }; + const fragments = fragmentMessageIds ? fragmentMessageIds.map(fragmentMessageId => { + return { + InboundConversationId: conversationId, + Layer: `Fragment#${fragmentMessageId}`, + InboundMessageId: fragmentMessageId, + ParentId: messageId, + CreatedAt: timestamp, + LastUpdatedAt: timestamp + }; + }) : []; + + const command = new TransactWriteCommand({ + TransactItems: [conversation, core, ...fragments].map((item) => ({ + Put: { + TableName: this.tableName, Item: item, ConditionExpression: "attribute_not_exists(InboundConversationId)" + } + })) + }); + + await this.client.send(command); + } + + async getCurrentHealthRecordIdForPatient(nhsNumber) { + // to replace the existing method of the same name + + const params = { + TableName: this.tableName, + IndexName: "NhsNumberSecondaryIndex", + ExpressionAttributeValues: { + ":nhsNumber": nhsNumber + }, + ExpressionAttributeNames: { + "#NhsNumber": "NhsNumber" + }, + KeyConditionExpression: "#NhsNumber = :nhsNumber" + }; + + const command = new QueryCommand(params); + + const response = await this.client.send(command); + const items = response?.Items; + if (!items) { + throw new Error("No records was found for given NHS number"); + } + + // TODO: to compare "completed at" rather then "created at" + const currentRecord = items.reduce((prev, current) => { + return (current && current?.CreatedAt > prev?.CreatedAt) ? current : prev; + }); + + return currentRecord.InboundConversationId; + } +} \ No newline at end of file diff --git a/src/services/database/dynamo-transfer-tracker.js b/src/services/database/dynamo-transfer-tracker.js deleted file mode 100644 index 32930b63..00000000 --- a/src/services/database/dynamo-transfer-tracker.js +++ /dev/null @@ -1,55 +0,0 @@ -import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; -import { DynamoDBDocumentClient, TransactWriteCommand } from "@aws-sdk/lib-dynamodb"; -import moment from "moment"; - - -export class EhrTransferTracker { - constructor() { - const baseClient = new DynamoDBClient({ - endpoint: "http://localhost:4573/" - }); - - this.tableName = "local-ehr-transfer-tracker"; - this.client = DynamoDBDocumentClient.from(baseClient); - } - - async createCore(ehrExtract) { - const { conversationId, messageId, nhsNumber } = ehrExtract; - const timestamp = moment().toISOString(); - const conversation = { - InboundConversationId: conversationId, - Layer: "Conversation", - NhsNumber: nhsNumber, - CreatedAt: timestamp, - LastUpdatedAt: timestamp - }; - const core = { - InboundConversationId: conversationId, - Layer: "Core", - InboundMessageId: messageId, - CreatedAt: timestamp, - LastUpdatedAt: timestamp - }; - - const command = new TransactWriteCommand({ - TransactItems: [ - { - Put: { - TableName: this.tableName, - Item: conversation - } - }, - { - Put: { - TableName: this.tableName, - Item: core - } - } - ] - } - ); - - - await this.client.send(command); - } -} \ No newline at end of file diff --git a/tasks b/tasks index 6d3b4831..53038934 100755 --- a/tasks +++ b/tasks @@ -110,6 +110,13 @@ function configure_local_envs { export REPOSITORY_URI=$IMAGE_REPO_NAME export SKIP_DB_MIGRATION=false export NODE_ENV=local + + export DYNAMODB_NAME=local-ehr-transfer-tracker + export DYNAMODB_ENDPOINT=http://localstack-dynamo:4566/ + export AWS_REGION=$AWS_DEFAULT_REGION + export AWS_ACCESS_KEY_ID=$(generate_secure_string 20) + export AWS_SECRET_ACCESS_KEY=$(generate_secure_string 20) + configure_service_url } @@ -120,6 +127,9 @@ function configure_db_migration_envs { export DATABASE_NAME=$(_get_aws_ssm_secret "/repo/${NHS_ENVIRONMENT}/output/prm-deductions-ehr-repository/db-name") export DATABASE_PASSWORD="$(aws rds generate-db-auth-token --hostname $DATABASE_HOST --port 5432 --region $AWS_DEFAULT_REGION --username $DATABASE_USER)" export USE_SSL_FOR_DB="true" + + # TODO: read dynamodb name from ssm + export DYNAMODB_NAME="${NHS_ENVIRONMENT}-ehr-transfer-tracker" } function get_aws_account_id { @@ -250,6 +260,9 @@ function get_db_credentials { export DATABASE_USER=$(get_aws_ssm_secret "/repo/${NHS_ENVIRONMENT}/user-input/ehr-repo-db-username") export DATABASE_HOST=$(get_aws_ssm_secret "/repo/${NHS_ENVIRONMENT}/output/prm-deductions-ehr-repository/db-host") export DATABASE_NAME="deductions_db" + + # TODO: read dynamodb name from ssm + export DYNAMODB_NAME="${NHS_ENVIRONMENT}-ehr-transfer-tracker" } function drop_db { From d6747b924895b269daa3c5a8c1bb2f6fb46c35cb Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Mon, 4 Mar 2024 14:50:39 +0000 Subject: [PATCH 03/67] [PRMT-4565] minor change in config, small change to generate_secure_string to address a mac specific problem --- docker-compose-itest.yml | 23 +++++++++---------- .../database/dynamo-ehr-transfer-tracker.js | 13 +++++++---- tasks | 3 +-- 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/docker-compose-itest.yml b/docker-compose-itest.yml index 09fa151c..886a5bdc 100644 --- a/docker-compose-itest.yml +++ b/docker-compose-itest.yml @@ -4,7 +4,7 @@ services: links: - db:db - localstack:localstack -# - localstack-dynamo:localstack-dynamo + - localstack-dynamo:localstack-dynamo ports: - "3000:3000" db: @@ -29,14 +29,13 @@ services: - /var/run/docker.sock:/var/run/docker.sock logging: driver: none -# localstack-dynamo: -# image: localstack/localstack -# environment: -# - SERVICES=dynamodb -# - DEBUG=1 -# - DYNAMODB_SHARE_DB=1 -# volumes: -# - ./scripts/ehr-transfer-tracker-db.json:/etc/localstack/init/ready.d/ehr-transfer-tracker-db.json -# - ./scripts/create-dynamodb-table.sh:/etc/localstack/init/ready.d/create-dynamodb-table.sh -# ports: -# - "4573:4566" \ No newline at end of file + localstack-dynamo: + image: localstack/localstack + environment: + - SERVICES=dynamodb + - DYNAMODB_SHARE_DB=1 + volumes: + - ./scripts/ehr-transfer-tracker-db.json:/etc/localstack/init/ready.d/ehr-transfer-tracker-db.json + - ./scripts/create-dynamodb-table.sh:/etc/localstack/init/ready.d/create-dynamodb-table.sh + ports: + - "4573:4566" \ No newline at end of file diff --git a/src/services/database/dynamo-ehr-transfer-tracker.js b/src/services/database/dynamo-ehr-transfer-tracker.js index 1e753337..0a1ced9d 100644 --- a/src/services/database/dynamo-ehr-transfer-tracker.js +++ b/src/services/database/dynamo-ehr-transfer-tracker.js @@ -10,14 +10,17 @@ export class EhrTransferTracker { } EhrTransferTracker._instance = this; - // TOOD: read env var DYNAMODB_NAME from param store this.tableName = process.env.DYNAMODB_NAME; - const localDynamodbConfig = { - endpoint: process.env.DYNAMODB_ENDPOINT ?? "http://localhost:4573/" - }; + const clientConfig = { + region: process.env.AWS_DEFAULT_REGION ?? 'eu-west-2' + } + const isInLocal = process.env.nhsEnvironment === "local" || !process.env.nhsEnvironment; - const clientConfig = isInLocal ? localDynamodbConfig : {}; + if (isInLocal) { + clientConfig.endpoint = process.env.DYNAMODB_ENDPOINT; + this.tableName = process.env.DYNAMODB_NAME; + } const baseClient = new DynamoDBClient(clientConfig); this.client = DynamoDBDocumentClient.from(baseClient); diff --git a/tasks b/tasks index 53038934..00e20ff3 100755 --- a/tasks +++ b/tasks @@ -96,7 +96,7 @@ function prepare_local_envs_for_ide { function generate_secure_string { local length=$1 - tr -dc 'A-Za-z0-9_!@#$%^&*()' < /dev/urandom | head -c $length ; echo + LC_CTYPE="en_GB.UTF8" tr -dc 'A-Za-z0-9' < /dev/urandom | head -c $length ; echo } function configure_local_envs { @@ -113,7 +113,6 @@ function configure_local_envs { export DYNAMODB_NAME=local-ehr-transfer-tracker export DYNAMODB_ENDPOINT=http://localstack-dynamo:4566/ - export AWS_REGION=$AWS_DEFAULT_REGION export AWS_ACCESS_KEY_ID=$(generate_secure_string 20) export AWS_SECRET_ACCESS_KEY=$(generate_secure_string 20) From 41ec33a51cf8c5e97bc735fe46fbbf6a81e01b25 Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Mon, 4 Mar 2024 15:26:11 +0000 Subject: [PATCH 04/67] [PRMT-4565] Read dynamodb table name from SSM, change the table name for local testing --- docker-compose-itest.yml | 2 +- scripts/create-dynamodb-table.sh | 2 +- ...transfer-tracker-db.json => local-test-db-scheme.json} | 2 +- tasks | 8 +++----- 4 files changed, 6 insertions(+), 8 deletions(-) rename scripts/{ehr-transfer-tracker-db.json => local-test-db-scheme.json} (96%) diff --git a/docker-compose-itest.yml b/docker-compose-itest.yml index 886a5bdc..0773471a 100644 --- a/docker-compose-itest.yml +++ b/docker-compose-itest.yml @@ -35,7 +35,7 @@ services: - SERVICES=dynamodb - DYNAMODB_SHARE_DB=1 volumes: - - ./scripts/ehr-transfer-tracker-db.json:/etc/localstack/init/ready.d/ehr-transfer-tracker-db.json + - ./scripts/local-test-db-scheme.json:/etc/localstack/init/ready.d/local-test-db-scheme.json - ./scripts/create-dynamodb-table.sh:/etc/localstack/init/ready.d/create-dynamodb-table.sh ports: - "4573:4566" \ No newline at end of file diff --git a/scripts/create-dynamodb-table.sh b/scripts/create-dynamodb-table.sh index 0bb2dfcd..6d843dc8 100755 --- a/scripts/create-dynamodb-table.sh +++ b/scripts/create-dynamodb-table.sh @@ -7,5 +7,5 @@ export AWS_REGION=eu-west-2 set -e echo Creating dynamodb table in localstack... cd "$(dirname "$0")" -awslocal --region eu-west-2 dynamodb create-table --cli-input-json file://ehr-transfer-tracker-db.json +awslocal --region eu-west-2 dynamodb create-table --cli-input-json file://local-test-db-scheme.json set +e \ No newline at end of file diff --git a/scripts/ehr-transfer-tracker-db.json b/scripts/local-test-db-scheme.json similarity index 96% rename from scripts/ehr-transfer-tracker-db.json rename to scripts/local-test-db-scheme.json index 47b63442..362ecc21 100644 --- a/scripts/ehr-transfer-tracker-db.json +++ b/scripts/local-test-db-scheme.json @@ -1,5 +1,5 @@ { - "TableName": "local-ehr-transfer-tracker", + "TableName": "local-test-db", "KeySchema": [ { "AttributeName": "InboundConversationId", diff --git a/tasks b/tasks index 00e20ff3..63d856d5 100755 --- a/tasks +++ b/tasks @@ -111,7 +111,7 @@ function configure_local_envs { export SKIP_DB_MIGRATION=false export NODE_ENV=local - export DYNAMODB_NAME=local-ehr-transfer-tracker + export DYNAMODB_NAME=local-test-db export DYNAMODB_ENDPOINT=http://localstack-dynamo:4566/ export AWS_ACCESS_KEY_ID=$(generate_secure_string 20) export AWS_SECRET_ACCESS_KEY=$(generate_secure_string 20) @@ -127,8 +127,7 @@ function configure_db_migration_envs { export DATABASE_PASSWORD="$(aws rds generate-db-auth-token --hostname $DATABASE_HOST --port 5432 --region $AWS_DEFAULT_REGION --username $DATABASE_USER)" export USE_SSL_FOR_DB="true" - # TODO: read dynamodb name from ssm - export DYNAMODB_NAME="${NHS_ENVIRONMENT}-ehr-transfer-tracker" + export DYNAMODB_NAME=$(_get_aws_ssm_secret "/repo/${NHS_ENVIRONMENT}/output/prm-deductions-infra/ehr-transfer-tracker-db-name") } function get_aws_account_id { @@ -260,8 +259,7 @@ function get_db_credentials { export DATABASE_HOST=$(get_aws_ssm_secret "/repo/${NHS_ENVIRONMENT}/output/prm-deductions-ehr-repository/db-host") export DATABASE_NAME="deductions_db" - # TODO: read dynamodb name from ssm - export DYNAMODB_NAME="${NHS_ENVIRONMENT}-ehr-transfer-tracker" + export DYNAMODB_NAME=$(_get_aws_ssm_secret "/repo/${NHS_ENVIRONMENT}/output/prm-deductions-infra/ehr-transfer-tracker-db-name") } function drop_db { From 15e8a1c05d150a9e56bb24cbdcd207e5a972b7da Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Mon, 4 Mar 2024 17:10:42 +0000 Subject: [PATCH 05/67] [PRMDR-4565] Replace localstack with dynamodb-local for running dynamodb integration test --- docker-compose-itest.yml | 16 ++++++---------- scripts/create-dynamodb-table.sh | 8 ++------ .../database/dynamo-ehr-transfer-tracker.js | 6 +++--- tasks | 3 ++- 4 files changed, 13 insertions(+), 20 deletions(-) diff --git a/docker-compose-itest.yml b/docker-compose-itest.yml index 0773471a..f4cd11ff 100644 --- a/docker-compose-itest.yml +++ b/docker-compose-itest.yml @@ -4,7 +4,7 @@ services: links: - db:db - localstack:localstack - - localstack-dynamo:localstack-dynamo + - dynamodb-local:dynamodb-local ports: - "3000:3000" db: @@ -29,13 +29,9 @@ services: - /var/run/docker.sock:/var/run/docker.sock logging: driver: none - localstack-dynamo: - image: localstack/localstack - environment: - - SERVICES=dynamodb - - DYNAMODB_SHARE_DB=1 - volumes: - - ./scripts/local-test-db-scheme.json:/etc/localstack/init/ready.d/local-test-db-scheme.json - - ./scripts/create-dynamodb-table.sh:/etc/localstack/init/ready.d/create-dynamodb-table.sh + dynamodb-local: + image: amazon/dynamodb-local + command: "-jar DynamoDBLocal.jar -sharedDb -inMemory" ports: - - "4573:4566" \ No newline at end of file + - "4573:8000" + diff --git a/scripts/create-dynamodb-table.sh b/scripts/create-dynamodb-table.sh index 6d843dc8..57553c4b 100755 --- a/scripts/create-dynamodb-table.sh +++ b/scripts/create-dynamodb-table.sh @@ -1,11 +1,7 @@ #!/bin/bash -export AWS_SECRET_ACCESS_KEY=test_key -export AWS_ACCESS_KEY_ID=test_id -export AWS_REGION=eu-west-2 - set -e -echo Creating dynamodb table in localstack... +echo Creating a table for test in dynamodb-local... cd "$(dirname "$0")" -awslocal --region eu-west-2 dynamodb create-table --cli-input-json file://local-test-db-scheme.json +aws --region eu-west-2 --endpoint=http://dynamodb-local:8000 dynamodb create-table --cli-input-json file://local-test-db-scheme.json set +e \ No newline at end of file diff --git a/src/services/database/dynamo-ehr-transfer-tracker.js b/src/services/database/dynamo-ehr-transfer-tracker.js index 0a1ced9d..166185d0 100644 --- a/src/services/database/dynamo-ehr-transfer-tracker.js +++ b/src/services/database/dynamo-ehr-transfer-tracker.js @@ -13,13 +13,13 @@ export class EhrTransferTracker { this.tableName = process.env.DYNAMODB_NAME; const clientConfig = { - region: process.env.AWS_DEFAULT_REGION ?? 'eu-west-2' - } + region: process.env.AWS_DEFAULT_REGION ?? "eu-west-2" + }; const isInLocal = process.env.nhsEnvironment === "local" || !process.env.nhsEnvironment; + if (isInLocal) { clientConfig.endpoint = process.env.DYNAMODB_ENDPOINT; - this.tableName = process.env.DYNAMODB_NAME; } const baseClient = new DynamoDBClient(clientConfig); diff --git a/tasks b/tasks index 63d856d5..d98895dd 100755 --- a/tasks +++ b/tasks @@ -112,7 +112,7 @@ function configure_local_envs { export NODE_ENV=local export DYNAMODB_NAME=local-test-db - export DYNAMODB_ENDPOINT=http://localstack-dynamo:4566/ + export DYNAMODB_ENDPOINT=http://dynamodb-local:8000/ export AWS_ACCESS_KEY_ID=$(generate_secure_string 20) export AWS_SECRET_ACCESS_KEY=$(generate_secure_string 20) @@ -395,6 +395,7 @@ case "${command}" in npm install npm run db:migrate node scripts/wait-for-localstack.js + sh scripts/create-dynamodb-table.sh npm run test:integration ;; test_integration) From 50afcd4cded03f6d3d30b30cbf84aa7bb2e47a46 Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Tue, 5 Mar 2024 11:12:31 +0000 Subject: [PATCH 06/67] [PRMT-4565] Change timestamp format to include timezone offset --- package-lock.json | 13 +++++++------ package.json | 1 + .../database/dynamo-ehr-transfer-tracker.js | 10 +++++----- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5f9d5f77..84359fe7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,7 @@ "lodash.clonedeep": "^4.5.0", "lodash.merge": "^4.6.2", "moment": "^2.30.1", + "moment-timezone": "^0.5.45", "pg": "^8.7.3", "pg-hstore": "^2.3.4", "sequelize": "^6.33.0", @@ -9766,9 +9767,9 @@ } }, "node_modules/moment-timezone": { - "version": "0.5.43", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.43.tgz", - "integrity": "sha512-72j3aNyuIsDxdF1i7CEgV2FfxM1r6aaqJyLB2vwb33mXYyoyLly+F1zbWqhA3/bVIoJ4szlUoMbUnVdid32NUQ==", + "version": "0.5.45", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.45.tgz", + "integrity": "sha512-HIWmqA86KcmCAhnMAN0wuDOARV/525R2+lOLotuGFzn4HO+FH+/645z2wx0Dt3iDv6/p61SIvKnDstISainhLQ==", "dependencies": { "moment": "^2.29.4" }, @@ -19459,9 +19460,9 @@ "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==" }, "moment-timezone": { - "version": "0.5.43", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.43.tgz", - "integrity": "sha512-72j3aNyuIsDxdF1i7CEgV2FfxM1r6aaqJyLB2vwb33mXYyoyLly+F1zbWqhA3/bVIoJ4szlUoMbUnVdid32NUQ==", + "version": "0.5.45", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.45.tgz", + "integrity": "sha512-HIWmqA86KcmCAhnMAN0wuDOARV/525R2+lOLotuGFzn4HO+FH+/645z2wx0Dt3iDv6/p61SIvKnDstISainhLQ==", "requires": { "moment": "^2.29.4" } diff --git a/package.json b/package.json index f7059dc7..edd9ae1d 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,7 @@ "lodash.clonedeep": "^4.5.0", "lodash.merge": "^4.6.2", "moment": "^2.30.1", + "moment-timezone": "^0.5.45", "pg": "^8.7.3", "pg-hstore": "^2.3.4", "sequelize": "^6.33.0", diff --git a/src/services/database/dynamo-ehr-transfer-tracker.js b/src/services/database/dynamo-ehr-transfer-tracker.js index 166185d0..ecde93e2 100644 --- a/src/services/database/dynamo-ehr-transfer-tracker.js +++ b/src/services/database/dynamo-ehr-transfer-tracker.js @@ -1,6 +1,6 @@ import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; import { DynamoDBDocumentClient, TransactWriteCommand, QueryCommand } from "@aws-sdk/lib-dynamodb"; -import moment from "moment"; +import moment from "moment-timezone"; export class EhrTransferTracker { @@ -36,13 +36,13 @@ export class EhrTransferTracker { async createCore(ehrExtract) { // to replace the existing `createEhrExtract` method const { conversationId, messageId, nhsNumber, fragmentMessageIds } = ehrExtract; - const timestamp = moment().toISOString(); + const timestamp = moment().tz('Europe/London').format('YYYY-MM-DDThh:mm:ssZ'); const conversation = { InboundConversationId: conversationId, Layer: "Conversation", NhsNumber: nhsNumber, CreatedAt: timestamp, - LastUpdatedAt: timestamp + UpdatedAt: timestamp }; const core = { InboundConversationId: conversationId, @@ -50,7 +50,7 @@ export class EhrTransferTracker { InboundMessageId: messageId, CreatedAt: timestamp, ReceivedAt: timestamp, - LastUpdatedAt: timestamp + UpdatedAt: timestamp }; const fragments = fragmentMessageIds ? fragmentMessageIds.map(fragmentMessageId => { return { @@ -59,7 +59,7 @@ export class EhrTransferTracker { InboundMessageId: fragmentMessageId, ParentId: messageId, CreatedAt: timestamp, - LastUpdatedAt: timestamp + UpdatedAt: timestamp }; }) : []; From fee2d4fd4582b1549b3ba6cdd02d87cdefc893df Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Tue, 5 Mar 2024 15:18:00 +0000 Subject: [PATCH 07/67] [PRMT-4566] add new version of updateFragmentAndCreateItsParts, start separating model and repository layer --- src/models/conversation.js | 25 ++++ src/models/ehrCore.js | 36 ++++++ src/models/ehrFragment.js | 25 ++++ ...o-ehr-transfer-tracker.integration.test.js | 48 ++++++- .../database/dynamo-ehr-transfer-tracker.js | 122 +++++++++++++++--- src/services/time.js | 6 + 6 files changed, 243 insertions(+), 19 deletions(-) create mode 100644 src/models/conversation.js create mode 100644 src/models/ehrCore.js create mode 100644 src/models/ehrFragment.js diff --git a/src/models/conversation.js b/src/models/conversation.js new file mode 100644 index 00000000..20956c02 --- /dev/null +++ b/src/models/conversation.js @@ -0,0 +1,25 @@ +import { getUKTimestamp } from "../services/time"; +import { EhrTransferTracker } from "../services/database/dynamo-ehr-transfer-tracker"; + +export const createConversationForTest = async (conversationId, nhsNumber) => { + // This method is only meant for testing purpose, + // as the inbound conversation record is supposed to be created by other service. + + const isInLocal = process.env.NHS_ENVIRONMENT === "local" || !process.env.NHS_ENVIRONMENT; + if (!isInLocal) { + throw new Error("Unexpected call to createConversation method in non-local environment"); + } + + const timestamp = getUKTimestamp(); + const db = EhrTransferTracker.getInstance(); + + const item = { + InboundConversationId: conversationId, + Layer: "Conversation", + NhsNumber: nhsNumber, + CreatedAt: timestamp, + UpdatedAt: timestamp + }; + + await db.writeItemsToTable([item]); +}; \ No newline at end of file diff --git a/src/models/ehrCore.js b/src/models/ehrCore.js new file mode 100644 index 00000000..daacdc5f --- /dev/null +++ b/src/models/ehrCore.js @@ -0,0 +1,36 @@ +import { getUKTimestamp } from "../services/time"; +import { EhrTransferTracker } from "../services/database/dynamo-ehr-transfer-tracker"; +import { arrayOfFragments } from "./ehrFragment"; + +export const createCore = async ({ inboundConversationId, messageId, fragmentMessageIds }) => { + // to replace the existing `createEhrExtract` method + + const db = EhrTransferTracker.getInstance(); + const itemsToWrite = [ehrCore({ inboundConversationId, messageId })]; + + if (fragmentMessageIds) { + const directFragments = arrayOfFragments( + { + inboundConversationId, + fragmentMessageIds, + parentMessageId: messageId + } + ); + itemsToWrite.push(...directFragments); + } + + await db.writeItemsToTable(itemsToWrite); +}; + + +export const ehrCore = ({ inboundConversationId, messageId }) => { + const timestamp = getUKTimestamp(); + return { + InboundConversationId: inboundConversationId, + Layer: `Core#${messageId}`, + InboundMessageId: messageId, + CreatedAt: timestamp, + ReceivedAt: timestamp, + UpdatedAt: timestamp + }; +}; \ No newline at end of file diff --git a/src/models/ehrFragment.js b/src/models/ehrFragment.js new file mode 100644 index 00000000..77250a48 --- /dev/null +++ b/src/models/ehrFragment.js @@ -0,0 +1,25 @@ +import { getUKTimestamp } from "../services/time"; +import { EhrTransferTracker } from "../services/database/dynamo-ehr-transfer-tracker"; + + + +export const singleFragment = ({ inboundConversationId, fragmentMessageId, parentMessageId }) => { + const timestamp = getUKTimestamp(); + return { + InboundConversationId: inboundConversationId, + Layer: `Fragment#${fragmentMessageId}`, + InboundMessageId: fragmentMessageId, + ParentId: parentMessageId, + CreatedAt: timestamp, + UpdatedAt: timestamp + }; +}; + +export const arrayOfFragments = ({ inboundConversationId, fragmentMessageIds, parentMessageId }) => { + if (!fragmentMessageIds || !Array.isArray(fragmentMessageIds)) { + return []; + } + return fragmentMessageIds.map(fragmentMessageId => singleFragment( + { inboundConversationId, fragmentMessageId, parentMessageId } + )); +}; \ No newline at end of file diff --git a/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js b/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js index c449ab27..6ddfceb5 100644 --- a/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js +++ b/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js @@ -1,8 +1,11 @@ import { EhrTransferTracker } from "../dynamo-ehr-transfer-tracker"; import { v4 as uuid } from "uuid"; +import { UpdateCommand } from "@aws-sdk/lib-dynamodb"; +import { createConversationForTest } from "../../../models/conversation"; describe("EhrTransferTracker", () => { it("create and read an inbound conversation in dynamodb", async () => { + // given const db = EhrTransferTracker.getInstance(); const testConversationId = uuid(); const testMessageId = uuid(); @@ -13,10 +16,53 @@ describe("EhrTransferTracker", () => { messageId: testMessageId, nhsNumber: testNhsNumber }; - await db.createCore(ehrExtract); + + // when + await createConversationForTest(testConversationId, testNhsNumber); + + // await db.createCore(ehrExtract); const currentRecordId = await db.getCurrentHealthRecordIdForPatient(testNhsNumber); + // then expect(currentRecordId).toEqual(testConversationId); }); + + it("updateFragmentAndCreateItsParts", async () => { + // given + const db = EhrTransferTracker.getInstance(); + + + const testConversationId = uuid(); + const testMessageId = uuid(); + const testNhsNumber = "9000000002"; + const testChildMessageIds = [uuid(), uuid(), uuid()] + + const ehrExtract = { + conversationId: testConversationId, + messageId: testMessageId, + nhsNumber: testNhsNumber, + fragmentMessageIds: testChildMessageIds + }; + + await createConversationForTest(testConversationId, testNhsNumber); + await db.createCore(ehrExtract) + + // when + await db.updateFragmentAndCreateItsParts(testChildMessageIds[0], testConversationId) + + const records = await db.queryTableByConversationId(testConversationId); + + // then + + const expectedSize = 5; // Conversation + core + 3 children fragments + expect(records).toHaveLength(expectedSize) + + const receivedFragment = records.filter(item => item.Layer.startsWith('Fragment') && item.ReceivedAt) + expect(receivedFragment).toHaveLength(1) + expect(receivedFragment[0].InboundMessageId).toEqual(testChildMessageIds[0]) + + const nonReceivedFragments = records.filter(item => item.Layer.startsWith('Fragment') && !item.ReceivedAt) + expect(nonReceivedFragments).toHaveLength(2) + }) }); \ No newline at end of file diff --git a/src/services/database/dynamo-ehr-transfer-tracker.js b/src/services/database/dynamo-ehr-transfer-tracker.js index ecde93e2..ddda70d7 100644 --- a/src/services/database/dynamo-ehr-transfer-tracker.js +++ b/src/services/database/dynamo-ehr-transfer-tracker.js @@ -1,6 +1,9 @@ import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; -import { DynamoDBDocumentClient, TransactWriteCommand, QueryCommand } from "@aws-sdk/lib-dynamodb"; -import moment from "moment-timezone"; +import { DynamoDBDocumentClient, TransactWriteCommand, QueryCommand, UpdateCommand } from "@aws-sdk/lib-dynamodb"; + +import { getUKTimestamp } from "../time"; +import { logError } from "../../middleware/logging"; +import { createConversationForTest } from "../../models/conversation"; export class EhrTransferTracker { @@ -16,11 +19,17 @@ export class EhrTransferTracker { region: process.env.AWS_DEFAULT_REGION ?? "eu-west-2" }; - const isInLocal = process.env.nhsEnvironment === "local" || !process.env.nhsEnvironment; + const isInLocal = process.env.NHS_ENVIRONMENT === "local" || !process.env.NHS_ENVIRONMENT; + const isInDojo = process.env.DOJO_VERSION !== undefined if (isInLocal) { clientConfig.endpoint = process.env.DYNAMODB_ENDPOINT; } + // for running individual test in IDE + if (isInLocal && !isInDojo) { + clientConfig.endpoint = "http://localhost:4573"; + this.tableName = "local-test-db"; + } const baseClient = new DynamoDBClient(clientConfig); this.client = DynamoDBDocumentClient.from(baseClient); @@ -34,16 +43,10 @@ export class EhrTransferTracker { } async createCore(ehrExtract) { - // to replace the existing `createEhrExtract` method + const { conversationId, messageId, nhsNumber, fragmentMessageIds } = ehrExtract; - const timestamp = moment().tz('Europe/London').format('YYYY-MM-DDThh:mm:ssZ'); - const conversation = { - InboundConversationId: conversationId, - Layer: "Conversation", - NhsNumber: nhsNumber, - CreatedAt: timestamp, - UpdatedAt: timestamp - }; + const timestamp = getUKTimestamp(); + const core = { InboundConversationId: conversationId, Layer: `Core#${messageId}`, @@ -63,10 +66,19 @@ export class EhrTransferTracker { }; }) : []; + const itemsToWrite = [core, ...fragments]; + + await this.writeItemsToTable(itemsToWrite); + } + + async writeItemsToTable(items) { + if (!items || !Array.isArray(items)) { + throw new Error("The given argument `items` is not an array"); + } const command = new TransactWriteCommand({ - TransactItems: [conversation, core, ...fragments].map((item) => ({ + TransactItems: items.map((item) => ({ Put: { - TableName: this.tableName, Item: item, ConditionExpression: "attribute_not_exists(InboundConversationId)" + TableName: this.tableName, Item: item } })) }); @@ -74,9 +86,7 @@ export class EhrTransferTracker { await this.client.send(command); } - async getCurrentHealthRecordIdForPatient(nhsNumber) { - // to replace the existing method of the same name - + async queryTableByNhsNumber(nhsNumber) { const params = { TableName: this.tableName, IndexName: "NhsNumberSecondaryIndex", @@ -94,7 +104,18 @@ export class EhrTransferTracker { const response = await this.client.send(command); const items = response?.Items; if (!items) { - throw new Error("No records was found for given NHS number"); + logError("Received an empty response from dynamodb during query"); + } + return items; + } + + async getCurrentHealthRecordIdForPatient(nhsNumber) { + // to replace the existing method of the same name + + const items = await this.queryTableByNhsNumber(nhsNumber); + + if (!items || items.length === 0) { + throw new Error("No record was found for given NHS number"); } // TODO: to compare "completed at" rather then "created at" @@ -104,4 +125,69 @@ export class EhrTransferTracker { return currentRecord.InboundConversationId; } + + async queryTableByConversationId(inboundConversationId) { + const params = { + TableName: this.tableName, + ExpressionAttributeValues: { + ":InboundConversationId": inboundConversationId + }, + ExpressionAttributeNames: { + "#InboundConversationId": "InboundConversationId" + }, + KeyConditionExpression: "#InboundConversationId = :InboundConversationId" + }; + + const command = new QueryCommand(params); + + const response = await this.client.send(command); + const items = response?.Items; + if (!items) { + logError("Received an empty response from dynamodb during query"); + } + return items; + } + + async updateFragmentAndCreateItsParts(messageId, + conversationId, + remainingPartsIds) { + // to replace existing method of the same name + const timestamp = getUKTimestamp(); + + const currentFragment = { + TableName: this.tableName, + Key: { + InboundConversationId: conversationId, + Layer: `Fragment#${messageId}` + }, + UpdateExpression: "set ReceivedAt = :now, CreatedAt = if_not_exists(CreatedAt, :now), UpdatedAt = :now", + ExpressionAttributeValues: { + ":now": timestamp + } + }; + + const childFragments = remainingPartsIds ? remainingPartsIds.map(fragmentPartId => { + return ({ + TableName: this.tableName, + Key: { + InboundConversationId: conversationId, + Layer: `Fragment#${fragmentPartId}` + }, + UpdateExpression: "set CreatedAt = if_not_exists(CreatedAt, :now), UpdatedAt = :now, ParentId = :parentMessageId", + ExpressionAttributeValues: { + ":now": timestamp, + ":parentMessageId": messageId + } + }); + }) : []; + + const updateParams = [currentFragment, ...childFragments]; + const command = new TransactWriteCommand({ + TransactItems: updateParams.map((param) => ({ + Update: param + })) + }); + await this.client.send(command); + }; + } \ No newline at end of file diff --git a/src/services/time.js b/src/services/time.js index 9fe86b15..00dd8f58 100644 --- a/src/services/time.js +++ b/src/services/time.js @@ -1 +1,7 @@ +import moment from "moment-timezone"; + export const getNow = () => new Date(); + +export const getUKTimestamp = () => { + return moment().tz('Europe/London').format('YYYY-MM-DDThh:mm:ssZ'); +} \ No newline at end of file From 6f3fbc61679582e1de15ed6e7b654a556a0578b9 Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Tue, 5 Mar 2024 15:58:17 +0000 Subject: [PATCH 08/67] [PRMT-4566] Config test setup and teardown --- src/models/conversation.js | 37 ++++++++++++ ...o-ehr-transfer-tracker.integration.test.js | 58 ++++++++++++------- .../database/dynamo-ehr-transfer-tracker.js | 6 +- 3 files changed, 75 insertions(+), 26 deletions(-) diff --git a/src/models/conversation.js b/src/models/conversation.js index 20956c02..22cfacfa 100644 --- a/src/models/conversation.js +++ b/src/models/conversation.js @@ -1,5 +1,6 @@ import { getUKTimestamp } from "../services/time"; import { EhrTransferTracker } from "../services/database/dynamo-ehr-transfer-tracker"; +import { DeleteCommand, TransactWriteCommand } from "@aws-sdk/lib-dynamodb"; export const createConversationForTest = async (conversationId, nhsNumber) => { // This method is only meant for testing purpose, @@ -22,4 +23,40 @@ export const createConversationForTest = async (conversationId, nhsNumber) => { }; await db.writeItemsToTable([item]); +}; + +export const deleteConversationForTest = async (conversationId) => { + // This method is only meant for testing purpose, + + const isInLocal = process.env.NHS_ENVIRONMENT === "local" || !process.env.NHS_ENVIRONMENT; + if (!isInLocal) { + throw new Error("Unexpected call to createConversation method in non-local environment"); + } + + const db = EhrTransferTracker.getInstance(); + const records = await db.queryTableByConversationId(conversationId); + const deleteCommand = new TransactWriteCommand( + { + TransactItems: records.map(item => ({ + Delete: { + TableName: db.tableName, + Key: { + InboundConversationId: item.InboundConversationId, + Layer: item.Layer + } + } + })) + } + ); + + + // const deleteCommand = new DeleteCommand({ + // TableName: db.tableName, + // Key: { + // InboundConversationId: records[0].InboundConversationId, + // Layer: records[0].Layer + // } + // }); + + await db.client.send(deleteCommand); }; \ No newline at end of file diff --git a/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js b/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js index 6ddfceb5..b09a5f6a 100644 --- a/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js +++ b/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js @@ -1,13 +1,22 @@ import { EhrTransferTracker } from "../dynamo-ehr-transfer-tracker"; import { v4 as uuid } from "uuid"; -import { UpdateCommand } from "@aws-sdk/lib-dynamodb"; -import { createConversationForTest } from "../../../models/conversation"; +import { createConversationForTest, deleteConversationForTest } from "../../../models/conversation"; describe("EhrTransferTracker", () => { - it("create and read an inbound conversation in dynamodb", async () => { + const testConversationId = uuid(); + const testNhsNumber = "9000000001"; + + beforeEach(async () => { + await createConversationForTest(testConversationId, testNhsNumber); + }); + + afterEach(async () => { + await deleteConversationForTest(testConversationId); + }); + + it("create and read an ehrCore in dynamodb", async () => { // given const db = EhrTransferTracker.getInstance(); - const testConversationId = uuid(); const testMessageId = uuid(); const testNhsNumber = "9000000001"; @@ -18,14 +27,21 @@ describe("EhrTransferTracker", () => { }; // when - await createConversationForTest(testConversationId, testNhsNumber); - - // await db.createCore(ehrExtract); - - const currentRecordId = await db.getCurrentHealthRecordIdForPatient(testNhsNumber); + await db.createCore(ehrExtract); // then - expect(currentRecordId).toEqual(testConversationId); + const records = await db.queryTableByConversationId(testConversationId); + const actual = records.filter(item => item.Layer.startsWith("Core")); + + expect(actual).toHaveLength(1); + expect(actual[0]).toMatchObject({ + InboundConversationId: testConversationId, + InboundMessageId: testMessageId, + Layer: `Core#${testMessageId}`, + ReceivedAt: expect.any(String), + CreatedAt: expect.any(String), + UpdatedAt: expect.any(String) + }); }); it("updateFragmentAndCreateItsParts", async () => { @@ -33,10 +49,9 @@ describe("EhrTransferTracker", () => { const db = EhrTransferTracker.getInstance(); - const testConversationId = uuid(); const testMessageId = uuid(); const testNhsNumber = "9000000002"; - const testChildMessageIds = [uuid(), uuid(), uuid()] + const testChildMessageIds = [uuid(), uuid(), uuid()]; const ehrExtract = { conversationId: testConversationId, @@ -45,24 +60,23 @@ describe("EhrTransferTracker", () => { fragmentMessageIds: testChildMessageIds }; - await createConversationForTest(testConversationId, testNhsNumber); - await db.createCore(ehrExtract) + await db.createCore(ehrExtract); // when - await db.updateFragmentAndCreateItsParts(testChildMessageIds[0], testConversationId) + await db.updateFragmentAndCreateItsParts(testChildMessageIds[0], testConversationId); const records = await db.queryTableByConversationId(testConversationId); // then const expectedSize = 5; // Conversation + core + 3 children fragments - expect(records).toHaveLength(expectedSize) + expect(records).toHaveLength(expectedSize); - const receivedFragment = records.filter(item => item.Layer.startsWith('Fragment') && item.ReceivedAt) - expect(receivedFragment).toHaveLength(1) - expect(receivedFragment[0].InboundMessageId).toEqual(testChildMessageIds[0]) + const receivedFragment = records.filter(item => item.Layer.startsWith("Fragment") && item.ReceivedAt); + expect(receivedFragment).toHaveLength(1); + expect(receivedFragment[0].InboundMessageId).toEqual(testChildMessageIds[0]); - const nonReceivedFragments = records.filter(item => item.Layer.startsWith('Fragment') && !item.ReceivedAt) - expect(nonReceivedFragments).toHaveLength(2) - }) + const nonReceivedFragments = records.filter(item => item.Layer.startsWith("Fragment") && !item.ReceivedAt); + expect(nonReceivedFragments).toHaveLength(2); + }); }); \ No newline at end of file diff --git a/src/services/database/dynamo-ehr-transfer-tracker.js b/src/services/database/dynamo-ehr-transfer-tracker.js index ddda70d7..ce27b3f3 100644 --- a/src/services/database/dynamo-ehr-transfer-tracker.js +++ b/src/services/database/dynamo-ehr-transfer-tracker.js @@ -1,9 +1,8 @@ import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; -import { DynamoDBDocumentClient, TransactWriteCommand, QueryCommand, UpdateCommand } from "@aws-sdk/lib-dynamodb"; +import { DynamoDBDocumentClient, TransactWriteCommand, QueryCommand } from "@aws-sdk/lib-dynamodb"; import { getUKTimestamp } from "../time"; import { logError } from "../../middleware/logging"; -import { createConversationForTest } from "../../models/conversation"; export class EhrTransferTracker { @@ -151,7 +150,7 @@ export class EhrTransferTracker { async updateFragmentAndCreateItsParts(messageId, conversationId, remainingPartsIds) { - // to replace existing method of the same name + // to replace the existing methods `updateFragmentAndCreateItsParts` and `createFragmentPart` const timestamp = getUKTimestamp(); const currentFragment = { @@ -189,5 +188,4 @@ export class EhrTransferTracker { }); await this.client.send(command); }; - } \ No newline at end of file From 9e332eef82a309c7e2d7ab7a6e4b864807145c90 Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Tue, 5 Mar 2024 16:27:25 +0000 Subject: [PATCH 09/67] [PRMT-4566] Add option to query specific record type --- src/models/queryType.js | 8 +++++ ...o-ehr-transfer-tracker.integration.test.js | 14 ++++----- .../database/dynamo-ehr-transfer-tracker.js | 30 +++++++++++++++---- 3 files changed, 39 insertions(+), 13 deletions(-) create mode 100644 src/models/queryType.js diff --git a/src/models/queryType.js b/src/models/queryType.js new file mode 100644 index 00000000..ea47a0f2 --- /dev/null +++ b/src/models/queryType.js @@ -0,0 +1,8 @@ +export const QueryType = { + ALL: 'ALL', + CONVERSATION: 'Conversation', + CORE: 'Core', + FRAGMENT: 'Fragment', +}; + +Object.freeze(QueryType) \ No newline at end of file diff --git a/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js b/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js index b09a5f6a..194f7b79 100644 --- a/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js +++ b/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js @@ -1,6 +1,7 @@ import { EhrTransferTracker } from "../dynamo-ehr-transfer-tracker"; import { v4 as uuid } from "uuid"; import { createConversationForTest, deleteConversationForTest } from "../../../models/conversation"; +import { QueryType } from "../../../models/queryType"; describe("EhrTransferTracker", () => { const testConversationId = uuid(); @@ -30,8 +31,7 @@ describe("EhrTransferTracker", () => { await db.createCore(ehrExtract); // then - const records = await db.queryTableByConversationId(testConversationId); - const actual = records.filter(item => item.Layer.startsWith("Core")); + const actual = await db.queryTableByConversationId(testConversationId, QueryType.CORE); expect(actual).toHaveLength(1); expect(actual[0]).toMatchObject({ @@ -65,18 +65,16 @@ describe("EhrTransferTracker", () => { // when await db.updateFragmentAndCreateItsParts(testChildMessageIds[0], testConversationId); - const records = await db.queryTableByConversationId(testConversationId); + const actual = await db.queryTableByConversationId(testConversationId, QueryType.FRAGMENT); // then + expect(actual).toHaveLength(testChildMessageIds.length); - const expectedSize = 5; // Conversation + core + 3 children fragments - expect(records).toHaveLength(expectedSize); - - const receivedFragment = records.filter(item => item.Layer.startsWith("Fragment") && item.ReceivedAt); + const receivedFragment = actual.filter(item => item.ReceivedAt); expect(receivedFragment).toHaveLength(1); expect(receivedFragment[0].InboundMessageId).toEqual(testChildMessageIds[0]); - const nonReceivedFragments = records.filter(item => item.Layer.startsWith("Fragment") && !item.ReceivedAt); + const nonReceivedFragments = actual.filter(item => !item.ReceivedAt); expect(nonReceivedFragments).toHaveLength(2); }); }); \ No newline at end of file diff --git a/src/services/database/dynamo-ehr-transfer-tracker.js b/src/services/database/dynamo-ehr-transfer-tracker.js index ce27b3f3..4a413511 100644 --- a/src/services/database/dynamo-ehr-transfer-tracker.js +++ b/src/services/database/dynamo-ehr-transfer-tracker.js @@ -2,7 +2,9 @@ import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; import { DynamoDBDocumentClient, TransactWriteCommand, QueryCommand } from "@aws-sdk/lib-dynamodb"; import { getUKTimestamp } from "../time"; -import { logError } from "../../middleware/logging"; +import { logError, logInfo } from "../../middleware/logging"; +import { updateHealthRecordCompleteness } from "./health-record-repository"; +import { QueryType } from "../../models/QueryType"; export class EhrTransferTracker { @@ -125,18 +127,32 @@ export class EhrTransferTracker { return currentRecord.InboundConversationId; } - async queryTableByConversationId(inboundConversationId) { + async queryTableByConversationId(inboundConversationId, queryType = QueryType.ALL) { const params = { TableName: this.tableName, - ExpressionAttributeValues: { - ":InboundConversationId": inboundConversationId - }, ExpressionAttributeNames: { "#InboundConversationId": "InboundConversationId" }, + ExpressionAttributeValues: { + ":InboundConversationId": inboundConversationId + }, KeyConditionExpression: "#InboundConversationId = :InboundConversationId" }; + switch (queryType) { + case QueryType.ALL: + break; + case QueryType.CONVERSATION: + case QueryType.CORE: + case QueryType.FRAGMENT: + params.ExpressionAttributeNames['#sortKey'] = 'Layer'; + params.ExpressionAttributeValues[':sortKey'] = queryType; + params.KeyConditionExpression += " AND begins_with(#sortKey, :sortKey)"; + break; + default: + logInfo(`Received unexpected queryType: ${queryType}. Will treat it as 'ALL'.`) + } + const command = new QueryCommand(params); const response = await this.client.send(command); @@ -188,4 +204,8 @@ export class EhrTransferTracker { }); await this.client.send(command); }; + + async updateHealthRecordCompleteness(conversationId) { + + } } \ No newline at end of file From 554bec97d634703b38ec49e6597ea77395c5c358 Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Tue, 5 Mar 2024 17:51:33 +0000 Subject: [PATCH 10/67] [PRMT-4566] Add state enums --- src/models/enums.js | 29 ++++++++++++ src/models/queryType.js | 8 ---- ...o-ehr-transfer-tracker.integration.test.js | 2 +- .../database/dynamo-ehr-transfer-tracker.js | 46 ++++++++++++++----- 4 files changed, 65 insertions(+), 20 deletions(-) create mode 100644 src/models/enums.js delete mode 100644 src/models/queryType.js diff --git a/src/models/enums.js b/src/models/enums.js new file mode 100644 index 00000000..b6047dcd --- /dev/null +++ b/src/models/enums.js @@ -0,0 +1,29 @@ +export const QueryType = { + ALL: 'ALL', + CONVERSATION: 'Conversation', + CORE: 'Core', + FRAGMENT: 'Fragment', +}; + +export const ConversationStates = { + STARTED: 'INBOUND_STARTED', + REQUEST_SENT: 'INBOUND_REQUEST_SENT', + CONTINUE_REQUEST_SENT: 'INBOUND_CONTINUE_REQUEST_SENT', + COMPLETE: 'INBOUND_COMPLETE', + FAILED: 'INBOUND_FAILED', + TIMEOUT: 'INBOUND_TIMEOUT', +} + +export const CoreStates = { + COMPLETE: 'INBOUND_COMPLETE', +} + +export const FragmentStates = { + PENDING: 'INBOUND_REQUEST_SENT', + COMPLETE: 'INBOUND_COMPLETE', +} + +Object.freeze(QueryType) +Object.freeze(ConversationStates) +Object.freeze(CoreStates) +Object.freeze(FragmentStates) \ No newline at end of file diff --git a/src/models/queryType.js b/src/models/queryType.js deleted file mode 100644 index ea47a0f2..00000000 --- a/src/models/queryType.js +++ /dev/null @@ -1,8 +0,0 @@ -export const QueryType = { - ALL: 'ALL', - CONVERSATION: 'Conversation', - CORE: 'Core', - FRAGMENT: 'Fragment', -}; - -Object.freeze(QueryType) \ No newline at end of file diff --git a/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js b/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js index 194f7b79..36e74b24 100644 --- a/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js +++ b/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js @@ -1,7 +1,7 @@ import { EhrTransferTracker } from "../dynamo-ehr-transfer-tracker"; import { v4 as uuid } from "uuid"; import { createConversationForTest, deleteConversationForTest } from "../../../models/conversation"; -import { QueryType } from "../../../models/queryType"; +import { QueryType } from "../../../models/enums"; describe("EhrTransferTracker", () => { const testConversationId = uuid(); diff --git a/src/services/database/dynamo-ehr-transfer-tracker.js b/src/services/database/dynamo-ehr-transfer-tracker.js index 4a413511..7d663a67 100644 --- a/src/services/database/dynamo-ehr-transfer-tracker.js +++ b/src/services/database/dynamo-ehr-transfer-tracker.js @@ -1,10 +1,9 @@ import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; -import { DynamoDBDocumentClient, TransactWriteCommand, QueryCommand } from "@aws-sdk/lib-dynamodb"; +import { DynamoDBDocumentClient, TransactWriteCommand, QueryCommand, UpdateCommand } from "@aws-sdk/lib-dynamodb"; import { getUKTimestamp } from "../time"; import { logError, logInfo } from "../../middleware/logging"; -import { updateHealthRecordCompleteness } from "./health-record-repository"; -import { QueryType } from "../../models/QueryType"; +import { QueryType, ConversationStates } from "../../models/enums"; export class EhrTransferTracker { @@ -21,13 +20,14 @@ export class EhrTransferTracker { }; const isInLocal = process.env.NHS_ENVIRONMENT === "local" || !process.env.NHS_ENVIRONMENT; - const isInDojo = process.env.DOJO_VERSION !== undefined + const isInDojo = process.env.DOJO_VERSION !== undefined; if (isInLocal) { + // for running whole integration test suite in dojo clientConfig.endpoint = process.env.DYNAMODB_ENDPOINT; } - // for running individual test in IDE if (isInLocal && !isInDojo) { + // for running individual test with IDE clientConfig.endpoint = "http://localhost:4573"; this.tableName = "local-test-db"; } @@ -119,9 +119,10 @@ export class EhrTransferTracker { throw new Error("No record was found for given NHS number"); } - // TODO: to compare "completed at" rather then "created at" - const currentRecord = items.reduce((prev, current) => { - return (current && current?.CreatedAt > prev?.CreatedAt) ? current : prev; + const completedRecords = items.filter(item => item.State === ConversationStates.COMPLETE || item.State.startsWith('Outbound')); + + const currentRecord = completedRecords.reduce((prev, current) => { + return (current && current?.UpdatedAt > prev?.UpdatedAt) ? current : prev; }); return currentRecord.InboundConversationId; @@ -145,12 +146,12 @@ export class EhrTransferTracker { case QueryType.CONVERSATION: case QueryType.CORE: case QueryType.FRAGMENT: - params.ExpressionAttributeNames['#sortKey'] = 'Layer'; - params.ExpressionAttributeValues[':sortKey'] = queryType; + params.ExpressionAttributeNames["#sortKey"] = "Layer"; + params.ExpressionAttributeValues[":sortKey"] = queryType; params.KeyConditionExpression += " AND begins_with(#sortKey, :sortKey)"; break; default: - logInfo(`Received unexpected queryType: ${queryType}. Will treat it as 'ALL'.`) + logInfo(`Received unexpected queryType: ${queryType}. Will treat it as 'ALL'.`); } const command = new QueryCommand(params); @@ -206,6 +207,29 @@ export class EhrTransferTracker { }; async updateHealthRecordCompleteness(conversationId) { + const allFragments = this.queryTableByConversationId(conversationId, QueryType.FRAGMENT); + const pendingMessages = allFragments.filter(fragment => fragment.receivedAt === undefined); + if (pendingMessages.length !== 0) { + logInfo(`${pendingMessages.length} more fragments to be received.`); + return; + } + + logInfo("All fragments are received. Will mark this conversation as complete"); + + const timestamp = getUKTimestamp(); + const updateParam = { + TableName: this.tableName, + Key: { + InboundConversationId: conversationId, + Layer: "Conversation" + }, + UpdateExpression: "set UpdatedAt = :now, State = :complete", + ExpressionAttributeValues: { + ":now": timestamp, + ":complete": ConversationStates.COMPLETE + } + }; + await this.client.send(new UpdateCommand(updateParam)); } } \ No newline at end of file From 1fac37fe65b5f4bbb31a6cca0f81b643d59a8a3b Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Tue, 5 Mar 2024 18:34:08 +0000 Subject: [PATCH 11/67] [PRMT-4566] separate client and repository layer --- src/models/conversation.js | 11 +---- src/models/ehrCore.js | 12 +++-- src/models/ehrFragment.js | 1 - ...o-ehr-transfer-tracker.integration.test.js | 11 ++--- .../database/dynamo-ehr-transfer-tracker.js | 49 ++----------------- src/services/database/dynamodb-client.js | 23 +++++++++ 6 files changed, 41 insertions(+), 66 deletions(-) create mode 100644 src/services/database/dynamodb-client.js diff --git a/src/models/conversation.js b/src/models/conversation.js index 22cfacfa..a0461b3e 100644 --- a/src/models/conversation.js +++ b/src/models/conversation.js @@ -25,7 +25,7 @@ export const createConversationForTest = async (conversationId, nhsNumber) => { await db.writeItemsToTable([item]); }; -export const deleteConversationForTest = async (conversationId) => { +export const cleanupRecordsForTest = async (conversationId) => { // This method is only meant for testing purpose, const isInLocal = process.env.NHS_ENVIRONMENT === "local" || !process.env.NHS_ENVIRONMENT; @@ -49,14 +49,5 @@ export const deleteConversationForTest = async (conversationId) => { } ); - - // const deleteCommand = new DeleteCommand({ - // TableName: db.tableName, - // Key: { - // InboundConversationId: records[0].InboundConversationId, - // Layer: records[0].Layer - // } - // }); - await db.client.send(deleteCommand); }; \ No newline at end of file diff --git a/src/models/ehrCore.js b/src/models/ehrCore.js index daacdc5f..5a660328 100644 --- a/src/models/ehrCore.js +++ b/src/models/ehrCore.js @@ -1,17 +1,18 @@ import { getUKTimestamp } from "../services/time"; import { EhrTransferTracker } from "../services/database/dynamo-ehr-transfer-tracker"; import { arrayOfFragments } from "./ehrFragment"; +import { CoreStates } from "./enums"; -export const createCore = async ({ inboundConversationId, messageId, fragmentMessageIds }) => { +export const createCore = async ({ conversationId, messageId, fragmentMessageIds }) => { // to replace the existing `createEhrExtract` method const db = EhrTransferTracker.getInstance(); - const itemsToWrite = [ehrCore({ inboundConversationId, messageId })]; + const itemsToWrite = [ehrCore(conversationId, messageId)]; if (fragmentMessageIds) { const directFragments = arrayOfFragments( { - inboundConversationId, + inboundConversationId: conversationId, fragmentMessageIds, parentMessageId: messageId } @@ -23,7 +24,7 @@ export const createCore = async ({ inboundConversationId, messageId, fragmentMes }; -export const ehrCore = ({ inboundConversationId, messageId }) => { +export const ehrCore = (inboundConversationId, messageId) => { const timestamp = getUKTimestamp(); return { InboundConversationId: inboundConversationId, @@ -31,6 +32,7 @@ export const ehrCore = ({ inboundConversationId, messageId }) => { InboundMessageId: messageId, CreatedAt: timestamp, ReceivedAt: timestamp, - UpdatedAt: timestamp + UpdatedAt: timestamp, + State: CoreStates.COMPLETE }; }; \ No newline at end of file diff --git a/src/models/ehrFragment.js b/src/models/ehrFragment.js index 77250a48..6dff9917 100644 --- a/src/models/ehrFragment.js +++ b/src/models/ehrFragment.js @@ -1,5 +1,4 @@ import { getUKTimestamp } from "../services/time"; -import { EhrTransferTracker } from "../services/database/dynamo-ehr-transfer-tracker"; diff --git a/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js b/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js index 36e74b24..787905ea 100644 --- a/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js +++ b/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js @@ -1,7 +1,8 @@ import { EhrTransferTracker } from "../dynamo-ehr-transfer-tracker"; import { v4 as uuid } from "uuid"; -import { createConversationForTest, deleteConversationForTest } from "../../../models/conversation"; +import { createConversationForTest, cleanupRecordsForTest } from "../../../models/conversation"; import { QueryType } from "../../../models/enums"; +import { createCore } from "../../../models/ehrCore"; describe("EhrTransferTracker", () => { const testConversationId = uuid(); @@ -12,14 +13,13 @@ describe("EhrTransferTracker", () => { }); afterEach(async () => { - await deleteConversationForTest(testConversationId); + await cleanupRecordsForTest(testConversationId); }); it("create and read an ehrCore in dynamodb", async () => { // given const db = EhrTransferTracker.getInstance(); const testMessageId = uuid(); - const testNhsNumber = "9000000001"; const ehrExtract = { conversationId: testConversationId, @@ -27,8 +27,7 @@ describe("EhrTransferTracker", () => { nhsNumber: testNhsNumber }; - // when - await db.createCore(ehrExtract); + await createCore(ehrExtract); // then const actual = await db.queryTableByConversationId(testConversationId, QueryType.CORE); @@ -60,7 +59,7 @@ describe("EhrTransferTracker", () => { fragmentMessageIds: testChildMessageIds }; - await db.createCore(ehrExtract); + await createCore(ehrExtract); // when await db.updateFragmentAndCreateItsParts(testChildMessageIds[0], testConversationId); diff --git a/src/services/database/dynamo-ehr-transfer-tracker.js b/src/services/database/dynamo-ehr-transfer-tracker.js index 7d663a67..5c3bf513 100644 --- a/src/services/database/dynamo-ehr-transfer-tracker.js +++ b/src/services/database/dynamo-ehr-transfer-tracker.js @@ -1,9 +1,9 @@ -import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; -import { DynamoDBDocumentClient, TransactWriteCommand, QueryCommand, UpdateCommand } from "@aws-sdk/lib-dynamodb"; +import { TransactWriteCommand, QueryCommand, UpdateCommand } from "@aws-sdk/lib-dynamodb"; import { getUKTimestamp } from "../time"; import { logError, logInfo } from "../../middleware/logging"; import { QueryType, ConversationStates } from "../../models/enums"; +import { getDynamodbClient } from "./dynamodb-client"; export class EhrTransferTracker { @@ -15,25 +15,15 @@ export class EhrTransferTracker { this.tableName = process.env.DYNAMODB_NAME; - const clientConfig = { - region: process.env.AWS_DEFAULT_REGION ?? "eu-west-2" - }; - const isInLocal = process.env.NHS_ENVIRONMENT === "local" || !process.env.NHS_ENVIRONMENT; const isInDojo = process.env.DOJO_VERSION !== undefined; - if (isInLocal) { - // for running whole integration test suite in dojo - clientConfig.endpoint = process.env.DYNAMODB_ENDPOINT; - } if (isInLocal && !isInDojo) { // for running individual test with IDE - clientConfig.endpoint = "http://localhost:4573"; this.tableName = "local-test-db"; } - const baseClient = new DynamoDBClient(clientConfig); - this.client = DynamoDBDocumentClient.from(baseClient); + this.client = getDynamodbClient(); } static getInstance() { @@ -43,35 +33,6 @@ export class EhrTransferTracker { return new this(); } - async createCore(ehrExtract) { - - const { conversationId, messageId, nhsNumber, fragmentMessageIds } = ehrExtract; - const timestamp = getUKTimestamp(); - - const core = { - InboundConversationId: conversationId, - Layer: `Core#${messageId}`, - InboundMessageId: messageId, - CreatedAt: timestamp, - ReceivedAt: timestamp, - UpdatedAt: timestamp - }; - const fragments = fragmentMessageIds ? fragmentMessageIds.map(fragmentMessageId => { - return { - InboundConversationId: conversationId, - Layer: `Fragment#${fragmentMessageId}`, - InboundMessageId: fragmentMessageId, - ParentId: messageId, - CreatedAt: timestamp, - UpdatedAt: timestamp - }; - }) : []; - - const itemsToWrite = [core, ...fragments]; - - await this.writeItemsToTable(itemsToWrite); - } - async writeItemsToTable(items) { if (!items || !Array.isArray(items)) { throw new Error("The given argument `items` is not an array"); @@ -132,12 +93,12 @@ export class EhrTransferTracker { const params = { TableName: this.tableName, ExpressionAttributeNames: { - "#InboundConversationId": "InboundConversationId" + "#PrimaryKey": "InboundConversationId" }, ExpressionAttributeValues: { ":InboundConversationId": inboundConversationId }, - KeyConditionExpression: "#InboundConversationId = :InboundConversationId" + KeyConditionExpression: "#PrimaryKey = :InboundConversationId" }; switch (queryType) { diff --git a/src/services/database/dynamodb-client.js b/src/services/database/dynamodb-client.js new file mode 100644 index 00000000..a5cf4271 --- /dev/null +++ b/src/services/database/dynamodb-client.js @@ -0,0 +1,23 @@ +import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; +import { DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb"; + +export const getDynamodbClient = () => { + const clientConfig = { + region: process.env.AWS_DEFAULT_REGION ?? "eu-west-2" + }; + + const isInLocal = process.env.NHS_ENVIRONMENT === "local" || !process.env.NHS_ENVIRONMENT; + const isInDojo = process.env.DOJO_VERSION !== undefined; + + if (isInLocal) { + // for running whole integration test suite in dojo + clientConfig.endpoint = process.env.DYNAMODB_ENDPOINT; + } + if (isInLocal && !isInDojo) { + // for running individual test with IDE + clientConfig.endpoint = "http://localhost:4573"; + } + + const baseClient = new DynamoDBClient(clientConfig); + return DynamoDBDocumentClient.from(baseClient); +} \ No newline at end of file From 68ef951554698d84cc9a9c195764a303a4e0819d Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Tue, 5 Mar 2024 22:08:54 +0000 Subject: [PATCH 12/67] [PRMT-4566] Reorganise new codes --- src/models/core.js | 15 +++++ src/models/ehrCore.js | 38 ------------ src/models/ehrFragment.js | 24 -------- src/models/fragment.js | 54 ++++++++++++++++ ...o-ehr-transfer-tracker.integration.test.js | 12 ++-- .../database/dynamo-ehr-transfer-tracker.js | 61 ++++++++----------- src/services/database/ehr-core-repository.js | 24 ++++++++ .../database/ehr-fragment-repository.js | 5 ++ 8 files changed, 132 insertions(+), 101 deletions(-) create mode 100644 src/models/core.js delete mode 100644 src/models/ehrCore.js delete mode 100644 src/models/ehrFragment.js create mode 100644 src/models/fragment.js create mode 100644 src/services/database/ehr-core-repository.js create mode 100644 src/services/database/ehr-fragment-repository.js diff --git a/src/models/core.js b/src/models/core.js new file mode 100644 index 00000000..b3b4750e --- /dev/null +++ b/src/models/core.js @@ -0,0 +1,15 @@ +import { getUKTimestamp } from "../services/time"; +import { CoreStates } from "./enums"; + +export const core = (inboundConversationId, messageId) => { + const timestamp = getUKTimestamp(); + return { + InboundConversationId: inboundConversationId, + Layer: `Core#${messageId}`, + InboundMessageId: messageId, + CreatedAt: timestamp, + ReceivedAt: timestamp, + UpdatedAt: timestamp, + State: CoreStates.COMPLETE + }; +}; \ No newline at end of file diff --git a/src/models/ehrCore.js b/src/models/ehrCore.js deleted file mode 100644 index 5a660328..00000000 --- a/src/models/ehrCore.js +++ /dev/null @@ -1,38 +0,0 @@ -import { getUKTimestamp } from "../services/time"; -import { EhrTransferTracker } from "../services/database/dynamo-ehr-transfer-tracker"; -import { arrayOfFragments } from "./ehrFragment"; -import { CoreStates } from "./enums"; - -export const createCore = async ({ conversationId, messageId, fragmentMessageIds }) => { - // to replace the existing `createEhrExtract` method - - const db = EhrTransferTracker.getInstance(); - const itemsToWrite = [ehrCore(conversationId, messageId)]; - - if (fragmentMessageIds) { - const directFragments = arrayOfFragments( - { - inboundConversationId: conversationId, - fragmentMessageIds, - parentMessageId: messageId - } - ); - itemsToWrite.push(...directFragments); - } - - await db.writeItemsToTable(itemsToWrite); -}; - - -export const ehrCore = (inboundConversationId, messageId) => { - const timestamp = getUKTimestamp(); - return { - InboundConversationId: inboundConversationId, - Layer: `Core#${messageId}`, - InboundMessageId: messageId, - CreatedAt: timestamp, - ReceivedAt: timestamp, - UpdatedAt: timestamp, - State: CoreStates.COMPLETE - }; -}; \ No newline at end of file diff --git a/src/models/ehrFragment.js b/src/models/ehrFragment.js deleted file mode 100644 index 6dff9917..00000000 --- a/src/models/ehrFragment.js +++ /dev/null @@ -1,24 +0,0 @@ -import { getUKTimestamp } from "../services/time"; - - - -export const singleFragment = ({ inboundConversationId, fragmentMessageId, parentMessageId }) => { - const timestamp = getUKTimestamp(); - return { - InboundConversationId: inboundConversationId, - Layer: `Fragment#${fragmentMessageId}`, - InboundMessageId: fragmentMessageId, - ParentId: parentMessageId, - CreatedAt: timestamp, - UpdatedAt: timestamp - }; -}; - -export const arrayOfFragments = ({ inboundConversationId, fragmentMessageIds, parentMessageId }) => { - if (!fragmentMessageIds || !Array.isArray(fragmentMessageIds)) { - return []; - } - return fragmentMessageIds.map(fragmentMessageId => singleFragment( - { inboundConversationId, fragmentMessageId, parentMessageId } - )); -}; \ No newline at end of file diff --git a/src/models/fragment.js b/src/models/fragment.js new file mode 100644 index 00000000..ddc05868 --- /dev/null +++ b/src/models/fragment.js @@ -0,0 +1,54 @@ +import { getUKTimestamp } from "../services/time"; + + + +export const singleFragment = ({ inboundConversationId, fragmentMessageId, parentMessageId }) => { + const timestamp = getUKTimestamp(); + return { + InboundConversationId: inboundConversationId, + Layer: `Fragment#${fragmentMessageId}`, + InboundMessageId: fragmentMessageId, + ParentId: parentMessageId, + CreatedAt: timestamp, + UpdatedAt: timestamp + }; +}; + +export const arrayOfFragments = ({ inboundConversationId, fragmentMessageIds, parentMessageId }) => { + if (!fragmentMessageIds || !Array.isArray(fragmentMessageIds)) { + return []; + } + return fragmentMessageIds.map(fragmentMessageId => singleFragment( + { inboundConversationId, fragmentMessageId, parentMessageId } + )); +}; + +const FieldsAllowedToUpdate = { + State: 'State', + ParentId: 'ParentId', + ReceivedAt: 'ReceivedAt', + DeletedAt: 'DeletedAt' +} + +export const fragmentUpdateParams = (conversationId, messageId, changes) => { + const params = { + Key: { + InboundConversationId: conversationId, + Layer: `Fragment#${messageId}` + }, + UpdateExpression: "set CreatedAt = if_not_exists(CreatedAt, :now), UpdatedAt = :now", + ExpressionAttributeValues: { + ":now": getUKTimestamp() + } + }; + + for (const fieldname in FieldsAllowedToUpdate) { + if (fieldname in changes){ + const colonKey = `:${fieldname}` + params.UpdateExpression += `, ${fieldname} = ${colonKey}`; + params.ExpressionAttributeValues[colonKey] = changes[fieldname] + } + } + + return params; +} \ No newline at end of file diff --git a/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js b/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js index 787905ea..3ccec96c 100644 --- a/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js +++ b/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js @@ -2,7 +2,7 @@ import { EhrTransferTracker } from "../dynamo-ehr-transfer-tracker"; import { v4 as uuid } from "uuid"; import { createConversationForTest, cleanupRecordsForTest } from "../../../models/conversation"; import { QueryType } from "../../../models/enums"; -import { createCore } from "../../../models/ehrCore"; +import { createCore } from "../ehr-core-repository"; describe("EhrTransferTracker", () => { const testConversationId = uuid(); @@ -13,7 +13,7 @@ describe("EhrTransferTracker", () => { }); afterEach(async () => { - await cleanupRecordsForTest(testConversationId); + // await cleanupRecordsForTest(testConversationId); }); it("create and read an ehrCore in dynamodb", async () => { @@ -51,6 +51,7 @@ describe("EhrTransferTracker", () => { const testMessageId = uuid(); const testNhsNumber = "9000000002"; const testChildMessageIds = [uuid(), uuid(), uuid()]; + const testNestedChildIds = [uuid(), uuid(), uuid()]; const ehrExtract = { conversationId: testConversationId, @@ -62,18 +63,19 @@ describe("EhrTransferTracker", () => { await createCore(ehrExtract); // when - await db.updateFragmentAndCreateItsParts(testChildMessageIds[0], testConversationId); + await db.updateFragmentAndCreateItsParts(testChildMessageIds[0], testConversationId, testNestedChildIds); const actual = await db.queryTableByConversationId(testConversationId, QueryType.FRAGMENT); // then - expect(actual).toHaveLength(testChildMessageIds.length); + const expectedTotalMessages = testChildMessageIds.length + testNestedChildIds.length; + expect(actual).toHaveLength(expectedTotalMessages); const receivedFragment = actual.filter(item => item.ReceivedAt); expect(receivedFragment).toHaveLength(1); expect(receivedFragment[0].InboundMessageId).toEqual(testChildMessageIds[0]); const nonReceivedFragments = actual.filter(item => !item.ReceivedAt); - expect(nonReceivedFragments).toHaveLength(2); + expect(nonReceivedFragments).toHaveLength(5); }); }); \ No newline at end of file diff --git a/src/services/database/dynamo-ehr-transfer-tracker.js b/src/services/database/dynamo-ehr-transfer-tracker.js index 5c3bf513..f953794b 100644 --- a/src/services/database/dynamo-ehr-transfer-tracker.js +++ b/src/services/database/dynamo-ehr-transfer-tracker.js @@ -4,6 +4,7 @@ import { getUKTimestamp } from "../time"; import { logError, logInfo } from "../../middleware/logging"; import { QueryType, ConversationStates } from "../../models/enums"; import { getDynamodbClient } from "./dynamodb-client"; +import { fragmentUpdateParams } from "../../models/fragment"; export class EhrTransferTracker { @@ -48,6 +49,21 @@ export class EhrTransferTracker { await this.client.send(command); } + async updateItemsInTransaction(updateParams) { + if (!updateParams || !Array.isArray(updateParams)) { + throw new Error("The given argument `updateParams` is not an array"); + } + const command = new TransactWriteCommand({ + TransactItems: updateParams.map((params) => ({ + Update: { + TableName: this.tableName, ...params + } + })) + }); + + await this.client.send(command); + } + async queryTableByNhsNumber(nhsNumber) { const params = { TableName: this.tableName, @@ -80,7 +96,7 @@ export class EhrTransferTracker { throw new Error("No record was found for given NHS number"); } - const completedRecords = items.filter(item => item.State === ConversationStates.COMPLETE || item.State.startsWith('Outbound')); + const completedRecords = items.filter(item => item.State === ConversationStates.COMPLETE || item.State.startsWith("Outbound")); const currentRecord = completedRecords.reduce((prev, current) => { return (current && current?.UpdatedAt > prev?.UpdatedAt) ? current : prev; @@ -127,44 +143,21 @@ export class EhrTransferTracker { async updateFragmentAndCreateItsParts(messageId, conversationId, - remainingPartsIds) { + remainingPartsIds = []) { // to replace the existing methods `updateFragmentAndCreateItsParts` and `createFragmentPart` const timestamp = getUKTimestamp(); - const currentFragment = { - TableName: this.tableName, - Key: { - InboundConversationId: conversationId, - Layer: `Fragment#${messageId}` - }, - UpdateExpression: "set ReceivedAt = :now, CreatedAt = if_not_exists(CreatedAt, :now), UpdatedAt = :now", - ExpressionAttributeValues: { - ":now": timestamp - } - }; - - const childFragments = remainingPartsIds ? remainingPartsIds.map(fragmentPartId => { - return ({ - TableName: this.tableName, - Key: { - InboundConversationId: conversationId, - Layer: `Fragment#${fragmentPartId}` - }, - UpdateExpression: "set CreatedAt = if_not_exists(CreatedAt, :now), UpdatedAt = :now, ParentId = :parentMessageId", - ExpressionAttributeValues: { - ":now": timestamp, - ":parentMessageId": messageId - } - }); - }) : []; + const currentFragmentParams = fragmentUpdateParams(conversationId, messageId, { ReceivedAt: timestamp }); - const updateParams = [currentFragment, ...childFragments]; - const command = new TransactWriteCommand({ - TransactItems: updateParams.map((param) => ({ - Update: param - })) + const childFragmentsParams = remainingPartsIds.map(fragmentPartId => { + return fragmentUpdateParams( + conversationId, + fragmentPartId, + { ParentId: messageId } + ); }); - await this.client.send(command); + + await this.updateItemsInTransaction([currentFragmentParams, ...childFragmentsParams]); }; async updateHealthRecordCompleteness(conversationId) { diff --git a/src/services/database/ehr-core-repository.js b/src/services/database/ehr-core-repository.js new file mode 100644 index 00000000..c439ca08 --- /dev/null +++ b/src/services/database/ehr-core-repository.js @@ -0,0 +1,24 @@ +import { EhrTransferTracker } from "./dynamo-ehr-transfer-tracker"; +import { arrayOfFragments } from "../../models/fragment"; +import { core } from "../../models/core"; + +export const createCore = async ({ conversationId, messageId, fragmentMessageIds }) => { + // to replace the existing `createEhrExtract` method + + const db = EhrTransferTracker.getInstance(); + const itemsToWrite = [core(conversationId, messageId)]; + + if (fragmentMessageIds) { + const directFragments = arrayOfFragments( + { + inboundConversationId: conversationId, + fragmentMessageIds, + parentMessageId: messageId + } + ); + itemsToWrite.push(...directFragments); + } + + await db.writeItemsToTable(itemsToWrite); +}; + diff --git a/src/services/database/ehr-fragment-repository.js b/src/services/database/ehr-fragment-repository.js new file mode 100644 index 00000000..122e2ddb --- /dev/null +++ b/src/services/database/ehr-fragment-repository.js @@ -0,0 +1,5 @@ +import { getUKTimestamp } from "../time"; +import { TransactWriteCommand } from "@aws-sdk/lib-dynamodb"; +import { EhrTransferTracker } from "./dynamo-ehr-transfer-tracker"; + +// TODO: move new version of updateFragmentAndCreateItsParts to here \ No newline at end of file From ff535989d7b014708062149252219b8e86f62daa Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Wed, 6 Mar 2024 09:14:32 +0000 Subject: [PATCH 13/67] [PRMT-4566] Add terraform iam policy for accessing the ehr-transfer-tracker table --- terraform-db-roles/dynamodb.tf | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 terraform-db-roles/dynamodb.tf diff --git a/terraform-db-roles/dynamodb.tf b/terraform-db-roles/dynamodb.tf new file mode 100644 index 00000000..24139c6f --- /dev/null +++ b/terraform-db-roles/dynamodb.tf @@ -0,0 +1,24 @@ +data "aws_iam_policy_document" "ehr-transfer-tracker-db-access" { + statement { + actions = [ + "dynamodb:GetItem", + "dynamodb:PutItem", + "dynamodb:UpdateItem", + "dynamodb:Query" + ] + resources = [ + "arn:aws:dynamodb:${var.region}:${data.aws_caller_identity.current.account_id}:table/${var.environment}-ehr-transfer-tracker" + ] + } +} + +resource "aws_iam_policy" "ehr-transfer-tracker-db-access" { + name = "${var.environment}-${var.component_name}-transfer-tracker-db-access" + policy = data.aws_iam_policy_document.ehr-transfer-tracker-db-access.json +} + +# Grant ECS Task permissions to access the transfer tracker db +resource "aws_iam_role_policy_attachment" "dynamodb_application_user_policy_attach" { + role = "${var.environment}-${var.component_name}-EcsTaskRole" + policy_arn = aws_iam_policy.ehr-transfer-tracker-db-access.arn +} From 590c11feff8c42dd1d547aed97bbea4137e37575 Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Wed, 6 Mar 2024 09:16:37 +0000 Subject: [PATCH 14/67] [PRMT-4566] Reorganise code, use CreatedAt instead of UpdatedAt to determine latest record --- src/models/conversation.js | 50 ----------------- ...o-ehr-transfer-tracker.integration.test.js | 4 +- .../database/dynamo-ehr-transfer-tracker.js | 2 +- .../database/ehr-conversation-repository.js | 0 src/utilities/integration-test-utilities.js | 55 +++++++++++++++++++ 5 files changed, 58 insertions(+), 53 deletions(-) create mode 100644 src/services/database/ehr-conversation-repository.js diff --git a/src/models/conversation.js b/src/models/conversation.js index a0461b3e..d0c36dc7 100644 --- a/src/models/conversation.js +++ b/src/models/conversation.js @@ -1,53 +1,3 @@ import { getUKTimestamp } from "../services/time"; import { EhrTransferTracker } from "../services/database/dynamo-ehr-transfer-tracker"; import { DeleteCommand, TransactWriteCommand } from "@aws-sdk/lib-dynamodb"; - -export const createConversationForTest = async (conversationId, nhsNumber) => { - // This method is only meant for testing purpose, - // as the inbound conversation record is supposed to be created by other service. - - const isInLocal = process.env.NHS_ENVIRONMENT === "local" || !process.env.NHS_ENVIRONMENT; - if (!isInLocal) { - throw new Error("Unexpected call to createConversation method in non-local environment"); - } - - const timestamp = getUKTimestamp(); - const db = EhrTransferTracker.getInstance(); - - const item = { - InboundConversationId: conversationId, - Layer: "Conversation", - NhsNumber: nhsNumber, - CreatedAt: timestamp, - UpdatedAt: timestamp - }; - - await db.writeItemsToTable([item]); -}; - -export const cleanupRecordsForTest = async (conversationId) => { - // This method is only meant for testing purpose, - - const isInLocal = process.env.NHS_ENVIRONMENT === "local" || !process.env.NHS_ENVIRONMENT; - if (!isInLocal) { - throw new Error("Unexpected call to createConversation method in non-local environment"); - } - - const db = EhrTransferTracker.getInstance(); - const records = await db.queryTableByConversationId(conversationId); - const deleteCommand = new TransactWriteCommand( - { - TransactItems: records.map(item => ({ - Delete: { - TableName: db.tableName, - Key: { - InboundConversationId: item.InboundConversationId, - Layer: item.Layer - } - } - })) - } - ); - - await db.client.send(deleteCommand); -}; \ No newline at end of file diff --git a/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js b/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js index 3ccec96c..f62cf598 100644 --- a/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js +++ b/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js @@ -1,8 +1,8 @@ import { EhrTransferTracker } from "../dynamo-ehr-transfer-tracker"; import { v4 as uuid } from "uuid"; -import { createConversationForTest, cleanupRecordsForTest } from "../../../models/conversation"; import { QueryType } from "../../../models/enums"; import { createCore } from "../ehr-core-repository"; +import { cleanupRecordsForTest, createConversationForTest } from "../../../utilities/integration-test-utilities"; describe("EhrTransferTracker", () => { const testConversationId = uuid(); @@ -13,7 +13,7 @@ describe("EhrTransferTracker", () => { }); afterEach(async () => { - // await cleanupRecordsForTest(testConversationId); + await cleanupRecordsForTest(testConversationId); }); it("create and read an ehrCore in dynamodb", async () => { diff --git a/src/services/database/dynamo-ehr-transfer-tracker.js b/src/services/database/dynamo-ehr-transfer-tracker.js index f953794b..3aa11097 100644 --- a/src/services/database/dynamo-ehr-transfer-tracker.js +++ b/src/services/database/dynamo-ehr-transfer-tracker.js @@ -99,7 +99,7 @@ export class EhrTransferTracker { const completedRecords = items.filter(item => item.State === ConversationStates.COMPLETE || item.State.startsWith("Outbound")); const currentRecord = completedRecords.reduce((prev, current) => { - return (current && current?.UpdatedAt > prev?.UpdatedAt) ? current : prev; + return (current && current?.CreatedAt > prev?.CreatedAt) ? current : prev; }); return currentRecord.InboundConversationId; diff --git a/src/services/database/ehr-conversation-repository.js b/src/services/database/ehr-conversation-repository.js new file mode 100644 index 00000000..e69de29b diff --git a/src/utilities/integration-test-utilities.js b/src/utilities/integration-test-utilities.js index 6fdc4438..46275331 100644 --- a/src/utilities/integration-test-utilities.js +++ b/src/utilities/integration-test-utilities.js @@ -1,4 +1,7 @@ import { v4 as uuidv4 } from 'uuid'; +import { getUKTimestamp } from "../services/time"; +import { EhrTransferTracker } from "../services/database/dynamo-ehr-transfer-tracker"; +import { TransactWriteCommand } from "@aws-sdk/lib-dynamodb"; export const generateRandomNhsNumber = () => (Math.floor(Math.random() * 9e9) + 1e9).toString(); @@ -9,3 +12,55 @@ export const generateMultipleUUID = (amount, isUppercase) => Array(amount) .fill(undefined) .map(() => (isUppercase ? uuidv4().toUpperCase() : uuidv4())); + + + +export const createConversationForTest = async (conversationId, nhsNumber) => { + // This method is only meant for testing purpose, + // as the inbound conversation record is supposed to be created by other service. + + const isInLocal = process.env.NHS_ENVIRONMENT === "local" || !process.env.NHS_ENVIRONMENT; + if (!isInLocal) { + throw new Error("Unexpected call to createConversation method in non-local environment"); + } + + const timestamp = getUKTimestamp(); + const db = EhrTransferTracker.getInstance(); + + const item = { + InboundConversationId: conversationId, + Layer: "Conversation", + NhsNumber: nhsNumber, + CreatedAt: timestamp, + UpdatedAt: timestamp + }; + + await db.writeItemsToTable([item]); +}; + +export const cleanupRecordsForTest = async (conversationId) => { + // This method is only meant for testing purpose + + const isInLocal = process.env.NHS_ENVIRONMENT === "local" || !process.env.NHS_ENVIRONMENT; + if (!isInLocal) { + throw new Error("Unexpected call to createConversation method in non-local environment"); + } + + const db = EhrTransferTracker.getInstance(); + const records = await db.queryTableByConversationId(conversationId); + const deleteCommand = new TransactWriteCommand( + { + TransactItems: records.map(item => ({ + Delete: { + TableName: db.tableName, + Key: { + InboundConversationId: item.InboundConversationId, + Layer: item.Layer + } + } + })) + } + ); + + await db.client.send(deleteCommand); +}; \ No newline at end of file From 45bc5e3e683e59662a7fea39adbc845aba56ae04 Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Wed, 6 Mar 2024 12:13:04 +0000 Subject: [PATCH 15/67] [PRMT-4566] Migrate all tests in message-repository.integration.test.js to new dynamodb-based implementation --- src/models/conversation.js | 27 +- src/models/core.js | 15 +- src/models/enums.js | 14 +- src/models/fragment.js | 51 ++-- ...o-ehr-transfer-tracker.integration.test.js | 45 ++-- .../message-repository.integration.test.js | 6 +- ...new-message-repository.integration.test.js | 240 ++++++++++++++++++ .../database/dynamo-ehr-transfer-tracker.js | 134 +++++----- src/services/database/dynamodb-client.js | 12 +- .../database/ehr-conversation-repository.js | 40 +++ src/services/database/ehr-core-repository.js | 38 ++- .../database/ehr-fragment-repository.js | 52 +++- src/services/database/message-repository.js | 16 ++ src/services/time.js | 4 +- src/utilities/integration-test-utilities.js | 46 ++-- 15 files changed, 567 insertions(+), 173 deletions(-) create mode 100644 src/services/database/__tests__/new-message-repository.integration.test.js diff --git a/src/models/conversation.js b/src/models/conversation.js index d0c36dc7..b6aa3e63 100644 --- a/src/models/conversation.js +++ b/src/models/conversation.js @@ -1,3 +1,26 @@ import { getUKTimestamp } from "../services/time"; -import { EhrTransferTracker } from "../services/database/dynamo-ehr-transfer-tracker"; -import { DeleteCommand, TransactWriteCommand } from "@aws-sdk/lib-dynamodb"; + +const fieldsAllowedToUpdate = ['State', 'FailureCode']; + +export const buildConversationUpdateParams = (conversationId, changes) => { + const params = { + Key: { + InboundConversationId: conversationId, + Layer: 'Conversation' + }, + UpdateExpression: "set UpdatedAt = :now", + ExpressionAttributeValues: { + ":now": getUKTimestamp() + } + }; + + for (const fieldname in fieldsAllowedToUpdate) { + if (fieldname in changes){ + const colonKey = `:${fieldname}` + params.UpdateExpression += `, ${fieldname} = ${colonKey}`; + params.ExpressionAttributeValues[colonKey] = changes[fieldname] + } + } + + return params; +} \ No newline at end of file diff --git a/src/models/core.js b/src/models/core.js index b3b4750e..4e31680a 100644 --- a/src/models/core.js +++ b/src/models/core.js @@ -1,8 +1,15 @@ -import { getUKTimestamp } from "../services/time"; -import { CoreStates } from "./enums"; +import { getUKTimestamp } from '../services/time'; +import { CoreStates } from './enums'; +import { validate } from 'uuid'; export const core = (inboundConversationId, messageId) => { const timestamp = getUKTimestamp(); + + const uuidsAreValid = validate(inboundConversationId) && validate(messageId); + if (!uuidsAreValid) { + throw new Error('received invalid uuid as either conversationId or messageId'); + } + return { InboundConversationId: inboundConversationId, Layer: `Core#${messageId}`, @@ -10,6 +17,6 @@ export const core = (inboundConversationId, messageId) => { CreatedAt: timestamp, ReceivedAt: timestamp, UpdatedAt: timestamp, - State: CoreStates.COMPLETE + State: CoreStates.COMPLETE, }; -}; \ No newline at end of file +}; diff --git a/src/models/enums.js b/src/models/enums.js index b6047dcd..f494d7b8 100644 --- a/src/models/enums.js +++ b/src/models/enums.js @@ -12,18 +12,18 @@ export const ConversationStates = { COMPLETE: 'INBOUND_COMPLETE', FAILED: 'INBOUND_FAILED', TIMEOUT: 'INBOUND_TIMEOUT', -} +}; export const CoreStates = { COMPLETE: 'INBOUND_COMPLETE', -} +}; export const FragmentStates = { PENDING: 'INBOUND_REQUEST_SENT', COMPLETE: 'INBOUND_COMPLETE', -} +}; -Object.freeze(QueryType) -Object.freeze(ConversationStates) -Object.freeze(CoreStates) -Object.freeze(FragmentStates) \ No newline at end of file +Object.freeze(QueryType); +Object.freeze(ConversationStates); +Object.freeze(CoreStates); +Object.freeze(FragmentStates); diff --git a/src/models/fragment.js b/src/models/fragment.js index ddc05868..cda822dc 100644 --- a/src/models/fragment.js +++ b/src/models/fragment.js @@ -1,54 +1,69 @@ -import { getUKTimestamp } from "../services/time"; - +import { getUKTimestamp } from '../services/time'; +import { validate } from 'uuid'; +const validateIds = (conversationId, messageId) => { + const uuidsAreValid = validate(conversationId) && validate(messageId); + if (!uuidsAreValid) { + throw new Error('received invalid uuid as either conversationId or messageId'); + } +}; export const singleFragment = ({ inboundConversationId, fragmentMessageId, parentMessageId }) => { const timestamp = getUKTimestamp(); + + validateIds(inboundConversationId, fragmentMessageId); + return { InboundConversationId: inboundConversationId, Layer: `Fragment#${fragmentMessageId}`, InboundMessageId: fragmentMessageId, ParentId: parentMessageId, CreatedAt: timestamp, - UpdatedAt: timestamp + UpdatedAt: timestamp, }; }; -export const arrayOfFragments = ({ inboundConversationId, fragmentMessageIds, parentMessageId }) => { +export const arrayOfFragments = ({ + inboundConversationId, + fragmentMessageIds, + parentMessageId, +}) => { if (!fragmentMessageIds || !Array.isArray(fragmentMessageIds)) { return []; } - return fragmentMessageIds.map(fragmentMessageId => singleFragment( - { inboundConversationId, fragmentMessageId, parentMessageId } - )); + return fragmentMessageIds.map((fragmentMessageId) => + singleFragment({ inboundConversationId, fragmentMessageId, parentMessageId }) + ); }; const FieldsAllowedToUpdate = { State: 'State', ParentId: 'ParentId', ReceivedAt: 'ReceivedAt', - DeletedAt: 'DeletedAt' -} + DeletedAt: 'DeletedAt', +}; + +export const buildFragmentUpdateParams = (conversationId, messageId, changes) => { + validateIds(conversationId, messageId); -export const fragmentUpdateParams = (conversationId, messageId, changes) => { const params = { Key: { InboundConversationId: conversationId, - Layer: `Fragment#${messageId}` + Layer: `Fragment#${messageId}`, }, - UpdateExpression: "set CreatedAt = if_not_exists(CreatedAt, :now), UpdatedAt = :now", + UpdateExpression: 'set CreatedAt = if_not_exists(CreatedAt, :now), UpdatedAt = :now', ExpressionAttributeValues: { - ":now": getUKTimestamp() - } + ':now': getUKTimestamp(), + }, }; for (const fieldname in FieldsAllowedToUpdate) { - if (fieldname in changes){ - const colonKey = `:${fieldname}` + if (fieldname in changes) { + const colonKey = `:${fieldname}`; params.UpdateExpression += `, ${fieldname} = ${colonKey}`; - params.ExpressionAttributeValues[colonKey] = changes[fieldname] + params.ExpressionAttributeValues[colonKey] = changes[fieldname]; } } return params; -} \ No newline at end of file +}; diff --git a/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js b/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js index f62cf598..6ebf99fa 100644 --- a/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js +++ b/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js @@ -1,12 +1,16 @@ -import { EhrTransferTracker } from "../dynamo-ehr-transfer-tracker"; -import { v4 as uuid } from "uuid"; -import { QueryType } from "../../../models/enums"; -import { createCore } from "../ehr-core-repository"; -import { cleanupRecordsForTest, createConversationForTest } from "../../../utilities/integration-test-utilities"; - -describe("EhrTransferTracker", () => { +import { EhrTransferTracker } from '../dynamo-ehr-transfer-tracker'; +import { v4 as uuid } from 'uuid'; +import { QueryType } from '../../../models/enums'; +import { createCore } from '../ehr-core-repository'; +import { + cleanupRecordsForTest, + createConversationForTest, +} from '../../../utilities/integration-test-utilities'; +import { markFragmentAsReceivedAndCreateItsParts } from '../ehr-fragment-repository'; + +describe('EhrTransferTracker', () => { const testConversationId = uuid(); - const testNhsNumber = "9000000001"; + const testNhsNumber = '9000000001'; beforeEach(async () => { await createConversationForTest(testConversationId, testNhsNumber); @@ -16,7 +20,7 @@ describe("EhrTransferTracker", () => { await cleanupRecordsForTest(testConversationId); }); - it("create and read an ehrCore in dynamodb", async () => { + it('create and read an ehrCore in dynamodb', async () => { // given const db = EhrTransferTracker.getInstance(); const testMessageId = uuid(); @@ -24,7 +28,7 @@ describe("EhrTransferTracker", () => { const ehrExtract = { conversationId: testConversationId, messageId: testMessageId, - nhsNumber: testNhsNumber + nhsNumber: testNhsNumber, }; await createCore(ehrExtract); @@ -39,17 +43,16 @@ describe("EhrTransferTracker", () => { Layer: `Core#${testMessageId}`, ReceivedAt: expect.any(String), CreatedAt: expect.any(String), - UpdatedAt: expect.any(String) + UpdatedAt: expect.any(String), }); }); - it("updateFragmentAndCreateItsParts", async () => { + it('updateFragmentAndCreateItsParts', async () => { // given const db = EhrTransferTracker.getInstance(); - const testMessageId = uuid(); - const testNhsNumber = "9000000002"; + const testNhsNumber = '9000000002'; const testChildMessageIds = [uuid(), uuid(), uuid()]; const testNestedChildIds = [uuid(), uuid(), uuid()]; @@ -57,13 +60,17 @@ describe("EhrTransferTracker", () => { conversationId: testConversationId, messageId: testMessageId, nhsNumber: testNhsNumber, - fragmentMessageIds: testChildMessageIds + fragmentMessageIds: testChildMessageIds, }; await createCore(ehrExtract); // when - await db.updateFragmentAndCreateItsParts(testChildMessageIds[0], testConversationId, testNestedChildIds); + await markFragmentAsReceivedAndCreateItsParts( + testChildMessageIds[0], + testConversationId, + testNestedChildIds + ); const actual = await db.queryTableByConversationId(testConversationId, QueryType.FRAGMENT); @@ -71,11 +78,11 @@ describe("EhrTransferTracker", () => { const expectedTotalMessages = testChildMessageIds.length + testNestedChildIds.length; expect(actual).toHaveLength(expectedTotalMessages); - const receivedFragment = actual.filter(item => item.ReceivedAt); + const receivedFragment = actual.filter((item) => item.ReceivedAt); expect(receivedFragment).toHaveLength(1); expect(receivedFragment[0].InboundMessageId).toEqual(testChildMessageIds[0]); - const nonReceivedFragments = actual.filter(item => !item.ReceivedAt); + const nonReceivedFragments = actual.filter((item) => !item.ReceivedAt); expect(nonReceivedFragments).toHaveLength(5); }); -}); \ No newline at end of file +}); diff --git a/src/services/database/__tests__/message-repository.integration.test.js b/src/services/database/__tests__/message-repository.integration.test.js index 5d3a1f7f..ebbff854 100644 --- a/src/services/database/__tests__/message-repository.integration.test.js +++ b/src/services/database/__tests__/message-repository.integration.test.js @@ -16,7 +16,11 @@ import expect from 'expect'; jest.mock('../../../middleware/logging'); jest.mock('../../time'); -describe('messageRepository', () => { +describe.skip('messageRepository', () => { + /** + * @deprecated + * All the tests here are migrated to new test suite of dynamodb-based implementation + */ const Message = ModelFactory.getByName(messageModelName); const HealthRecord = ModelFactory.getByName(healthRecordModelName); const ehrExtractType = MessageType.EHR_EXTRACT; diff --git a/src/services/database/__tests__/new-message-repository.integration.test.js b/src/services/database/__tests__/new-message-repository.integration.test.js new file mode 100644 index 00000000..92d77fd3 --- /dev/null +++ b/src/services/database/__tests__/new-message-repository.integration.test.js @@ -0,0 +1,240 @@ +import { v4 as uuid } from 'uuid'; +import { logError } from '../../../middleware/logging'; +// import * as time from '../../time'; +import { createCore, getCoreByKey } from '../ehr-core-repository'; +import { + fragmentExists, + getFragmentByKey, + markFragmentAsReceivedAndCreateItsParts, +} from '../ehr-fragment-repository'; +import { singleFragment } from '../../../models/fragment'; +import { EhrTransferTracker } from '../dynamo-ehr-transfer-tracker'; + +// Mocking +jest.mock('../../../middleware/logging'); + +describe('messageRepository', () => { + const expectedTimestamp = '2024-03-06T12:34:56+00:00'; + const mockTime = new Date(Date.parse(expectedTimestamp)); + + beforeEach(async () => { + jest.useFakeTimers().setSystemTime(mockTime); + }); + + describe('createCore', () => { + it('should create core message in db', async () => { + // given + const conversationId = uuid(); + const messageId = uuid(); + const ehrExtract = { messageId, conversationId, fragmentMessageIds: [] }; + + // when + await createCore(ehrExtract); + + // then + const actualMessage = await getCoreByKey(conversationId, messageId); + + expect(actualMessage.InboundMessageId).toBe(messageId); + expect(actualMessage.InboundConversationId).toBe(conversationId); + expect(actualMessage.Layer).toBe(`Core#${messageId}`); + expect(actualMessage.ReceivedAt).toEqual(expectedTimestamp); + }); + + // Note: old test it('should create health record in db') is not migrated, + // as now the responsibility of creating a new conversation is handled by another service + + it('should create fragments message in db when health record has fragments', async () => { + // given + const conversationId = uuid(); + const messageId = uuid(); + const fragmentMessageId = uuid(); + const fragmentMessageIds = [fragmentMessageId]; + const ehrExtract = { messageId, conversationId, fragmentMessageIds }; + + // when + await createCore(ehrExtract); + + // then + const fragmentMessage = await getFragmentByKey(conversationId, fragmentMessageId); + expect(fragmentMessage.InboundConversationId).toBe(conversationId); + expect(fragmentMessage.Layer).toBe(`Fragment#${fragmentMessageId}`); + expect(fragmentMessage.InboundMessageId).toBe(fragmentMessageId); + expect(fragmentMessage.ParentId).toBe(messageId); + expect(fragmentMessage.ReceivedAt).toBeUndefined(); + }); + + it('should not save message with wrong type', async () => { + // given + const conversationId = uuid(); + const messageId = 'not-a-valid-message-id'; + const ehrExtract = { + messageId, + conversationId, + fragmentMessageIds: [], + }; + + try { + // when + await createCore(ehrExtract); + } catch (err) { + // then + expect(err).not.toBeNull(); + expect(logError).toHaveBeenCalledWith('Message could not be stored', err); + } + const actualMessage = await getCoreByKey(conversationId, messageId); + expect(actualMessage).toBeNull(); + }); + + // Note: not migrating old test it('should not save message or health record with wrong nhs number'), + // because now the nhs number field only present at conversation level. + }); + + describe('markFragmentAsReceivedAndCreateItsParts', () => { + it('should update receivedAt for a fragment with current date', async () => { + // given + const conversationId = uuid(); + const ehrMessageId = uuid(); + const fragmentMessageId = uuid(); + await createCore({ + conversationId, + messageId: ehrMessageId, + fragmentMessageIds: [fragmentMessageId], + }); + + // when + await markFragmentAsReceivedAndCreateItsParts(fragmentMessageId, conversationId, []); + + // then + const fragment = await getFragmentByKey(conversationId, fragmentMessageId); + expect(fragment.ReceivedAt).toEqual(expectedTimestamp); + }); + + it('should not update receivedAt for a given fragment if database update query throws', async () => { + // given + const conversationId = uuid(); + try { + // when + await markFragmentAsReceivedAndCreateItsParts('not-valid', conversationId, []); + } catch (err) { + // then + expect(err).not.toBeNull(); + expect(logError).toHaveBeenCalledWith('Message could not be stored', err); + } + }); + + it('should create messages for nested fragments', async () => { + // given + const conversationId = uuid(); + const ehrMessageId = uuid(); + const fragmentMessageId = uuid(); + const nestedFragmentMessageId = uuid(); + await createCore({ + conversationId, + messageId: ehrMessageId, + fragmentMessageIds: [fragmentMessageId], + }); + + // when + await markFragmentAsReceivedAndCreateItsParts(fragmentMessageId, conversationId, [ + nestedFragmentMessageId, + ]); + + // then + const nestedFragmentMessage = await getFragmentByKey(conversationId, nestedFragmentMessageId); + expect(nestedFragmentMessage.ReceivedAt).toEqual(undefined); + expect(nestedFragmentMessage.ParentId).toEqual(fragmentMessageId); + expect(nestedFragmentMessage.InboundConversationId).toEqual(conversationId); + }); + + it('should update parentId for a nested fragment already existing in the DB', async () => { + // given + const conversationId = uuid(); + const ehrMessageId = uuid(); + const fragmentMessageId = uuid(); + const nestedFragmentMessageId = uuid(); + + await createCore({ + conversationId, + messageId: ehrMessageId, + fragmentMessageIds: [fragmentMessageId], + }); + + const nestedFragmentArrivedEarly = singleFragment({ + inboundConversationId: conversationId, + fragmentMessageId: nestedFragmentMessageId, + }); + const db = EhrTransferTracker.getInstance(); + await db.writeItemsToTable([nestedFragmentArrivedEarly]); + + // when + await markFragmentAsReceivedAndCreateItsParts(fragmentMessageId, conversationId, [ + nestedFragmentMessageId, + ]); + + // then + const nestedFragmentMessage = await getFragmentByKey(conversationId, nestedFragmentMessageId); + expect(nestedFragmentMessage.ParentId).toEqual(fragmentMessageId); + }); + }); + + describe('fragmentExists', () => { + it('should return true for a fragment existing in the database', async () => { + const conversationId = uuid(); + const messageId = uuid(); + + const fragment = singleFragment({ + inboundConversationId: conversationId, + fragmentMessageId: messageId, + }); + const db = EhrTransferTracker.getInstance(); + await db.writeItemsToTable([fragment]); + + expect(await fragmentExists(conversationId, messageId)).toBe(true); + }); + + it('should return false for a fragment that does not exist in the database', async () => { + const conversationId = uuid(); + const messageId = uuid(); + expect(await fragmentExists(conversationId, messageId)).toBe(false); + }); + + it('should throw if database querying throws', async () => { + const messageId = 'not-valid'; + try { + await fragmentExists(messageId); + } catch (err) { + expect(err).not.toBeNull(); + expect(logError).toHaveBeenCalledWith('Querying database for fragment message failed', err); + } + }); + }); + + describe('markFragmentAsReceivedAndCreateItsParts', () => { + // Note: this describe block is migrated from the test of old method "createFragmentPart" + it('should create fragment entry in the database', async () => { + const messageId = uuid(); + const conversationId = uuid(); + await markFragmentAsReceivedAndCreateItsParts(messageId, conversationId); + + const fragment = await getFragmentByKey(conversationId, messageId); + + expect(fragment.InboundConversationId).toEqual(conversationId); + expect(fragment.ReceivedAt).toEqual(expectedTimestamp); + expect(fragment.Layer).toEqual(`Fragment#${messageId}`); + expect(fragment.ParentId).toBeUndefined(); + }); + + it('should throw if database creation query throws', async () => { + const conversationId = uuid(); + const messageId = 'not-valid'; + let err = null; + try { + await markFragmentAsReceivedAndCreateItsParts(messageId, conversationId); + } catch (error) { + err = error; + } + expect(err).not.toBeNull(); + expect(logError).toHaveBeenCalledWith('Message could not be stored', err); + }); + }); +}); diff --git a/src/services/database/dynamo-ehr-transfer-tracker.js b/src/services/database/dynamo-ehr-transfer-tracker.js index 3aa11097..951c5517 100644 --- a/src/services/database/dynamo-ehr-transfer-tracker.js +++ b/src/services/database/dynamo-ehr-transfer-tracker.js @@ -1,11 +1,15 @@ -import { TransactWriteCommand, QueryCommand, UpdateCommand } from "@aws-sdk/lib-dynamodb"; - -import { getUKTimestamp } from "../time"; -import { logError, logInfo } from "../../middleware/logging"; -import { QueryType, ConversationStates } from "../../models/enums"; -import { getDynamodbClient } from "./dynamodb-client"; -import { fragmentUpdateParams } from "../../models/fragment"; - +import { + TransactWriteCommand, + QueryCommand, + UpdateCommand, + GetCommand, +} from '@aws-sdk/lib-dynamodb'; + +import { getUKTimestamp } from '../time'; +import { logError, logInfo } from '../../middleware/logging'; +import { QueryType, ConversationStates } from '../../models/enums'; +import { getDynamodbClient } from './dynamodb-client'; +import { buildFragmentUpdateParams } from '../../models/fragment'; export class EhrTransferTracker { constructor() { @@ -16,12 +20,12 @@ export class EhrTransferTracker { this.tableName = process.env.DYNAMODB_NAME; - const isInLocal = process.env.NHS_ENVIRONMENT === "local" || !process.env.NHS_ENVIRONMENT; + const isInLocal = process.env.NHS_ENVIRONMENT === 'local' || !process.env.NHS_ENVIRONMENT; const isInDojo = process.env.DOJO_VERSION !== undefined; if (isInLocal && !isInDojo) { // for running individual test with IDE - this.tableName = "local-test-db"; + this.tableName = 'local-test-db'; } this.client = getDynamodbClient(); @@ -36,14 +40,15 @@ export class EhrTransferTracker { async writeItemsToTable(items) { if (!items || !Array.isArray(items)) { - throw new Error("The given argument `items` is not an array"); + throw new Error('The given argument `items` is not an array'); } const command = new TransactWriteCommand({ TransactItems: items.map((item) => ({ Put: { - TableName: this.tableName, Item: item - } - })) + TableName: this.tableName, + Item: item, + }, + })), }); await this.client.send(command); @@ -51,14 +56,15 @@ export class EhrTransferTracker { async updateItemsInTransaction(updateParams) { if (!updateParams || !Array.isArray(updateParams)) { - throw new Error("The given argument `updateParams` is not an array"); + throw new Error('The given argument `updateParams` is not an array'); } const command = new TransactWriteCommand({ TransactItems: updateParams.map((params) => ({ Update: { - TableName: this.tableName, ...params - } - })) + TableName: this.tableName, + ...params, + }, + })), }); await this.client.send(command); @@ -67,14 +73,14 @@ export class EhrTransferTracker { async queryTableByNhsNumber(nhsNumber) { const params = { TableName: this.tableName, - IndexName: "NhsNumberSecondaryIndex", + IndexName: 'NhsNumberSecondaryIndex', ExpressionAttributeValues: { - ":nhsNumber": nhsNumber + ':nhsNumber': nhsNumber, }, ExpressionAttributeNames: { - "#NhsNumber": "NhsNumber" + '#NhsNumber': 'NhsNumber', }, - KeyConditionExpression: "#NhsNumber = :nhsNumber" + KeyConditionExpression: '#NhsNumber = :nhsNumber', }; const command = new QueryCommand(params); @@ -82,7 +88,7 @@ export class EhrTransferTracker { const response = await this.client.send(command); const items = response?.Items; if (!items) { - logError("Received an empty response from dynamodb during query"); + logError('Received an empty response from dynamodb during query'); } return items; } @@ -93,13 +99,15 @@ export class EhrTransferTracker { const items = await this.queryTableByNhsNumber(nhsNumber); if (!items || items.length === 0) { - throw new Error("No record was found for given NHS number"); + throw new Error('No record was found for given NHS number'); } - const completedRecords = items.filter(item => item.State === ConversationStates.COMPLETE || item.State.startsWith("Outbound")); + const completedRecords = items.filter( + (item) => item.State === ConversationStates.COMPLETE || item.State.startsWith('Outbound') + ); const currentRecord = completedRecords.reduce((prev, current) => { - return (current && current?.CreatedAt > prev?.CreatedAt) ? current : prev; + return current && current?.CreatedAt > prev?.CreatedAt ? current : prev; }); return currentRecord.InboundConversationId; @@ -109,12 +117,12 @@ export class EhrTransferTracker { const params = { TableName: this.tableName, ExpressionAttributeNames: { - "#PrimaryKey": "InboundConversationId" + '#PrimaryKey': 'InboundConversationId', }, ExpressionAttributeValues: { - ":InboundConversationId": inboundConversationId + ':InboundConversationId': inboundConversationId, }, - KeyConditionExpression: "#PrimaryKey = :InboundConversationId" + KeyConditionExpression: '#PrimaryKey = :InboundConversationId', }; switch (queryType) { @@ -123,9 +131,9 @@ export class EhrTransferTracker { case QueryType.CONVERSATION: case QueryType.CORE: case QueryType.FRAGMENT: - params.ExpressionAttributeNames["#sortKey"] = "Layer"; - params.ExpressionAttributeValues[":sortKey"] = queryType; - params.KeyConditionExpression += " AND begins_with(#sortKey, :sortKey)"; + params.ExpressionAttributeNames['#sortKey'] = 'Layer'; + params.ExpressionAttributeValues[':sortKey'] = queryType; + params.KeyConditionExpression += ' AND begins_with(#sortKey, :sortKey)'; break; default: logInfo(`Received unexpected queryType: ${queryType}. Will treat it as 'ALL'.`); @@ -136,54 +144,38 @@ export class EhrTransferTracker { const response = await this.client.send(command); const items = response?.Items; if (!items) { - logError("Received an empty response from dynamodb during query"); + logError('Received an empty response from dynamodb during query'); } return items; } - async updateFragmentAndCreateItsParts(messageId, - conversationId, - remainingPartsIds = []) { - // to replace the existing methods `updateFragmentAndCreateItsParts` and `createFragmentPart` - const timestamp = getUKTimestamp(); - - const currentFragmentParams = fragmentUpdateParams(conversationId, messageId, { ReceivedAt: timestamp }); - - const childFragmentsParams = remainingPartsIds.map(fragmentPartId => { - return fragmentUpdateParams( - conversationId, - fragmentPartId, - { ParentId: messageId } - ); - }); - - await this.updateItemsInTransaction([currentFragmentParams, ...childFragmentsParams]); - }; + async getItemByKey(inboundConversationId, inboundMessageId, queryType = QueryType.FRAGMENT) { + const expectedQueryTypes = [QueryType.CORE, QueryType.FRAGMENT]; - async updateHealthRecordCompleteness(conversationId) { - const allFragments = this.queryTableByConversationId(conversationId, QueryType.FRAGMENT); - const pendingMessages = allFragments.filter(fragment => fragment.receivedAt === undefined); - if (pendingMessages.length !== 0) { - logInfo(`${pendingMessages.length} more fragments to be received.`); - return; + if (!expectedQueryTypes.includes(queryType)) { + throw new Error('queryType has to be either Core or Fragment'); + } + if (!inboundConversationId && !inboundMessageId) { + throw new Error('must be called with both conversationId and inboundMessageId'); } - logInfo("All fragments are received. Will mark this conversation as complete"); - - const timestamp = getUKTimestamp(); - const updateParam = { + const command = new GetCommand({ TableName: this.tableName, Key: { - InboundConversationId: conversationId, - Layer: "Conversation" + InboundConversationId: inboundConversationId, + Layer: `${queryType}#${inboundMessageId}`, }, - UpdateExpression: "set UpdatedAt = :now, State = :complete", - ExpressionAttributeValues: { - ":now": timestamp, - ":complete": ConversationStates.COMPLETE - } - }; + }); + + const response = await this.client.send(command); + + if (response?.Item) { + logError('Received an empty response from dynamodb during query'); + } + return response?.Item ?? null; + } - await this.client.send(new UpdateCommand(updateParam)); + async getFragmentByKey(inboundConversationId, inboundMessageId) { + return this.getItemByKey(inboundConversationId, inboundMessageId, QueryType.FRAGMENT); } -} \ No newline at end of file +} diff --git a/src/services/database/dynamodb-client.js b/src/services/database/dynamodb-client.js index a5cf4271..6a533d54 100644 --- a/src/services/database/dynamodb-client.js +++ b/src/services/database/dynamodb-client.js @@ -1,12 +1,12 @@ -import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; -import { DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb"; +import { DynamoDBClient } from '@aws-sdk/client-dynamodb'; +import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb'; export const getDynamodbClient = () => { const clientConfig = { - region: process.env.AWS_DEFAULT_REGION ?? "eu-west-2" + region: process.env.AWS_DEFAULT_REGION ?? 'eu-west-2', }; - const isInLocal = process.env.NHS_ENVIRONMENT === "local" || !process.env.NHS_ENVIRONMENT; + const isInLocal = process.env.NHS_ENVIRONMENT === 'local' || !process.env.NHS_ENVIRONMENT; const isInDojo = process.env.DOJO_VERSION !== undefined; if (isInLocal) { @@ -15,9 +15,9 @@ export const getDynamodbClient = () => { } if (isInLocal && !isInDojo) { // for running individual test with IDE - clientConfig.endpoint = "http://localhost:4573"; + clientConfig.endpoint = 'http://localhost:4573'; } const baseClient = new DynamoDBClient(clientConfig); return DynamoDBDocumentClient.from(baseClient); -} \ No newline at end of file +}; diff --git a/src/services/database/ehr-conversation-repository.js b/src/services/database/ehr-conversation-repository.js index e69de29b..7fb5947c 100644 --- a/src/services/database/ehr-conversation-repository.js +++ b/src/services/database/ehr-conversation-repository.js @@ -0,0 +1,40 @@ +import { ConversationStates, QueryType } from '../../models/enums'; +import { logInfo } from '../../middleware/logging'; +import { getUKTimestamp } from '../time'; +import { UpdateCommand } from '@aws-sdk/lib-dynamodb'; +import { EhrTransferTracker } from './dynamo-ehr-transfer-tracker'; +import { buildConversationUpdateParams } from '../../models/conversation'; + +export const updateInboundConversationCompleteness = async (conversationId) => { + // to replace the existing method `updateHealthRecordCompleteness` + const db = EhrTransferTracker.getInstance(); + + const allFragments = this.queryTableByConversationId(conversationId, QueryType.FRAGMENT); + const pendingMessages = allFragments.filter((fragment) => fragment.receivedAt === undefined); + if (pendingMessages.length !== 0) { + logInfo(`${pendingMessages.length} more fragments to be received.`); + return; + } + + logInfo('All fragments are received. Will mark this conversation as complete'); + + const timestamp = getUKTimestamp(); + + const updateParam = { + TableName: this.tableName, + Key: { + InboundConversationId: conversationId, + Layer: 'Conversation', + }, + UpdateExpression: 'set UpdatedAt = :now, State = :complete', + ExpressionAttributeValues: { + ':now': timestamp, + ':complete': ConversationStates.COMPLETE, + }, + + // TODO: try switch to below after integration test green + // const updateParam = buildConversationUpdateParams(conversationId, {State: ConversationStates.COMPLETE}) + }; + + await db.client.send(new UpdateCommand(updateParam)); +}; diff --git a/src/services/database/ehr-core-repository.js b/src/services/database/ehr-core-repository.js index c439ca08..fa128988 100644 --- a/src/services/database/ehr-core-repository.js +++ b/src/services/database/ehr-core-repository.js @@ -1,24 +1,34 @@ -import { EhrTransferTracker } from "./dynamo-ehr-transfer-tracker"; -import { arrayOfFragments } from "../../models/fragment"; -import { core } from "../../models/core"; +import { EhrTransferTracker } from './dynamo-ehr-transfer-tracker'; +import { arrayOfFragments } from '../../models/fragment'; +import { core } from '../../models/core'; +import { QueryType } from '../../models/enums'; +import { logError } from '../../middleware/logging'; export const createCore = async ({ conversationId, messageId, fragmentMessageIds }) => { // to replace the existing `createEhrExtract` method - const db = EhrTransferTracker.getInstance(); - const itemsToWrite = [core(conversationId, messageId)]; + try { + const db = EhrTransferTracker.getInstance(); + const itemsToWrite = [core(conversationId, messageId)]; - if (fragmentMessageIds) { - const directFragments = arrayOfFragments( - { + if (fragmentMessageIds) { + const directFragments = arrayOfFragments({ inboundConversationId: conversationId, fragmentMessageIds, - parentMessageId: messageId - } - ); - itemsToWrite.push(...directFragments); - } + parentMessageId: messageId, + }); + itemsToWrite.push(...directFragments); + } - await db.writeItemsToTable(itemsToWrite); + await db.writeItemsToTable(itemsToWrite); + } catch (e) { + logError('Message could not be stored', e); + throw e; + } }; +export const getCoreByKey = (inboundConversationId, inboundMessageId) => { + // to replace the findByPk default method from sequalize + const db = EhrTransferTracker.getInstance(); + return db.getItemByKey(inboundConversationId, inboundMessageId, QueryType.CORE); +}; diff --git a/src/services/database/ehr-fragment-repository.js b/src/services/database/ehr-fragment-repository.js index 122e2ddb..752662d6 100644 --- a/src/services/database/ehr-fragment-repository.js +++ b/src/services/database/ehr-fragment-repository.js @@ -1,5 +1,49 @@ -import { getUKTimestamp } from "../time"; -import { TransactWriteCommand } from "@aws-sdk/lib-dynamodb"; -import { EhrTransferTracker } from "./dynamo-ehr-transfer-tracker"; +import { getUKTimestamp } from '../time'; +import { TransactWriteCommand } from '@aws-sdk/lib-dynamodb'; +import { EhrTransferTracker } from './dynamo-ehr-transfer-tracker'; +import { buildFragmentUpdateParams } from '../../models/fragment'; +import { QueryType } from '../../models/enums'; +import { logError } from '../../middleware/logging'; -// TODO: move new version of updateFragmentAndCreateItsParts to here \ No newline at end of file +export const markFragmentAsReceivedAndCreateItsParts = async ( + messageId, + conversationId, + remainingPartsIds = [] +) => { + // to replace the existing methods `updateFragmentAndCreateItsParts` and `createFragmentPart` + + try { + const db = EhrTransferTracker.getInstance(); + const timestamp = getUKTimestamp(); + + const currentFragmentParams = buildFragmentUpdateParams(conversationId, messageId, { + ReceivedAt: timestamp, + }); + + const childFragmentsParams = remainingPartsIds.map((fragmentPartId) => { + return buildFragmentUpdateParams(conversationId, fragmentPartId, { ParentId: messageId }); + }); + + await db.updateItemsInTransaction([currentFragmentParams, ...childFragmentsParams]); + } catch (e) { + logError('Message could not be stored', e); + throw e; + } +}; + +export const getFragmentByKey = (inboundConversationId, inboundMessageId) => { + // to replace the findByPk default method from sequalize + const db = EhrTransferTracker.getInstance(); + return db.getItemByKey(inboundConversationId, inboundMessageId, QueryType.FRAGMENT); +}; + +export const fragmentExists = async (inboundConversationId, inboundMessageId) => { + // to replace the method with same name + try { + const fragment = await getFragmentByKey(inboundConversationId, inboundMessageId); + return !!fragment; + } catch (e) { + logError('Querying database for fragment message failed', e); + throw e; + } +}; diff --git a/src/services/database/message-repository.js b/src/services/database/message-repository.js index cad6b43a..9a47d9f5 100644 --- a/src/services/database/message-repository.js +++ b/src/services/database/message-repository.js @@ -5,6 +5,10 @@ import ModelFactory from '../../models'; import { getNow } from '../time'; export const createEhrExtract = async (ehrExtract) => { + /** + * @deprecated + * To be replaced by new method `createCore` + */ const Message = ModelFactory.getByName(messageModelName); const HealthRecord = ModelFactory.getByName(healthRecordModelName); const { conversationId, messageId, nhsNumber, fragmentMessageIds } = ehrExtract; @@ -45,6 +49,10 @@ export const updateFragmentAndCreateItsParts = async ( conversationId, remainingPartsIds ) => { + /** + * @deprecated + * To be replaced by new method `markFragmentAsReceivedAndCreateItsParts` + */ const Message = ModelFactory.getByName(messageModelName); const sequelize = ModelFactory.sequelize; const t = await sequelize.transaction(); @@ -81,6 +89,10 @@ export const updateFragmentAndCreateItsParts = async ( }; export const fragmentExists = async (id) => { + /** + * @deprecated + * To be replaced by new method of the same name at ehr-fragment-repository + */ const Message = ModelFactory.getByName(messageModelName); try { @@ -94,6 +106,10 @@ export const fragmentExists = async (id) => { }; export const createFragmentPart = async (id, conversationId) => { + /** + * @deprecated + * To be replaced by new method `markFragmentAsReceivedAndCreateItsParts` + */ const Message = ModelFactory.getByName(messageModelName); const sequelize = ModelFactory.sequelize; const t = await sequelize.transaction(); diff --git a/src/services/time.js b/src/services/time.js index 00dd8f58..a4805852 100644 --- a/src/services/time.js +++ b/src/services/time.js @@ -1,7 +1,7 @@ -import moment from "moment-timezone"; +import moment from 'moment-timezone'; export const getNow = () => new Date(); export const getUKTimestamp = () => { return moment().tz('Europe/London').format('YYYY-MM-DDThh:mm:ssZ'); -} \ No newline at end of file +}; diff --git a/src/utilities/integration-test-utilities.js b/src/utilities/integration-test-utilities.js index 46275331..606ac100 100644 --- a/src/utilities/integration-test-utilities.js +++ b/src/utilities/integration-test-utilities.js @@ -1,7 +1,7 @@ import { v4 as uuidv4 } from 'uuid'; -import { getUKTimestamp } from "../services/time"; -import { EhrTransferTracker } from "../services/database/dynamo-ehr-transfer-tracker"; -import { TransactWriteCommand } from "@aws-sdk/lib-dynamodb"; +import { getUKTimestamp } from '../services/time'; +import { EhrTransferTracker } from '../services/database/dynamo-ehr-transfer-tracker'; +import { TransactWriteCommand } from '@aws-sdk/lib-dynamodb'; export const generateRandomNhsNumber = () => (Math.floor(Math.random() * 9e9) + 1e9).toString(); @@ -13,15 +13,13 @@ export const generateMultipleUUID = (amount, isUppercase) => .fill(undefined) .map(() => (isUppercase ? uuidv4().toUpperCase() : uuidv4())); - - export const createConversationForTest = async (conversationId, nhsNumber) => { // This method is only meant for testing purpose, // as the inbound conversation record is supposed to be created by other service. - const isInLocal = process.env.NHS_ENVIRONMENT === "local" || !process.env.NHS_ENVIRONMENT; + const isInLocal = process.env.NHS_ENVIRONMENT === 'local' || !process.env.NHS_ENVIRONMENT; if (!isInLocal) { - throw new Error("Unexpected call to createConversation method in non-local environment"); + throw new Error('Unexpected call to createConversation method in non-local environment'); } const timestamp = getUKTimestamp(); @@ -29,10 +27,10 @@ export const createConversationForTest = async (conversationId, nhsNumber) => { const item = { InboundConversationId: conversationId, - Layer: "Conversation", + Layer: 'Conversation', NhsNumber: nhsNumber, CreatedAt: timestamp, - UpdatedAt: timestamp + UpdatedAt: timestamp, }; await db.writeItemsToTable([item]); @@ -41,26 +39,24 @@ export const createConversationForTest = async (conversationId, nhsNumber) => { export const cleanupRecordsForTest = async (conversationId) => { // This method is only meant for testing purpose - const isInLocal = process.env.NHS_ENVIRONMENT === "local" || !process.env.NHS_ENVIRONMENT; + const isInLocal = process.env.NHS_ENVIRONMENT === 'local' || !process.env.NHS_ENVIRONMENT; if (!isInLocal) { - throw new Error("Unexpected call to createConversation method in non-local environment"); + throw new Error('Unexpected call to createConversation method in non-local environment'); } const db = EhrTransferTracker.getInstance(); const records = await db.queryTableByConversationId(conversationId); - const deleteCommand = new TransactWriteCommand( - { - TransactItems: records.map(item => ({ - Delete: { - TableName: db.tableName, - Key: { - InboundConversationId: item.InboundConversationId, - Layer: item.Layer - } - } - })) - } - ); + const deleteCommand = new TransactWriteCommand({ + TransactItems: records.map((item) => ({ + Delete: { + TableName: db.tableName, + Key: { + InboundConversationId: item.InboundConversationId, + Layer: item.Layer, + }, + }, + })), + }); await db.client.send(deleteCommand); -}; \ No newline at end of file +}; From 047d5f1c5fb433a061ef9c0142a372ed37133f46 Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Wed, 6 Mar 2024 12:24:01 +0000 Subject: [PATCH 16/67] [PRMT-4566] Rename test files --- .../ehr-core-repository.integration.test.js | 85 +++++++++++++++++++ ...r-fragment-repository.integration.test.js} | 75 +--------------- 2 files changed, 88 insertions(+), 72 deletions(-) create mode 100644 src/services/database/__tests__/ehr-core-repository.integration.test.js rename src/services/database/__tests__/{new-message-repository.integration.test.js => ehr-fragment-repository.integration.test.js} (67%) diff --git a/src/services/database/__tests__/ehr-core-repository.integration.test.js b/src/services/database/__tests__/ehr-core-repository.integration.test.js new file mode 100644 index 00000000..cdcc1aa8 --- /dev/null +++ b/src/services/database/__tests__/ehr-core-repository.integration.test.js @@ -0,0 +1,85 @@ +import { v4 as uuid } from 'uuid'; + +import { logError } from '../../../middleware/logging'; +import { createCore, getCoreByKey } from '../ehr-core-repository'; +import { getFragmentByKey } from '../ehr-fragment-repository'; + +// Mocking +jest.mock('../../../middleware/logging'); + +describe('ehr-core-repository', () => { + const expectedTimestamp = '2024-03-06T12:34:56+00:00'; + const mockTime = new Date(Date.parse(expectedTimestamp)); + + beforeEach(async () => { + jest.useFakeTimers().setSystemTime(mockTime); + }); + + describe('createCore', () => { + it('should create core message in db', async () => { + // given + const conversationId = uuid(); + const messageId = uuid(); + const ehrExtract = { messageId, conversationId, fragmentMessageIds: [] }; + + // when + await createCore(ehrExtract); + + // then + const actualMessage = await getCoreByKey(conversationId, messageId); + + expect(actualMessage.InboundMessageId).toBe(messageId); + expect(actualMessage.InboundConversationId).toBe(conversationId); + expect(actualMessage.Layer).toBe(`Core#${messageId}`); + expect(actualMessage.ReceivedAt).toEqual(expectedTimestamp); + }); + + // Note: old test it('should create health record in db') is not migrated, + // as now the responsibility of creating a new conversation is handled by another service + + it('should create fragments message in db when health record has fragments', async () => { + // given + const conversationId = uuid(); + const messageId = uuid(); + const fragmentMessageId = uuid(); + const fragmentMessageIds = [fragmentMessageId]; + const ehrExtract = { messageId, conversationId, fragmentMessageIds }; + + // when + await createCore(ehrExtract); + + // then + const fragmentMessage = await getFragmentByKey(conversationId, fragmentMessageId); + expect(fragmentMessage.InboundConversationId).toBe(conversationId); + expect(fragmentMessage.Layer).toBe(`Fragment#${fragmentMessageId}`); + expect(fragmentMessage.InboundMessageId).toBe(fragmentMessageId); + expect(fragmentMessage.ParentId).toBe(messageId); + expect(fragmentMessage.ReceivedAt).toBeUndefined(); + }); + + it('should not save message with wrong type', async () => { + // given + const conversationId = uuid(); + const messageId = 'not-a-valid-message-id'; + const ehrExtract = { + messageId, + conversationId, + fragmentMessageIds: [], + }; + + try { + // when + await createCore(ehrExtract); + } catch (err) { + // then + expect(err).not.toBeNull(); + expect(logError).toHaveBeenCalledWith('Message could not be stored', err); + } + const actualMessage = await getCoreByKey(conversationId, messageId); + expect(actualMessage).toBeNull(); + }); + + // Note: not migrating old test it('should not save message or health record with wrong nhs number'), + // because now the nhs number field only present at conversation level. + }); +}); diff --git a/src/services/database/__tests__/new-message-repository.integration.test.js b/src/services/database/__tests__/ehr-fragment-repository.integration.test.js similarity index 67% rename from src/services/database/__tests__/new-message-repository.integration.test.js rename to src/services/database/__tests__/ehr-fragment-repository.integration.test.js index 92d77fd3..f2b656a4 100644 --- a/src/services/database/__tests__/new-message-repository.integration.test.js +++ b/src/services/database/__tests__/ehr-fragment-repository.integration.test.js @@ -1,7 +1,6 @@ import { v4 as uuid } from 'uuid'; import { logError } from '../../../middleware/logging'; -// import * as time from '../../time'; -import { createCore, getCoreByKey } from '../ehr-core-repository'; +import { createCore } from '../ehr-core-repository'; import { fragmentExists, getFragmentByKey, @@ -13,7 +12,7 @@ import { EhrTransferTracker } from '../dynamo-ehr-transfer-tracker'; // Mocking jest.mock('../../../middleware/logging'); -describe('messageRepository', () => { +describe('ehr-fragment-repository', () => { const expectedTimestamp = '2024-03-06T12:34:56+00:00'; const mockTime = new Date(Date.parse(expectedTimestamp)); @@ -21,74 +20,6 @@ describe('messageRepository', () => { jest.useFakeTimers().setSystemTime(mockTime); }); - describe('createCore', () => { - it('should create core message in db', async () => { - // given - const conversationId = uuid(); - const messageId = uuid(); - const ehrExtract = { messageId, conversationId, fragmentMessageIds: [] }; - - // when - await createCore(ehrExtract); - - // then - const actualMessage = await getCoreByKey(conversationId, messageId); - - expect(actualMessage.InboundMessageId).toBe(messageId); - expect(actualMessage.InboundConversationId).toBe(conversationId); - expect(actualMessage.Layer).toBe(`Core#${messageId}`); - expect(actualMessage.ReceivedAt).toEqual(expectedTimestamp); - }); - - // Note: old test it('should create health record in db') is not migrated, - // as now the responsibility of creating a new conversation is handled by another service - - it('should create fragments message in db when health record has fragments', async () => { - // given - const conversationId = uuid(); - const messageId = uuid(); - const fragmentMessageId = uuid(); - const fragmentMessageIds = [fragmentMessageId]; - const ehrExtract = { messageId, conversationId, fragmentMessageIds }; - - // when - await createCore(ehrExtract); - - // then - const fragmentMessage = await getFragmentByKey(conversationId, fragmentMessageId); - expect(fragmentMessage.InboundConversationId).toBe(conversationId); - expect(fragmentMessage.Layer).toBe(`Fragment#${fragmentMessageId}`); - expect(fragmentMessage.InboundMessageId).toBe(fragmentMessageId); - expect(fragmentMessage.ParentId).toBe(messageId); - expect(fragmentMessage.ReceivedAt).toBeUndefined(); - }); - - it('should not save message with wrong type', async () => { - // given - const conversationId = uuid(); - const messageId = 'not-a-valid-message-id'; - const ehrExtract = { - messageId, - conversationId, - fragmentMessageIds: [], - }; - - try { - // when - await createCore(ehrExtract); - } catch (err) { - // then - expect(err).not.toBeNull(); - expect(logError).toHaveBeenCalledWith('Message could not be stored', err); - } - const actualMessage = await getCoreByKey(conversationId, messageId); - expect(actualMessage).toBeNull(); - }); - - // Note: not migrating old test it('should not save message or health record with wrong nhs number'), - // because now the nhs number field only present at conversation level. - }); - describe('markFragmentAsReceivedAndCreateItsParts', () => { it('should update receivedAt for a fragment with current date', async () => { // given @@ -210,7 +141,7 @@ describe('messageRepository', () => { }); describe('markFragmentAsReceivedAndCreateItsParts', () => { - // Note: this describe block is migrated from the test of old method "createFragmentPart" + // Note: this describe block is migrated from the tests of old method "createFragmentPart" it('should create fragment entry in the database', async () => { const messageId = uuid(); const conversationId = uuid(); From 8189facef7bf5d2cd6651d36c464cf3c431fab93 Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Wed, 6 Mar 2024 16:25:09 +0000 Subject: [PATCH 17/67] [PRMT-4566] Continue migrating database methods and integration tests. Store current progress --- .../health-record-controller.test.js | 3 +- src/api/patients/health-record-controller.js | 3 +- src/errors/errors.js | 22 + src/models/conversation.js | 29 +- src/models/enums.js | 7 + ...alth-record-repository.integration.test.js | 2 +- ...alth-record-repository.integration.test.js | 455 ++++++++++++++++++ .../database/dynamo-ehr-transfer-tracker.js | 1 + .../database/ehr-conversation-repository.js | 98 +++- .../database/health-record-repository.js | 19 +- src/utilities/integration-test-utilities.js | 3 +- 11 files changed, 589 insertions(+), 53 deletions(-) create mode 100644 src/errors/errors.js create mode 100644 src/services/database/__tests__/new-health-record-repository.integration.test.js diff --git a/src/api/patients/__tests__/health-record-controller.test.js b/src/api/patients/__tests__/health-record-controller.test.js index f2362422..183df590 100644 --- a/src/api/patients/__tests__/health-record-controller.test.js +++ b/src/api/patients/__tests__/health-record-controller.test.js @@ -3,9 +3,10 @@ import { v4 as uuid } from 'uuid'; import app from '../../../app'; import { getHealthRecordStatus, - HealthRecordStatus, + } from '../../../services/database/health-record-repository'; import { initializeConfig } from '../../../config'; +import { HealthRecordStatus } from "../../../models/enums"; jest.mock('../../../services/database/health-record-repository'); jest.mock('../../../config', () => ({ diff --git a/src/api/patients/health-record-controller.js b/src/api/patients/health-record-controller.js index 3b3d1564..51a792f7 100644 --- a/src/api/patients/health-record-controller.js +++ b/src/api/patients/health-record-controller.js @@ -1,9 +1,10 @@ import { param } from 'express-validator'; import { getHealthRecordStatus, - HealthRecordStatus, + } from '../../services/database/health-record-repository'; import { setCurrentSpanAttributes } from '../../config/tracing'; +import { HealthRecordStatus } from "../../models/enums"; export const healthRecordControllerValidation = [ param('conversationId').isUUID().withMessage("'conversationId' provided is not a UUID"), diff --git a/src/errors/errors.js b/src/errors/errors.js new file mode 100644 index 00000000..df73eda1 --- /dev/null +++ b/src/errors/errors.js @@ -0,0 +1,22 @@ +import { logError } from "../middleware/logging"; + +export const errorMessages = { + HealthRecordNotFound: 'No complete health record was found with given criteria', + MessageNotFound: 'There were no undeleted messages associated with conversation id' +} + + +export class HealthRecordNotFoundError extends Error { + constructor(error) { + super(errorMessages.HealthRecordNotFound); + logError(errorMessages.HealthRecordNotFound, error); + }; +} + + +export class MessageNotFoundError extends Error { + constructor(error) { + super(errorMessages.MessageNotFound); + logError(errorMessages.MessageNotFound, error); + }; +} \ No newline at end of file diff --git a/src/models/conversation.js b/src/models/conversation.js index b6aa3e63..a4d26e51 100644 --- a/src/models/conversation.js +++ b/src/models/conversation.js @@ -1,4 +1,5 @@ -import { getUKTimestamp } from "../services/time"; +import { getUKTimestamp } from '../services/time'; +import { logError } from "../middleware/logging"; const fieldsAllowedToUpdate = ['State', 'FailureCode']; @@ -6,21 +7,27 @@ export const buildConversationUpdateParams = (conversationId, changes) => { const params = { Key: { InboundConversationId: conversationId, - Layer: 'Conversation' + Layer: 'Conversation', }, - UpdateExpression: "set UpdatedAt = :now", + UpdateExpression: 'set UpdatedAt = :now', ExpressionAttributeValues: { - ":now": getUKTimestamp() - } + ':now': getUKTimestamp(), + }, }; - for (const fieldname in fieldsAllowedToUpdate) { - if (fieldname in changes){ - const colonKey = `:${fieldname}` - params.UpdateExpression += `, ${fieldname} = ${colonKey}`; - params.ExpressionAttributeValues[colonKey] = changes[fieldname] + for (const [fieldName, updatedValue] of Object.entries(changes)) { + if (fieldsAllowedToUpdate.includes(fieldName)) { + const keyToken = `#${fieldName}`; + const valueToken = `:${fieldName}`; + + params.UpdateExpression += `, ${keyToken} = ${valueToken}`; + params.ExpressionAttributeValues[valueToken] = updatedValue; + params.ExpressionAttributeNames = params.ExpressionAttributeNames ?? {}; + params.ExpressionAttributeNames[keyToken] = fieldName; + } else { + logError(`Ignoring attempt to update non-allowed field ${fieldName}`) } } return params; -} \ No newline at end of file +}; diff --git a/src/models/enums.js b/src/models/enums.js index f494d7b8..05f7449a 100644 --- a/src/models/enums.js +++ b/src/models/enums.js @@ -23,6 +23,13 @@ export const FragmentStates = { COMPLETE: 'INBOUND_COMPLETE', }; +export const HealthRecordStatus = { + COMPLETE: "complete", + PENDING: "pending", + NOT_FOUND: "notFound" +}; + +Object.freeze(HealthRecordStatus); Object.freeze(QueryType); Object.freeze(ConversationStates); Object.freeze(CoreStates); diff --git a/src/services/database/__tests__/health-record-repository.integration.test.js b/src/services/database/__tests__/health-record-repository.integration.test.js index b0de6ab5..d3982792 100644 --- a/src/services/database/__tests__/health-record-repository.integration.test.js +++ b/src/services/database/__tests__/health-record-repository.integration.test.js @@ -2,7 +2,6 @@ import { v4 as uuid } from 'uuid'; import { getHealthRecordStatus, updateHealthRecordCompleteness, - HealthRecordStatus, getCurrentHealthRecordIdForPatient, getHealthRecordMessageIds, messageAlreadyReceived, @@ -12,6 +11,7 @@ import ModelFactory from '../../../models'; import { modelName as healthRecordModelName } from '../../../models/health-record'; import { MessageType, modelName as messageModelName } from '../../../models/message'; import { logError } from '../../../middleware/logging'; +import { HealthRecordStatus } from "../../../models/enums"; jest.mock('../../../middleware/logging'); diff --git a/src/services/database/__tests__/new-health-record-repository.integration.test.js b/src/services/database/__tests__/new-health-record-repository.integration.test.js new file mode 100644 index 00000000..f87f3234 --- /dev/null +++ b/src/services/database/__tests__/new-health-record-repository.integration.test.js @@ -0,0 +1,455 @@ +import { v4 as uuid } from 'uuid'; +import { logError } from '../../../middleware/logging'; +import { + getConversationById, + getCurrentHealthRecordIdForPatient, + getHealthRecordStatus, + updateConversationCompleteness, +} from '../ehr-conversation-repository'; +import { ConversationStates, HealthRecordStatus } from '../../../models/enums'; +import { createConversationForTest } from '../../../utilities/integration-test-utilities'; +import { createCore } from '../ehr-core-repository'; +import { EhrTransferTracker } from '../dynamo-ehr-transfer-tracker'; +import { markFragmentAsReceivedAndCreateItsParts } from '../ehr-fragment-repository'; +import { HealthRecordNotFoundError } from "../../../errors/errors"; + +jest.mock('../../../middleware/logging'); + +describe('healthRecordRepository', () => { + // ========================= COMMON PROPERTIES ========================= + // const HealthRecord = ModelFactory.getByName(healthRecordModelName); + // const Message = ModelFactory.getByName(messageModelName); + const db = EhrTransferTracker.getInstance(); + const markFragmentAsReceived = markFragmentAsReceivedAndCreateItsParts; + const fail = (reason) => { + throw new Error(reason); + }; + const tableNameBackup = db.tableName; + const mimicDynamodbFail = () => { + db.tableName = 'non-exist-table'; + }; + const undoMimicDynamodbFail = () => { + db.tableName = tableNameBackup; + }; + // ===================================================================== + + // ========================= SET UP / TEAR DOWN ======================== + beforeEach(async () => { + // await HealthRecord.truncate(); + // await Message.truncate(); + // await ModelFactory.sequelize.sync({ force: true }); + }); + + afterEach(() => { + undoMimicDynamodbFail(); + }); + + afterAll(async () => { + // await ModelFactory.sequelize.close(); + }); + // ===================================================================== + + describe('getHealthRecordStatus', () => { + it("should return status 'complete' when conversation status is Complete", async () => { + // given + const conversationId = uuid(); + const nhsNumber = '1234567890'; + await createConversationForTest(conversationId, nhsNumber, { + State: ConversationStates.COMPLETE, + }); + + // when + const status = await getHealthRecordStatus(conversationId); + + // then + expect(status).toEqual(HealthRecordStatus.COMPLETE); + }); + + it("should return status 'pending' when conversation status is not Complete", async () => { + // given + const conversationId = uuid(); + const nhsNumber = '1234567890'; + await createConversationForTest(conversationId, nhsNumber, { + State: ConversationStates.CONTINUE_REQUEST_SENT, + }); + + // when + const status = await getHealthRecordStatus(conversationId); + + // then + expect(status).toEqual(HealthRecordStatus.PENDING); + }); + + it("should return status 'notFound' when health record is not found", async () => { + // given + const conversationId = uuid(); + + // when + const status = await getHealthRecordStatus(conversationId); + + // then + expect(status).toEqual(HealthRecordStatus.NOT_FOUND); + }); + + it('should throw error if there is a problem retrieving health record from database', async () => { + // given + const conversationId = uuid(); + mimicDynamodbFail(); + + try { + // when + await getHealthRecordStatus(conversationId); + fail('should have throw'); + } catch (err) { + // then + expect(err).not.toBeNull(); + expect(logError).toHaveBeenCalledWith( + 'Health Record could not be retrieved from database', + err + ); + } + }); + }); + + describe('updateHealthRecordCompleteness', () => { + it("should set conversation state to 'Complete' for a small health record", async () => { + // given + const conversationId = uuid(); + const messageId = uuid(); + const nhsNumber = '1234567890'; + await createConversationForTest(conversationId, nhsNumber, { + State: ConversationStates.REQUEST_SENT, + }); + await createCore({ conversationId, messageId, fragmentMessageIds: [] }); + + // when + await updateConversationCompleteness(conversationId); + + // then + const conversation = await getConversationById(conversationId); + + expect(conversation.State).toBe(ConversationStates.COMPLETE); + }); + + it('should not set State to Complete if there are still messages to be received', async () => { + // given + const conversationId = uuid(); + const messageId = uuid(); + const fragmentMessageId = uuid(); + const nhsNumber = '1234567890'; + + await createConversationForTest(conversationId, nhsNumber, { + State: ConversationStates.CONTINUE_REQUEST_SENT, + }); + await createCore({ conversationId, messageId, fragmentMessageIds: [fragmentMessageId] }); + + // when + await updateConversationCompleteness(conversationId); + + // then + const conversation = await getConversationById(conversationId); + + expect(conversation.State).toBe(ConversationStates.CONTINUE_REQUEST_SENT); + }); + + it('Should set State to Complete if all fragments are received', async () => { + // given + const conversationId = uuid(); + const messageId = uuid(); + const fragmentMessageIds = [uuid(), uuid(), uuid()]; + const nhsNumber = '1234567890'; + + await createConversationForTest(conversationId, nhsNumber, { + State: ConversationStates.CONTINUE_REQUEST_SENT, + }); + await createCore({ conversationId, messageId, fragmentMessageIds: fragmentMessageIds }); + for (const fragmentId of fragmentMessageIds) { + await markFragmentAsReceived(fragmentId, conversationId); + } + + // when + await updateConversationCompleteness(conversationId); + + // then + const conversation = await getConversationById(conversationId); + + expect(conversation.State).toBe(ConversationStates.COMPLETE); + }); + + it('should throw an error when database query fails', async () => { + // given + const conversationId = 'not-valid'; + mimicDynamodbFail(); + + try { + // when + await updateConversationCompleteness(conversationId); + fail('should have throw'); + } catch (err) { + // then + expect(err).not.toBeNull(); + expect(logError).toHaveBeenCalledWith('Failed to update health record completeness', err); + } + }); + }); + + describe('getCurrentHealthRecordIdForPatient', () => { + it('should return most recent complete health record conversation id', async () => { + // given + const nhsNumber = '9876543210'; + const previousHealthRecordConversationId = uuid(); + const incompleteHealthRecordConversationId = uuid(); + const currentHealthRecordConversationId = uuid(); + + await createConversationForTest(previousHealthRecordConversationId, nhsNumber, { + State: ConversationStates.COMPLETE, + }); + + await createConversationForTest(incompleteHealthRecordConversationId, nhsNumber, { + State: ConversationStates.TIMEOUT, + }); + + await createConversationForTest(currentHealthRecordConversationId, nhsNumber, { + State: ConversationStates.COMPLETE, + }); + + // when + const actual = await getCurrentHealthRecordIdForPatient(nhsNumber); + + // then + expect(actual).toEqual(currentHealthRecordConversationId); + }); + + it('should throw an error if no complete health record is found', async () => { + // given + const nhsNumber = '9876543211'; + const incompleteHealthRecordConversationId = uuid(); + await createConversationForTest(incompleteHealthRecordConversationId, nhsNumber, { + State: ConversationStates.TIMEOUT, + }); + + // when + await expect(() => getCurrentHealthRecordIdForPatient(nhsNumber)) + // then + .rejects.toThrowError(HealthRecordNotFoundError); + }); + + it('should throw an error when cannot find any health record', async () => { + // given + const nhsNumber = '1111111112'; + + // when + await expect(() => getCurrentHealthRecordIdForPatient(nhsNumber)) + // then + .rejects.toThrowError(HealthRecordNotFoundError); + }); + }); + + describe.skip('getHealthRecordMessageIds', () => { + it('should throw a meaningful error if there are no undeleted messages associated with conversation id', async () => { + const conversationId = uuid(); + await Message.create({ + messageId: uuid(), + conversationId, + type: MessageType.EHR_EXTRACT, + receivedAt: new Date(), + deletedAt: new Date(), // nb magical sequelize "paranoid" deletion + }); + + try { + await getHealthRecordMessageIds(conversationId); + fail('should have thrown'); + } catch (e) { + expect(e.message).toEqual( + 'There were no undeleted messages associated with conversation id' + ); + } + }); + + it('should return health record extract message id given a conversation id for a small health record', async () => { + const messageId = uuid(); + const conversationId = uuid(); + + await Message.create({ + messageId, + conversationId, + type: MessageType.EHR_EXTRACT, + receivedAt: new Date(), + }); + const { coreMessageId, fragmentMessageIds } = await getHealthRecordMessageIds(conversationId); + + expect(coreMessageId).toEqual(messageId); + expect(fragmentMessageIds).toEqual([]); + }); + + it('should return health record extract message id and fragment message ids given singular fragment', async () => { + const messageId = uuid(); + const conversationId = uuid(); + const fragmentMessageId = uuid(); + + await Message.create({ + messageId, + conversationId, + type: MessageType.EHR_EXTRACT, + receivedAt: new Date(), + }); + + await Message.create({ + messageId: fragmentMessageId, + conversationId, + type: MessageType.FRAGMENT, + receivedAt: new Date(), + parentId: messageId, + }); + const { coreMessageId, fragmentMessageIds } = await getHealthRecordMessageIds(conversationId); + + expect(coreMessageId).toEqual(messageId); + expect(fragmentMessageIds).toEqual([fragmentMessageId]); + }); + + it('should return health record extract message id and fragment message ids given nested fragments', async () => { + const messageId = uuid(); + const conversationId = uuid(); + const fragmentMessageId = uuid(); + const nestedFragmentId = uuid(); + + await Message.create({ + messageId, + conversationId, + type: MessageType.EHR_EXTRACT, + receivedAt: new Date(), + }); + + await Message.create({ + messageId: fragmentMessageId, + conversationId, + type: MessageType.FRAGMENT, + receivedAt: new Date(), + parentId: messageId, + }); + + await Message.create({ + messageId: nestedFragmentId, + conversationId, + type: MessageType.FRAGMENT, + receivedAt: new Date(), + parentId: fragmentMessageId, + }); + + const { coreMessageId, fragmentMessageIds } = await getHealthRecordMessageIds(conversationId); + + expect(coreMessageId).toEqual(messageId); + expect(fragmentMessageIds).toEqual([fragmentMessageId, nestedFragmentId]); + }); + }); + + describe.skip('healthRecordExists', () => { + it("should return false if 'messageId' is not found in db", async () => { + const messageId = uuid(); + const result = await messageAlreadyReceived(messageId); + expect(result).toEqual(false); + }); + + it("should return true if 'messageId' is found in db", async () => { + const conversationId = uuid(); + const messageId = uuid(); + const nhsNumber = '9876543211'; + + await HealthRecord.create({ + conversationId, + nhsNumber, + }); + + await Message.create({ + messageId, + conversationId, + type: MessageType.EHR_EXTRACT, + receivedAt: new Date(), + }); + + const result = await messageAlreadyReceived(messageId); + expect(result).toEqual(true); + }); + + it('should throw if database querying throws', async () => { + const messageId = 'not-valid'; + try { + await messageAlreadyReceived(messageId); + fail('should have throw'); + } catch (err) { + expect(err).not.toBeNull(); + expect(logError).toHaveBeenCalledWith('Querying database for health record failed', err); + } + }); + }); + + describe.skip('markHealthRecordAsDeletedForPatient', () => { + async function createHealthRecordAndMessage(nhsNumber, conversationId, messageId) { + await HealthRecord.create({ + conversationId, + nhsNumber, + completedAt: new Date(), + }); + await Message.create({ + conversationId, + messageId, + type: MessageType.EHR_EXTRACT, + receivedAt: new Date(), + }); + } + + it('should return conversation id for the patient marked as deleted', async () => { + const nhsNumber = '9898989898'; + const messageId = uuid(); + const conversationId = uuid(); + + await createHealthRecordAndMessage(nhsNumber, conversationId, messageId); + + const result = await markHealthRecordAsDeletedForPatient(nhsNumber); + + const healthRecordMarkedAsDeleted = await HealthRecord.findAll({ + where: { nhsNumber }, + paranoid: false, + }); + + const messagesMarkedAsDeleted = await Message.findAll({ + where: { conversationId }, + paranoid: false, + }); + + const healthRecordStatusAfterwards = await getHealthRecordStatus(conversationId); + + expect(result).toEqual([conversationId]); + expect(healthRecordStatusAfterwards).toEqual(HealthRecordStatus.NOT_FOUND); + expect(healthRecordMarkedAsDeleted[0].deletedAt).not.toBeNull(); + expect(messagesMarkedAsDeleted[0].deletedAt).not.toBeNull(); + }); + + it('should return conversation id for the patient marked as deleted when the patient has several health records', async () => { + const nhsNumber = '6767676767'; + const firstMessageId = uuid(); + const secondMessageId = uuid(); + const firstConversationId = uuid(); + const secondConversationId = uuid(); + + await createHealthRecordAndMessage(nhsNumber, firstConversationId, firstMessageId); + await createHealthRecordAndMessage(nhsNumber, secondConversationId, secondMessageId); + + const result = await markHealthRecordAsDeletedForPatient(nhsNumber); + + const healthRecordMarkedAsDeleted = await HealthRecord.findAll({ + where: { nhsNumber }, + paranoid: false, + }); + + const messagesMarkedAsDeleted = await Message.findAll({ + where: { conversationId: [firstConversationId, secondConversationId] }, + paranoid: false, + }); + + expect(result).toEqual([firstConversationId, secondConversationId]); + expect(healthRecordMarkedAsDeleted).not.toHaveProperty('deletedAt', null); + expect(messagesMarkedAsDeleted).not.toHaveProperty('deletedAt', null); + }); + }); +}); diff --git a/src/services/database/dynamo-ehr-transfer-tracker.js b/src/services/database/dynamo-ehr-transfer-tracker.js index 951c5517..071f3dd0 100644 --- a/src/services/database/dynamo-ehr-transfer-tracker.js +++ b/src/services/database/dynamo-ehr-transfer-tracker.js @@ -145,6 +145,7 @@ export class EhrTransferTracker { const items = response?.Items; if (!items) { logError('Received an empty response from dynamodb during query'); + return []; } return items; } diff --git a/src/services/database/ehr-conversation-repository.js b/src/services/database/ehr-conversation-repository.js index 7fb5947c..cb75a02b 100644 --- a/src/services/database/ehr-conversation-repository.js +++ b/src/services/database/ehr-conversation-repository.js @@ -1,40 +1,86 @@ -import { ConversationStates, QueryType } from '../../models/enums'; -import { logInfo } from '../../middleware/logging'; +import { ConversationStates, HealthRecordStatus, QueryType } from '../../models/enums'; +import { logError, logInfo } from '../../middleware/logging'; import { getUKTimestamp } from '../time'; import { UpdateCommand } from '@aws-sdk/lib-dynamodb'; import { EhrTransferTracker } from './dynamo-ehr-transfer-tracker'; import { buildConversationUpdateParams } from '../../models/conversation'; +import { HealthRecordNotFoundError } from '../../errors/errors'; -export const updateInboundConversationCompleteness = async (conversationId) => { - // to replace the existing method `updateHealthRecordCompleteness` +export const getHealthRecordStatus = async (conversationId) => { + // to replace the method with same name + try { + const conversation = await getConversationById(conversationId); + if (conversation.State === ConversationStates.COMPLETE) { + return HealthRecordStatus.COMPLETE; + } else { + return HealthRecordStatus.PENDING; + } + } catch (err) { + if (err instanceof HealthRecordNotFoundError) { + logInfo('No record was found for given conversationId'); + return HealthRecordStatus.NOT_FOUND; + } + + logError('Health Record could not be retrieved from database', err); + throw err; + } +}; + +export const getConversationById = async (conversationId) => { const db = EhrTransferTracker.getInstance(); - const allFragments = this.queryTableByConversationId(conversationId, QueryType.FRAGMENT); - const pendingMessages = allFragments.filter((fragment) => fragment.receivedAt === undefined); - if (pendingMessages.length !== 0) { - logInfo(`${pendingMessages.length} more fragments to be received.`); - return; + const results = await db.queryTableByConversationId(conversationId, QueryType.CONVERSATION); + if (!results || results.length === 0) { + logInfo('Health Record not found'); + throw new HealthRecordNotFoundError(); + } + logInfo('Health Record retrieved from the database'); + return results[0]; +}; + +export const updateConversationCompleteness = async (conversationId) => { + // to replace the existing method `updateHealthRecordCompleteness` + + try { + const db = EhrTransferTracker.getInstance(); + + const allFragments = await db.queryTableByConversationId(conversationId, QueryType.FRAGMENT); + const pendingMessages = allFragments.filter((fragment) => fragment.ReceivedAt === undefined); + + if (pendingMessages.length !== 0) { + logInfo(`${pendingMessages.length} more fragments to be received.`); + return; + } + + logInfo('All fragments are received. Will mark this inbound conversation as complete'); + + const updateParam = buildConversationUpdateParams(conversationId, { + State: ConversationStates.COMPLETE, + }); + + await db.updateItemsInTransaction([updateParam]); + } catch (err) { + logError('Failed to update health record completeness', err); + throw err; } +}; - logInfo('All fragments are received. Will mark this conversation as complete'); +export const getCurrentHealthRecordIdForPatient = async (nhsNumber) => { + // to replace the existing method of the same name + const db = EhrTransferTracker.getInstance(); + const conversations = await db.queryTableByNhsNumber(nhsNumber); - const timestamp = getUKTimestamp(); + const completedRecords = conversations?.filter( + (item) => item.State === ConversationStates.COMPLETE || item.State.startsWith('Outbound') + ); - const updateParam = { - TableName: this.tableName, - Key: { - InboundConversationId: conversationId, - Layer: 'Conversation', - }, - UpdateExpression: 'set UpdatedAt = :now, State = :complete', - ExpressionAttributeValues: { - ':now': timestamp, - ':complete': ConversationStates.COMPLETE, - }, + if (!completedRecords || completedRecords.length === 0) { + throw new HealthRecordNotFoundError(); + } - // TODO: try switch to below after integration test green - // const updateParam = buildConversationUpdateParams(conversationId, {State: ConversationStates.COMPLETE}) - }; + const currentRecord = completedRecords.reduce((prev, current) => { + return current?.CreatedAt > prev?.CreatedAt ? current : prev; + }); - await db.client.send(new UpdateCommand(updateParam)); + return currentRecord.InboundConversationId; }; diff --git a/src/services/database/health-record-repository.js b/src/services/database/health-record-repository.js index b8624663..f21c4bd6 100644 --- a/src/services/database/health-record-repository.js +++ b/src/services/database/health-record-repository.js @@ -1,15 +1,10 @@ -import { MessageType, modelName as messageModelName } from '../../models/message'; -import { modelName as healthRecordModelName } from '../../models/health-record'; -import { logError, logInfo } from '../../middleware/logging'; -import Sequelize from 'sequelize'; -import ModelFactory from '../../models'; -import { getNow } from '../time'; - -export const HealthRecordStatus = { - COMPLETE: 'complete', - PENDING: 'pending', - NOT_FOUND: 'notFound', -}; +import { MessageType, modelName as messageModelName } from "../../models/message"; +import { modelName as healthRecordModelName } from "../../models/health-record"; +import { logError, logInfo } from "../../middleware/logging"; +import Sequelize from "sequelize"; +import ModelFactory from "../../models"; +import { getNow } from "../time"; +import { HealthRecordStatus } from "../../models/enums"; Object.freeze(HealthRecordStatus); diff --git a/src/utilities/integration-test-utilities.js b/src/utilities/integration-test-utilities.js index 606ac100..27ecb729 100644 --- a/src/utilities/integration-test-utilities.js +++ b/src/utilities/integration-test-utilities.js @@ -13,7 +13,7 @@ export const generateMultipleUUID = (amount, isUppercase) => .fill(undefined) .map(() => (isUppercase ? uuidv4().toUpperCase() : uuidv4())); -export const createConversationForTest = async (conversationId, nhsNumber) => { +export const createConversationForTest = async (conversationId, nhsNumber, overrides) => { // This method is only meant for testing purpose, // as the inbound conversation record is supposed to be created by other service. @@ -31,6 +31,7 @@ export const createConversationForTest = async (conversationId, nhsNumber) => { NhsNumber: nhsNumber, CreatedAt: timestamp, UpdatedAt: timestamp, + ...overrides }; await db.writeItemsToTable([item]); From 8b7bfda79063fcfd0dab6ff14d7b663f01d9979a Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Wed, 6 Mar 2024 18:18:16 +0000 Subject: [PATCH 18/67] [PRMT-4566] Migrating integrated test for more methods --- src/models/core.js | 4 + src/models/enums.js | 4 +- src/models/fragment.js | 9 +- ...o-ehr-transfer-tracker.integration.test.js | 6 +- ...alth-record-repository.integration.test.js | 150 ++++++++---------- .../database/dynamo-ehr-transfer-tracker.js | 31 ++-- .../database/ehr-conversation-repository.js | 8 +- src/services/database/ehr-core-repository.js | 28 +++- .../database/ehr-fragment-repository.js | 17 +- src/services/time.js | 5 + 10 files changed, 148 insertions(+), 114 deletions(-) diff --git a/src/models/core.js b/src/models/core.js index 4e31680a..48501337 100644 --- a/src/models/core.js +++ b/src/models/core.js @@ -20,3 +20,7 @@ export const core = (inboundConversationId, messageId) => { State: CoreStates.COMPLETE, }; }; + +export const isCore = (dynamoDbItem) => { + return dynamoDbItem?.Layer?.startsWith('Core'); +} diff --git a/src/models/enums.js b/src/models/enums.js index 05f7449a..f2e992fc 100644 --- a/src/models/enums.js +++ b/src/models/enums.js @@ -1,4 +1,4 @@ -export const QueryType = { +export const RecordType = { ALL: 'ALL', CONVERSATION: 'Conversation', CORE: 'Core', @@ -30,7 +30,7 @@ export const HealthRecordStatus = { }; Object.freeze(HealthRecordStatus); -Object.freeze(QueryType); +Object.freeze(RecordType); Object.freeze(ConversationStates); Object.freeze(CoreStates); Object.freeze(FragmentStates); diff --git a/src/models/fragment.js b/src/models/fragment.js index cda822dc..482a3cd2 100644 --- a/src/models/fragment.js +++ b/src/models/fragment.js @@ -51,9 +51,12 @@ export const buildFragmentUpdateParams = (conversationId, messageId, changes) => InboundConversationId: conversationId, Layer: `Fragment#${messageId}`, }, - UpdateExpression: 'set CreatedAt = if_not_exists(CreatedAt, :now), UpdatedAt = :now', + UpdateExpression: `set CreatedAt = if_not_exists(CreatedAt, :now), \ + InboundMessageId = if_not_exists(InboundMessageId, :messageId), \ + UpdatedAt = :now`, ExpressionAttributeValues: { ':now': getUKTimestamp(), + ':messageId': messageId }, }; @@ -67,3 +70,7 @@ export const buildFragmentUpdateParams = (conversationId, messageId, changes) => return params; }; + +export const isFragment = (dynamoDbItem) => { + return dynamoDbItem?.Layer?.startsWith('Fragment'); +} diff --git a/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js b/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js index 6ebf99fa..b076ea4c 100644 --- a/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js +++ b/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js @@ -1,6 +1,6 @@ import { EhrTransferTracker } from '../dynamo-ehr-transfer-tracker'; import { v4 as uuid } from 'uuid'; -import { QueryType } from '../../../models/enums'; +import { RecordType } from '../../../models/enums'; import { createCore } from '../ehr-core-repository'; import { cleanupRecordsForTest, @@ -34,7 +34,7 @@ describe('EhrTransferTracker', () => { await createCore(ehrExtract); // then - const actual = await db.queryTableByConversationId(testConversationId, QueryType.CORE); + const actual = await db.queryTableByConversationId(testConversationId, RecordType.CORE); expect(actual).toHaveLength(1); expect(actual[0]).toMatchObject({ @@ -72,7 +72,7 @@ describe('EhrTransferTracker', () => { testNestedChildIds ); - const actual = await db.queryTableByConversationId(testConversationId, QueryType.FRAGMENT); + const actual = await db.queryTableByConversationId(testConversationId, RecordType.FRAGMENT); // then const expectedTotalMessages = testChildMessageIds.length + testNestedChildIds.length; diff --git a/src/services/database/__tests__/new-health-record-repository.integration.test.js b/src/services/database/__tests__/new-health-record-repository.integration.test.js index f87f3234..c7f05eee 100644 --- a/src/services/database/__tests__/new-health-record-repository.integration.test.js +++ b/src/services/database/__tests__/new-health-record-repository.integration.test.js @@ -8,10 +8,15 @@ import { } from '../ehr-conversation-repository'; import { ConversationStates, HealthRecordStatus } from '../../../models/enums'; import { createConversationForTest } from '../../../utilities/integration-test-utilities'; -import { createCore } from '../ehr-core-repository'; +import { createCore, getHealthRecordMessageIds } from '../ehr-core-repository'; import { EhrTransferTracker } from '../dynamo-ehr-transfer-tracker'; -import { markFragmentAsReceivedAndCreateItsParts } from '../ehr-fragment-repository'; -import { HealthRecordNotFoundError } from "../../../errors/errors"; +import { + fragmentAlreadyReceived, + markFragmentAsReceivedAndCreateItsParts, +} from '../ehr-fragment-repository'; +import { HealthRecordNotFoundError, MessageNotFoundError } from '../../../errors/errors'; +import { core } from '../../../models/core'; +import { getEpochTimeInSecond } from '../../time'; jest.mock('../../../middleware/logging'); @@ -245,140 +250,125 @@ describe('healthRecordRepository', () => { }); }); - describe.skip('getHealthRecordMessageIds', () => { - it('should throw a meaningful error if there are no undeleted messages associated with conversation id', async () => { + describe('getHealthRecordMessageIds', () => { + it('should throw an error if no message found with given conversationId', async () => { + // given const conversationId = uuid(); - await Message.create({ - messageId: uuid(), - conversationId, - type: MessageType.EHR_EXTRACT, - receivedAt: new Date(), - deletedAt: new Date(), // nb magical sequelize "paranoid" deletion - }); - try { - await getHealthRecordMessageIds(conversationId); - fail('should have thrown'); - } catch (e) { - expect(e.message).toEqual( - 'There were no undeleted messages associated with conversation id' - ); - } + // when + await expect(() => getHealthRecordMessageIds(conversationId)) + // then + .rejects.toThrowError(MessageNotFoundError); + }); + + it('should throw an error if the only existing messages were deleted', async () => { + // given + const conversationId = uuid(); + const messageId = uuid(); + const item = { ...core(conversationId, messageId), DeletedAt: getEpochTimeInSecond() }; + + await db.writeItemsToTable([item]); + + // when + await expect(() => getHealthRecordMessageIds(conversationId)) + // then + .rejects.toThrowError(MessageNotFoundError); }); it('should return health record extract message id given a conversation id for a small health record', async () => { + // given const messageId = uuid(); const conversationId = uuid(); + await createCore({ conversationId, messageId, fragmentMessageIds: [] }); - await Message.create({ - messageId, - conversationId, - type: MessageType.EHR_EXTRACT, - receivedAt: new Date(), - }); + // when const { coreMessageId, fragmentMessageIds } = await getHealthRecordMessageIds(conversationId); + // then expect(coreMessageId).toEqual(messageId); expect(fragmentMessageIds).toEqual([]); }); it('should return health record extract message id and fragment message ids given singular fragment', async () => { + // given const messageId = uuid(); const conversationId = uuid(); const fragmentMessageId = uuid(); - await Message.create({ - messageId, - conversationId, - type: MessageType.EHR_EXTRACT, - receivedAt: new Date(), - }); + await createCore({ conversationId, messageId, fragmentMessageIds: [fragmentMessageId] }); + await markFragmentAsReceived(fragmentMessageId, conversationId); - await Message.create({ - messageId: fragmentMessageId, - conversationId, - type: MessageType.FRAGMENT, - receivedAt: new Date(), - parentId: messageId, - }); + // when const { coreMessageId, fragmentMessageIds } = await getHealthRecordMessageIds(conversationId); + // then expect(coreMessageId).toEqual(messageId); expect(fragmentMessageIds).toEqual([fragmentMessageId]); }); it('should return health record extract message id and fragment message ids given nested fragments', async () => { + // given const messageId = uuid(); const conversationId = uuid(); const fragmentMessageId = uuid(); const nestedFragmentId = uuid(); - await Message.create({ - messageId, - conversationId, - type: MessageType.EHR_EXTRACT, - receivedAt: new Date(), - }); - - await Message.create({ - messageId: fragmentMessageId, - conversationId, - type: MessageType.FRAGMENT, - receivedAt: new Date(), - parentId: messageId, - }); - - await Message.create({ - messageId: nestedFragmentId, - conversationId, - type: MessageType.FRAGMENT, - receivedAt: new Date(), - parentId: fragmentMessageId, - }); + await createCore({ conversationId, messageId, fragmentMessageIds: [fragmentMessageId] }); + await markFragmentAsReceivedAndCreateItsParts(fragmentMessageId, conversationId, [ + nestedFragmentId, + ]); + await markFragmentAsReceived(nestedFragmentId, conversationId); + // when const { coreMessageId, fragmentMessageIds } = await getHealthRecordMessageIds(conversationId); + // then expect(coreMessageId).toEqual(messageId); - expect(fragmentMessageIds).toEqual([fragmentMessageId, nestedFragmentId]); + expect(fragmentMessageIds.sort()).toEqual([fragmentMessageId, nestedFragmentId].sort()); }); }); - describe.skip('healthRecordExists', () => { + describe('fragmentAlreadyReceived', () => { it("should return false if 'messageId' is not found in db", async () => { + // given const messageId = uuid(); - const result = await messageAlreadyReceived(messageId); + + // when + const result = await fragmentAlreadyReceived(messageId); + + // then expect(result).toEqual(false); }); - it("should return true if 'messageId' is found in db", async () => { + it("should return true if 'messageId' is received in db", async () => { + // given const conversationId = uuid(); const messageId = uuid(); - const nhsNumber = '9876543211'; + const fragmentMessageId = uuid(); - await HealthRecord.create({ - conversationId, - nhsNumber, - }); + await createCore({ conversationId, messageId, fragmentMessageIds: [fragmentMessageId] }); + await markFragmentAsReceived(fragmentMessageId, conversationId); - await Message.create({ - messageId, - conversationId, - type: MessageType.EHR_EXTRACT, - receivedAt: new Date(), - }); + // when + const result = await fragmentAlreadyReceived(conversationId, fragmentMessageId); - const result = await messageAlreadyReceived(messageId); + // then expect(result).toEqual(true); }); it('should throw if database querying throws', async () => { - const messageId = 'not-valid'; + // given + const messageId = uuid(); + mimicDynamodbFail(); + + // when try { - await messageAlreadyReceived(messageId); + await fragmentAlreadyReceived(messageId); fail('should have throw'); } catch (err) { + // then expect(err).not.toBeNull(); - expect(logError).toHaveBeenCalledWith('Querying database for health record failed', err); + expect(logError).toHaveBeenCalledWith('Querying database for fragment message failed', err); } }); }); diff --git a/src/services/database/dynamo-ehr-transfer-tracker.js b/src/services/database/dynamo-ehr-transfer-tracker.js index 071f3dd0..42ce2abd 100644 --- a/src/services/database/dynamo-ehr-transfer-tracker.js +++ b/src/services/database/dynamo-ehr-transfer-tracker.js @@ -7,7 +7,7 @@ import { import { getUKTimestamp } from '../time'; import { logError, logInfo } from '../../middleware/logging'; -import { QueryType, ConversationStates } from '../../models/enums'; +import { RecordType, ConversationStates } from '../../models/enums'; import { getDynamodbClient } from './dynamodb-client'; import { buildFragmentUpdateParams } from '../../models/fragment'; @@ -113,7 +113,7 @@ export class EhrTransferTracker { return currentRecord.InboundConversationId; } - async queryTableByConversationId(inboundConversationId, queryType = QueryType.ALL) { + async queryTableByConversationId(inboundConversationId, recordType = RecordType.ALL) { const params = { TableName: this.tableName, ExpressionAttributeNames: { @@ -123,20 +123,21 @@ export class EhrTransferTracker { ':InboundConversationId': inboundConversationId, }, KeyConditionExpression: '#PrimaryKey = :InboundConversationId', + FilterExpression: 'attribute_not_exists(DeletedAt)' }; - switch (queryType) { - case QueryType.ALL: + switch (recordType) { + case RecordType.ALL: break; - case QueryType.CONVERSATION: - case QueryType.CORE: - case QueryType.FRAGMENT: + case RecordType.CONVERSATION: + case RecordType.CORE: + case RecordType.FRAGMENT: params.ExpressionAttributeNames['#sortKey'] = 'Layer'; - params.ExpressionAttributeValues[':sortKey'] = queryType; + params.ExpressionAttributeValues[':sortKey'] = recordType; params.KeyConditionExpression += ' AND begins_with(#sortKey, :sortKey)'; break; default: - logInfo(`Received unexpected queryType: ${queryType}. Will treat it as 'ALL'.`); + logInfo(`Received unexpected queryType: ${recordType}. Will treat it as 'ALL'.`); } const command = new QueryCommand(params); @@ -150,11 +151,11 @@ export class EhrTransferTracker { return items; } - async getItemByKey(inboundConversationId, inboundMessageId, queryType = QueryType.FRAGMENT) { - const expectedQueryTypes = [QueryType.CORE, QueryType.FRAGMENT]; + async getItemByKey(inboundConversationId, inboundMessageId, recordType = RecordType.FRAGMENT) { + const expectedTypes = [RecordType.CORE, RecordType.FRAGMENT]; - if (!expectedQueryTypes.includes(queryType)) { - throw new Error('queryType has to be either Core or Fragment'); + if (!expectedTypes.includes(recordType)) { + throw new Error('recordType has to be either Core or Fragment'); } if (!inboundConversationId && !inboundMessageId) { throw new Error('must be called with both conversationId and inboundMessageId'); @@ -164,7 +165,7 @@ export class EhrTransferTracker { TableName: this.tableName, Key: { InboundConversationId: inboundConversationId, - Layer: `${queryType}#${inboundMessageId}`, + Layer: `${recordType}#${inboundMessageId}`, }, }); @@ -177,6 +178,6 @@ export class EhrTransferTracker { } async getFragmentByKey(inboundConversationId, inboundMessageId) { - return this.getItemByKey(inboundConversationId, inboundMessageId, QueryType.FRAGMENT); + return this.getItemByKey(inboundConversationId, inboundMessageId, RecordType.FRAGMENT); } } diff --git a/src/services/database/ehr-conversation-repository.js b/src/services/database/ehr-conversation-repository.js index cb75a02b..99820adb 100644 --- a/src/services/database/ehr-conversation-repository.js +++ b/src/services/database/ehr-conversation-repository.js @@ -1,7 +1,5 @@ -import { ConversationStates, HealthRecordStatus, QueryType } from '../../models/enums'; +import { ConversationStates, HealthRecordStatus, RecordType } from '../../models/enums'; import { logError, logInfo } from '../../middleware/logging'; -import { getUKTimestamp } from '../time'; -import { UpdateCommand } from '@aws-sdk/lib-dynamodb'; import { EhrTransferTracker } from './dynamo-ehr-transfer-tracker'; import { buildConversationUpdateParams } from '../../models/conversation'; import { HealthRecordNotFoundError } from '../../errors/errors'; @@ -29,7 +27,7 @@ export const getHealthRecordStatus = async (conversationId) => { export const getConversationById = async (conversationId) => { const db = EhrTransferTracker.getInstance(); - const results = await db.queryTableByConversationId(conversationId, QueryType.CONVERSATION); + const results = await db.queryTableByConversationId(conversationId, RecordType.CONVERSATION); if (!results || results.length === 0) { logInfo('Health Record not found'); throw new HealthRecordNotFoundError(); @@ -44,7 +42,7 @@ export const updateConversationCompleteness = async (conversationId) => { try { const db = EhrTransferTracker.getInstance(); - const allFragments = await db.queryTableByConversationId(conversationId, QueryType.FRAGMENT); + const allFragments = await db.queryTableByConversationId(conversationId, RecordType.FRAGMENT); const pendingMessages = allFragments.filter((fragment) => fragment.ReceivedAt === undefined); if (pendingMessages.length !== 0) { diff --git a/src/services/database/ehr-core-repository.js b/src/services/database/ehr-core-repository.js index fa128988..ffc5e66b 100644 --- a/src/services/database/ehr-core-repository.js +++ b/src/services/database/ehr-core-repository.js @@ -1,8 +1,9 @@ import { EhrTransferTracker } from './dynamo-ehr-transfer-tracker'; -import { arrayOfFragments } from '../../models/fragment'; -import { core } from '../../models/core'; -import { QueryType } from '../../models/enums'; +import { arrayOfFragments, isFragment } from "../../models/fragment"; +import { core, isCore } from "../../models/core"; +import { RecordType } from '../../models/enums'; import { logError } from '../../middleware/logging'; +import { MessageNotFoundError } from "../../errors/errors"; export const createCore = async ({ conversationId, messageId, fragmentMessageIds }) => { // to replace the existing `createEhrExtract` method @@ -28,7 +29,24 @@ export const createCore = async ({ conversationId, messageId, fragmentMessageIds }; export const getCoreByKey = (inboundConversationId, inboundMessageId) => { - // to replace the findByPk default method from sequalize const db = EhrTransferTracker.getInstance(); - return db.getItemByKey(inboundConversationId, inboundMessageId, QueryType.CORE); + return db.getItemByKey(inboundConversationId, inboundMessageId, RecordType.CORE); }; + +export const getHealthRecordMessageIds = async (conversationId) => { + // to replace the method of same name + + const db = EhrTransferTracker.getInstance(); + const items = await db.queryTableByConversationId(conversationId, RecordType.ALL); + + const core = items.filter(isCore)?.[0]; + const fragments = items.filter(isFragment); + + if (!core) { + throw new MessageNotFoundError(); + } + const coreMessageId = core.InboundMessageId; + const fragmentMessageIds = fragments.map(message => message.InboundMessageId); + + return { coreMessageId, fragmentMessageIds }; +} diff --git a/src/services/database/ehr-fragment-repository.js b/src/services/database/ehr-fragment-repository.js index 752662d6..ee189b53 100644 --- a/src/services/database/ehr-fragment-repository.js +++ b/src/services/database/ehr-fragment-repository.js @@ -1,8 +1,7 @@ import { getUKTimestamp } from '../time'; -import { TransactWriteCommand } from '@aws-sdk/lib-dynamodb'; import { EhrTransferTracker } from './dynamo-ehr-transfer-tracker'; import { buildFragmentUpdateParams } from '../../models/fragment'; -import { QueryType } from '../../models/enums'; +import { RecordType } from '../../models/enums'; import { logError } from '../../middleware/logging'; export const markFragmentAsReceivedAndCreateItsParts = async ( @@ -34,7 +33,7 @@ export const markFragmentAsReceivedAndCreateItsParts = async ( export const getFragmentByKey = (inboundConversationId, inboundMessageId) => { // to replace the findByPk default method from sequalize const db = EhrTransferTracker.getInstance(); - return db.getItemByKey(inboundConversationId, inboundMessageId, QueryType.FRAGMENT); + return db.getItemByKey(inboundConversationId, inboundMessageId, RecordType.FRAGMENT); }; export const fragmentExists = async (inboundConversationId, inboundMessageId) => { @@ -43,7 +42,19 @@ export const fragmentExists = async (inboundConversationId, inboundMessageId) => const fragment = await getFragmentByKey(inboundConversationId, inboundMessageId); return !!fragment; } catch (e) { + // TODO: change this error message logError('Querying database for fragment message failed', e); throw e; } }; + +export const fragmentAlreadyReceived = async (conversationId, messageId) => { + // to replace the method `messageAlreadyReceived` + try { + const fragment = await getFragmentByKey(conversationId, messageId) + return fragment?.ReceivedAt !== undefined; + } catch (e) { + logError('Querying database for fragment message failed', e); + throw e; + } +} diff --git a/src/services/time.js b/src/services/time.js index a4805852..16997c38 100644 --- a/src/services/time.js +++ b/src/services/time.js @@ -5,3 +5,8 @@ export const getNow = () => new Date(); export const getUKTimestamp = () => { return moment().tz('Europe/London').format('YYYY-MM-DDThh:mm:ssZ'); }; + + +export const getEpochTimeInSecond = () => { + return moment().unix(); +} \ No newline at end of file From 8a6cfd4a8ef6dbeab21e220866f5337773105cfb Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Thu, 7 Mar 2024 09:11:57 +0000 Subject: [PATCH 19/67] PRMT-4566 reorganise code --- ...alth-record-repository.integration.test.js | 3 +- .../database/ehr-conversation-repository.js | 31 ++++++++++++++++--- src/services/database/ehr-core-repository.js | 28 +++-------------- 3 files changed, 33 insertions(+), 29 deletions(-) diff --git a/src/services/database/__tests__/new-health-record-repository.integration.test.js b/src/services/database/__tests__/new-health-record-repository.integration.test.js index c7f05eee..3cc9e06d 100644 --- a/src/services/database/__tests__/new-health-record-repository.integration.test.js +++ b/src/services/database/__tests__/new-health-record-repository.integration.test.js @@ -3,12 +3,13 @@ import { logError } from '../../../middleware/logging'; import { getConversationById, getCurrentHealthRecordIdForPatient, + getHealthRecordMessageIds, getHealthRecordStatus, updateConversationCompleteness, } from '../ehr-conversation-repository'; import { ConversationStates, HealthRecordStatus } from '../../../models/enums'; import { createConversationForTest } from '../../../utilities/integration-test-utilities'; -import { createCore, getHealthRecordMessageIds } from '../ehr-core-repository'; +import { createCore } from '../ehr-core-repository'; import { EhrTransferTracker } from '../dynamo-ehr-transfer-tracker'; import { fragmentAlreadyReceived, diff --git a/src/services/database/ehr-conversation-repository.js b/src/services/database/ehr-conversation-repository.js index 99820adb..760300f9 100644 --- a/src/services/database/ehr-conversation-repository.js +++ b/src/services/database/ehr-conversation-repository.js @@ -1,8 +1,10 @@ -import { ConversationStates, HealthRecordStatus, RecordType } from '../../models/enums'; -import { logError, logInfo } from '../../middleware/logging'; -import { EhrTransferTracker } from './dynamo-ehr-transfer-tracker'; -import { buildConversationUpdateParams } from '../../models/conversation'; -import { HealthRecordNotFoundError } from '../../errors/errors'; +import { ConversationStates, HealthRecordStatus, RecordType } from "../../models/enums"; +import { logError, logInfo } from "../../middleware/logging"; +import { EhrTransferTracker } from "./dynamo-ehr-transfer-tracker"; +import { buildConversationUpdateParams } from "../../models/conversation"; +import { HealthRecordNotFoundError, MessageNotFoundError } from "../../errors/errors"; +import { isCore } from "../../models/core"; +import { isFragment } from "../../models/fragment"; export const getHealthRecordStatus = async (conversationId) => { // to replace the method with same name @@ -82,3 +84,22 @@ export const getCurrentHealthRecordIdForPatient = async (nhsNumber) => { return currentRecord.InboundConversationId; }; + + +export const getHealthRecordMessageIds = async (conversationId) => { + // to replace the method of same name + + const db = EhrTransferTracker.getInstance(); + const items = await db.queryTableByConversationId(conversationId, RecordType.ALL); + + const core = items.filter(isCore)?.[0]; + const fragments = items.filter(isFragment); + + if (!core) { + throw new MessageNotFoundError(); + } + const coreMessageId = core.InboundMessageId; + const fragmentMessageIds = fragments.map(message => message.InboundMessageId); + + return { coreMessageId, fragmentMessageIds }; +}; \ No newline at end of file diff --git a/src/services/database/ehr-core-repository.js b/src/services/database/ehr-core-repository.js index ffc5e66b..3d78bd8e 100644 --- a/src/services/database/ehr-core-repository.js +++ b/src/services/database/ehr-core-repository.js @@ -1,9 +1,8 @@ -import { EhrTransferTracker } from './dynamo-ehr-transfer-tracker'; -import { arrayOfFragments, isFragment } from "../../models/fragment"; -import { core, isCore } from "../../models/core"; -import { RecordType } from '../../models/enums'; -import { logError } from '../../middleware/logging'; -import { MessageNotFoundError } from "../../errors/errors"; +import { EhrTransferTracker } from "./dynamo-ehr-transfer-tracker"; +import { arrayOfFragments } from "../../models/fragment"; +import { core } from "../../models/core"; +import { RecordType } from "../../models/enums"; +import { logError } from "../../middleware/logging"; export const createCore = async ({ conversationId, messageId, fragmentMessageIds }) => { // to replace the existing `createEhrExtract` method @@ -33,20 +32,3 @@ export const getCoreByKey = (inboundConversationId, inboundMessageId) => { return db.getItemByKey(inboundConversationId, inboundMessageId, RecordType.CORE); }; -export const getHealthRecordMessageIds = async (conversationId) => { - // to replace the method of same name - - const db = EhrTransferTracker.getInstance(); - const items = await db.queryTableByConversationId(conversationId, RecordType.ALL); - - const core = items.filter(isCore)?.[0]; - const fragments = items.filter(isFragment); - - if (!core) { - throw new MessageNotFoundError(); - } - const coreMessageId = core.InboundMessageId; - const fragmentMessageIds = fragments.map(message => message.InboundMessageId); - - return { coreMessageId, fragmentMessageIds }; -} From 88892de2d019338b41668a965280781efe217975 Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Thu, 7 Mar 2024 11:49:56 +0000 Subject: [PATCH 20/67] [PRMT-4566] Migrated all database-related integration tests. Store current progress --- src/models/conversation.js | 25 +-- src/models/core.js | 15 +- src/models/enums.js | 12 +- src/models/fragment.js | 29 +-- ...alth-record-repository.integration.test.js | 176 ++++++++++-------- .../database/dynamo-ehr-transfer-tracker.js | 16 +- .../database/ehr-conversation-repository.js | 48 +++-- src/services/database/ehr-core-repository.js | 13 +- src/services/time.js | 3 +- src/utilities/dynamodb-helper.js | 42 +++++ src/utilities/integration-test-utilities.js | 3 +- 11 files changed, 226 insertions(+), 156 deletions(-) create mode 100644 src/utilities/dynamodb-helper.js diff --git a/src/models/conversation.js b/src/models/conversation.js index a4d26e51..d7d299b7 100644 --- a/src/models/conversation.js +++ b/src/models/conversation.js @@ -1,10 +1,11 @@ import { getUKTimestamp } from '../services/time'; -import { logError } from "../middleware/logging"; +import { logError } from '../middleware/logging'; +import { addChangesToUpdateParams } from '../utilities/dynamodb-helper'; -const fieldsAllowedToUpdate = ['State', 'FailureCode']; +const fieldsAllowedToUpdate = ['TransferStatus', 'FailureCode', 'DeletedAt']; export const buildConversationUpdateParams = (conversationId, changes) => { - const params = { + const baseParams = { Key: { InboundConversationId: conversationId, Layer: 'Conversation', @@ -15,19 +16,9 @@ export const buildConversationUpdateParams = (conversationId, changes) => { }, }; - for (const [fieldName, updatedValue] of Object.entries(changes)) { - if (fieldsAllowedToUpdate.includes(fieldName)) { - const keyToken = `#${fieldName}`; - const valueToken = `:${fieldName}`; - - params.UpdateExpression += `, ${keyToken} = ${valueToken}`; - params.ExpressionAttributeValues[valueToken] = updatedValue; - params.ExpressionAttributeNames = params.ExpressionAttributeNames ?? {}; - params.ExpressionAttributeNames[keyToken] = fieldName; - } else { - logError(`Ignoring attempt to update non-allowed field ${fieldName}`) - } - } + return addChangesToUpdateParams(baseParams, changes, fieldsAllowedToUpdate); +}; - return params; +export const isConversation = (item) => { + return item.Layer === 'Conversation'; }; diff --git a/src/models/core.js b/src/models/core.js index 48501337..9fc66ef3 100644 --- a/src/models/core.js +++ b/src/models/core.js @@ -1,14 +1,13 @@ import { getUKTimestamp } from '../services/time'; -import { CoreStates } from './enums'; -import { validate } from 'uuid'; +import { CoreStatus } from './enums'; +import { validateIds } from '../utilities/dynamodb-helper'; + +const fieldsAllowedToUpdate = ['TransferStatus', 'DeletedAt']; export const core = (inboundConversationId, messageId) => { const timestamp = getUKTimestamp(); - const uuidsAreValid = validate(inboundConversationId) && validate(messageId); - if (!uuidsAreValid) { - throw new Error('received invalid uuid as either conversationId or messageId'); - } + validateIds(inboundConversationId, messageId); return { InboundConversationId: inboundConversationId, @@ -17,10 +16,10 @@ export const core = (inboundConversationId, messageId) => { CreatedAt: timestamp, ReceivedAt: timestamp, UpdatedAt: timestamp, - State: CoreStates.COMPLETE, + TransferStatus: CoreStatus.COMPLETE, }; }; export const isCore = (dynamoDbItem) => { return dynamoDbItem?.Layer?.startsWith('Core'); -} +}; diff --git a/src/models/enums.js b/src/models/enums.js index f2e992fc..6c872dcf 100644 --- a/src/models/enums.js +++ b/src/models/enums.js @@ -5,7 +5,7 @@ export const RecordType = { FRAGMENT: 'Fragment', }; -export const ConversationStates = { +export const ConversationStatus = { STARTED: 'INBOUND_STARTED', REQUEST_SENT: 'INBOUND_REQUEST_SENT', CONTINUE_REQUEST_SENT: 'INBOUND_CONTINUE_REQUEST_SENT', @@ -14,11 +14,11 @@ export const ConversationStates = { TIMEOUT: 'INBOUND_TIMEOUT', }; -export const CoreStates = { +export const CoreStatus = { COMPLETE: 'INBOUND_COMPLETE', }; -export const FragmentStates = { +export const FragmentStatus = { PENDING: 'INBOUND_REQUEST_SENT', COMPLETE: 'INBOUND_COMPLETE', }; @@ -31,6 +31,6 @@ export const HealthRecordStatus = { Object.freeze(HealthRecordStatus); Object.freeze(RecordType); -Object.freeze(ConversationStates); -Object.freeze(CoreStates); -Object.freeze(FragmentStates); +Object.freeze(ConversationStatus); +Object.freeze(CoreStatus); +Object.freeze(FragmentStatus); diff --git a/src/models/fragment.js b/src/models/fragment.js index 482a3cd2..f6b01be0 100644 --- a/src/models/fragment.js +++ b/src/models/fragment.js @@ -1,12 +1,8 @@ import { getUKTimestamp } from '../services/time'; import { validate } from 'uuid'; +import { addChangesToUpdateParams, validateIds } from "../utilities/dynamodb-helper"; -const validateIds = (conversationId, messageId) => { - const uuidsAreValid = validate(conversationId) && validate(messageId); - if (!uuidsAreValid) { - throw new Error('received invalid uuid as either conversationId or messageId'); - } -}; +const fieldsAllowedToUpdate = ['TransferStatus', 'ParentId', 'ReceivedAt', 'DeletedAt']; export const singleFragment = ({ inboundConversationId, fragmentMessageId, parentMessageId }) => { const timestamp = getUKTimestamp(); @@ -36,13 +32,6 @@ export const arrayOfFragments = ({ ); }; -const FieldsAllowedToUpdate = { - State: 'State', - ParentId: 'ParentId', - ReceivedAt: 'ReceivedAt', - DeletedAt: 'DeletedAt', -}; - export const buildFragmentUpdateParams = (conversationId, messageId, changes) => { validateIds(conversationId, messageId); @@ -56,21 +45,13 @@ export const buildFragmentUpdateParams = (conversationId, messageId, changes) => UpdatedAt = :now`, ExpressionAttributeValues: { ':now': getUKTimestamp(), - ':messageId': messageId + ':messageId': messageId, }, }; - for (const fieldname in FieldsAllowedToUpdate) { - if (fieldname in changes) { - const colonKey = `:${fieldname}`; - params.UpdateExpression += `, ${fieldname} = ${colonKey}`; - params.ExpressionAttributeValues[colonKey] = changes[fieldname]; - } - } - - return params; + return addChangesToUpdateParams(params, changes, fieldsAllowedToUpdate); }; export const isFragment = (dynamoDbItem) => { return dynamoDbItem?.Layer?.startsWith('Fragment'); -} +}; diff --git a/src/services/database/__tests__/new-health-record-repository.integration.test.js b/src/services/database/__tests__/new-health-record-repository.integration.test.js index 3cc9e06d..3d62a524 100644 --- a/src/services/database/__tests__/new-health-record-repository.integration.test.js +++ b/src/services/database/__tests__/new-health-record-repository.integration.test.js @@ -5,10 +5,14 @@ import { getCurrentHealthRecordIdForPatient, getHealthRecordMessageIds, getHealthRecordStatus, + markHealthRecordAsDeletedForPatient, updateConversationCompleteness, } from '../ehr-conversation-repository'; -import { ConversationStates, HealthRecordStatus } from '../../../models/enums'; -import { createConversationForTest } from '../../../utilities/integration-test-utilities'; +import { ConversationStatus, HealthRecordStatus, RecordType } from '../../../models/enums'; +import { + cleanupRecordsForTest, + createConversationForTest, +} from '../../../utilities/integration-test-utilities'; import { createCore } from '../ehr-core-repository'; import { EhrTransferTracker } from '../dynamo-ehr-transfer-tracker'; import { @@ -23,8 +27,6 @@ jest.mock('../../../middleware/logging'); describe('healthRecordRepository', () => { // ========================= COMMON PROPERTIES ========================= - // const HealthRecord = ModelFactory.getByName(healthRecordModelName); - // const Message = ModelFactory.getByName(messageModelName); const db = EhrTransferTracker.getInstance(); const markFragmentAsReceived = markFragmentAsReceivedAndCreateItsParts; const fail = (reason) => { @@ -37,22 +39,10 @@ describe('healthRecordRepository', () => { const undoMimicDynamodbFail = () => { db.tableName = tableNameBackup; }; - // ===================================================================== - // ========================= SET UP / TEAR DOWN ======================== - beforeEach(async () => { - // await HealthRecord.truncate(); - // await Message.truncate(); - // await ModelFactory.sequelize.sync({ force: true }); - }); - afterEach(() => { undoMimicDynamodbFail(); }); - - afterAll(async () => { - // await ModelFactory.sequelize.close(); - }); // ===================================================================== describe('getHealthRecordStatus', () => { @@ -61,7 +51,7 @@ describe('healthRecordRepository', () => { const conversationId = uuid(); const nhsNumber = '1234567890'; await createConversationForTest(conversationId, nhsNumber, { - State: ConversationStates.COMPLETE, + TransferStatus: ConversationStatus.COMPLETE, }); // when @@ -76,7 +66,7 @@ describe('healthRecordRepository', () => { const conversationId = uuid(); const nhsNumber = '1234567890'; await createConversationForTest(conversationId, nhsNumber, { - State: ConversationStates.CONTINUE_REQUEST_SENT, + TransferStatus: ConversationStatus.CONTINUE_REQUEST_SENT, }); // when @@ -124,7 +114,7 @@ describe('healthRecordRepository', () => { const messageId = uuid(); const nhsNumber = '1234567890'; await createConversationForTest(conversationId, nhsNumber, { - State: ConversationStates.REQUEST_SENT, + TransferStatus: ConversationStatus.REQUEST_SENT, }); await createCore({ conversationId, messageId, fragmentMessageIds: [] }); @@ -134,7 +124,7 @@ describe('healthRecordRepository', () => { // then const conversation = await getConversationById(conversationId); - expect(conversation.State).toBe(ConversationStates.COMPLETE); + expect(conversation.TransferStatus).toBe(ConversationStatus.COMPLETE); }); it('should not set State to Complete if there are still messages to be received', async () => { @@ -145,7 +135,7 @@ describe('healthRecordRepository', () => { const nhsNumber = '1234567890'; await createConversationForTest(conversationId, nhsNumber, { - State: ConversationStates.CONTINUE_REQUEST_SENT, + TransferStatus: ConversationStatus.CONTINUE_REQUEST_SENT, }); await createCore({ conversationId, messageId, fragmentMessageIds: [fragmentMessageId] }); @@ -155,7 +145,7 @@ describe('healthRecordRepository', () => { // then const conversation = await getConversationById(conversationId); - expect(conversation.State).toBe(ConversationStates.CONTINUE_REQUEST_SENT); + expect(conversation.TransferStatus).toBe(ConversationStatus.CONTINUE_REQUEST_SENT); }); it('Should set State to Complete if all fragments are received', async () => { @@ -166,7 +156,7 @@ describe('healthRecordRepository', () => { const nhsNumber = '1234567890'; await createConversationForTest(conversationId, nhsNumber, { - State: ConversationStates.CONTINUE_REQUEST_SENT, + TransferStatus: ConversationStatus.CONTINUE_REQUEST_SENT, }); await createCore({ conversationId, messageId, fragmentMessageIds: fragmentMessageIds }); for (const fragmentId of fragmentMessageIds) { @@ -179,7 +169,7 @@ describe('healthRecordRepository', () => { // then const conversation = await getConversationById(conversationId); - expect(conversation.State).toBe(ConversationStates.COMPLETE); + expect(conversation.TransferStatus).toBe(ConversationStatus.COMPLETE); }); it('should throw an error when database query fails', async () => { @@ -202,21 +192,22 @@ describe('healthRecordRepository', () => { describe('getCurrentHealthRecordIdForPatient', () => { it('should return most recent complete health record conversation id', async () => { // given - const nhsNumber = '9876543210'; + const nhsNumber = '9876543212'; const previousHealthRecordConversationId = uuid(); const incompleteHealthRecordConversationId = uuid(); const currentHealthRecordConversationId = uuid(); await createConversationForTest(previousHealthRecordConversationId, nhsNumber, { - State: ConversationStates.COMPLETE, + TransferStatus: ConversationStatus.COMPLETE, + CreatedAt: '2024-01-01T10:00:00+00:00', }); await createConversationForTest(incompleteHealthRecordConversationId, nhsNumber, { - State: ConversationStates.TIMEOUT, + TransferStatus: ConversationStatus.TIMEOUT, }); await createConversationForTest(currentHealthRecordConversationId, nhsNumber, { - State: ConversationStates.COMPLETE, + TransferStatus: ConversationStatus.COMPLETE, }); // when @@ -231,7 +222,7 @@ describe('healthRecordRepository', () => { const nhsNumber = '9876543211'; const incompleteHealthRecordConversationId = uuid(); await createConversationForTest(incompleteHealthRecordConversationId, nhsNumber, { - State: ConversationStates.TIMEOUT, + TransferStatus: ConversationStatus.TIMEOUT, }); // when @@ -374,73 +365,110 @@ describe('healthRecordRepository', () => { }); }); - describe.skip('markHealthRecordAsDeletedForPatient', () => { - async function createHealthRecordAndMessage(nhsNumber, conversationId, messageId) { - await HealthRecord.create({ - conversationId, - nhsNumber, - completedAt: new Date(), - }); - await Message.create({ - conversationId, - messageId, - type: MessageType.EHR_EXTRACT, - receivedAt: new Date(), + describe('markHealthRecordAsDeletedForPatient', () => { + // HELPER SETUPS + let conversationIdUsed = []; + const mockTime = new Date(Date.parse('2024-03-06T12:34:56+00:00')); + const mockTimeInEpochSecond = mockTime / 1000; + + beforeEach(async () => { + jest.useFakeTimers().setSystemTime(mockTime); + }); + + afterEach(async () => { + await Promise.all(conversationIdUsed.map(cleanupRecordsForTest)); + conversationIdUsed = []; + }); + + const makeConversationIdForTest = () => { + const id = uuid(); + conversationIdUsed.push(id); + return id; + }; + + const createCompleteRecordForTest = async ( + nhsNumber, + conversationId, + messageId, + fragmentMessageIds = [] + ) => { + await createConversationForTest(conversationId, nhsNumber, { + TransferStatus: ConversationStatus.COMPLETE, }); - } + await createCore({ conversationId, messageId, fragmentMessageIds }); + for (const fragmentMessageId of fragmentMessageIds) { + await markFragmentAsReceived(fragmentMessageId, conversationId); + } + }; + + // TESTS START FROM HERE it('should return conversation id for the patient marked as deleted', async () => { + // given const nhsNumber = '9898989898'; const messageId = uuid(); - const conversationId = uuid(); + const conversationId = makeConversationIdForTest(); + const fragmentIds = [uuid(), uuid(), uuid()]; - await createHealthRecordAndMessage(nhsNumber, conversationId, messageId); + await createCompleteRecordForTest(nhsNumber, conversationId, messageId, fragmentIds); + // when const result = await markHealthRecordAsDeletedForPatient(nhsNumber); - const healthRecordMarkedAsDeleted = await HealthRecord.findAll({ - where: { nhsNumber }, - paranoid: false, - }); - - const messagesMarkedAsDeleted = await Message.findAll({ - where: { conversationId }, - paranoid: false, - }); + // then const healthRecordStatusAfterwards = await getHealthRecordStatus(conversationId); - expect(result).toEqual([conversationId]); expect(healthRecordStatusAfterwards).toEqual(HealthRecordStatus.NOT_FOUND); - expect(healthRecordMarkedAsDeleted[0].deletedAt).not.toBeNull(); - expect(messagesMarkedAsDeleted[0].deletedAt).not.toBeNull(); + + const deletedRecords = await db.queryTableByConversationId( + conversationId, + RecordType.ALL, + true + ); + expect(deletedRecords).toHaveLength(5); // conversation + core + 3 fragments + + for (const item of deletedRecords) { + expect(item).toMatchObject({ + InboundConversationId: conversationId, + DeletedAt: mockTimeInEpochSecond, + }); + } }); - it('should return conversation id for the patient marked as deleted when the patient has several health records', async () => { + it('should return conversation ids for the patient marked as deleted when the patient has several health records', async () => { + // given const nhsNumber = '6767676767'; const firstMessageId = uuid(); const secondMessageId = uuid(); - const firstConversationId = uuid(); - const secondConversationId = uuid(); + const firstConversationId = makeConversationIdForTest(); + const secondConversationId = makeConversationIdForTest(); - await createHealthRecordAndMessage(nhsNumber, firstConversationId, firstMessageId); - await createHealthRecordAndMessage(nhsNumber, secondConversationId, secondMessageId); + await createCompleteRecordForTest(nhsNumber, firstConversationId, firstMessageId); + await createCompleteRecordForTest(nhsNumber, secondConversationId, secondMessageId); + // when const result = await markHealthRecordAsDeletedForPatient(nhsNumber); - const healthRecordMarkedAsDeleted = await HealthRecord.findAll({ - where: { nhsNumber }, - paranoid: false, - }); - - const messagesMarkedAsDeleted = await Message.findAll({ - where: { conversationId: [firstConversationId, secondConversationId] }, - paranoid: false, - }); - - expect(result).toEqual([firstConversationId, secondConversationId]); - expect(healthRecordMarkedAsDeleted).not.toHaveProperty('deletedAt', null); - expect(messagesMarkedAsDeleted).not.toHaveProperty('deletedAt', null); + // then + expect(result).toHaveLength(2); + expect(result.sort()).toEqual([firstConversationId, secondConversationId].sort()); + + const deletedRecords = ( + await Promise.all( + result.map((conversationId) => + db.queryTableByConversationId(conversationId, RecordType.ALL, true) + ) + ) + ).flat(); + + expect(deletedRecords).toHaveLength(4); // two sets of conversation + core + + for (const item of deletedRecords) { + expect(item).toMatchObject({ + DeletedAt: mockTimeInEpochSecond, + }); + } }); }); }); diff --git a/src/services/database/dynamo-ehr-transfer-tracker.js b/src/services/database/dynamo-ehr-transfer-tracker.js index 42ce2abd..7c261311 100644 --- a/src/services/database/dynamo-ehr-transfer-tracker.js +++ b/src/services/database/dynamo-ehr-transfer-tracker.js @@ -7,7 +7,7 @@ import { import { getUKTimestamp } from '../time'; import { logError, logInfo } from '../../middleware/logging'; -import { RecordType, ConversationStates } from '../../models/enums'; +import { RecordType, ConversationStatus } from '../../models/enums'; import { getDynamodbClient } from './dynamodb-client'; import { buildFragmentUpdateParams } from '../../models/fragment'; @@ -103,7 +103,9 @@ export class EhrTransferTracker { } const completedRecords = items.filter( - (item) => item.State === ConversationStates.COMPLETE || item.State.startsWith('Outbound') + (item) => + item.TransferStatus === ConversationStatus.COMPLETE || + item.TransferStatus.startsWith('Outbound') ); const currentRecord = completedRecords.reduce((prev, current) => { @@ -113,7 +115,11 @@ export class EhrTransferTracker { return currentRecord.InboundConversationId; } - async queryTableByConversationId(inboundConversationId, recordType = RecordType.ALL) { + async queryTableByConversationId( + inboundConversationId, + recordType = RecordType.ALL, + includeDeletedRecord = false + ) { const params = { TableName: this.tableName, ExpressionAttributeNames: { @@ -123,8 +129,10 @@ export class EhrTransferTracker { ':InboundConversationId': inboundConversationId, }, KeyConditionExpression: '#PrimaryKey = :InboundConversationId', - FilterExpression: 'attribute_not_exists(DeletedAt)' }; + if (!includeDeletedRecord) { + params.FilterExpression = 'attribute_not_exists(DeletedAt)'; + } switch (recordType) { case RecordType.ALL: diff --git a/src/services/database/ehr-conversation-repository.js b/src/services/database/ehr-conversation-repository.js index 760300f9..a6aa5f02 100644 --- a/src/services/database/ehr-conversation-repository.js +++ b/src/services/database/ehr-conversation-repository.js @@ -1,16 +1,18 @@ -import { ConversationStates, HealthRecordStatus, RecordType } from "../../models/enums"; -import { logError, logInfo } from "../../middleware/logging"; -import { EhrTransferTracker } from "./dynamo-ehr-transfer-tracker"; -import { buildConversationUpdateParams } from "../../models/conversation"; -import { HealthRecordNotFoundError, MessageNotFoundError } from "../../errors/errors"; -import { isCore } from "../../models/core"; -import { isFragment } from "../../models/fragment"; +import { ConversationStatus, HealthRecordStatus, RecordType } from '../../models/enums'; +import { logError, logInfo } from '../../middleware/logging'; +import { EhrTransferTracker } from './dynamo-ehr-transfer-tracker'; +import { buildConversationUpdateParams } from '../../models/conversation'; +import { HealthRecordNotFoundError, MessageNotFoundError } from '../../errors/errors'; +import { isCore } from '../../models/core'; +import { isFragment } from '../../models/fragment'; +import { getEpochTimeInSecond, getUKTimestamp } from '../time'; +import { buildSoftDeleteUpdateParams } from '../../utilities/dynamodb-helper'; export const getHealthRecordStatus = async (conversationId) => { // to replace the method with same name try { const conversation = await getConversationById(conversationId); - if (conversation.State === ConversationStates.COMPLETE) { + if (conversation.TransferStatus === ConversationStatus.COMPLETE) { return HealthRecordStatus.COMPLETE; } else { return HealthRecordStatus.PENDING; @@ -55,7 +57,7 @@ export const updateConversationCompleteness = async (conversationId) => { logInfo('All fragments are received. Will mark this inbound conversation as complete'); const updateParam = buildConversationUpdateParams(conversationId, { - State: ConversationStates.COMPLETE, + TransferStatus: ConversationStatus.COMPLETE, }); await db.updateItemsInTransaction([updateParam]); @@ -71,7 +73,9 @@ export const getCurrentHealthRecordIdForPatient = async (nhsNumber) => { const conversations = await db.queryTableByNhsNumber(nhsNumber); const completedRecords = conversations?.filter( - (item) => item.State === ConversationStates.COMPLETE || item.State.startsWith('Outbound') + (item) => + item.TransferStatus === ConversationStatus.COMPLETE || + item.TransferStatus?.startsWith('Outbound') ); if (!completedRecords || completedRecords.length === 0) { @@ -85,7 +89,6 @@ export const getCurrentHealthRecordIdForPatient = async (nhsNumber) => { return currentRecord.InboundConversationId; }; - export const getHealthRecordMessageIds = async (conversationId) => { // to replace the method of same name @@ -99,7 +102,26 @@ export const getHealthRecordMessageIds = async (conversationId) => { throw new MessageNotFoundError(); } const coreMessageId = core.InboundMessageId; - const fragmentMessageIds = fragments.map(message => message.InboundMessageId); + const fragmentMessageIds = fragments.map((message) => message.InboundMessageId); return { coreMessageId, fragmentMessageIds }; -}; \ No newline at end of file +}; + +export const markHealthRecordAsDeletedForPatient = async (nhsNumber) => { + // to replace the method of same name + + const db = EhrTransferTracker.getInstance(); + const allConversations = await db.queryTableByNhsNumber(nhsNumber); + const allConversationIds = allConversations.map(item => item.InboundConversationId); + + const allRecords = []; + for (const conversationId of allConversationIds) { + const items = await db.queryTableByConversationId(conversationId, RecordType.ALL); + allRecords.push(...items); + } + + const allUpdateParams = allRecords.map(buildSoftDeleteUpdateParams); + + await db.updateItemsInTransaction(allUpdateParams); + return allConversationIds; +}; diff --git a/src/services/database/ehr-core-repository.js b/src/services/database/ehr-core-repository.js index 3d78bd8e..d2ea28dc 100644 --- a/src/services/database/ehr-core-repository.js +++ b/src/services/database/ehr-core-repository.js @@ -1,10 +1,10 @@ -import { EhrTransferTracker } from "./dynamo-ehr-transfer-tracker"; -import { arrayOfFragments } from "../../models/fragment"; -import { core } from "../../models/core"; -import { RecordType } from "../../models/enums"; -import { logError } from "../../middleware/logging"; +import { EhrTransferTracker } from './dynamo-ehr-transfer-tracker'; +import { arrayOfFragments } from '../../models/fragment'; +import { core } from '../../models/core'; +import { RecordType } from '../../models/enums'; +import { logError } from '../../middleware/logging'; -export const createCore = async ({ conversationId, messageId, fragmentMessageIds }) => { +export const createCore = async ({ conversationId, messageId, fragmentMessageIds = [] }) => { // to replace the existing `createEhrExtract` method try { @@ -31,4 +31,3 @@ export const getCoreByKey = (inboundConversationId, inboundMessageId) => { const db = EhrTransferTracker.getInstance(); return db.getItemByKey(inboundConversationId, inboundMessageId, RecordType.CORE); }; - diff --git a/src/services/time.js b/src/services/time.js index 16997c38..c77decf6 100644 --- a/src/services/time.js +++ b/src/services/time.js @@ -6,7 +6,6 @@ export const getUKTimestamp = () => { return moment().tz('Europe/London').format('YYYY-MM-DDThh:mm:ssZ'); }; - export const getEpochTimeInSecond = () => { return moment().unix(); -} \ No newline at end of file +}; diff --git a/src/utilities/dynamodb-helper.js b/src/utilities/dynamodb-helper.js new file mode 100644 index 00000000..3115951b --- /dev/null +++ b/src/utilities/dynamodb-helper.js @@ -0,0 +1,42 @@ +import { logError } from '../middleware/logging'; +import { validate } from 'uuid'; +import { getEpochTimeInSecond, getUKTimestamp } from '../services/time'; + +export const validateIds = (conversationId, messageId) => { + const uuidsAreValid = validate(conversationId) && validate(messageId); + if (!uuidsAreValid) { + throw new Error('received invalid uuid as either conversationId or messageId'); + } +}; +export const addChangesToUpdateParams = (params, changes, fieldsAllowedToUpdate) => { + for (const [fieldName, updatedValue] of Object.entries(changes)) { + if (!fieldsAllowedToUpdate.includes(fieldName)) { + logError(`Ignoring attempt to update non-allowed field ${fieldName}`); + continue; + } + const keyToken = `#${fieldName}`; + const valueToken = `:${fieldName}`; + + params.UpdateExpression += `, ${keyToken} = ${valueToken}`; + params.ExpressionAttributeValues[valueToken] = updatedValue; + params.ExpressionAttributeNames = params.ExpressionAttributeNames ?? {}; + params.ExpressionAttributeNames[keyToken] = fieldName; + } + + return params; +}; + +export const buildSoftDeleteUpdateParams = (item) => { + return { + Key: { + InboundConversationId: item.InboundConversationId, + Layer: item.Layer, + }, + UpdateExpression: `set UpdatedAt = :now, DeletedAt = :deletedAt`, + ExpressionAttributeValues: { + ':now': getUKTimestamp(), + ':deletedAt': getEpochTimeInSecond(), + }, + ReturnValues: 'ALL_NEW' + }; +}; diff --git a/src/utilities/integration-test-utilities.js b/src/utilities/integration-test-utilities.js index 27ecb729..4305c846 100644 --- a/src/utilities/integration-test-utilities.js +++ b/src/utilities/integration-test-utilities.js @@ -2,6 +2,7 @@ import { v4 as uuidv4 } from 'uuid'; import { getUKTimestamp } from '../services/time'; import { EhrTransferTracker } from '../services/database/dynamo-ehr-transfer-tracker'; import { TransactWriteCommand } from '@aws-sdk/lib-dynamodb'; +import { RecordType } from "../models/enums"; export const generateRandomNhsNumber = () => (Math.floor(Math.random() * 9e9) + 1e9).toString(); @@ -46,7 +47,7 @@ export const cleanupRecordsForTest = async (conversationId) => { } const db = EhrTransferTracker.getInstance(); - const records = await db.queryTableByConversationId(conversationId); + const records = await db.queryTableByConversationId(conversationId, RecordType.ALL, true); const deleteCommand = new TransactWriteCommand({ TransactItems: records.map((item) => ({ Delete: { From afdacf5704436c656be8f864dd0656aa2bfab408 Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Thu, 7 Mar 2024 12:02:55 +0000 Subject: [PATCH 21/67] [PRMT-4566] Rename tests files --- src/models/core.js | 2 - ...o-ehr-transfer-tracker.integration.test.js | 48 +------------ ...nversation-repository.integration.test.js} | 54 +------------- ...hr-fragment-repository.integration.test.js | 70 +++++++++++++++++-- .../database/dynamo-ehr-transfer-tracker.js | 22 ------ .../database/ehr-conversation-repository.js | 3 +- src/utilities/dynamodb-helper.js | 1 - 7 files changed, 73 insertions(+), 127 deletions(-) rename src/services/database/__tests__/{new-health-record-repository.integration.test.js => ehr-conversation-repository.integration.test.js} (91%) diff --git a/src/models/core.js b/src/models/core.js index 9fc66ef3..f028d186 100644 --- a/src/models/core.js +++ b/src/models/core.js @@ -2,8 +2,6 @@ import { getUKTimestamp } from '../services/time'; import { CoreStatus } from './enums'; import { validateIds } from '../utilities/dynamodb-helper'; -const fieldsAllowedToUpdate = ['TransferStatus', 'DeletedAt']; - export const core = (inboundConversationId, messageId) => { const timestamp = getUKTimestamp(); diff --git a/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js b/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js index b076ea4c..85227bae 100644 --- a/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js +++ b/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js @@ -7,6 +7,7 @@ import { createConversationForTest, } from '../../../utilities/integration-test-utilities'; import { markFragmentAsReceivedAndCreateItsParts } from '../ehr-fragment-repository'; +import { core } from '../../../models/core'; describe('EhrTransferTracker', () => { const testConversationId = uuid(); @@ -25,13 +26,9 @@ describe('EhrTransferTracker', () => { const db = EhrTransferTracker.getInstance(); const testMessageId = uuid(); - const ehrExtract = { - conversationId: testConversationId, - messageId: testMessageId, - nhsNumber: testNhsNumber, - }; + const ehrCore = core(testConversationId, testMessageId); - await createCore(ehrExtract); + await db.writeItemsToTable([ehrCore]); // then const actual = await db.queryTableByConversationId(testConversationId, RecordType.CORE); @@ -46,43 +43,4 @@ describe('EhrTransferTracker', () => { UpdatedAt: expect.any(String), }); }); - - it('updateFragmentAndCreateItsParts', async () => { - // given - const db = EhrTransferTracker.getInstance(); - - const testMessageId = uuid(); - const testNhsNumber = '9000000002'; - const testChildMessageIds = [uuid(), uuid(), uuid()]; - const testNestedChildIds = [uuid(), uuid(), uuid()]; - - const ehrExtract = { - conversationId: testConversationId, - messageId: testMessageId, - nhsNumber: testNhsNumber, - fragmentMessageIds: testChildMessageIds, - }; - - await createCore(ehrExtract); - - // when - await markFragmentAsReceivedAndCreateItsParts( - testChildMessageIds[0], - testConversationId, - testNestedChildIds - ); - - const actual = await db.queryTableByConversationId(testConversationId, RecordType.FRAGMENT); - - // then - const expectedTotalMessages = testChildMessageIds.length + testNestedChildIds.length; - expect(actual).toHaveLength(expectedTotalMessages); - - const receivedFragment = actual.filter((item) => item.ReceivedAt); - expect(receivedFragment).toHaveLength(1); - expect(receivedFragment[0].InboundMessageId).toEqual(testChildMessageIds[0]); - - const nonReceivedFragments = actual.filter((item) => !item.ReceivedAt); - expect(nonReceivedFragments).toHaveLength(5); - }); }); diff --git a/src/services/database/__tests__/new-health-record-repository.integration.test.js b/src/services/database/__tests__/ehr-conversation-repository.integration.test.js similarity index 91% rename from src/services/database/__tests__/new-health-record-repository.integration.test.js rename to src/services/database/__tests__/ehr-conversation-repository.integration.test.js index 3d62a524..0614048d 100644 --- a/src/services/database/__tests__/new-health-record-repository.integration.test.js +++ b/src/services/database/__tests__/ehr-conversation-repository.integration.test.js @@ -16,7 +16,6 @@ import { import { createCore } from '../ehr-core-repository'; import { EhrTransferTracker } from '../dynamo-ehr-transfer-tracker'; import { - fragmentAlreadyReceived, markFragmentAsReceivedAndCreateItsParts, } from '../ehr-fragment-repository'; import { HealthRecordNotFoundError, MessageNotFoundError } from '../../../errors/errors'; @@ -25,7 +24,7 @@ import { getEpochTimeInSecond } from '../../time'; jest.mock('../../../middleware/logging'); -describe('healthRecordRepository', () => { +describe('ehr-conversation-repository', () => { // ========================= COMMON PROPERTIES ========================= const db = EhrTransferTracker.getInstance(); const markFragmentAsReceived = markFragmentAsReceivedAndCreateItsParts; @@ -320,53 +319,8 @@ describe('healthRecordRepository', () => { }); }); - describe('fragmentAlreadyReceived', () => { - it("should return false if 'messageId' is not found in db", async () => { - // given - const messageId = uuid(); - - // when - const result = await fragmentAlreadyReceived(messageId); - - // then - expect(result).toEqual(false); - }); - - it("should return true if 'messageId' is received in db", async () => { - // given - const conversationId = uuid(); - const messageId = uuid(); - const fragmentMessageId = uuid(); - - await createCore({ conversationId, messageId, fragmentMessageIds: [fragmentMessageId] }); - await markFragmentAsReceived(fragmentMessageId, conversationId); - - // when - const result = await fragmentAlreadyReceived(conversationId, fragmentMessageId); - - // then - expect(result).toEqual(true); - }); - - it('should throw if database querying throws', async () => { - // given - const messageId = uuid(); - mimicDynamodbFail(); - - // when - try { - await fragmentAlreadyReceived(messageId); - fail('should have throw'); - } catch (err) { - // then - expect(err).not.toBeNull(); - expect(logError).toHaveBeenCalledWith('Querying database for fragment message failed', err); - } - }); - }); - describe('markHealthRecordAsDeletedForPatient', () => { - // HELPER SETUPS + // ========================= HELPER SETUPS FOR THIS BLOCK ========================= let conversationIdUsed = []; const mockTime = new Date(Date.parse('2024-03-06T12:34:56+00:00')); const mockTimeInEpochSecond = mockTime / 1000; @@ -401,8 +355,7 @@ describe('healthRecordRepository', () => { } }; - // TESTS START FROM HERE - + // ========================= TESTS BEGINS HERE ================================= it('should return conversation id for the patient marked as deleted', async () => { // given const nhsNumber = '9898989898'; @@ -416,7 +369,6 @@ describe('healthRecordRepository', () => { const result = await markHealthRecordAsDeletedForPatient(nhsNumber); // then - const healthRecordStatusAfterwards = await getHealthRecordStatus(conversationId); expect(result).toEqual([conversationId]); expect(healthRecordStatusAfterwards).toEqual(HealthRecordStatus.NOT_FOUND); diff --git a/src/services/database/__tests__/ehr-fragment-repository.integration.test.js b/src/services/database/__tests__/ehr-fragment-repository.integration.test.js index f2b656a4..3cbd1c1b 100644 --- a/src/services/database/__tests__/ehr-fragment-repository.integration.test.js +++ b/src/services/database/__tests__/ehr-fragment-repository.integration.test.js @@ -2,6 +2,7 @@ import { v4 as uuid } from 'uuid'; import { logError } from '../../../middleware/logging'; import { createCore } from '../ehr-core-repository'; import { + fragmentAlreadyReceived, fragmentExists, getFragmentByKey, markFragmentAsReceivedAndCreateItsParts, @@ -13,13 +14,29 @@ import { EhrTransferTracker } from '../dynamo-ehr-transfer-tracker'; jest.mock('../../../middleware/logging'); describe('ehr-fragment-repository', () => { + // ========================= COMMON PROPERTIES ========================= const expectedTimestamp = '2024-03-06T12:34:56+00:00'; const mockTime = new Date(Date.parse(expectedTimestamp)); - + const db = EhrTransferTracker.getInstance(); + + // ========================= HELPERS========== ========================= + const markFragmentAsReceived = markFragmentAsReceivedAndCreateItsParts; + const tableNameBackup = db.tableName; + const mimicDynamodbFail = () => { + db.tableName = 'non-exist-table'; + }; + const undoMimicDynamodbFail = () => { + db.tableName = tableNameBackup; + }; + + // ========================= SET UP / TEAR DOWN ======================== beforeEach(async () => { jest.useFakeTimers().setSystemTime(mockTime); }); - + afterEach(() => { + undoMimicDynamodbFail(); + }); + // ===================================================================== describe('markFragmentAsReceivedAndCreateItsParts', () => { it('should update receivedAt for a fragment with current date', async () => { // given @@ -33,7 +50,7 @@ describe('ehr-fragment-repository', () => { }); // when - await markFragmentAsReceivedAndCreateItsParts(fragmentMessageId, conversationId, []); + await markFragmentAsReceivedAndCreateItsParts(fragmentMessageId, conversationId); // then const fragment = await getFragmentByKey(conversationId, fragmentMessageId); @@ -45,7 +62,7 @@ describe('ehr-fragment-repository', () => { const conversationId = uuid(); try { // when - await markFragmentAsReceivedAndCreateItsParts('not-valid', conversationId, []); + await markFragmentAsReceivedAndCreateItsParts('not-valid', conversationId); } catch (err) { // then expect(err).not.toBeNull(); @@ -168,4 +185,49 @@ describe('ehr-fragment-repository', () => { expect(logError).toHaveBeenCalledWith('Message could not be stored', err); }); }); + + describe('fragmentAlreadyReceived', () => { + it("should return false if 'messageId' is not found in db", async () => { + // given + const messageId = uuid(); + + // when + const result = await fragmentAlreadyReceived(messageId); + + // then + expect(result).toEqual(false); + }); + + it("should return true if 'messageId' is received in db", async () => { + // given + const conversationId = uuid(); + const messageId = uuid(); + const fragmentMessageId = uuid(); + + await createCore({ conversationId, messageId, fragmentMessageIds: [fragmentMessageId] }); + await markFragmentAsReceived(fragmentMessageId, conversationId); + + // when + const result = await fragmentAlreadyReceived(conversationId, fragmentMessageId); + + // then + expect(result).toEqual(true); + }); + + it('should throw if database querying throws', async () => { + // given + const messageId = uuid(); + mimicDynamodbFail(); + + // when + try { + await fragmentAlreadyReceived(messageId); + fail('should have throw'); + } catch (err) { + // then + expect(err).not.toBeNull(); + expect(logError).toHaveBeenCalledWith('Querying database for fragment message failed', err); + } + }); + }); }); diff --git a/src/services/database/dynamo-ehr-transfer-tracker.js b/src/services/database/dynamo-ehr-transfer-tracker.js index 7c261311..ec5ef757 100644 --- a/src/services/database/dynamo-ehr-transfer-tracker.js +++ b/src/services/database/dynamo-ehr-transfer-tracker.js @@ -93,28 +93,6 @@ export class EhrTransferTracker { return items; } - async getCurrentHealthRecordIdForPatient(nhsNumber) { - // to replace the existing method of the same name - - const items = await this.queryTableByNhsNumber(nhsNumber); - - if (!items || items.length === 0) { - throw new Error('No record was found for given NHS number'); - } - - const completedRecords = items.filter( - (item) => - item.TransferStatus === ConversationStatus.COMPLETE || - item.TransferStatus.startsWith('Outbound') - ); - - const currentRecord = completedRecords.reduce((prev, current) => { - return current && current?.CreatedAt > prev?.CreatedAt ? current : prev; - }); - - return currentRecord.InboundConversationId; - } - async queryTableByConversationId( inboundConversationId, recordType = RecordType.ALL, diff --git a/src/services/database/ehr-conversation-repository.js b/src/services/database/ehr-conversation-repository.js index a6aa5f02..3ae57829 100644 --- a/src/services/database/ehr-conversation-repository.js +++ b/src/services/database/ehr-conversation-repository.js @@ -5,7 +5,6 @@ import { buildConversationUpdateParams } from '../../models/conversation'; import { HealthRecordNotFoundError, MessageNotFoundError } from '../../errors/errors'; import { isCore } from '../../models/core'; import { isFragment } from '../../models/fragment'; -import { getEpochTimeInSecond, getUKTimestamp } from '../time'; import { buildSoftDeleteUpdateParams } from '../../utilities/dynamodb-helper'; export const getHealthRecordStatus = async (conversationId) => { @@ -112,7 +111,7 @@ export const markHealthRecordAsDeletedForPatient = async (nhsNumber) => { const db = EhrTransferTracker.getInstance(); const allConversations = await db.queryTableByNhsNumber(nhsNumber); - const allConversationIds = allConversations.map(item => item.InboundConversationId); + const allConversationIds = allConversations.map((item) => item.InboundConversationId); const allRecords = []; for (const conversationId of allConversationIds) { diff --git a/src/utilities/dynamodb-helper.js b/src/utilities/dynamodb-helper.js index 3115951b..e54feb5e 100644 --- a/src/utilities/dynamodb-helper.js +++ b/src/utilities/dynamodb-helper.js @@ -37,6 +37,5 @@ export const buildSoftDeleteUpdateParams = (item) => { ':now': getUKTimestamp(), ':deletedAt': getEpochTimeInSecond(), }, - ReturnValues: 'ALL_NEW' }; }; From 74710433d13c1295b7bd2c4cf4f3fb8b6f2af7c4 Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Thu, 7 Mar 2024 12:23:06 +0000 Subject: [PATCH 22/67] [PRMT-4566] Rename methods to avoid import conflict, add deprecated mark and ticket number to old postgres related codes --- src/config/database.js | 5 ++ src/models/health-check.js | 5 ++ src/models/health-record.js | 5 ++ src/models/index.js | 5 ++ src/models/message.js | 5 ++ src/models/models.js | 5 ++ src/models/parameters.js | 5 ++ ...onversation-repository.integration.test.js | 48 +++++++++---------- ...hr-fragment-repository.integration.test.js | 10 ++-- ...alth-record-repository.integration.test.js | 5 ++ .../message-repository.integration.test.js | 3 +- src/services/database/check-db-health.js | 5 ++ .../database/ehr-conversation-repository.js | 44 ++++++++--------- .../database/ehr-fragment-repository.js | 7 +-- .../database/health-record-repository.js | 31 ++++++++++++ src/services/database/message-repository.js | 9 ++-- src/services/database/pg-error-codes.js | 6 +++ 17 files changed, 139 insertions(+), 64 deletions(-) diff --git a/src/config/database.js b/src/config/database.js index a292d0a0..930fdd03 100644 --- a/src/config/database.js +++ b/src/config/database.js @@ -1,6 +1,11 @@ const use_ssl = process.env.USE_SSL_FOR_DB === 'true'; const use_rds_credentials = process.env.USE_AWS_RDS_CREDENTIALS === 'true'; +/** + * @deprecated + * Postgres DB related stubs + * To be deleted PRMT-4568 + */ const sequelizeConfig = { username: process.env.DATABASE_USER, password: process.env.DATABASE_PASSWORD, diff --git a/src/models/health-check.js b/src/models/health-check.js index da405795..dcbc9218 100644 --- a/src/models/health-check.js +++ b/src/models/health-check.js @@ -1,5 +1,10 @@ import getParameters from './parameters'; +/** + * @deprecated + * Postgres DB related stubs + * To be deleted PRMT-4568 + */ export const modelName = 'HealthCheck'; const tableName = 'health_checks'; diff --git a/src/models/health-record.js b/src/models/health-record.js index e31b6d30..8657d3ac 100644 --- a/src/models/health-record.js +++ b/src/models/health-record.js @@ -1,5 +1,10 @@ import { getParametersRefactored } from './parameters'; +/** + * @deprecated + * Postgres DB related stubs + * To be deleted PRMT-4568 + */ export const modelName = 'HealthRecord'; const tableName = 'health_records'; diff --git a/src/models/index.js b/src/models/index.js index 29f57469..1f1abb5a 100644 --- a/src/models/index.js +++ b/src/models/index.js @@ -6,6 +6,11 @@ import AWS from 'aws-sdk'; import { logError, logInfo } from '../middleware/logging'; class ModelFactory { + /** + * @deprecated + * Postgres DB related stubs + * To be deleted PRMT-4568 + */ constructor() { this.db = {}; this.sequelize = {}; diff --git a/src/models/message.js b/src/models/message.js index a3b39727..92dadff5 100644 --- a/src/models/message.js +++ b/src/models/message.js @@ -1,5 +1,10 @@ import { getParametersRefactored } from './parameters'; +/** + * @deprecated + * Postgres DB related stubs + * To be deleted PRMT-4568 + */ export const modelName = 'Message'; const tableName = 'messages'; diff --git a/src/models/models.js b/src/models/models.js index e34956f8..b6516659 100644 --- a/src/models/models.js +++ b/src/models/models.js @@ -2,6 +2,11 @@ import HealthCheck from './health-check'; import Message from './message'; import HealthRecord from './health-record'; +/** + * @deprecated + * Postgres DB related stubs + * To be deleted PRMT-4568 + */ export { HealthCheck }; export { Message }; export { HealthRecord }; diff --git a/src/models/parameters.js b/src/models/parameters.js index c56d86a9..2ef58ca4 100644 --- a/src/models/parameters.js +++ b/src/models/parameters.js @@ -1,3 +1,8 @@ +/** + * @deprecated + * Postgres DB related stubs + * To be deleted PRMT-4568 + */ const getParameters = (tableName) => ({ tableName: tableName, createdAt: 'created_at', diff --git a/src/services/database/__tests__/ehr-conversation-repository.integration.test.js b/src/services/database/__tests__/ehr-conversation-repository.integration.test.js index 0614048d..1788d67a 100644 --- a/src/services/database/__tests__/ehr-conversation-repository.integration.test.js +++ b/src/services/database/__tests__/ehr-conversation-repository.integration.test.js @@ -2,10 +2,10 @@ import { v4 as uuid } from 'uuid'; import { logError } from '../../../middleware/logging'; import { getConversationById, - getCurrentHealthRecordIdForPatient, - getHealthRecordMessageIds, - getHealthRecordStatus, - markHealthRecordAsDeletedForPatient, + getCurrentConversationIdForPatient, + getMessageIdsForConversation, + getConversationStatus, + markRecordAsSoftDeleteForPatient, updateConversationCompleteness, } from '../ehr-conversation-repository'; import { ConversationStatus, HealthRecordStatus, RecordType } from '../../../models/enums'; @@ -44,7 +44,7 @@ describe('ehr-conversation-repository', () => { }); // ===================================================================== - describe('getHealthRecordStatus', () => { + describe('getConversationStatus', () => { it("should return status 'complete' when conversation status is Complete", async () => { // given const conversationId = uuid(); @@ -54,7 +54,7 @@ describe('ehr-conversation-repository', () => { }); // when - const status = await getHealthRecordStatus(conversationId); + const status = await getConversationStatus(conversationId); // then expect(status).toEqual(HealthRecordStatus.COMPLETE); @@ -69,7 +69,7 @@ describe('ehr-conversation-repository', () => { }); // when - const status = await getHealthRecordStatus(conversationId); + const status = await getConversationStatus(conversationId); // then expect(status).toEqual(HealthRecordStatus.PENDING); @@ -80,7 +80,7 @@ describe('ehr-conversation-repository', () => { const conversationId = uuid(); // when - const status = await getHealthRecordStatus(conversationId); + const status = await getConversationStatus(conversationId); // then expect(status).toEqual(HealthRecordStatus.NOT_FOUND); @@ -93,7 +93,7 @@ describe('ehr-conversation-repository', () => { try { // when - await getHealthRecordStatus(conversationId); + await getConversationStatus(conversationId); fail('should have throw'); } catch (err) { // then @@ -106,7 +106,7 @@ describe('ehr-conversation-repository', () => { }); }); - describe('updateHealthRecordCompleteness', () => { + describe('updateConversationCompleteness', () => { it("should set conversation state to 'Complete' for a small health record", async () => { // given const conversationId = uuid(); @@ -188,7 +188,7 @@ describe('ehr-conversation-repository', () => { }); }); - describe('getCurrentHealthRecordIdForPatient', () => { + describe('getCurrentConversationIdForPatient', () => { it('should return most recent complete health record conversation id', async () => { // given const nhsNumber = '9876543212'; @@ -210,7 +210,7 @@ describe('ehr-conversation-repository', () => { }); // when - const actual = await getCurrentHealthRecordIdForPatient(nhsNumber); + const actual = await getCurrentConversationIdForPatient(nhsNumber); // then expect(actual).toEqual(currentHealthRecordConversationId); @@ -225,7 +225,7 @@ describe('ehr-conversation-repository', () => { }); // when - await expect(() => getCurrentHealthRecordIdForPatient(nhsNumber)) + await expect(() => getCurrentConversationIdForPatient(nhsNumber)) // then .rejects.toThrowError(HealthRecordNotFoundError); }); @@ -235,19 +235,19 @@ describe('ehr-conversation-repository', () => { const nhsNumber = '1111111112'; // when - await expect(() => getCurrentHealthRecordIdForPatient(nhsNumber)) + await expect(() => getCurrentConversationIdForPatient(nhsNumber)) // then .rejects.toThrowError(HealthRecordNotFoundError); }); }); - describe('getHealthRecordMessageIds', () => { + describe('getMessageIdsForConversation', () => { it('should throw an error if no message found with given conversationId', async () => { // given const conversationId = uuid(); // when - await expect(() => getHealthRecordMessageIds(conversationId)) + await expect(() => getMessageIdsForConversation(conversationId)) // then .rejects.toThrowError(MessageNotFoundError); }); @@ -261,7 +261,7 @@ describe('ehr-conversation-repository', () => { await db.writeItemsToTable([item]); // when - await expect(() => getHealthRecordMessageIds(conversationId)) + await expect(() => getMessageIdsForConversation(conversationId)) // then .rejects.toThrowError(MessageNotFoundError); }); @@ -273,7 +273,7 @@ describe('ehr-conversation-repository', () => { await createCore({ conversationId, messageId, fragmentMessageIds: [] }); // when - const { coreMessageId, fragmentMessageIds } = await getHealthRecordMessageIds(conversationId); + const { coreMessageId, fragmentMessageIds } = await getMessageIdsForConversation(conversationId); // then expect(coreMessageId).toEqual(messageId); @@ -290,7 +290,7 @@ describe('ehr-conversation-repository', () => { await markFragmentAsReceived(fragmentMessageId, conversationId); // when - const { coreMessageId, fragmentMessageIds } = await getHealthRecordMessageIds(conversationId); + const { coreMessageId, fragmentMessageIds } = await getMessageIdsForConversation(conversationId); // then expect(coreMessageId).toEqual(messageId); @@ -311,7 +311,7 @@ describe('ehr-conversation-repository', () => { await markFragmentAsReceived(nestedFragmentId, conversationId); // when - const { coreMessageId, fragmentMessageIds } = await getHealthRecordMessageIds(conversationId); + const { coreMessageId, fragmentMessageIds } = await getMessageIdsForConversation(conversationId); // then expect(coreMessageId).toEqual(messageId); @@ -319,7 +319,7 @@ describe('ehr-conversation-repository', () => { }); }); - describe('markHealthRecordAsDeletedForPatient', () => { + describe('markAllRecordAsDeletedForPatient', () => { // ========================= HELPER SETUPS FOR THIS BLOCK ========================= let conversationIdUsed = []; const mockTime = new Date(Date.parse('2024-03-06T12:34:56+00:00')); @@ -366,10 +366,10 @@ describe('ehr-conversation-repository', () => { await createCompleteRecordForTest(nhsNumber, conversationId, messageId, fragmentIds); // when - const result = await markHealthRecordAsDeletedForPatient(nhsNumber); + const result = await markRecordAsSoftDeleteForPatient(nhsNumber); // then - const healthRecordStatusAfterwards = await getHealthRecordStatus(conversationId); + const healthRecordStatusAfterwards = await getConversationStatus(conversationId); expect(result).toEqual([conversationId]); expect(healthRecordStatusAfterwards).toEqual(HealthRecordStatus.NOT_FOUND); @@ -400,7 +400,7 @@ describe('ehr-conversation-repository', () => { await createCompleteRecordForTest(nhsNumber, secondConversationId, secondMessageId); // when - const result = await markHealthRecordAsDeletedForPatient(nhsNumber); + const result = await markRecordAsSoftDeleteForPatient(nhsNumber); // then expect(result).toHaveLength(2); diff --git a/src/services/database/__tests__/ehr-fragment-repository.integration.test.js b/src/services/database/__tests__/ehr-fragment-repository.integration.test.js index 3cbd1c1b..2022a510 100644 --- a/src/services/database/__tests__/ehr-fragment-repository.integration.test.js +++ b/src/services/database/__tests__/ehr-fragment-repository.integration.test.js @@ -3,7 +3,7 @@ import { logError } from '../../../middleware/logging'; import { createCore } from '../ehr-core-repository'; import { fragmentAlreadyReceived, - fragmentExists, + fragmentExistsInRecord, getFragmentByKey, markFragmentAsReceivedAndCreateItsParts, } from '../ehr-fragment-repository'; @@ -125,7 +125,7 @@ describe('ehr-fragment-repository', () => { }); }); - describe('fragmentExists', () => { + describe('fragmentExistsInRecord', () => { it('should return true for a fragment existing in the database', async () => { const conversationId = uuid(); const messageId = uuid(); @@ -137,19 +137,19 @@ describe('ehr-fragment-repository', () => { const db = EhrTransferTracker.getInstance(); await db.writeItemsToTable([fragment]); - expect(await fragmentExists(conversationId, messageId)).toBe(true); + expect(await fragmentExistsInRecord(conversationId, messageId)).toBe(true); }); it('should return false for a fragment that does not exist in the database', async () => { const conversationId = uuid(); const messageId = uuid(); - expect(await fragmentExists(conversationId, messageId)).toBe(false); + expect(await fragmentExistsInRecord(conversationId, messageId)).toBe(false); }); it('should throw if database querying throws', async () => { const messageId = 'not-valid'; try { - await fragmentExists(messageId); + await fragmentExistsInRecord(messageId); } catch (err) { expect(err).not.toBeNull(); expect(logError).toHaveBeenCalledWith('Querying database for fragment message failed', err); diff --git a/src/services/database/__tests__/health-record-repository.integration.test.js b/src/services/database/__tests__/health-record-repository.integration.test.js index d3982792..49c01546 100644 --- a/src/services/database/__tests__/health-record-repository.integration.test.js +++ b/src/services/database/__tests__/health-record-repository.integration.test.js @@ -16,6 +16,11 @@ import { HealthRecordStatus } from "../../../models/enums"; jest.mock('../../../middleware/logging'); describe('healthRecordRepository', () => { + /** + * @deprecated + * All the tests here are migrated to new test suite of dynamodb-based implementation + * To be deleted PRMT-4568 + */ // ========================= COMMON PROPERTIES ========================= const HealthRecord = ModelFactory.getByName(healthRecordModelName); const Message = ModelFactory.getByName(messageModelName); diff --git a/src/services/database/__tests__/message-repository.integration.test.js b/src/services/database/__tests__/message-repository.integration.test.js index ebbff854..8ac0617d 100644 --- a/src/services/database/__tests__/message-repository.integration.test.js +++ b/src/services/database/__tests__/message-repository.integration.test.js @@ -16,10 +16,11 @@ import expect from 'expect'; jest.mock('../../../middleware/logging'); jest.mock('../../time'); -describe.skip('messageRepository', () => { +describe('messageRepository', () => { /** * @deprecated * All the tests here are migrated to new test suite of dynamodb-based implementation + * To be deleted PRMT-4568 */ const Message = ModelFactory.getByName(messageModelName); const HealthRecord = ModelFactory.getByName(healthRecordModelName); diff --git a/src/services/database/check-db-health.js b/src/services/database/check-db-health.js index 4d88d16c..06ef614b 100644 --- a/src/services/database/check-db-health.js +++ b/src/services/database/check-db-health.js @@ -3,6 +3,11 @@ import { ERROR_CODES } from './pg-error-codes'; import { modelName } from '../../models/health-check'; export const checkDbHealth = () => { + /** + * @deprecated + * It is decided we will not carry out health check by read/write into database anymore + * To be deleted PRMT-4568 + */ const HealthCheck = ModelFactory.getByName(modelName); return HealthCheck.create() diff --git a/src/services/database/ehr-conversation-repository.js b/src/services/database/ehr-conversation-repository.js index 3ae57829..4f5454e1 100644 --- a/src/services/database/ehr-conversation-repository.js +++ b/src/services/database/ehr-conversation-repository.js @@ -7,8 +7,7 @@ import { isCore } from '../../models/core'; import { isFragment } from '../../models/fragment'; import { buildSoftDeleteUpdateParams } from '../../utilities/dynamodb-helper'; -export const getHealthRecordStatus = async (conversationId) => { - // to replace the method with same name +export const getConversationStatus = async (conversationId) => { try { const conversation = await getConversationById(conversationId); if (conversation.TransferStatus === ConversationStatus.COMPLETE) { @@ -66,8 +65,7 @@ export const updateConversationCompleteness = async (conversationId) => { } }; -export const getCurrentHealthRecordIdForPatient = async (nhsNumber) => { - // to replace the existing method of the same name +export const getCurrentConversationIdForPatient = async (nhsNumber) => { const db = EhrTransferTracker.getInstance(); const conversations = await db.queryTableByNhsNumber(nhsNumber); @@ -88,27 +86,7 @@ export const getCurrentHealthRecordIdForPatient = async (nhsNumber) => { return currentRecord.InboundConversationId; }; -export const getHealthRecordMessageIds = async (conversationId) => { - // to replace the method of same name - - const db = EhrTransferTracker.getInstance(); - const items = await db.queryTableByConversationId(conversationId, RecordType.ALL); - - const core = items.filter(isCore)?.[0]; - const fragments = items.filter(isFragment); - - if (!core) { - throw new MessageNotFoundError(); - } - const coreMessageId = core.InboundMessageId; - const fragmentMessageIds = fragments.map((message) => message.InboundMessageId); - - return { coreMessageId, fragmentMessageIds }; -}; - -export const markHealthRecordAsDeletedForPatient = async (nhsNumber) => { - // to replace the method of same name - +export const markRecordAsSoftDeleteForPatient = async (nhsNumber) => { const db = EhrTransferTracker.getInstance(); const allConversations = await db.queryTableByNhsNumber(nhsNumber); const allConversationIds = allConversations.map((item) => item.InboundConversationId); @@ -124,3 +102,19 @@ export const markHealthRecordAsDeletedForPatient = async (nhsNumber) => { await db.updateItemsInTransaction(allUpdateParams); return allConversationIds; }; + +export const getMessageIdsForConversation = async (conversationId) => { + const db = EhrTransferTracker.getInstance(); + const items = await db.queryTableByConversationId(conversationId, RecordType.ALL); + + const core = items.filter(isCore)?.[0]; + const fragments = items.filter(isFragment); + + if (!core) { + throw new MessageNotFoundError(); + } + const coreMessageId = core.InboundMessageId; + const fragmentMessageIds = fragments.map((message) => message.InboundMessageId); + + return { coreMessageId, fragmentMessageIds }; +}; diff --git a/src/services/database/ehr-fragment-repository.js b/src/services/database/ehr-fragment-repository.js index ee189b53..06f69a57 100644 --- a/src/services/database/ehr-fragment-repository.js +++ b/src/services/database/ehr-fragment-repository.js @@ -9,8 +9,6 @@ export const markFragmentAsReceivedAndCreateItsParts = async ( conversationId, remainingPartsIds = [] ) => { - // to replace the existing methods `updateFragmentAndCreateItsParts` and `createFragmentPart` - try { const db = EhrTransferTracker.getInstance(); const timestamp = getUKTimestamp(); @@ -36,20 +34,17 @@ export const getFragmentByKey = (inboundConversationId, inboundMessageId) => { return db.getItemByKey(inboundConversationId, inboundMessageId, RecordType.FRAGMENT); }; -export const fragmentExists = async (inboundConversationId, inboundMessageId) => { - // to replace the method with same name +export const fragmentExistsInRecord = async (inboundConversationId, inboundMessageId) => { try { const fragment = await getFragmentByKey(inboundConversationId, inboundMessageId); return !!fragment; } catch (e) { - // TODO: change this error message logError('Querying database for fragment message failed', e); throw e; } }; export const fragmentAlreadyReceived = async (conversationId, messageId) => { - // to replace the method `messageAlreadyReceived` try { const fragment = await getFragmentByKey(conversationId, messageId) return fragment?.ReceivedAt !== undefined; diff --git a/src/services/database/health-record-repository.js b/src/services/database/health-record-repository.js index f21c4bd6..b0840cdd 100644 --- a/src/services/database/health-record-repository.js +++ b/src/services/database/health-record-repository.js @@ -5,10 +5,16 @@ import Sequelize from "sequelize"; import ModelFactory from "../../models"; import { getNow } from "../time"; import { HealthRecordStatus } from "../../models/enums"; +import { markRecordAsSoftDeleteForPatient } from "./ehr-conversation-repository"; Object.freeze(HealthRecordStatus); export const getHealthRecordStatus = async (conversationId) => { + /** + * @deprecated + * Replaced by new method `getConversationStatus` + * To be deleted PRMT-4568 + */ const HealthRecord = ModelFactory.getByName(healthRecordModelName); try { const healthRecord = await HealthRecord.findByPk(conversationId); @@ -31,6 +37,11 @@ export const getHealthRecordStatus = async (conversationId) => { }; export const updateHealthRecordCompleteness = async (conversationId) => { + /** + * @deprecated + * Replaced by new method `updateConversationCompleteness` + * To be deleted PRMT-4568 + */ const HealthRecord = ModelFactory.getByName(healthRecordModelName); const Message = ModelFactory.getByName(messageModelName); const sequelize = ModelFactory.sequelize; @@ -59,6 +70,11 @@ export const updateHealthRecordCompleteness = async (conversationId) => { }; export const getCurrentHealthRecordIdForPatient = async (nhsNumber) => { + /** + * @deprecated + * Replaced by new method `getCurrentConversationIdForPatient` + * To be deleted PRMT-4568 + */ try { const Op = Sequelize.Op; const HealthRecord = ModelFactory.getByName(healthRecordModelName); @@ -88,6 +104,11 @@ export const getCurrentHealthRecordIdForPatient = async (nhsNumber) => { }; export const markHealthRecordAsDeletedForPatient = async (nhsNumber) => { + /** + * @deprecated + * Replaced by new method `markRecordAsSoftDeleteForPatient` + * To be deleted PRMT-4568 + */ const HealthRecord = ModelFactory.getByName(healthRecordModelName); const Message = ModelFactory.getByName(messageModelName); const sequelize = ModelFactory.sequelize; @@ -128,6 +149,11 @@ export const markHealthRecordAsDeletedForPatient = async (nhsNumber) => { }; export const getHealthRecordMessageIds = async (conversationId) => { + /** + * @deprecated + * Replaced by new method `getMessageIdsForConversation` + * To be deleted PRMT-4568 + */ const Message = ModelFactory.getByName(messageModelName); logInfo('finding messages for conversation id ' + conversationId); @@ -155,6 +181,11 @@ export const getHealthRecordMessageIds = async (conversationId) => { }; export const messageAlreadyReceived = async (messageId) => { + /** + * @deprecated + * Replaced by new method `fragmentAlreadyReceived` + * To be deleted PRMT-4568 + */ const Message = ModelFactory.getByName(messageModelName); try { const message = await Message.findByPk(messageId); diff --git a/src/services/database/message-repository.js b/src/services/database/message-repository.js index 9a47d9f5..1ce8c80f 100644 --- a/src/services/database/message-repository.js +++ b/src/services/database/message-repository.js @@ -7,7 +7,8 @@ import { getNow } from '../time'; export const createEhrExtract = async (ehrExtract) => { /** * @deprecated - * To be replaced by new method `createCore` + * Replaced by new method `createCore` + * To be deleted PRMT-4568 */ const Message = ModelFactory.getByName(messageModelName); const HealthRecord = ModelFactory.getByName(healthRecordModelName); @@ -51,7 +52,8 @@ export const updateFragmentAndCreateItsParts = async ( ) => { /** * @deprecated - * To be replaced by new method `markFragmentAsReceivedAndCreateItsParts` + * Replaced by new method `markFragmentAsReceivedAndCreateItsParts` + * To be deleted PRMT-4568 */ const Message = ModelFactory.getByName(messageModelName); const sequelize = ModelFactory.sequelize; @@ -91,7 +93,8 @@ export const updateFragmentAndCreateItsParts = async ( export const fragmentExists = async (id) => { /** * @deprecated - * To be replaced by new method of the same name at ehr-fragment-repository + * Replaced by new method `fragmentExistsInRecord` + * To be deleted PRMT-4568 */ const Message = ModelFactory.getByName(messageModelName); diff --git a/src/services/database/pg-error-codes.js b/src/services/database/pg-error-codes.js index 49ca5114..16673b6d 100644 --- a/src/services/database/pg-error-codes.js +++ b/src/services/database/pg-error-codes.js @@ -1,4 +1,10 @@ + export const ERROR_CODES = Object.freeze({ + /** + * @deprecated + * Postgres DB related stubs + * To be deleted PRMT-4568 + */ INVALID_CREDENTIALS: '28000', INVALID_USER_PASSWORD: '28P01', INVALID_DATABASE: '3D000', From 122642051033df11c491f9a869f5ec6d8cac5637 Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Thu, 7 Mar 2024 12:38:31 +0000 Subject: [PATCH 23/67] [PRMT-4566] Rename methods --- src/models/conversation.js | 7 ++++++- src/models/core.js | 2 +- src/models/fragment.js | 7 +++---- .../dynamo-ehr-transfer-tracker.integration.test.js | 6 +++--- .../ehr-conversation-repository.integration.test.js | 6 +++--- .../ehr-fragment-repository.integration.test.js | 10 +++++----- src/services/database/dynamo-ehr-transfer-tracker.js | 6 +----- src/services/database/ehr-conversation-repository.js | 8 ++------ src/services/database/ehr-core-repository.js | 10 +++++----- src/utilities/integration-test-utilities.js | 10 +++++----- 10 files changed, 34 insertions(+), 38 deletions(-) diff --git a/src/models/conversation.js b/src/models/conversation.js index d7d299b7..93edd8ce 100644 --- a/src/models/conversation.js +++ b/src/models/conversation.js @@ -1,6 +1,6 @@ import { getUKTimestamp } from '../services/time'; -import { logError } from '../middleware/logging'; import { addChangesToUpdateParams } from '../utilities/dynamodb-helper'; +import { ConversationStatus } from "./enums"; const fieldsAllowedToUpdate = ['TransferStatus', 'FailureCode', 'DeletedAt']; @@ -22,3 +22,8 @@ export const buildConversationUpdateParams = (conversationId, changes) => { export const isConversation = (item) => { return item.Layer === 'Conversation'; }; + +export const isInCompleteStatus = (conversation) => { + const status = conversation?.TransferStatus; + return status === ConversationStatus.COMPLETE || status?.startsWith('Outbound') +} \ No newline at end of file diff --git a/src/models/core.js b/src/models/core.js index f028d186..3d45e888 100644 --- a/src/models/core.js +++ b/src/models/core.js @@ -2,7 +2,7 @@ import { getUKTimestamp } from '../services/time'; import { CoreStatus } from './enums'; import { validateIds } from '../utilities/dynamodb-helper'; -export const core = (inboundConversationId, messageId) => { +export const buildCore = (inboundConversationId, messageId) => { const timestamp = getUKTimestamp(); validateIds(inboundConversationId, messageId); diff --git a/src/models/fragment.js b/src/models/fragment.js index f6b01be0..cd8d574f 100644 --- a/src/models/fragment.js +++ b/src/models/fragment.js @@ -1,10 +1,9 @@ import { getUKTimestamp } from '../services/time'; -import { validate } from 'uuid'; import { addChangesToUpdateParams, validateIds } from "../utilities/dynamodb-helper"; const fieldsAllowedToUpdate = ['TransferStatus', 'ParentId', 'ReceivedAt', 'DeletedAt']; -export const singleFragment = ({ inboundConversationId, fragmentMessageId, parentMessageId }) => { +export const buildFragment = ({ inboundConversationId, fragmentMessageId, parentMessageId }) => { const timestamp = getUKTimestamp(); validateIds(inboundConversationId, fragmentMessageId); @@ -19,7 +18,7 @@ export const singleFragment = ({ inboundConversationId, fragmentMessageId, paren }; }; -export const arrayOfFragments = ({ +export const buildMultipleFragments = ({ inboundConversationId, fragmentMessageIds, parentMessageId, @@ -28,7 +27,7 @@ export const arrayOfFragments = ({ return []; } return fragmentMessageIds.map((fragmentMessageId) => - singleFragment({ inboundConversationId, fragmentMessageId, parentMessageId }) + buildFragment({ inboundConversationId, fragmentMessageId, parentMessageId }) ); }; diff --git a/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js b/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js index 85227bae..7f70df65 100644 --- a/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js +++ b/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js @@ -7,7 +7,7 @@ import { createConversationForTest, } from '../../../utilities/integration-test-utilities'; import { markFragmentAsReceivedAndCreateItsParts } from '../ehr-fragment-repository'; -import { core } from '../../../models/core'; +import { buildCore } from '../../../models/core'; describe('EhrTransferTracker', () => { const testConversationId = uuid(); @@ -26,9 +26,9 @@ describe('EhrTransferTracker', () => { const db = EhrTransferTracker.getInstance(); const testMessageId = uuid(); - const ehrCore = core(testConversationId, testMessageId); + const ehrCore = buildCore(testConversationId, testMessageId); - await db.writeItemsToTable([ehrCore]); + await db.writeItemsInTransaction([ehrCore]); // then const actual = await db.queryTableByConversationId(testConversationId, RecordType.CORE); diff --git a/src/services/database/__tests__/ehr-conversation-repository.integration.test.js b/src/services/database/__tests__/ehr-conversation-repository.integration.test.js index 1788d67a..f4f5e043 100644 --- a/src/services/database/__tests__/ehr-conversation-repository.integration.test.js +++ b/src/services/database/__tests__/ehr-conversation-repository.integration.test.js @@ -19,7 +19,7 @@ import { markFragmentAsReceivedAndCreateItsParts, } from '../ehr-fragment-repository'; import { HealthRecordNotFoundError, MessageNotFoundError } from '../../../errors/errors'; -import { core } from '../../../models/core'; +import { buildCore } from '../../../models/core'; import { getEpochTimeInSecond } from '../../time'; jest.mock('../../../middleware/logging'); @@ -256,9 +256,9 @@ describe('ehr-conversation-repository', () => { // given const conversationId = uuid(); const messageId = uuid(); - const item = { ...core(conversationId, messageId), DeletedAt: getEpochTimeInSecond() }; + const item = { ...buildCore(conversationId, messageId), DeletedAt: getEpochTimeInSecond() }; - await db.writeItemsToTable([item]); + await db.writeItemsInTransaction([item]); // when await expect(() => getMessageIdsForConversation(conversationId)) diff --git a/src/services/database/__tests__/ehr-fragment-repository.integration.test.js b/src/services/database/__tests__/ehr-fragment-repository.integration.test.js index 2022a510..1696651e 100644 --- a/src/services/database/__tests__/ehr-fragment-repository.integration.test.js +++ b/src/services/database/__tests__/ehr-fragment-repository.integration.test.js @@ -7,7 +7,7 @@ import { getFragmentByKey, markFragmentAsReceivedAndCreateItsParts, } from '../ehr-fragment-repository'; -import { singleFragment } from '../../../models/fragment'; +import { buildFragment } from '../../../models/fragment'; import { EhrTransferTracker } from '../dynamo-ehr-transfer-tracker'; // Mocking @@ -107,12 +107,12 @@ describe('ehr-fragment-repository', () => { fragmentMessageIds: [fragmentMessageId], }); - const nestedFragmentArrivedEarly = singleFragment({ + const nestedFragmentArrivedEarly = buildFragment({ inboundConversationId: conversationId, fragmentMessageId: nestedFragmentMessageId, }); const db = EhrTransferTracker.getInstance(); - await db.writeItemsToTable([nestedFragmentArrivedEarly]); + await db.writeItemsInTransaction([nestedFragmentArrivedEarly]); // when await markFragmentAsReceivedAndCreateItsParts(fragmentMessageId, conversationId, [ @@ -130,12 +130,12 @@ describe('ehr-fragment-repository', () => { const conversationId = uuid(); const messageId = uuid(); - const fragment = singleFragment({ + const fragment = buildFragment({ inboundConversationId: conversationId, fragmentMessageId: messageId, }); const db = EhrTransferTracker.getInstance(); - await db.writeItemsToTable([fragment]); + await db.writeItemsInTransaction([fragment]); expect(await fragmentExistsInRecord(conversationId, messageId)).toBe(true); }); diff --git a/src/services/database/dynamo-ehr-transfer-tracker.js b/src/services/database/dynamo-ehr-transfer-tracker.js index ec5ef757..fdbebb82 100644 --- a/src/services/database/dynamo-ehr-transfer-tracker.js +++ b/src/services/database/dynamo-ehr-transfer-tracker.js @@ -38,7 +38,7 @@ export class EhrTransferTracker { return new this(); } - async writeItemsToTable(items) { + async writeItemsInTransaction(items) { if (!items || !Array.isArray(items)) { throw new Error('The given argument `items` is not an array'); } @@ -162,8 +162,4 @@ export class EhrTransferTracker { } return response?.Item ?? null; } - - async getFragmentByKey(inboundConversationId, inboundMessageId) { - return this.getItemByKey(inboundConversationId, inboundMessageId, RecordType.FRAGMENT); - } } diff --git a/src/services/database/ehr-conversation-repository.js b/src/services/database/ehr-conversation-repository.js index 4f5454e1..9f2a777a 100644 --- a/src/services/database/ehr-conversation-repository.js +++ b/src/services/database/ehr-conversation-repository.js @@ -1,7 +1,7 @@ import { ConversationStatus, HealthRecordStatus, RecordType } from '../../models/enums'; import { logError, logInfo } from '../../middleware/logging'; import { EhrTransferTracker } from './dynamo-ehr-transfer-tracker'; -import { buildConversationUpdateParams } from '../../models/conversation'; +import { buildConversationUpdateParams, isInCompleteStatus } from '../../models/conversation'; import { HealthRecordNotFoundError, MessageNotFoundError } from '../../errors/errors'; import { isCore } from '../../models/core'; import { isFragment } from '../../models/fragment'; @@ -69,11 +69,7 @@ export const getCurrentConversationIdForPatient = async (nhsNumber) => { const db = EhrTransferTracker.getInstance(); const conversations = await db.queryTableByNhsNumber(nhsNumber); - const completedRecords = conversations?.filter( - (item) => - item.TransferStatus === ConversationStatus.COMPLETE || - item.TransferStatus?.startsWith('Outbound') - ); + const completedRecords = conversations?.filter(isInCompleteStatus); if (!completedRecords || completedRecords.length === 0) { throw new HealthRecordNotFoundError(); diff --git a/src/services/database/ehr-core-repository.js b/src/services/database/ehr-core-repository.js index d2ea28dc..e82e22db 100644 --- a/src/services/database/ehr-core-repository.js +++ b/src/services/database/ehr-core-repository.js @@ -1,6 +1,6 @@ import { EhrTransferTracker } from './dynamo-ehr-transfer-tracker'; -import { arrayOfFragments } from '../../models/fragment'; -import { core } from '../../models/core'; +import { buildMultipleFragments } from '../../models/fragment'; +import { buildCore } from '../../models/core'; import { RecordType } from '../../models/enums'; import { logError } from '../../middleware/logging'; @@ -9,10 +9,10 @@ export const createCore = async ({ conversationId, messageId, fragmentMessageIds try { const db = EhrTransferTracker.getInstance(); - const itemsToWrite = [core(conversationId, messageId)]; + const itemsToWrite = [buildCore(conversationId, messageId)]; if (fragmentMessageIds) { - const directFragments = arrayOfFragments({ + const directFragments = buildMultipleFragments({ inboundConversationId: conversationId, fragmentMessageIds, parentMessageId: messageId, @@ -20,7 +20,7 @@ export const createCore = async ({ conversationId, messageId, fragmentMessageIds itemsToWrite.push(...directFragments); } - await db.writeItemsToTable(itemsToWrite); + await db.writeItemsInTransaction(itemsToWrite); } catch (e) { logError('Message could not be stored', e); throw e; diff --git a/src/utilities/integration-test-utilities.js b/src/utilities/integration-test-utilities.js index 4305c846..b76589c8 100644 --- a/src/utilities/integration-test-utilities.js +++ b/src/utilities/integration-test-utilities.js @@ -15,12 +15,12 @@ export const generateMultipleUUID = (amount, isUppercase) => .map(() => (isUppercase ? uuidv4().toUpperCase() : uuidv4())); export const createConversationForTest = async (conversationId, nhsNumber, overrides) => { - // This method is only meant for testing purpose, - // as the inbound conversation record is supposed to be created by other service. + // This method is only meant for testing purpose. + // the inbound conversation record is supposed to be created by other service. const isInLocal = process.env.NHS_ENVIRONMENT === 'local' || !process.env.NHS_ENVIRONMENT; if (!isInLocal) { - throw new Error('Unexpected call to createConversation method in non-local environment'); + throw new Error('Unexpected call to createConversationForTest method in non-local environment'); } const timestamp = getUKTimestamp(); @@ -35,7 +35,7 @@ export const createConversationForTest = async (conversationId, nhsNumber, overr ...overrides }; - await db.writeItemsToTable([item]); + await db.writeItemsInTransaction([item]); }; export const cleanupRecordsForTest = async (conversationId) => { @@ -43,7 +43,7 @@ export const cleanupRecordsForTest = async (conversationId) => { const isInLocal = process.env.NHS_ENVIRONMENT === 'local' || !process.env.NHS_ENVIRONMENT; if (!isInLocal) { - throw new Error('Unexpected call to createConversation method in non-local environment'); + throw new Error('Unexpected call to cleanupRecordsForTest method in non-local environment'); } const db = EhrTransferTracker.getInstance(); From d0b6a3d6ee63b526e7ba5b2fa14e462b50764b8b Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Thu, 7 Mar 2024 12:40:33 +0000 Subject: [PATCH 24/67] [PRMT-4566] minor change in wordings --- .../__tests__/dynamo-ehr-transfer-tracker.integration.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js b/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js index 7f70df65..3048b5a9 100644 --- a/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js +++ b/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js @@ -21,7 +21,7 @@ describe('EhrTransferTracker', () => { await cleanupRecordsForTest(testConversationId); }); - it('create and read an ehrCore in dynamodb', async () => { + it('can create and read a record in dynamodb', async () => { // given const db = EhrTransferTracker.getInstance(); const testMessageId = uuid(); From 85778d75fed7e81617243759335aee3c7bfee551 Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Thu, 7 Mar 2024 14:41:11 +0000 Subject: [PATCH 25/67] [PRMT-4566] Correct dynamodb related env vars --- src/services/database/dynamodb-client.js | 2 +- tasks | 6 +----- terraform/data.tf | 4 ++++ terraform/ecs-task.tf | 3 ++- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/services/database/dynamodb-client.js b/src/services/database/dynamodb-client.js index 6a533d54..66790621 100644 --- a/src/services/database/dynamodb-client.js +++ b/src/services/database/dynamodb-client.js @@ -11,7 +11,7 @@ export const getDynamodbClient = () => { if (isInLocal) { // for running whole integration test suite in dojo - clientConfig.endpoint = process.env.DYNAMODB_ENDPOINT; + clientConfig.endpoint = process.env.DYNAMODB_LOCAL_ENDPOINT; } if (isInLocal && !isInDojo) { // for running individual test with IDE diff --git a/tasks b/tasks index d98895dd..7ae20282 100755 --- a/tasks +++ b/tasks @@ -112,7 +112,7 @@ function configure_local_envs { export NODE_ENV=local export DYNAMODB_NAME=local-test-db - export DYNAMODB_ENDPOINT=http://dynamodb-local:8000/ + export DYNAMODB_LOCAL_ENDPOINT=http://dynamodb-local:8000/ export AWS_ACCESS_KEY_ID=$(generate_secure_string 20) export AWS_SECRET_ACCESS_KEY=$(generate_secure_string 20) @@ -126,8 +126,6 @@ function configure_db_migration_envs { export DATABASE_NAME=$(_get_aws_ssm_secret "/repo/${NHS_ENVIRONMENT}/output/prm-deductions-ehr-repository/db-name") export DATABASE_PASSWORD="$(aws rds generate-db-auth-token --hostname $DATABASE_HOST --port 5432 --region $AWS_DEFAULT_REGION --username $DATABASE_USER)" export USE_SSL_FOR_DB="true" - - export DYNAMODB_NAME=$(_get_aws_ssm_secret "/repo/${NHS_ENVIRONMENT}/output/prm-deductions-infra/ehr-transfer-tracker-db-name") } function get_aws_account_id { @@ -258,8 +256,6 @@ function get_db_credentials { export DATABASE_USER=$(get_aws_ssm_secret "/repo/${NHS_ENVIRONMENT}/user-input/ehr-repo-db-username") export DATABASE_HOST=$(get_aws_ssm_secret "/repo/${NHS_ENVIRONMENT}/output/prm-deductions-ehr-repository/db-host") export DATABASE_NAME="deductions_db" - - export DYNAMODB_NAME=$(_get_aws_ssm_secret "/repo/${NHS_ENVIRONMENT}/output/prm-deductions-infra/ehr-transfer-tracker-db-name") } function drop_db { diff --git a/terraform/data.tf b/terraform/data.tf index 804b8653..3d10595d 100644 --- a/terraform/data.tf +++ b/terraform/data.tf @@ -18,4 +18,8 @@ data "aws_ssm_parameter" "db-username" { data "aws_ssm_parameter" "db-password" { name = "/repo/${var.environment}/user-input/ehr-repo-db-password" +} + +data "aws_ssm_parameter" "dynamodb-name" { + name = "/repo/${var.environment}/output/prm-deductions-infra/ehr-transfer-tracker-db-name" } \ No newline at end of file diff --git a/terraform/ecs-task.tf b/terraform/ecs-task.tf index 1e45cba1..3b24d956 100644 --- a/terraform/ecs-task.tf +++ b/terraform/ecs-task.tf @@ -14,7 +14,8 @@ locals { { name = "AWS_REGION", value = var.region }, { name = "SKIP_DB_MIGRATION", value = "true" }, { name = "USE_SSL_FOR_DB", value = "true" }, - { name = "LOG_LEVEL", value = var.log_level } + { name = "LOG_LEVEL", value = var.log_level }, + { name = "DYNAMODB_NAME", value = data.aws_ssm_parameter.dynamodb-name.value }, ] } From 54f9d94306a572f91f84b1f9d887f7e86da0d1b6 Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Mon, 11 Mar 2024 09:40:18 +0000 Subject: [PATCH 26/67] [PRMT-4566] Change DeletedAt field to store ttl time of 8 weeks later --- .../ehr-conversation-repository.integration.test.js | 10 +++++++--- src/services/time.js | 4 ++-- src/utilities/dynamodb-helper.js | 5 ++++- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/services/database/__tests__/ehr-conversation-repository.integration.test.js b/src/services/database/__tests__/ehr-conversation-repository.integration.test.js index f4f5e043..12e9c5c7 100644 --- a/src/services/database/__tests__/ehr-conversation-repository.integration.test.js +++ b/src/services/database/__tests__/ehr-conversation-repository.integration.test.js @@ -21,6 +21,7 @@ import { import { HealthRecordNotFoundError, MessageNotFoundError } from '../../../errors/errors'; import { buildCore } from '../../../models/core'; import { getEpochTimeInSecond } from '../../time'; +import moment from "moment-timezone"; jest.mock('../../../middleware/logging'); @@ -323,7 +324,6 @@ describe('ehr-conversation-repository', () => { // ========================= HELPER SETUPS FOR THIS BLOCK ========================= let conversationIdUsed = []; const mockTime = new Date(Date.parse('2024-03-06T12:34:56+00:00')); - const mockTimeInEpochSecond = mockTime / 1000; beforeEach(async () => { jest.useFakeTimers().setSystemTime(mockTime); @@ -380,10 +380,12 @@ describe('ehr-conversation-repository', () => { ); expect(deletedRecords).toHaveLength(5); // conversation + core + 3 fragments + const expectedDeletedAtTime = moment(mockTime).add(8, 'week').unix(); + for (const item of deletedRecords) { expect(item).toMatchObject({ InboundConversationId: conversationId, - DeletedAt: mockTimeInEpochSecond, + DeletedAt: expectedDeletedAtTime, }); } }); @@ -416,9 +418,11 @@ describe('ehr-conversation-repository', () => { expect(deletedRecords).toHaveLength(4); // two sets of conversation + core + const expectedDeletedAtTime = moment(mockTime).add(8, 'week').unix(); + for (const item of deletedRecords) { expect(item).toMatchObject({ - DeletedAt: mockTimeInEpochSecond, + DeletedAt: expectedDeletedAtTime, }); } }); diff --git a/src/services/time.js b/src/services/time.js index c77decf6..4c82a4b4 100644 --- a/src/services/time.js +++ b/src/services/time.js @@ -6,6 +6,6 @@ export const getUKTimestamp = () => { return moment().tz('Europe/London').format('YYYY-MM-DDThh:mm:ssZ'); }; -export const getEpochTimeInSecond = () => { - return moment().unix(); +export const getEpochTimeInSecond = (datetime) => { + return moment(datetime).unix(); }; diff --git a/src/utilities/dynamodb-helper.js b/src/utilities/dynamodb-helper.js index e54feb5e..c6f5ebff 100644 --- a/src/utilities/dynamodb-helper.js +++ b/src/utilities/dynamodb-helper.js @@ -1,6 +1,7 @@ import { logError } from '../middleware/logging'; import { validate } from 'uuid'; import { getEpochTimeInSecond, getUKTimestamp } from '../services/time'; +import moment from "moment-timezone"; export const validateIds = (conversationId, messageId) => { const uuidsAreValid = validate(conversationId) && validate(messageId); @@ -27,6 +28,8 @@ export const addChangesToUpdateParams = (params, changes, fieldsAllowedToUpdate) }; export const buildSoftDeleteUpdateParams = (item) => { + const eightWeeksAfter = moment().add(8, 'week'); + return { Key: { InboundConversationId: item.InboundConversationId, @@ -35,7 +38,7 @@ export const buildSoftDeleteUpdateParams = (item) => { UpdateExpression: `set UpdatedAt = :now, DeletedAt = :deletedAt`, ExpressionAttributeValues: { ':now': getUKTimestamp(), - ':deletedAt': getEpochTimeInSecond(), + ':deletedAt': getEpochTimeInSecond(eightWeeksAfter), }, }; }; From 9196cf55de6b0ced5171a43444abaed2220e301a Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Thu, 7 Mar 2024 16:06:28 +0000 Subject: [PATCH 27/67] [PRMT-4567] Migrated store-message-controller.js and its tests --- .../new-store-message-controller.test.js | 448 ++++++++++++++++++ 1 file changed, 448 insertions(+) create mode 100644 src/api/messages/__tests__/new-store-message-controller.test.js diff --git a/src/api/messages/__tests__/new-store-message-controller.test.js b/src/api/messages/__tests__/new-store-message-controller.test.js new file mode 100644 index 00000000..cd6401a3 --- /dev/null +++ b/src/api/messages/__tests__/new-store-message-controller.test.js @@ -0,0 +1,448 @@ +import { v4 as uuid } from 'uuid'; +import request from 'supertest'; +import app from '../../../app'; + +import { logError } from '../../../middleware/logging'; +import { + getConversationStatus, + updateConversationCompleteness +} from "../../../services/database/ehr-conversation-repository"; + +jest.mock('../../../services/database/ehr-conversation-repository'); +jest.mock('../../../middleware/logging'); +jest.mock('../../../config', () => ({ + initializeConfig: jest.fn().mockReturnValue({}), +})); + +describe('storeMessageController', () => { + const initializeConfig = () => ({ + consumerApiKeys: { TEST_USER: 'correct-key' } + }); + const MessageType = { + EHR_EXTRACT: 'ehrExtract', + FRAGMENT: 'fragment', + }; + // initializeConfig.mockReturnValue({ + // consumerApiKeys: { TEST_USER: 'correct-key' }, + // }); + + const authorizationKeys = 'correct-key'; + const conversationId = uuid(); + const nhsNumber = '1234567890'; + const messageId = uuid(); + const fragmentMessageIds = [uuid()]; + + describe('success', () => { + let requestBody; + + beforeEach(() => { + requestBody = { + data: { + type: 'messages', + id: messageId, + attributes: { + conversationId, + messageType: MessageType.EHR_EXTRACT, + nhsNumber, + fragmentMessageIds, + }, + }, + }; + }); + + it('should return a 201 and health record status when message has successfully been stored in database', async () => { + getConversationStatus.mockResolvedValueOnce('complete'); + const ehrExtract = { messageId, conversationId,fragmentMessageIds }; + const res = await request(app) + .post('/messages') + .send(requestBody) + .set('Authorization', authorizationKeys); + + expect(res.status).toBe(201); + expect(res.body).toEqual({ healthRecordStatus: 'complete' }); + expect(createCore).toHaveBeenCalledWith(ehrExtract); + expect(updateConversationCompleteness).toHaveBeenCalledWith(conversationId); + expect(getConversationStatus).toHaveBeenCalledWith(conversationId); + }); + + it('should update receivedAt for given fragment and store its parts', async () => { + const nestedFragmentId = uuid(); + requestBody.data.attributes = { + messageType: MessageType.FRAGMENT, + conversationId, + fragmentMessageIds: [nestedFragmentId], + }; + + fragmentExists.mockResolvedValueOnce(true); + + const res = await request(app) + .post('/messages') + .send(requestBody) + .set('Authorization', authorizationKeys); + + expect(res.status).toBe(201); + expect(createEhrExtract).not.toHaveBeenCalled(); + expect(updateFragmentAndCreateItsParts).toHaveBeenCalledWith(messageId, conversationId, [ + nestedFragmentId, + ]); + expect(updateHealthRecordCompleteness).toHaveBeenCalledWith(conversationId); + }); + + it('should create message in the database when a nested fragment arrives before first fragment', async () => { + const nestedFragmentId = uuid(); + requestBody.data.id = nestedFragmentId; + requestBody.data.attributes = { + messageType: MessageType.FRAGMENT, + conversationId, + fragmentMessageIds: [], + }; + + fragmentExists.mockResolvedValueOnce(false); + + const res = await request(app) + .post('/messages') + .send(requestBody) + .set('Authorization', authorizationKeys); + + expect(res.status).toBe(201); + expect(fragmentExists).toHaveBeenCalledWith(nestedFragmentId); + expect(createFragmentPart).toHaveBeenCalledWith(nestedFragmentId, conversationId); + expect(updateFragmentAndCreateItsParts).not.toHaveBeenCalled(); + expect(updateHealthRecordCompleteness).toHaveBeenCalledWith(conversationId); + }); + }); + + describe('failure', () => { + const requestBody = { + data: { + type: 'messages', + id: messageId, + attributes: { + conversationId, + messageType: MessageType.EHR_EXTRACT, + nhsNumber, + fragmentMessageIds, + }, + }, + }; + it('should return a 503 when message cannot be stored in the database', async () => { + createEhrExtract.mockRejectedValueOnce({ error: 'db is down' }); + const res = await request(app) + .post('/messages') + .send(requestBody) + .set('Authorization', authorizationKeys); + + expect(logError).toHaveBeenCalledWith( + 'Returned 503 due to error while saving message', + expect.anything() + ); + expect(res.status).toBe(503); + }); + }); + + describe('validation', () => { + it('should return 422 and an error message when conversationId is not a UUID', async () => { + const requestBody = { + data: { + attributes: { + conversationId: 'not-a-uuid', + }, + }, + }; + const errorMessage = { + 'data.attributes.conversationId': "'conversationId' provided is not a UUID", + }; + + const res = await request(app) + .post('/messages') + .send(requestBody) + .set('Authorization', authorizationKeys); + + expect(res.status).toBe(422); + expect(res.body.errors).toContainEqual(errorMessage); + }); + + it('should return 422 and an error message when messageId is not a UUID', async () => { + const requestBody = { + data: { + id: 'not-a-uuid', + }, + }; + const errorMessage = { 'data.id': "'id' provided is not a UUID" }; + + const res = await request(app) + .post('/messages') + .send(requestBody) + .set('Authorization', authorizationKeys); + + expect(res.status).toBe(422); + expect(res.body.errors).toContainEqual(errorMessage); + }); + + it('should return 422 and an error message when type is not messages', async () => { + const requestBody = { + data: { + type: 'not-messages', + }, + }; + const errorMessage = { 'data.type': 'Invalid value' }; + + const res = await request(app) + .post('/messages') + .send(requestBody) + .set('Authorization', authorizationKeys); + + expect(res.status).toBe(422); + expect(res.body.errors).toContainEqual(errorMessage); + }); + + it('should return 422 and an error message when nhsNumber is not numeric', async () => { + const requestBody = { + data: { + type: 'messages', + id: uuid(), + attributes: { + conversationId: uuid(), + nhsNumber: 'not-an-nhs-number', + messageType: 'ehrExtract', + }, + }, + }; + const errorMessage = { 'data.attributes.nhsNumber': "'nhsNumber' provided is not numeric" }; + + const res = await request(app) + .post('/messages') + .send(requestBody) + .set('Authorization', authorizationKeys); + + expect(res.status).toBe(422); + expect(res.body.errors).toContainEqual(errorMessage); + }); + + it('should return 422 and an error message when nhsNumber is not 10 digits long', async () => { + const requestBody = { + data: { + attributes: { + nhsNumber: '1234567890987654', + messageType: 'ehrExtract', + }, + }, + }; + const errorMessage = { + 'data.attributes.nhsNumber': "'nhsNumber' provided is not 10 characters", + }; + + const res = await request(app) + .post('/messages') + .send(requestBody) + .set('Authorization', authorizationKeys); + + expect(res.status).toBe(422); + expect(res.body.errors).toContainEqual(errorMessage); + }); + + it('should return 422 and an error message when messageType is not recognised', async () => { + const requestBody = { + data: { + attributes: { + messageType: 'not-a-message-type', + }, + }, + }; + const errorMessage = { + 'data.attributes.messageType': + "'messageType' provided is not one of the following: ehrExtract, fragment", + }; + + const res = await request(app) + .post('/messages') + .send(requestBody) + .set('Authorization', authorizationKeys); + + expect(res.status).toBe(422); + expect(res.body.errors).toContainEqual(errorMessage); + }); + + it('should return 201 when messageType is recognised', async () => { + const requestBody = { + data: { + type: 'messages', + id: uuid(), + attributes: { + conversationId: uuid(), + nhsNumber: '1234567890', + messageType: 'ehrExtract', + fragmentMessageIds: [], + }, + }, + }; + + const res = await request(app) + .post('/messages') + .send(requestBody) + .set('Authorization', authorizationKeys); + + expect(res.status).toBe(201); + }); + + it('should return 422 and an error message when nhsNumber has not been provided for messageType ehrExtract', async () => { + const requestBody = { + data: { + attributes: { + messageType: 'ehrExtract', + }, + }, + }; + + const errorMessage = { + 'data.attributes.nhsNumber': "'nhsNumber' is required for messageType ehrExtract", + }; + + const res = await request(app) + .post('/messages') + .send(requestBody) + .set('Authorization', authorizationKeys); + + expect(res.status).toBe(422); + expect(res.body.errors).toContainEqual(errorMessage); + }); + + it('should return 201 when nhsNumber has been provided for messageType ehrExtract', async () => { + const requestBody = { + data: { + type: 'messages', + id: uuid(), + attributes: { + conversationId: uuid(), + nhsNumber: '1234567890', + messageType: 'ehrExtract', + fragmentMessageIds: [], + }, + }, + }; + const res = await request(app) + .post('/messages') + .send(requestBody) + .set('Authorization', authorizationKeys); + + expect(res.status).toBe(201); + }); + + it('should return 422 and an error message when nhsNumber has been provided for messageType fragment', async () => { + const requestBody = { + data: { + attributes: { + nhsNumber: '1234567890', + messageType: 'fragment', + }, + }, + }; + + const errorMessage = { + 'data.attributes.nhsNumber': "'nhsNumber' should be empty for messageType fragment", + }; + + const res = await request(app) + .post('/messages') + .send(requestBody) + .set('Authorization', authorizationKeys); + + expect(res.status).toBe(422); + expect(res.body.errors).toContainEqual(errorMessage); + }); + + it('should return 201 when nhsNumber has not been provided for messageType fragment', async () => { + const requestBody = { + data: { + type: 'messages', + id: uuid(), + attributes: { + conversationId: uuid(), + messageType: 'fragment', + fragmentMessageIds: [], + }, + }, + }; + + const res = await request(app) + .post('/messages') + .send(requestBody) + .set('Authorization', authorizationKeys); + + expect(res.status).toBe(201); + }); + + it('should return 422 and an error message when fragmentMessageIds is not uuids', async () => { + const requestBody = { + data: { + attributes: { + fragmentMessageIds: ['not-a-uuid'], + }, + }, + }; + + const errorMessage = { + 'data.attributes.fragmentMessageIds[0]': "'fragmentMessageIds' should be UUIDs", + }; + + const res = await request(app) + .post('/messages') + .send(requestBody) + .set('Authorization', authorizationKeys); + + expect(res.status).toBe(422); + expect(res.body.errors).toContainEqual(errorMessage); + }); + + it('should return 422 and an error message when fragmentMessageIds is not an array', async () => { + const requestBody = { + data: { + attributes: { + fragmentMessageIds: 'not-an-array', + }, + }, + }; + + const errorMessage = { + 'data.attributes.fragmentMessageIds': "'fragmentMessageIds' should be an array", + }; + + const res = await request(app) + .post('/messages') + .send(requestBody) + .set('Authorization', authorizationKeys); + + expect(res.status).toBe(422); + expect(res.body.errors).toContainEqual(errorMessage); + }); + }); + + describe('authentication', () => { + const requestBody = { + data: { + type: 'messages', + id: uuid(), + attributes: { + conversationId: uuid(), + nhsNumber: '1234567890', + messageType: 'ehrExtract', + }, + }, + }; + + it('should return 401 when authentication keys are missing', async () => { + const res = await request(app).post('/messages').send(requestBody); + + expect(res.status).toBe(401); + }); + + it('should return 403 when authentication keys are incorrect', async () => { + const res = await request(app) + .post('/messages') + .send(requestBody) + .set('Authorization', 'incorrect'); + + expect(res.status).toBe(403); + }); + }); +}); From d78e32c6b0db5cc49b5bb8d0f97310c0a78c9109 Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Thu, 7 Mar 2024 16:13:42 +0000 Subject: [PATCH 28/67] [PRMT-4567] migrated message-location-controller.js and its tests --- .../new-message-location-controller.test.js | 108 ++++++++++++++++++ .../new-store-message-controller.test.js | 44 +++---- .../messages/message-location-controller.js | 27 ++++- src/api/messages/store-message-controller.js | 71 ++++++++++-- src/models/enums.js | 12 +- src/models/index.js | 2 + 6 files changed, 226 insertions(+), 38 deletions(-) create mode 100644 src/api/messages/__tests__/new-message-location-controller.test.js diff --git a/src/api/messages/__tests__/new-message-location-controller.test.js b/src/api/messages/__tests__/new-message-location-controller.test.js new file mode 100644 index 00000000..35bebf95 --- /dev/null +++ b/src/api/messages/__tests__/new-message-location-controller.test.js @@ -0,0 +1,108 @@ +import request from 'supertest'; +import app from '../../../app'; +import { getSignedUrl } from '../../../services/storage'; +import { v4 as uuid } from 'uuid'; +import { logError, logInfo } from '../../../middleware/logging'; +import { initializeConfig } from '../../../config'; +import { fragmentAlreadyReceived } from "../../../services/database/ehr-fragment-repository"; + +jest.mock('../../../services/storage'); +jest.mock("../../../services/database/ehr-fragment-repository"); +jest.mock('../../../middleware/logging'); +jest.mock('../../../config', () => ({ + initializeConfig: jest.fn().mockReturnValue({}), +})); + +describe('messageLocationController', () => { + initializeConfig.mockReturnValue({ + consumerApiKeys: { TEST_USER: 'correct-key' }, + }); + + const authorizationKeys = 'correct-key'; + + describe('success', () => { + const conversationId = uuid(); + const messageId = uuid(); + + it('should return a 200 with presigned url in body', async () => { + getSignedUrl.mockResolvedValue('presigned-url'); + + const res = await request(app) + .get(`/messages/${conversationId}/${messageId}`) + .set('Authorization', authorizationKeys); + + expect(res.status).toBe(200); + expect(getSignedUrl).toHaveBeenCalledWith(conversationId, messageId, 'putObject'); + expect(res.text).toEqual('presigned-url'); + expect(logInfo).toHaveBeenCalledWith('Presigned URL sent successfully'); + }); + }); + + describe('conflict', () => { + const conversationId = uuid(); + const messageId = uuid(); + + it('should return a 409 when ehr already exists', async () => { + fragmentAlreadyReceived.mockResolvedValueOnce(true); + + const res = await request(app) + .get(`/messages/${conversationId}/${messageId}`) + .set('Authorization', authorizationKeys); + + expect(res.status).toBe(409); + expect(getSignedUrl).not.toHaveBeenCalled(); + expect(fragmentAlreadyReceived).toHaveBeenCalledWith(messageId); + }); + }); + + describe('error', () => { + const conversationId = uuid(); + const messageId = uuid(); + + it('should return a 503 when getSignedUrl promise is rejected', async () => { + const error = new Error('error'); + getSignedUrl.mockRejectedValueOnce(error); + fragmentAlreadyReceived.mockResolvedValueOnce(false); + + const res = await request(app) + .get(`/messages/${conversationId}/${messageId}`) + .set('Authorization', authorizationKeys); + + expect(getSignedUrl).toHaveBeenCalledWith(conversationId, messageId, 'putObject'); + expect(logError).toHaveBeenCalledWith('Failed to retrieve pre-signed url', error); + expect(res.status).toBe(503); + }); + }); + + describe('validation', () => { + it('should return 422 and an error message when conversationId is not a UUID', async () => { + const conversationId = 'not-a-uuid'; + const messageId = uuid(); + const errorMessage = [{ conversationId: "'conversationId' provided is not a UUID" }]; + + const res = await request(app) + .get(`/messages/${conversationId}/${messageId}`) + .set('Authorization', authorizationKeys); + + expect(res.status).toBe(422); + expect(res.body).toEqual({ + errors: errorMessage, + }); + }); + + it('should return 422 and an error message when messageId is not a UUID', async () => { + const conversationId = uuid(); + const messageId = 'not-a-uuid'; + const errorMessage = [{ messageId: "'messageId' provided is not a UUID" }]; + + const res = await request(app) + .get(`/messages/${conversationId}/${messageId}`) + .set('Authorization', authorizationKeys); + + expect(res.status).toBe(422); + expect(res.body).toEqual({ + errors: errorMessage, + }); + }); + }); +}); diff --git a/src/api/messages/__tests__/new-store-message-controller.test.js b/src/api/messages/__tests__/new-store-message-controller.test.js index cd6401a3..4892ee7b 100644 --- a/src/api/messages/__tests__/new-store-message-controller.test.js +++ b/src/api/messages/__tests__/new-store-message-controller.test.js @@ -1,30 +1,31 @@ import { v4 as uuid } from 'uuid'; import request from 'supertest'; import app from '../../../app'; - +import { initializeConfig } from '../../../config'; import { logError } from '../../../middleware/logging'; +import { MessageType } from "../../../models/enums"; import { getConversationStatus, updateConversationCompleteness } from "../../../services/database/ehr-conversation-repository"; +import { createCore } from "../../../services/database/ehr-core-repository"; +import { + fragmentExistsInRecord, + markFragmentAsReceivedAndCreateItsParts +} from "../../../services/database/ehr-fragment-repository"; -jest.mock('../../../services/database/ehr-conversation-repository'); +jest.mock("../../../services/database/ehr-conversation-repository"); +jest.mock("../../../services/database/ehr-core-repository"); +jest.mock("../../../services/database/ehr-fragment-repository"); jest.mock('../../../middleware/logging'); jest.mock('../../../config', () => ({ initializeConfig: jest.fn().mockReturnValue({}), })); describe('storeMessageController', () => { - const initializeConfig = () => ({ - consumerApiKeys: { TEST_USER: 'correct-key' } + initializeConfig.mockReturnValue({ + consumerApiKeys: { TEST_USER: 'correct-key' }, }); - const MessageType = { - EHR_EXTRACT: 'ehrExtract', - FRAGMENT: 'fragment', - }; - // initializeConfig.mockReturnValue({ - // consumerApiKeys: { TEST_USER: 'correct-key' }, - // }); const authorizationKeys = 'correct-key'; const conversationId = uuid(); @@ -52,7 +53,7 @@ describe('storeMessageController', () => { it('should return a 201 and health record status when message has successfully been stored in database', async () => { getConversationStatus.mockResolvedValueOnce('complete'); - const ehrExtract = { messageId, conversationId,fragmentMessageIds }; + const ehrExtract = { messageId, conversationId, fragmentMessageIds }; const res = await request(app) .post('/messages') .send(requestBody) @@ -73,7 +74,7 @@ describe('storeMessageController', () => { fragmentMessageIds: [nestedFragmentId], }; - fragmentExists.mockResolvedValueOnce(true); + fragmentExistsInRecord.mockResolvedValueOnce(true); const res = await request(app) .post('/messages') @@ -81,11 +82,11 @@ describe('storeMessageController', () => { .set('Authorization', authorizationKeys); expect(res.status).toBe(201); - expect(createEhrExtract).not.toHaveBeenCalled(); - expect(updateFragmentAndCreateItsParts).toHaveBeenCalledWith(messageId, conversationId, [ + expect(createCore).not.toHaveBeenCalled(); + expect(markFragmentAsReceivedAndCreateItsParts).toHaveBeenCalledWith(messageId, conversationId, [ nestedFragmentId, ]); - expect(updateHealthRecordCompleteness).toHaveBeenCalledWith(conversationId); + expect(updateConversationCompleteness).toHaveBeenCalledWith(conversationId); }); it('should create message in the database when a nested fragment arrives before first fragment', async () => { @@ -97,7 +98,7 @@ describe('storeMessageController', () => { fragmentMessageIds: [], }; - fragmentExists.mockResolvedValueOnce(false); + fragmentExistsInRecord.mockResolvedValueOnce(false); const res = await request(app) .post('/messages') @@ -105,10 +106,9 @@ describe('storeMessageController', () => { .set('Authorization', authorizationKeys); expect(res.status).toBe(201); - expect(fragmentExists).toHaveBeenCalledWith(nestedFragmentId); - expect(createFragmentPart).toHaveBeenCalledWith(nestedFragmentId, conversationId); - expect(updateFragmentAndCreateItsParts).not.toHaveBeenCalled(); - expect(updateHealthRecordCompleteness).toHaveBeenCalledWith(conversationId); + expect(fragmentExistsInRecord).toHaveBeenCalledWith(nestedFragmentId); + expect(markFragmentAsReceivedAndCreateItsParts).toHaveBeenCalledWith(nestedFragmentId, conversationId); + expect(updateConversationCompleteness).toHaveBeenCalledWith(conversationId); }); }); @@ -126,7 +126,7 @@ describe('storeMessageController', () => { }, }; it('should return a 503 when message cannot be stored in the database', async () => { - createEhrExtract.mockRejectedValueOnce({ error: 'db is down' }); + createCore.mockRejectedValueOnce({ error: 'db is down' }); const res = await request(app) .post('/messages') .send(requestBody) diff --git a/src/api/messages/message-location-controller.js b/src/api/messages/message-location-controller.js index 5d96e2eb..81b42912 100644 --- a/src/api/messages/message-location-controller.js +++ b/src/api/messages/message-location-controller.js @@ -3,19 +3,44 @@ import { param } from 'express-validator'; import { logError, logInfo } from '../../middleware/logging'; import { setCurrentSpanAttributes } from '../../config/tracing'; import { messageAlreadyReceived } from '../../services/database/health-record-repository'; +import { fragmentAlreadyReceived } from "../../services/database/ehr-fragment-repository"; export const messageLocationControllerValidation = [ param('conversationId').isUUID().withMessage("'conversationId' provided is not a UUID"), param('messageId').isUUID().withMessage("'messageId' provided is not a UUID"), ]; +// @deprecated +// to be deleted PRMT-4568 +// +// export const messageLocationController = async (req, res) => { +// const { conversationId, messageId } = req.params; +// setCurrentSpanAttributes({ conversationId, messageId }); +// const operation = 'putObject'; +// +// try { +// if (await messageAlreadyReceived(messageId)) { +// logInfo('DUPLICATE: Message Id: ' + messageId + ' already received in the database'); +// res.sendStatus(409); +// return; +// } +// logInfo(`Retrieving presigned url to upload the fragment with message id: ${messageId}`); +// const presignedUrl = await getSignedUrl(conversationId, messageId, operation); +// res.status(200).send(presignedUrl); +// logInfo('Presigned URL sent successfully'); +// } catch (err) { +// logError('Failed to retrieve pre-signed url', err); +// res.sendStatus(503); +// } +// }; + export const messageLocationController = async (req, res) => { const { conversationId, messageId } = req.params; setCurrentSpanAttributes({ conversationId, messageId }); const operation = 'putObject'; try { - if (await messageAlreadyReceived(messageId)) { + if (await fragmentAlreadyReceived(messageId)) { logInfo('DUPLICATE: Message Id: ' + messageId + ' already received in the database'); res.sendStatus(409); return; diff --git a/src/api/messages/store-message-controller.js b/src/api/messages/store-message-controller.js index 413dd95c..d05900ac 100644 --- a/src/api/messages/store-message-controller.js +++ b/src/api/messages/store-message-controller.js @@ -12,6 +12,15 @@ import { getHealthRecordStatus, } from '../../services/database/health-record-repository'; import { setCurrentSpanAttributes } from '../../config/tracing'; +import { createCore } from "../../services/database/ehr-core-repository"; +import { + fragmentExistsInRecord, + markFragmentAsReceivedAndCreateItsParts +} from "../../services/database/ehr-fragment-repository"; +import { + getConversationStatus, + updateConversationCompleteness +} from "../../services/database/ehr-conversation-repository"; export const storeMessageControllerValidation = [ body('data.type').equals('messages'), @@ -44,34 +53,72 @@ export const storeMessageControllerValidation = [ .withMessage("'fragmentMessageIds' should be an array"), ]; +// @deprecated +// to be deleted PRMT-4568 +// +// export const storeMessageController = async (req, res) => { +// const { id, attributes } = req.body.data; +// const { conversationId, messageType, nhsNumber, fragmentMessageIds } = attributes; +// setCurrentSpanAttributes({ conversationId, messageId: id }); +// +// try { +// if (messageType === MessageType.EHR_EXTRACT) { +// await createEhrExtract({ +// messageId: id, +// conversationId, +// nhsNumber, +// fragmentMessageIds, +// }); +// } +// if (messageType === MessageType.FRAGMENT) { +// if (await fragmentExists(id)) { +// await updateFragmentAndCreateItsParts(id, conversationId, fragmentMessageIds); +// } else { +// logWarning( +// `Fragment message ${id} did not arrive in order. Fragment parts: ${JSON.stringify( +// fragmentMessageIds +// )}` +// ); +// await createFragmentPart(id, conversationId); +// } +// } +// await updateHealthRecordCompleteness(conversationId); +// const healthRecordStatus = await getHealthRecordStatus(conversationId); +// +// logInfo('Health record status for fragments: ' + healthRecordStatus); +// res.status(201).json({ healthRecordStatus }); +// } catch (e) { +// logError('Returned 503 due to error while saving message', e); +// res.sendStatus(503); +// } +// }; export const storeMessageController = async (req, res) => { - const { id, attributes } = req.body.data; - const { conversationId, messageType, nhsNumber, fragmentMessageIds } = attributes; - setCurrentSpanAttributes({ conversationId, messageId: id }); + const { id: messageId, attributes } = req.body.data; + const { conversationId, messageType, fragmentMessageIds } = attributes; + setCurrentSpanAttributes({ conversationId, messageId }); try { if (messageType === MessageType.EHR_EXTRACT) { - await createEhrExtract({ - messageId: id, + await createCore({ + messageId, conversationId, - nhsNumber, fragmentMessageIds, }); } if (messageType === MessageType.FRAGMENT) { - if (await fragmentExists(id)) { - await updateFragmentAndCreateItsParts(id, conversationId, fragmentMessageIds); + if (await fragmentExistsInRecord(messageId)) { + await markFragmentAsReceivedAndCreateItsParts(messageId, conversationId, fragmentMessageIds); } else { logWarning( - `Fragment message ${id} did not arrive in order. Fragment parts: ${JSON.stringify( + `Fragment message ${messageId} did not arrive in order. Fragment parts: ${JSON.stringify( fragmentMessageIds )}` ); - await createFragmentPart(id, conversationId); + await markFragmentAsReceivedAndCreateItsParts(messageId, conversationId); } } - await updateHealthRecordCompleteness(conversationId); - const healthRecordStatus = await getHealthRecordStatus(conversationId); + await updateConversationCompleteness(conversationId); + const healthRecordStatus = await getConversationStatus(conversationId); logInfo('Health record status for fragments: ' + healthRecordStatus); res.status(201).json({ healthRecordStatus }); diff --git a/src/models/enums.js b/src/models/enums.js index 6c872dcf..0b126fac 100644 --- a/src/models/enums.js +++ b/src/models/enums.js @@ -24,9 +24,14 @@ export const FragmentStatus = { }; export const HealthRecordStatus = { - COMPLETE: "complete", - PENDING: "pending", - NOT_FOUND: "notFound" + COMPLETE: 'complete', + PENDING: 'pending', + NOT_FOUND: 'notFound', +}; + +export const MessageType = { + EHR_EXTRACT: 'ehrExtract', + FRAGMENT: 'fragment', }; Object.freeze(HealthRecordStatus); @@ -34,3 +39,4 @@ Object.freeze(RecordType); Object.freeze(ConversationStatus); Object.freeze(CoreStatus); Object.freeze(FragmentStatus); +Object.freeze(MessageType); diff --git a/src/models/index.js b/src/models/index.js index 1f1abb5a..64c4c10d 100644 --- a/src/models/index.js +++ b/src/models/index.js @@ -29,6 +29,8 @@ class ModelFactory { } configure() { + return; + if (this.sequelize instanceof Sequelize) { this.sequelize.close(); } From 745f4abc90361125d831140696997e28f6534826 Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Thu, 7 Mar 2024 16:58:14 +0000 Subject: [PATCH 29/67] [PRMT-4567] migrated app integration tests for POST /messages endpoint --- src/__tests__/app.integration.test.js | 88 +++++++++++++------ src/api/messages/store-message-controller.js | 6 +- src/models/enums.js | 1 - .../database/ehr-conversation-repository.js | 3 +- .../database/ehr-fragment-repository.js | 3 +- src/services/time.js | 2 + 6 files changed, 66 insertions(+), 37 deletions(-) diff --git a/src/__tests__/app.integration.test.js b/src/__tests__/app.integration.test.js index 4b7b7da9..f0c84e81 100644 --- a/src/__tests__/app.integration.test.js +++ b/src/__tests__/app.integration.test.js @@ -7,6 +7,16 @@ import { expectStructuredLogToContain, transportSpy } from '../__builders__/logg import ModelFactory from '../models'; import { MessageType, modelName as messageModelName } from '../models/message'; import { modelName as healthRecordModelName } from '../models/health-record'; +import { EhrTransferTracker } from "../services/database/dynamo-ehr-transfer-tracker"; +import { getCoreByKey } from "../services/database/ehr-core-repository"; +import { cleanupRecordsForTest, createConversationForTest } from "../utilities/integration-test-utilities"; +import { getConversationById } from "../services/database/ehr-conversation-repository"; +import { ConversationStatus } from "../models/enums"; +import { isCore } from "../models/core"; +import { getFragmentByKey } from "../services/database/ehr-fragment-repository"; +import { isFragment } from "../models/fragment"; +import { isInCompleteStatus } from "../models/conversation"; +import { TIMESTAMP_REGEX } from "../services/time"; describe('app', () => { const config = initializeConfig(); @@ -326,8 +336,7 @@ describe('app', () => { }); describe('POST /messages', () => { - const Message = ModelFactory.getByName(messageModelName); - const HealthRecord = ModelFactory.getByName(healthRecordModelName); + const db = EhrTransferTracker.getInstance(); const nhsNumber = '1234567890'; let conversationId; let messageId; @@ -360,26 +369,33 @@ describe('app', () => { beforeEach(() => { messageId = uuid(); conversationId = uuid(); + createConversationForTest(conversationId, nhsNumber, {TransferStatus: ConversationStatus.STARTED}) }); + afterEach(() => { + cleanupRecordsForTest(conversationId); + }) + afterAll(async () => { await ModelFactory.sequelize.close(); }); it('should save health record without fragments in the database and return 201', async () => { + // when const response = await request(app) .post(`/messages`) .send(createReqBodyForEhr(messageId, conversationId, nhsNumber, [])) .set('Authorization', authorizationKeys); - const message = await Message.findByPk(messageId); - const healthRecord = await HealthRecord.findByPk(conversationId); - - expect(message.conversationId).toBe(conversationId); - expect(message.type).toBe(MessageType.EHR_EXTRACT); - expect(message.parentId).toBeNull(); - expect(healthRecord.nhsNumber).toBe(nhsNumber); - expect(healthRecord.completedAt).not.toBeNull(); + // then + const message = await getCoreByKey(conversationId, messageId); + const conversation = await getConversationById(conversationId); + + expect(message.InboundConversationId).toBe(conversationId); + expect(isCore(message)).toBe(true); + expect(message.ParentId).toBeUndefined(); + expect(conversation.NhsNumber).toBe(nhsNumber); + expect(conversation.TransferStatus).toBe(ConversationStatus.COMPLETE) expect(response.status).toBe(201); expectStructuredLogToContain(transportSpy, { messageId, @@ -389,20 +405,23 @@ describe('app', () => { }); it('should save health record with fragments in the database and return 201', async () => { + // given const fragmentMessageId = uuid(); + // when const response = await request(app) .post(`/messages`) .send(createReqBodyForEhr(messageId, conversationId, nhsNumber, [fragmentMessageId])) .set('Authorization', authorizationKeys); - const fragmentMessage = await Message.findByPk(fragmentMessageId); - const healthRecord = await HealthRecord.findByPk(conversationId); + // then + const fragmentMessage = await getFragmentByKey(conversationId, fragmentMessageId); + const conversation = await getConversationById(conversationId); - expect(fragmentMessage.conversationId).toBe(conversationId); - expect(fragmentMessage.type).toBe(MessageType.FRAGMENT); - expect(fragmentMessage.parentId).toBe(messageId); - expect(healthRecord.completedAt).toBeNull(); + expect(fragmentMessage.InboundConversationId).toBe(conversationId); + expect(isFragment(fragmentMessage)).toBe(true); + expect(fragmentMessage.ParentId).toBe(messageId); + expect(conversation.TransferStatus).toBe(ConversationStatus.STARTED); expect(response.status).toBe(201); expectStructuredLogToContain(transportSpy, { messageId, @@ -412,9 +431,11 @@ describe('app', () => { }); it('should create large nested fragment messages in the database and return 201', async () => { + // given const fragmentMessageId = uuid(); const nestedFragmentMessageId = uuid(); + // when await request(app) .post(`/messages`) .send(createReqBodyForEhr(messageId, conversationId, nhsNumber, [fragmentMessageId])) @@ -427,11 +448,12 @@ describe('app', () => { ) .set('Authorization', authorizationKeys); - const nestedFragmentMessage = await Message.findByPk(nestedFragmentMessageId); - const healthRecord = await HealthRecord.findByPk(conversationId); + // then + const nestedFragmentMessage = await getFragmentByKey(conversationId, nestedFragmentMessageId); + const conversation = await getConversationById(conversationId); - expect(nestedFragmentMessage.receivedAt).toBeNull(); - expect(healthRecord.completedAt).toBeNull(); + expect(nestedFragmentMessage.ReceivedAt).toBeUndefined(); + expect(conversation.TransferStatus).toBe(ConversationStatus.STARTED); expect(fragmentResponse.status).toBe(201); expectStructuredLogToContain(transportSpy, { messageId: fragmentMessageId, @@ -441,7 +463,10 @@ describe('app', () => { }); it('should update database for fragments and return 201 when all fragments have been received', async () => { + // given const fragmentMessageId = uuid(); + + // when await request(app) .post(`/messages`) .send(createReqBodyForEhr(messageId, conversationId, nhsNumber, [fragmentMessageId])) @@ -452,11 +477,12 @@ describe('app', () => { .send(createReqBodyForFragment(fragmentMessageId, conversationId)) .set('Authorization', authorizationKeys); - const fragmentMessage = await Message.findByPk(fragmentMessageId); - const healthRecord = await HealthRecord.findByPk(conversationId); + // then + const fragmentMessage = await getFragmentByKey(conversationId, fragmentMessageId); + const conversation = await getConversationById(conversationId); - expect(fragmentMessage.receivedAt).not.toBeNull(); - expect(healthRecord.completedAt).not.toBeNull(); + expect(fragmentMessage.ReceivedAt).toEqual(expect.stringMatching(TIMESTAMP_REGEX)); + expect(conversation.TransferStatus).toBe(ConversationStatus.COMPLETE); expect(fragmentResponse.status).toBe(201); expectStructuredLogToContain(transportSpy, { messageId: fragmentMessageId, @@ -466,8 +492,11 @@ describe('app', () => { }); it('should update database when a nested fragment arrives before its parent fragment', async () => { + // given const fragmentMessageId = uuid(); const nestedFragmentId = uuid(); + + // when await request(app) .post(`/messages`) .send(createReqBodyForEhr(messageId, conversationId, nhsNumber, [fragmentMessageId])) @@ -478,12 +507,13 @@ describe('app', () => { .send(createReqBodyForFragment(nestedFragmentId, conversationId)) .set('Authorization', authorizationKeys); - const nestedFragmentMessage = await Message.findByPk(nestedFragmentId); - const healthRecord = await HealthRecord.findByPk(conversationId); + // then + const nestedFragmentMessage = await getFragmentByKey(conversationId, nestedFragmentId); + const conversation = await getConversationById(conversationId); - expect(nestedFragmentMessage.conversationId).toEqual(conversationId); - expect(nestedFragmentMessage.receivedAt).not.toBeNull(); - expect(healthRecord.completedAt).toBeNull(); + expect(nestedFragmentMessage.InboundConversationId).toEqual(conversationId); + expect(nestedFragmentMessage.ReceivedAt).toEqual(expect.stringMatching(TIMESTAMP_REGEX)); + expect(conversation.TransferStatus).toBe(ConversationStatus.STARTED); expect(response.status).toBe(201); expectStructuredLogToContain(transportSpy, { messageId: nestedFragmentId, diff --git a/src/api/messages/store-message-controller.js b/src/api/messages/store-message-controller.js index d05900ac..801973dc 100644 --- a/src/api/messages/store-message-controller.js +++ b/src/api/messages/store-message-controller.js @@ -106,16 +106,14 @@ export const storeMessageController = async (req, res) => { }); } if (messageType === MessageType.FRAGMENT) { - if (await fragmentExistsInRecord(messageId)) { - await markFragmentAsReceivedAndCreateItsParts(messageId, conversationId, fragmentMessageIds); - } else { + if (!await fragmentExistsInRecord(messageId)) { logWarning( `Fragment message ${messageId} did not arrive in order. Fragment parts: ${JSON.stringify( fragmentMessageIds )}` ); - await markFragmentAsReceivedAndCreateItsParts(messageId, conversationId); } + await markFragmentAsReceivedAndCreateItsParts(messageId, conversationId, fragmentMessageIds); } await updateConversationCompleteness(conversationId); const healthRecordStatus = await getConversationStatus(conversationId); diff --git a/src/models/enums.js b/src/models/enums.js index 0b126fac..0664cbb9 100644 --- a/src/models/enums.js +++ b/src/models/enums.js @@ -19,7 +19,6 @@ export const CoreStatus = { }; export const FragmentStatus = { - PENDING: 'INBOUND_REQUEST_SENT', COMPLETE: 'INBOUND_COMPLETE', }; diff --git a/src/services/database/ehr-conversation-repository.js b/src/services/database/ehr-conversation-repository.js index 9f2a777a..2808ef9a 100644 --- a/src/services/database/ehr-conversation-repository.js +++ b/src/services/database/ehr-conversation-repository.js @@ -39,12 +39,11 @@ export const getConversationById = async (conversationId) => { }; export const updateConversationCompleteness = async (conversationId) => { - // to replace the existing method `updateHealthRecordCompleteness` - try { const db = EhrTransferTracker.getInstance(); const allFragments = await db.queryTableByConversationId(conversationId, RecordType.FRAGMENT); + const pendingMessages = allFragments.filter((fragment) => fragment.ReceivedAt === undefined); if (pendingMessages.length !== 0) { diff --git a/src/services/database/ehr-fragment-repository.js b/src/services/database/ehr-fragment-repository.js index 06f69a57..7757c8d3 100644 --- a/src/services/database/ehr-fragment-repository.js +++ b/src/services/database/ehr-fragment-repository.js @@ -1,7 +1,7 @@ import { getUKTimestamp } from '../time'; import { EhrTransferTracker } from './dynamo-ehr-transfer-tracker'; import { buildFragmentUpdateParams } from '../../models/fragment'; -import { RecordType } from '../../models/enums'; +import { FragmentStatus, RecordType } from "../../models/enums"; import { logError } from '../../middleware/logging'; export const markFragmentAsReceivedAndCreateItsParts = async ( @@ -15,6 +15,7 @@ export const markFragmentAsReceivedAndCreateItsParts = async ( const currentFragmentParams = buildFragmentUpdateParams(conversationId, messageId, { ReceivedAt: timestamp, + TransferStatus: FragmentStatus.COMPLETE }); const childFragmentsParams = remainingPartsIds.map((fragmentPartId) => { diff --git a/src/services/time.js b/src/services/time.js index 4c82a4b4..149d45fe 100644 --- a/src/services/time.js +++ b/src/services/time.js @@ -5,7 +5,9 @@ export const getNow = () => new Date(); export const getUKTimestamp = () => { return moment().tz('Europe/London').format('YYYY-MM-DDThh:mm:ssZ'); }; +export const TIMESTAMP_REGEX = /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\+\d{2}:00/; export const getEpochTimeInSecond = (datetime) => { return moment(datetime).unix(); }; + From 3f3cf46a7c06e411c002acdd12513de8e230b586 Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Thu, 7 Mar 2024 17:25:19 +0000 Subject: [PATCH 30/67] [PRMT-4567] rename test files --- .../message-location-controller.test.js | 12 +- .../new-message-location-controller.test.js | 108 ----- .../new-store-message-controller.test.js | 448 ------------------ .../store-message-controller.test.js | 57 ++- 4 files changed, 34 insertions(+), 591 deletions(-) delete mode 100644 src/api/messages/__tests__/new-message-location-controller.test.js delete mode 100644 src/api/messages/__tests__/new-store-message-controller.test.js diff --git a/src/api/messages/__tests__/message-location-controller.test.js b/src/api/messages/__tests__/message-location-controller.test.js index 055c256b..35bebf95 100644 --- a/src/api/messages/__tests__/message-location-controller.test.js +++ b/src/api/messages/__tests__/message-location-controller.test.js @@ -4,13 +4,13 @@ import { getSignedUrl } from '../../../services/storage'; import { v4 as uuid } from 'uuid'; import { logError, logInfo } from '../../../middleware/logging'; import { initializeConfig } from '../../../config'; -import { messageAlreadyReceived } from '../../../services/database/health-record-repository'; +import { fragmentAlreadyReceived } from "../../../services/database/ehr-fragment-repository"; jest.mock('../../../services/storage'); -jest.mock('../../../services/database/health-record-repository'); +jest.mock("../../../services/database/ehr-fragment-repository"); jest.mock('../../../middleware/logging'); jest.mock('../../../config', () => ({ - initializeConfig: jest.fn().mockReturnValue({ sequelize: { dialect: 'postgres' } }), + initializeConfig: jest.fn().mockReturnValue({}), })); describe('messageLocationController', () => { @@ -43,7 +43,7 @@ describe('messageLocationController', () => { const messageId = uuid(); it('should return a 409 when ehr already exists', async () => { - messageAlreadyReceived.mockResolvedValueOnce(true); + fragmentAlreadyReceived.mockResolvedValueOnce(true); const res = await request(app) .get(`/messages/${conversationId}/${messageId}`) @@ -51,7 +51,7 @@ describe('messageLocationController', () => { expect(res.status).toBe(409); expect(getSignedUrl).not.toHaveBeenCalled(); - expect(messageAlreadyReceived).toHaveBeenCalledWith(messageId); + expect(fragmentAlreadyReceived).toHaveBeenCalledWith(messageId); }); }); @@ -62,7 +62,7 @@ describe('messageLocationController', () => { it('should return a 503 when getSignedUrl promise is rejected', async () => { const error = new Error('error'); getSignedUrl.mockRejectedValueOnce(error); - messageAlreadyReceived.mockResolvedValueOnce(false); + fragmentAlreadyReceived.mockResolvedValueOnce(false); const res = await request(app) .get(`/messages/${conversationId}/${messageId}`) diff --git a/src/api/messages/__tests__/new-message-location-controller.test.js b/src/api/messages/__tests__/new-message-location-controller.test.js deleted file mode 100644 index 35bebf95..00000000 --- a/src/api/messages/__tests__/new-message-location-controller.test.js +++ /dev/null @@ -1,108 +0,0 @@ -import request from 'supertest'; -import app from '../../../app'; -import { getSignedUrl } from '../../../services/storage'; -import { v4 as uuid } from 'uuid'; -import { logError, logInfo } from '../../../middleware/logging'; -import { initializeConfig } from '../../../config'; -import { fragmentAlreadyReceived } from "../../../services/database/ehr-fragment-repository"; - -jest.mock('../../../services/storage'); -jest.mock("../../../services/database/ehr-fragment-repository"); -jest.mock('../../../middleware/logging'); -jest.mock('../../../config', () => ({ - initializeConfig: jest.fn().mockReturnValue({}), -})); - -describe('messageLocationController', () => { - initializeConfig.mockReturnValue({ - consumerApiKeys: { TEST_USER: 'correct-key' }, - }); - - const authorizationKeys = 'correct-key'; - - describe('success', () => { - const conversationId = uuid(); - const messageId = uuid(); - - it('should return a 200 with presigned url in body', async () => { - getSignedUrl.mockResolvedValue('presigned-url'); - - const res = await request(app) - .get(`/messages/${conversationId}/${messageId}`) - .set('Authorization', authorizationKeys); - - expect(res.status).toBe(200); - expect(getSignedUrl).toHaveBeenCalledWith(conversationId, messageId, 'putObject'); - expect(res.text).toEqual('presigned-url'); - expect(logInfo).toHaveBeenCalledWith('Presigned URL sent successfully'); - }); - }); - - describe('conflict', () => { - const conversationId = uuid(); - const messageId = uuid(); - - it('should return a 409 when ehr already exists', async () => { - fragmentAlreadyReceived.mockResolvedValueOnce(true); - - const res = await request(app) - .get(`/messages/${conversationId}/${messageId}`) - .set('Authorization', authorizationKeys); - - expect(res.status).toBe(409); - expect(getSignedUrl).not.toHaveBeenCalled(); - expect(fragmentAlreadyReceived).toHaveBeenCalledWith(messageId); - }); - }); - - describe('error', () => { - const conversationId = uuid(); - const messageId = uuid(); - - it('should return a 503 when getSignedUrl promise is rejected', async () => { - const error = new Error('error'); - getSignedUrl.mockRejectedValueOnce(error); - fragmentAlreadyReceived.mockResolvedValueOnce(false); - - const res = await request(app) - .get(`/messages/${conversationId}/${messageId}`) - .set('Authorization', authorizationKeys); - - expect(getSignedUrl).toHaveBeenCalledWith(conversationId, messageId, 'putObject'); - expect(logError).toHaveBeenCalledWith('Failed to retrieve pre-signed url', error); - expect(res.status).toBe(503); - }); - }); - - describe('validation', () => { - it('should return 422 and an error message when conversationId is not a UUID', async () => { - const conversationId = 'not-a-uuid'; - const messageId = uuid(); - const errorMessage = [{ conversationId: "'conversationId' provided is not a UUID" }]; - - const res = await request(app) - .get(`/messages/${conversationId}/${messageId}`) - .set('Authorization', authorizationKeys); - - expect(res.status).toBe(422); - expect(res.body).toEqual({ - errors: errorMessage, - }); - }); - - it('should return 422 and an error message when messageId is not a UUID', async () => { - const conversationId = uuid(); - const messageId = 'not-a-uuid'; - const errorMessage = [{ messageId: "'messageId' provided is not a UUID" }]; - - const res = await request(app) - .get(`/messages/${conversationId}/${messageId}`) - .set('Authorization', authorizationKeys); - - expect(res.status).toBe(422); - expect(res.body).toEqual({ - errors: errorMessage, - }); - }); - }); -}); diff --git a/src/api/messages/__tests__/new-store-message-controller.test.js b/src/api/messages/__tests__/new-store-message-controller.test.js deleted file mode 100644 index 4892ee7b..00000000 --- a/src/api/messages/__tests__/new-store-message-controller.test.js +++ /dev/null @@ -1,448 +0,0 @@ -import { v4 as uuid } from 'uuid'; -import request from 'supertest'; -import app from '../../../app'; -import { initializeConfig } from '../../../config'; -import { logError } from '../../../middleware/logging'; -import { MessageType } from "../../../models/enums"; -import { - getConversationStatus, - updateConversationCompleteness -} from "../../../services/database/ehr-conversation-repository"; -import { createCore } from "../../../services/database/ehr-core-repository"; -import { - fragmentExistsInRecord, - markFragmentAsReceivedAndCreateItsParts -} from "../../../services/database/ehr-fragment-repository"; - -jest.mock("../../../services/database/ehr-conversation-repository"); -jest.mock("../../../services/database/ehr-core-repository"); -jest.mock("../../../services/database/ehr-fragment-repository"); -jest.mock('../../../middleware/logging'); -jest.mock('../../../config', () => ({ - initializeConfig: jest.fn().mockReturnValue({}), -})); - -describe('storeMessageController', () => { - initializeConfig.mockReturnValue({ - consumerApiKeys: { TEST_USER: 'correct-key' }, - }); - - const authorizationKeys = 'correct-key'; - const conversationId = uuid(); - const nhsNumber = '1234567890'; - const messageId = uuid(); - const fragmentMessageIds = [uuid()]; - - describe('success', () => { - let requestBody; - - beforeEach(() => { - requestBody = { - data: { - type: 'messages', - id: messageId, - attributes: { - conversationId, - messageType: MessageType.EHR_EXTRACT, - nhsNumber, - fragmentMessageIds, - }, - }, - }; - }); - - it('should return a 201 and health record status when message has successfully been stored in database', async () => { - getConversationStatus.mockResolvedValueOnce('complete'); - const ehrExtract = { messageId, conversationId, fragmentMessageIds }; - const res = await request(app) - .post('/messages') - .send(requestBody) - .set('Authorization', authorizationKeys); - - expect(res.status).toBe(201); - expect(res.body).toEqual({ healthRecordStatus: 'complete' }); - expect(createCore).toHaveBeenCalledWith(ehrExtract); - expect(updateConversationCompleteness).toHaveBeenCalledWith(conversationId); - expect(getConversationStatus).toHaveBeenCalledWith(conversationId); - }); - - it('should update receivedAt for given fragment and store its parts', async () => { - const nestedFragmentId = uuid(); - requestBody.data.attributes = { - messageType: MessageType.FRAGMENT, - conversationId, - fragmentMessageIds: [nestedFragmentId], - }; - - fragmentExistsInRecord.mockResolvedValueOnce(true); - - const res = await request(app) - .post('/messages') - .send(requestBody) - .set('Authorization', authorizationKeys); - - expect(res.status).toBe(201); - expect(createCore).not.toHaveBeenCalled(); - expect(markFragmentAsReceivedAndCreateItsParts).toHaveBeenCalledWith(messageId, conversationId, [ - nestedFragmentId, - ]); - expect(updateConversationCompleteness).toHaveBeenCalledWith(conversationId); - }); - - it('should create message in the database when a nested fragment arrives before first fragment', async () => { - const nestedFragmentId = uuid(); - requestBody.data.id = nestedFragmentId; - requestBody.data.attributes = { - messageType: MessageType.FRAGMENT, - conversationId, - fragmentMessageIds: [], - }; - - fragmentExistsInRecord.mockResolvedValueOnce(false); - - const res = await request(app) - .post('/messages') - .send(requestBody) - .set('Authorization', authorizationKeys); - - expect(res.status).toBe(201); - expect(fragmentExistsInRecord).toHaveBeenCalledWith(nestedFragmentId); - expect(markFragmentAsReceivedAndCreateItsParts).toHaveBeenCalledWith(nestedFragmentId, conversationId); - expect(updateConversationCompleteness).toHaveBeenCalledWith(conversationId); - }); - }); - - describe('failure', () => { - const requestBody = { - data: { - type: 'messages', - id: messageId, - attributes: { - conversationId, - messageType: MessageType.EHR_EXTRACT, - nhsNumber, - fragmentMessageIds, - }, - }, - }; - it('should return a 503 when message cannot be stored in the database', async () => { - createCore.mockRejectedValueOnce({ error: 'db is down' }); - const res = await request(app) - .post('/messages') - .send(requestBody) - .set('Authorization', authorizationKeys); - - expect(logError).toHaveBeenCalledWith( - 'Returned 503 due to error while saving message', - expect.anything() - ); - expect(res.status).toBe(503); - }); - }); - - describe('validation', () => { - it('should return 422 and an error message when conversationId is not a UUID', async () => { - const requestBody = { - data: { - attributes: { - conversationId: 'not-a-uuid', - }, - }, - }; - const errorMessage = { - 'data.attributes.conversationId': "'conversationId' provided is not a UUID", - }; - - const res = await request(app) - .post('/messages') - .send(requestBody) - .set('Authorization', authorizationKeys); - - expect(res.status).toBe(422); - expect(res.body.errors).toContainEqual(errorMessage); - }); - - it('should return 422 and an error message when messageId is not a UUID', async () => { - const requestBody = { - data: { - id: 'not-a-uuid', - }, - }; - const errorMessage = { 'data.id': "'id' provided is not a UUID" }; - - const res = await request(app) - .post('/messages') - .send(requestBody) - .set('Authorization', authorizationKeys); - - expect(res.status).toBe(422); - expect(res.body.errors).toContainEqual(errorMessage); - }); - - it('should return 422 and an error message when type is not messages', async () => { - const requestBody = { - data: { - type: 'not-messages', - }, - }; - const errorMessage = { 'data.type': 'Invalid value' }; - - const res = await request(app) - .post('/messages') - .send(requestBody) - .set('Authorization', authorizationKeys); - - expect(res.status).toBe(422); - expect(res.body.errors).toContainEqual(errorMessage); - }); - - it('should return 422 and an error message when nhsNumber is not numeric', async () => { - const requestBody = { - data: { - type: 'messages', - id: uuid(), - attributes: { - conversationId: uuid(), - nhsNumber: 'not-an-nhs-number', - messageType: 'ehrExtract', - }, - }, - }; - const errorMessage = { 'data.attributes.nhsNumber': "'nhsNumber' provided is not numeric" }; - - const res = await request(app) - .post('/messages') - .send(requestBody) - .set('Authorization', authorizationKeys); - - expect(res.status).toBe(422); - expect(res.body.errors).toContainEqual(errorMessage); - }); - - it('should return 422 and an error message when nhsNumber is not 10 digits long', async () => { - const requestBody = { - data: { - attributes: { - nhsNumber: '1234567890987654', - messageType: 'ehrExtract', - }, - }, - }; - const errorMessage = { - 'data.attributes.nhsNumber': "'nhsNumber' provided is not 10 characters", - }; - - const res = await request(app) - .post('/messages') - .send(requestBody) - .set('Authorization', authorizationKeys); - - expect(res.status).toBe(422); - expect(res.body.errors).toContainEqual(errorMessage); - }); - - it('should return 422 and an error message when messageType is not recognised', async () => { - const requestBody = { - data: { - attributes: { - messageType: 'not-a-message-type', - }, - }, - }; - const errorMessage = { - 'data.attributes.messageType': - "'messageType' provided is not one of the following: ehrExtract, fragment", - }; - - const res = await request(app) - .post('/messages') - .send(requestBody) - .set('Authorization', authorizationKeys); - - expect(res.status).toBe(422); - expect(res.body.errors).toContainEqual(errorMessage); - }); - - it('should return 201 when messageType is recognised', async () => { - const requestBody = { - data: { - type: 'messages', - id: uuid(), - attributes: { - conversationId: uuid(), - nhsNumber: '1234567890', - messageType: 'ehrExtract', - fragmentMessageIds: [], - }, - }, - }; - - const res = await request(app) - .post('/messages') - .send(requestBody) - .set('Authorization', authorizationKeys); - - expect(res.status).toBe(201); - }); - - it('should return 422 and an error message when nhsNumber has not been provided for messageType ehrExtract', async () => { - const requestBody = { - data: { - attributes: { - messageType: 'ehrExtract', - }, - }, - }; - - const errorMessage = { - 'data.attributes.nhsNumber': "'nhsNumber' is required for messageType ehrExtract", - }; - - const res = await request(app) - .post('/messages') - .send(requestBody) - .set('Authorization', authorizationKeys); - - expect(res.status).toBe(422); - expect(res.body.errors).toContainEqual(errorMessage); - }); - - it('should return 201 when nhsNumber has been provided for messageType ehrExtract', async () => { - const requestBody = { - data: { - type: 'messages', - id: uuid(), - attributes: { - conversationId: uuid(), - nhsNumber: '1234567890', - messageType: 'ehrExtract', - fragmentMessageIds: [], - }, - }, - }; - const res = await request(app) - .post('/messages') - .send(requestBody) - .set('Authorization', authorizationKeys); - - expect(res.status).toBe(201); - }); - - it('should return 422 and an error message when nhsNumber has been provided for messageType fragment', async () => { - const requestBody = { - data: { - attributes: { - nhsNumber: '1234567890', - messageType: 'fragment', - }, - }, - }; - - const errorMessage = { - 'data.attributes.nhsNumber': "'nhsNumber' should be empty for messageType fragment", - }; - - const res = await request(app) - .post('/messages') - .send(requestBody) - .set('Authorization', authorizationKeys); - - expect(res.status).toBe(422); - expect(res.body.errors).toContainEqual(errorMessage); - }); - - it('should return 201 when nhsNumber has not been provided for messageType fragment', async () => { - const requestBody = { - data: { - type: 'messages', - id: uuid(), - attributes: { - conversationId: uuid(), - messageType: 'fragment', - fragmentMessageIds: [], - }, - }, - }; - - const res = await request(app) - .post('/messages') - .send(requestBody) - .set('Authorization', authorizationKeys); - - expect(res.status).toBe(201); - }); - - it('should return 422 and an error message when fragmentMessageIds is not uuids', async () => { - const requestBody = { - data: { - attributes: { - fragmentMessageIds: ['not-a-uuid'], - }, - }, - }; - - const errorMessage = { - 'data.attributes.fragmentMessageIds[0]': "'fragmentMessageIds' should be UUIDs", - }; - - const res = await request(app) - .post('/messages') - .send(requestBody) - .set('Authorization', authorizationKeys); - - expect(res.status).toBe(422); - expect(res.body.errors).toContainEqual(errorMessage); - }); - - it('should return 422 and an error message when fragmentMessageIds is not an array', async () => { - const requestBody = { - data: { - attributes: { - fragmentMessageIds: 'not-an-array', - }, - }, - }; - - const errorMessage = { - 'data.attributes.fragmentMessageIds': "'fragmentMessageIds' should be an array", - }; - - const res = await request(app) - .post('/messages') - .send(requestBody) - .set('Authorization', authorizationKeys); - - expect(res.status).toBe(422); - expect(res.body.errors).toContainEqual(errorMessage); - }); - }); - - describe('authentication', () => { - const requestBody = { - data: { - type: 'messages', - id: uuid(), - attributes: { - conversationId: uuid(), - nhsNumber: '1234567890', - messageType: 'ehrExtract', - }, - }, - }; - - it('should return 401 when authentication keys are missing', async () => { - const res = await request(app).post('/messages').send(requestBody); - - expect(res.status).toBe(401); - }); - - it('should return 403 when authentication keys are incorrect', async () => { - const res = await request(app) - .post('/messages') - .send(requestBody) - .set('Authorization', 'incorrect'); - - expect(res.status).toBe(403); - }); - }); -}); diff --git a/src/api/messages/__tests__/store-message-controller.test.js b/src/api/messages/__tests__/store-message-controller.test.js index 823b7b81..4892ee7b 100644 --- a/src/api/messages/__tests__/store-message-controller.test.js +++ b/src/api/messages/__tests__/store-message-controller.test.js @@ -1,25 +1,25 @@ import { v4 as uuid } from 'uuid'; import request from 'supertest'; import app from '../../../app'; -import { - updateFragmentAndCreateItsParts, - createEhrExtract, - createFragmentPart, - fragmentExists, -} from '../../../services/database/message-repository'; -import { - updateHealthRecordCompleteness, - getHealthRecordStatus, -} from '../../../services/database/health-record-repository'; import { initializeConfig } from '../../../config'; import { logError } from '../../../middleware/logging'; -import { MessageType } from '../../../models/message'; +import { MessageType } from "../../../models/enums"; +import { + getConversationStatus, + updateConversationCompleteness +} from "../../../services/database/ehr-conversation-repository"; +import { createCore } from "../../../services/database/ehr-core-repository"; +import { + fragmentExistsInRecord, + markFragmentAsReceivedAndCreateItsParts +} from "../../../services/database/ehr-fragment-repository"; -jest.mock('../../../services/database/message-repository'); -jest.mock('../../../services/database/health-record-repository'); +jest.mock("../../../services/database/ehr-conversation-repository"); +jest.mock("../../../services/database/ehr-core-repository"); +jest.mock("../../../services/database/ehr-fragment-repository"); jest.mock('../../../middleware/logging'); jest.mock('../../../config', () => ({ - initializeConfig: jest.fn().mockReturnValue({ sequelize: { dialect: 'postgres' } }), + initializeConfig: jest.fn().mockReturnValue({}), })); describe('storeMessageController', () => { @@ -52,8 +52,8 @@ describe('storeMessageController', () => { }); it('should return a 201 and health record status when message has successfully been stored in database', async () => { - getHealthRecordStatus.mockResolvedValueOnce('complete'); - const ehrExtract = { messageId, conversationId, nhsNumber, fragmentMessageIds }; + getConversationStatus.mockResolvedValueOnce('complete'); + const ehrExtract = { messageId, conversationId, fragmentMessageIds }; const res = await request(app) .post('/messages') .send(requestBody) @@ -61,9 +61,9 @@ describe('storeMessageController', () => { expect(res.status).toBe(201); expect(res.body).toEqual({ healthRecordStatus: 'complete' }); - expect(createEhrExtract).toHaveBeenCalledWith(ehrExtract); - expect(updateHealthRecordCompleteness).toHaveBeenCalledWith(conversationId); - expect(getHealthRecordStatus).toHaveBeenCalledWith(conversationId); + expect(createCore).toHaveBeenCalledWith(ehrExtract); + expect(updateConversationCompleteness).toHaveBeenCalledWith(conversationId); + expect(getConversationStatus).toHaveBeenCalledWith(conversationId); }); it('should update receivedAt for given fragment and store its parts', async () => { @@ -74,7 +74,7 @@ describe('storeMessageController', () => { fragmentMessageIds: [nestedFragmentId], }; - fragmentExists.mockResolvedValueOnce(true); + fragmentExistsInRecord.mockResolvedValueOnce(true); const res = await request(app) .post('/messages') @@ -82,11 +82,11 @@ describe('storeMessageController', () => { .set('Authorization', authorizationKeys); expect(res.status).toBe(201); - expect(createEhrExtract).not.toHaveBeenCalled(); - expect(updateFragmentAndCreateItsParts).toHaveBeenCalledWith(messageId, conversationId, [ + expect(createCore).not.toHaveBeenCalled(); + expect(markFragmentAsReceivedAndCreateItsParts).toHaveBeenCalledWith(messageId, conversationId, [ nestedFragmentId, ]); - expect(updateHealthRecordCompleteness).toHaveBeenCalledWith(conversationId); + expect(updateConversationCompleteness).toHaveBeenCalledWith(conversationId); }); it('should create message in the database when a nested fragment arrives before first fragment', async () => { @@ -98,7 +98,7 @@ describe('storeMessageController', () => { fragmentMessageIds: [], }; - fragmentExists.mockResolvedValueOnce(false); + fragmentExistsInRecord.mockResolvedValueOnce(false); const res = await request(app) .post('/messages') @@ -106,10 +106,9 @@ describe('storeMessageController', () => { .set('Authorization', authorizationKeys); expect(res.status).toBe(201); - expect(fragmentExists).toHaveBeenCalledWith(nestedFragmentId); - expect(createFragmentPart).toHaveBeenCalledWith(nestedFragmentId, conversationId); - expect(updateFragmentAndCreateItsParts).not.toHaveBeenCalled(); - expect(updateHealthRecordCompleteness).toHaveBeenCalledWith(conversationId); + expect(fragmentExistsInRecord).toHaveBeenCalledWith(nestedFragmentId); + expect(markFragmentAsReceivedAndCreateItsParts).toHaveBeenCalledWith(nestedFragmentId, conversationId); + expect(updateConversationCompleteness).toHaveBeenCalledWith(conversationId); }); }); @@ -127,7 +126,7 @@ describe('storeMessageController', () => { }, }; it('should return a 503 when message cannot be stored in the database', async () => { - createEhrExtract.mockRejectedValueOnce({ error: 'db is down' }); + createCore.mockRejectedValueOnce({ error: 'db is down' }); const res = await request(app) .post('/messages') .send(requestBody) From 7e9850be7fcd3a0b6dd2be7f7dbb359e717a7092 Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Thu, 7 Mar 2024 17:44:03 +0000 Subject: [PATCH 31/67] [PRMT-4567] Migrate get-fragment-controller.js and integration tests for /fragments/${conversationId}/${fragmentMessageId} endpoint --- src/__tests__/app.integration.test.js | 2 ++ .../__tests__/get-fragment-controller.test.js | 14 ++++---- src/api/fragments/get-fragment-controller.js | 33 +++++++++++++++++-- .../database/ehr-fragment-repository.js | 2 +- 4 files changed, 41 insertions(+), 10 deletions(-) diff --git a/src/__tests__/app.integration.test.js b/src/__tests__/app.integration.test.js index f0c84e81..92808c06 100644 --- a/src/__tests__/app.integration.test.js +++ b/src/__tests__/app.integration.test.js @@ -60,6 +60,8 @@ describe('app', () => { const fragmentMessageId = v4(); const nhsNumber = '2345678901'; + const db = EhrTransferTracker.getInstance(); + it('should return presigned url when the fragment record exists', async () => { // setting up database const coreMessageResponse = await request(app) diff --git a/src/api/fragments/__tests__/get-fragment-controller.test.js b/src/api/fragments/__tests__/get-fragment-controller.test.js index 4f1df79c..be512454 100644 --- a/src/api/fragments/__tests__/get-fragment-controller.test.js +++ b/src/api/fragments/__tests__/get-fragment-controller.test.js @@ -4,13 +4,13 @@ import { getSignedUrl } from '../../../services/storage'; import { v4 as uuid } from 'uuid'; import { logError, logInfo } from '../../../middleware/logging'; import { initializeConfig } from '../../../config'; -import { messageAlreadyReceived } from '../../../services/database/health-record-repository'; +import { fragmentAlreadyReceived } from '../../../services/database/ehr-fragment-repository'; jest.mock('../../../services/storage'); -jest.mock('../../../services/database/health-record-repository'); +jest.mock('../../../services/database/ehr-fragment-repository'); jest.mock('../../../middleware/logging'); jest.mock('../../../config', () => ({ - initializeConfig: jest.fn().mockReturnValue({ sequelize: { dialect: 'postgres' } }), + initializeConfig: jest.fn().mockReturnValue({}), })); describe('getFragmentController', () => { @@ -28,7 +28,7 @@ describe('getFragmentController', () => { const presignedUrl = 'presigned-url'; // when - messageAlreadyReceived.mockResolvedValueOnce(true); + fragmentAlreadyReceived.mockResolvedValueOnce(true); getSignedUrl.mockResolvedValue(presignedUrl); const response = await request(app) @@ -50,7 +50,7 @@ describe('getFragmentController', () => { it('should return a 404 error when the record is not found', async () => { // when - messageAlreadyReceived.mockResolvedValueOnce(false); + fragmentAlreadyReceived.mockResolvedValueOnce(false); const response = await request(app) .get(`/fragments/${conversationId}/${messageId}`) @@ -59,7 +59,7 @@ describe('getFragmentController', () => { // then expect(response.status).toBe(404); expect(getSignedUrl).not.toHaveBeenCalled(); - expect(messageAlreadyReceived).toHaveBeenCalledWith(messageId); + expect(fragmentAlreadyReceived).toHaveBeenCalledWith(conversationId, messageId); }); it('should return a 503 when getSignedUrl promise is rejected', async () => { @@ -67,7 +67,7 @@ describe('getFragmentController', () => { const error = new Error('error'); // when - messageAlreadyReceived.mockResolvedValueOnce(true); + fragmentAlreadyReceived.mockResolvedValueOnce(true); getSignedUrl.mockRejectedValueOnce(error); const response = await request(app) diff --git a/src/api/fragments/get-fragment-controller.js b/src/api/fragments/get-fragment-controller.js index ca7241d3..d2733082 100644 --- a/src/api/fragments/get-fragment-controller.js +++ b/src/api/fragments/get-fragment-controller.js @@ -2,20 +2,49 @@ import { getSignedUrl } from '../../services/storage'; import { param } from 'express-validator'; import { logError, logInfo } from '../../middleware/logging'; import { setCurrentSpanAttributes } from '../../config/tracing'; -import { messageAlreadyReceived } from '../../services/database/health-record-repository'; +import { fragmentAlreadyReceived } from "../../services/database/ehr-fragment-repository"; +// import { messageAlreadyReceived } from '../../services/database/health-record-repository'; export const getFragmentControllerValidation = [ param('conversationId').isUUID().withMessage("'conversationId' provided is not a UUID"), param('messageId').isUUID().withMessage("'messageId' provided is not a UUID"), ]; +// export const getFragmentController = async (req, res) => { +// /** +// * @deprecated +// * To be deleted PRMT-4568 +// */ +// const { conversationId, messageId } = req.params; +// setCurrentSpanAttributes({ conversationId, messageId }); +// const operation = 'getObject'; +// +// try { +// const noFragmentRecordFound = !(await messageAlreadyReceived(messageId)); +// if (noFragmentRecordFound) { +// logInfo('NO FRAGMENT RECORD: Could not find message Id: ' + messageId + ' in the database'); +// res.sendStatus(404); +// return; +// } +// +// logInfo(`Retrieving presigned url to download the fragment with message id: ${messageId}`); +// +// const presignedUrl = await getSignedUrl(conversationId, messageId, operation); +// res.status(200).send(presignedUrl); +// logInfo('Presigned URL sent successfully'); +// } catch (err) { +// logError('Failed to retrieve pre-signed url', err); +// res.sendStatus(503); +// } +// }; + export const getFragmentController = async (req, res) => { const { conversationId, messageId } = req.params; setCurrentSpanAttributes({ conversationId, messageId }); const operation = 'getObject'; try { - const noFragmentRecordFound = !(await messageAlreadyReceived(messageId)); + const noFragmentRecordFound = !(await fragmentAlreadyReceived(conversationId, messageId)); if (noFragmentRecordFound) { logInfo('NO FRAGMENT RECORD: Could not find message Id: ' + messageId + ' in the database'); res.sendStatus(404); diff --git a/src/services/database/ehr-fragment-repository.js b/src/services/database/ehr-fragment-repository.js index 7757c8d3..5790952a 100644 --- a/src/services/database/ehr-fragment-repository.js +++ b/src/services/database/ehr-fragment-repository.js @@ -47,7 +47,7 @@ export const fragmentExistsInRecord = async (inboundConversationId, inboundMessa export const fragmentAlreadyReceived = async (conversationId, messageId) => { try { - const fragment = await getFragmentByKey(conversationId, messageId) + const fragment = await getFragmentByKey(conversationId, messageId); return fragment?.ReceivedAt !== undefined; } catch (e) { logError('Querying database for fragment message failed', e); From 931d788a1f9ca3e1daf2855c28fa3afa533edceb Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Fri, 8 Mar 2024 10:40:28 +0000 Subject: [PATCH 32/67] [PRMT-4567] migrated endpoint `GET /patients/:nhsNumber` and controller at `src/api/patients/patient-details-controller.js` --- src/__tests__/app.integration.test.js | 109 ++++++++++++------ .../patient-details-controller.test.js | 50 +++++--- .../patients/patient-details-controller.js | 70 +++++++++-- src/utilities/integration-test-utilities.js | 9 ++ 4 files changed, 173 insertions(+), 65 deletions(-) diff --git a/src/__tests__/app.integration.test.js b/src/__tests__/app.integration.test.js index 92808c06..e168f00d 100644 --- a/src/__tests__/app.integration.test.js +++ b/src/__tests__/app.integration.test.js @@ -7,16 +7,19 @@ import { expectStructuredLogToContain, transportSpy } from '../__builders__/logg import ModelFactory from '../models'; import { MessageType, modelName as messageModelName } from '../models/message'; import { modelName as healthRecordModelName } from '../models/health-record'; -import { EhrTransferTracker } from "../services/database/dynamo-ehr-transfer-tracker"; -import { getCoreByKey } from "../services/database/ehr-core-repository"; -import { cleanupRecordsForTest, createConversationForTest } from "../utilities/integration-test-utilities"; -import { getConversationById } from "../services/database/ehr-conversation-repository"; -import { ConversationStatus } from "../models/enums"; -import { isCore } from "../models/core"; -import { getFragmentByKey } from "../services/database/ehr-fragment-repository"; -import { isFragment } from "../models/fragment"; -import { isInCompleteStatus } from "../models/conversation"; -import { TIMESTAMP_REGEX } from "../services/time"; +import { EhrTransferTracker } from '../services/database/dynamo-ehr-transfer-tracker'; +import { getCoreByKey } from '../services/database/ehr-core-repository'; +import { + cleanupRecordsForTest, cleanupRecordsForTestByNhsNumber, + createConversationForTest +} from "../utilities/integration-test-utilities"; +import { getConversationById } from '../services/database/ehr-conversation-repository'; +import { ConversationStatus } from '../models/enums'; +import { isCore } from '../models/core'; +import { getFragmentByKey } from '../services/database/ehr-fragment-repository'; +import { isFragment } from '../models/fragment'; +import { isInCompleteStatus } from '../models/conversation'; +import { TIMESTAMP_REGEX } from '../services/time'; describe('app', () => { const config = initializeConfig(); @@ -211,21 +214,33 @@ describe('app', () => { }); describe('GET /patients/:nhsNumber', () => { + // tear down to avoid interfering other tests + afterAll(async () => { + await cleanupRecordsForTestByNhsNumber('1234567890'); + await cleanupRecordsForTestByNhsNumber('1234567891'); + }); + it('should return 200 and patient health record link', async () => { - const conversationIdFromEhrIn = uuid(); - const healthRecordExtractId = uuid(); + // =============== given ==================== + const nhsNumber = '1234567890'; + const inboundConversationId = uuid(); + const coreMessageId = uuid(); const fragmentId = uuid(); const nestedFragmentId = uuid(); - const nhsNumber = '1234567890'; + + // mimic the record at Conversation Layer, which should in actual case should be already created by ehr-transfer-service + await createConversationForTest(inboundConversationId, nhsNumber, { + TransferStatus: ConversationStatus.STARTED, + }); const messageResponse = await request(app) .post(`/messages`) .send({ data: { - id: healthRecordExtractId, + id: coreMessageId, type: 'messages', attributes: { - conversationId: conversationIdFromEhrIn, + conversationId: inboundConversationId, messageType: MessageType.EHR_EXTRACT, nhsNumber, fragmentMessageIds: [fragmentId], @@ -242,7 +257,7 @@ describe('app', () => { id: fragmentId, type: 'messages', attributes: { - conversationId: conversationIdFromEhrIn, + conversationId: inboundConversationId, messageType: MessageType.FRAGMENT, fragmentMessageIds: [nestedFragmentId], }, @@ -259,7 +274,7 @@ describe('app', () => { id: nestedFragmentId, type: 'messages', attributes: { - conversationId: conversationIdFromEhrIn, + conversationId: inboundConversationId, messageType: MessageType.FRAGMENT, fragmentMessageIds: [], }, @@ -268,55 +283,69 @@ describe('app', () => { .set('Authorization', authorizationKeys); expect(nestedFragmentResponse.status).toEqual(201); - const conversationId = uuid(); + const outboundConversationId = uuid(); + + // =============== when ==================== const patientRes = await request(app) .get(`/patients/${nhsNumber}`) .set('Authorization', authorizationKeys) - .set('conversationId', conversationId); + .set('conversationId', outboundConversationId); + // =============== then ==================== expect(patientRes.status).toEqual(200); expect(patientRes.body.coreMessageUrl).toContain( - `${config.localstackUrl}/${config.awsS3BucketName}/${conversationIdFromEhrIn}/${healthRecordExtractId}` + `${config.localstackUrl}/${config.awsS3BucketName}/${inboundConversationId}/${coreMessageId}` ); - expect(patientRes.body.fragmentMessageIds[0]).toEqual(fragmentId); - expect(patientRes.body.fragmentMessageIds[1]).toEqual(nestedFragmentId); - expect(patientRes.body.conversationIdFromEhrIn).toEqual(conversationIdFromEhrIn); + expect(patientRes.body.fragmentMessageIds).toHaveLength(2); + expect(patientRes.body.fragmentMessageIds).toEqual( + expect.arrayContaining([fragmentId, nestedFragmentId]) + ); + expect(patientRes.body.conversationIdFromEhrIn).toEqual(inboundConversationId); expectStructuredLogToContain(transportSpy, { - conversationId: conversationId, + conversationId: outboundConversationId, traceId: expect.anything(), }); }); it('should have conversation Id in the logging context', async () => { - const conversationId = uuid(); + // given + const outboundConversationId = uuid(); const nhsNumber = '1234567890'; - const patientResponse = await request(app) + + // when + await request(app) .get(`/patients/${nhsNumber}`) .set('Authorization', authorizationKeys) - .set('conversationId', conversationId); - - expect(patientResponse.status).toEqual(200); + .set('conversationId', outboundConversationId); + // then expectStructuredLogToContain(transportSpy, { - conversationId: conversationId, + conversationId: outboundConversationId, traceId: expect.anything(), }); }); it('should return a 404 if no complete health record is found', async () => { - const conversationId = uuid(); - const healthRecordExtractId = uuid(); + // given + const inboundConversationId = uuid(); + const outboundConversationId = uuid(); + const coreMessageId = uuid(); const fragmentId = uuid(); const nhsNumber = '1234567891'; + // mimic the record at Conversation Layer, which should in actual case should be already created by ehr-transfer-service + await createConversationForTest(inboundConversationId, nhsNumber, { + TransferStatus: ConversationStatus.STARTED, + }); + const messageResponse = await request(app) .post(`/messages`) .send({ data: { - id: healthRecordExtractId, + id: coreMessageId, type: 'messages', attributes: { - conversationId, + conversationId: inboundConversationId, messageType: MessageType.EHR_EXTRACT, nhsNumber, fragmentMessageIds: [fragmentId], @@ -327,10 +356,12 @@ describe('app', () => { expect(messageResponse.status).toEqual(201); + // when const response = await request(app) .get(`/patients/${nhsNumber}`) - .set({ Authorization: authorizationKeys, conversationId: conversationId }); + .set({ Authorization: authorizationKeys, conversationId: outboundConversationId }); + // then expect(response.status).toEqual(404); expectStructuredLogToContain(transportSpy, { traceId: expect.anything() }); expectStructuredLogToContain(transportSpy, { level: 'WARN' }); @@ -371,12 +402,14 @@ describe('app', () => { beforeEach(() => { messageId = uuid(); conversationId = uuid(); - createConversationForTest(conversationId, nhsNumber, {TransferStatus: ConversationStatus.STARTED}) + createConversationForTest(conversationId, nhsNumber, { + TransferStatus: ConversationStatus.STARTED, + }); }); afterEach(() => { cleanupRecordsForTest(conversationId); - }) + }); afterAll(async () => { await ModelFactory.sequelize.close(); @@ -397,7 +430,7 @@ describe('app', () => { expect(isCore(message)).toBe(true); expect(message.ParentId).toBeUndefined(); expect(conversation.NhsNumber).toBe(nhsNumber); - expect(conversation.TransferStatus).toBe(ConversationStatus.COMPLETE) + expect(conversation.TransferStatus).toBe(ConversationStatus.COMPLETE); expect(response.status).toBe(201); expectStructuredLogToContain(transportSpy, { messageId, diff --git a/src/api/patients/__tests__/patient-details-controller.test.js b/src/api/patients/__tests__/patient-details-controller.test.js index b760338b..51f243e3 100644 --- a/src/api/patients/__tests__/patient-details-controller.test.js +++ b/src/api/patients/__tests__/patient-details-controller.test.js @@ -2,19 +2,20 @@ import request from 'supertest'; import { when } from 'jest-when'; import { v4 as uuid } from 'uuid'; import app from '../../../app'; -import { - getCurrentHealthRecordIdForPatient, - getHealthRecordMessageIds, -} from '../../../services/database/health-record-repository'; import { initializeConfig } from '../../../config'; import { logError, logInfo, logWarning } from '../../../middleware/logging'; import getSignedUrl from '../../../services/storage/get-signed-url'; +import { + getCurrentConversationIdForPatient, + getMessageIdsForConversation +} from "../../../services/database/ehr-conversation-repository"; +import { HealthRecordNotFoundError } from "../../../errors/errors"; -jest.mock('../../../services/database/health-record-repository'); +jest.mock('../../../services/database/ehr-conversation-repository'); jest.mock('../../../middleware/logging'); jest.mock('../../../services/storage/get-signed-url'); jest.mock('../../../config', () => ({ - initializeConfig: jest.fn().mockReturnValue({ sequelize: { dialect: 'postgres' } }), + initializeConfig: jest.fn().mockReturnValue({}), })); describe('patientDetailsController', () => { @@ -26,24 +27,27 @@ describe('patientDetailsController', () => { describe('success', () => { it('should return 200 and correct link to health record extract given a small record', async () => { + // given const nhsNumber = '1234567890'; const conversationId = uuid(); const messageId = uuid(); const presignedUrl = 'test-url'; - getCurrentHealthRecordIdForPatient.mockResolvedValue(conversationId); - getHealthRecordMessageIds.mockResolvedValue({ + getCurrentConversationIdForPatient.mockResolvedValue(conversationId); + getMessageIdsForConversation.mockResolvedValue({ coreMessageId: messageId, fragmentMessageIds: [], }); getSignedUrl.mockResolvedValue(presignedUrl); + // when const res = await request(app) .get(`/patients/${nhsNumber}`) .set({ Authorization: authorizationKeys, conversationId: conversationId }); + // then expect(res.status).toBe(200); - expect(getCurrentHealthRecordIdForPatient).toHaveBeenCalledWith(nhsNumber); + expect(getCurrentConversationIdForPatient).toHaveBeenCalledWith(nhsNumber); expect(getSignedUrl).toHaveBeenCalledWith(conversationId, messageId, 'getObject'); expect(res.body.coreMessageUrl).toEqual(presignedUrl); expect(res.body.fragmentMessageIds).toEqual([]); @@ -51,14 +55,15 @@ describe('patientDetailsController', () => { }); it('should return 200 and correct link to health record extract and fragment message IDs', async () => { + // given const nhsNumber = '1234567890'; const conversationId = uuid(); const healthRecordExtractId = uuid(); const fragmentMessageId = uuid(); const extractPresignedUrl = 'extract-url'; - getCurrentHealthRecordIdForPatient.mockResolvedValue(conversationId); - getHealthRecordMessageIds.mockResolvedValue({ + getCurrentConversationIdForPatient.mockResolvedValue(conversationId); + getMessageIdsForConversation.mockResolvedValue({ coreMessageId: healthRecordExtractId, fragmentMessageIds: [fragmentMessageId], }); @@ -66,12 +71,14 @@ describe('patientDetailsController', () => { .calledWith(conversationId, healthRecordExtractId, 'getObject') .mockResolvedValue(extractPresignedUrl); + // when const res = await request(app) .get(`/patients/${nhsNumber}`) .set({ Authorization: authorizationKeys, conversationId: conversationId }); + // then expect(res.status).toBe(200); - expect(getCurrentHealthRecordIdForPatient).toHaveBeenCalledWith(nhsNumber); + expect(getCurrentConversationIdForPatient).toHaveBeenCalledWith(nhsNumber); expect(getSignedUrl).toHaveBeenCalledWith(conversationId, healthRecordExtractId, 'getObject'); expect(getSignedUrl).not.toHaveBeenCalledWith(conversationId, fragmentMessageId, 'getObject'); expect(res.body.coreMessageUrl).toEqual(extractPresignedUrl); @@ -80,14 +87,15 @@ describe('patientDetailsController', () => { }); it('should return 200 but log a warning when conversation id is not passed as header', async () => { + // given const nhsNumber = '1234567890'; const conversationId = uuid(); const healthRecordExtractId = uuid(); const fragmentMessageId = uuid(); const extractPresignedUrl = 'extract-url'; - getCurrentHealthRecordIdForPatient.mockResolvedValue(conversationId); - getHealthRecordMessageIds.mockResolvedValue({ + getCurrentConversationIdForPatient.mockResolvedValue(conversationId); + getMessageIdsForConversation.mockResolvedValue({ coreMessageId: healthRecordExtractId, fragmentMessageIds: [fragmentMessageId], }); @@ -95,10 +103,12 @@ describe('patientDetailsController', () => { .calledWith(conversationId, healthRecordExtractId, 'getObject') .mockResolvedValue(extractPresignedUrl); + // when const res = await request(app) .get(`/patients/${nhsNumber}`) .set({ Authorization: authorizationKeys }); + // then expect(logWarning).toHaveBeenCalledWith('conversationId not passed as header'); expect(res.status).toEqual(200); }); @@ -109,21 +119,29 @@ describe('patientDetailsController', () => { const conversationId = 'fake-conversationId'; it('should return a 404 when no complete health record is found', async () => { - getCurrentHealthRecordIdForPatient.mockReturnValue(undefined); + // given + getCurrentConversationIdForPatient.mockRejectedValue(new HealthRecordNotFoundError()); + + // when const res = await request(app) .get(`/patients/${nhsNumber}`) .set({ Authorization: authorizationKeys, conversationId: conversationId }); + // then expect(res.status).toEqual(404); expect(logInfo).toHaveBeenCalledWith('Did not find a complete patient health record'); }); it('should return a 503 when cannot get patient health record from database', async () => { - getCurrentHealthRecordIdForPatient.mockRejectedValue({ bob: 'cheese' }); + // given + getCurrentConversationIdForPatient.mockRejectedValue({ bob: 'cheese' }); + + // when const res = await request(app) .get(`/patients/${nhsNumber}`) .set({ Authorization: authorizationKeys, conversationId: conversationId }); + // then expect(res.status).toEqual(503); expect(logError).toHaveBeenCalledWith('Could not retrieve patient health record', { bob: 'cheese', diff --git a/src/api/patients/patient-details-controller.js b/src/api/patients/patient-details-controller.js index 1ac57f53..121c1891 100644 --- a/src/api/patients/patient-details-controller.js +++ b/src/api/patients/patient-details-controller.js @@ -1,11 +1,16 @@ import { param } from 'express-validator'; -import { - getCurrentHealthRecordIdForPatient, - getHealthRecordMessageIds, -} from '../../services/database/health-record-repository'; +// import { +// getCurrentHealthRecordIdForPatient, +// getHealthRecordMessageIds, +// } from '../../services/database/health-record-repository'; import { logError, logInfo, logWarning } from '../../middleware/logging'; import getSignedUrl from '../../services/storage/get-signed-url'; import { setCurrentSpanAttributes } from '../../config/tracing'; +import { + getCurrentConversationIdForPatient, + getMessageIdsForConversation, +} from '../../services/database/ehr-conversation-repository'; +import { HealthRecordNotFoundError } from '../../errors/errors'; export const patientDetailsValidation = [ param('nhsNumber') @@ -15,20 +20,54 @@ export const patientDetailsValidation = [ .withMessage("'nhsNumber' provided is not 10 characters"), ]; +// @deprecated +// to be deleted PRMT-4568 +// +// export const patientDetailsController = async (req, res) => { +// const { nhsNumber } = req.params; +// addConversationIdToLogContext(req.get('conversationId')); +// +// try { +// const currentHealthRecordConversationId = await getCurrentHealthRecordIdForPatient(nhsNumber); +// if (!currentHealthRecordConversationId) { +// logInfo('Did not find a complete patient health record'); +// res.sendStatus(404); +// return; +// } +// +// logInfo('Getting fragment message ids'); +// const { coreMessageId, fragmentMessageIds } = await getHealthRecordMessageIds( +// currentHealthRecordConversationId +// ); +// +// const getOperation = 'getObject'; +// const coreMessageUrl = await getSignedUrl( +// currentHealthRecordConversationId, +// coreMessageId, +// getOperation +// ); +// +// const responseBody = { +// coreMessageUrl, +// fragmentMessageIds, +// conversationIdFromEhrIn: currentHealthRecordConversationId, +// }; +// +// res.status(200).json(responseBody); +// } catch (err) { +// logError('Could not retrieve patient health record', err); +// res.sendStatus(503); +// } +// }; export const patientDetailsController = async (req, res) => { const { nhsNumber } = req.params; addConversationIdToLogContext(req.get('conversationId')); try { - const currentHealthRecordConversationId = await getCurrentHealthRecordIdForPatient(nhsNumber); - if (!currentHealthRecordConversationId) { - logInfo('Did not find a complete patient health record'); - res.sendStatus(404); - return; - } + const currentHealthRecordConversationId = await getCurrentConversationIdForPatient(nhsNumber); logInfo('Getting fragment message ids'); - const { coreMessageId, fragmentMessageIds } = await getHealthRecordMessageIds( + const { coreMessageId, fragmentMessageIds } = await getMessageIdsForConversation( currentHealthRecordConversationId ); @@ -42,11 +81,20 @@ export const patientDetailsController = async (req, res) => { const responseBody = { coreMessageUrl, fragmentMessageIds, + // TODO: remove this duplicated `conversationIdFromEhrIn` field, + // after updating ehr-out to use the field name "inboundConversationId" conversationIdFromEhrIn: currentHealthRecordConversationId, + inboundConversationId: currentHealthRecordConversationId }; res.status(200).json(responseBody); } catch (err) { + if (err instanceof HealthRecordNotFoundError) { + logInfo('Did not find a complete patient health record'); + res.sendStatus(404); + return; + } + logError('Could not retrieve patient health record', err); res.sendStatus(503); } diff --git a/src/utilities/integration-test-utilities.js b/src/utilities/integration-test-utilities.js index b76589c8..be8c7f95 100644 --- a/src/utilities/integration-test-utilities.js +++ b/src/utilities/integration-test-utilities.js @@ -62,3 +62,12 @@ export const cleanupRecordsForTest = async (conversationId) => { await db.client.send(deleteCommand); }; + + +export const cleanupRecordsForTestByNhsNumber = async (nhsNumber) => { + // This method is only meant for testing purpose + const db = EhrTransferTracker.getInstance() + const allConversations = await db.queryTableByNhsNumber(nhsNumber); + const removeAllRecords = allConversations.map(item => cleanupRecordsForTest(item.InboundConversationId)); + return Promise.all(removeAllRecords); +} From e331497d3c91e0a6a622150641d1852f23189517 Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Fri, 8 Mar 2024 10:48:42 +0000 Subject: [PATCH 33/67] [PRMT-4567] Migrate controller `src/api/patients/health-record-controller.js` and endpoint `GET /patients/:nhsNumber/health-records/:conversationId` --- src/__tests__/app.integration.test.js | 10 ++++++- .../health-record-controller.test.js | 27 +++++++++---------- src/api/patients/health-record-controller.js | 7 ++--- 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/src/__tests__/app.integration.test.js b/src/__tests__/app.integration.test.js index e168f00d..272c2e7d 100644 --- a/src/__tests__/app.integration.test.js +++ b/src/__tests__/app.integration.test.js @@ -57,7 +57,6 @@ describe('app', () => { }); describe('GET /fragments/:conversationId/:messageId', () => { - // given const conversationId = v4(); const coreMessageId = v4(); const fragmentMessageId = v4(); @@ -137,6 +136,7 @@ describe('app', () => { describe('GET /patients/:nhsNumber/health-records/:conversationId', () => { it('should return 200', async () => { + // given const conversationId = uuid(); const messageId = uuid(); const nhsNumber = '1234567890'; @@ -159,15 +159,18 @@ describe('app', () => { expect(messageResponse.status).toEqual(201); + // when const recordResponse = await request(app) .get(`/patients/${nhsNumber}/health-records/${conversationId}`) .set('Authorization', authorizationKeys); + // then expect(recordResponse.status).toEqual(200); expectStructuredLogToContain(transportSpy, { conversationId, traceId: expect.anything() }); }); it('should return 404 when health record is not complete', async () => { + // given const conversationId = uuid(); const messageId = uuid(); const fragmentId = uuid(); @@ -191,22 +194,27 @@ describe('app', () => { expect(messageResponse.status).toEqual(201); + // when const recordResponse = await request(app) .get(`/patients/${nhsNumber}/health-records/${conversationId}`) .set('Authorization', authorizationKeys); + // then expect(recordResponse.status).toEqual(404); expectStructuredLogToContain(transportSpy, { conversationId, traceId: expect.anything() }); }); it('should return 404 when health record is not found', async () => { + // given const conversationId = uuid(); const nhsNumber = '1234567890'; + // when const recordResponse = await request(app) .get(`/patients/${nhsNumber}/health-records/${conversationId}`) .set('Authorization', authorizationKeys); + // then expect(recordResponse.status).toEqual(404); expectStructuredLogToContain(transportSpy, { conversationId, traceId: expect.anything() }); expectStructuredLogToContain(transportSpy, { level: 'WARN' }); diff --git a/src/api/patients/__tests__/health-record-controller.test.js b/src/api/patients/__tests__/health-record-controller.test.js index 183df590..5f5c9d93 100644 --- a/src/api/patients/__tests__/health-record-controller.test.js +++ b/src/api/patients/__tests__/health-record-controller.test.js @@ -1,16 +1,13 @@ import request from 'supertest'; import { v4 as uuid } from 'uuid'; import app from '../../../app'; -import { - getHealthRecordStatus, - -} from '../../../services/database/health-record-repository'; +import { getConversationStatus } from '../../../services/database/ehr-conversation-repository'; import { initializeConfig } from '../../../config'; -import { HealthRecordStatus } from "../../../models/enums"; +import { HealthRecordStatus } from '../../../models/enums'; -jest.mock('../../../services/database/health-record-repository'); +jest.mock('../../../services/database/ehr-conversation-repository'); jest.mock('../../../config', () => ({ - initializeConfig: jest.fn().mockReturnValue({ sequelize: { dialect: 'postgres' } }), + initializeConfig: jest.fn().mockReturnValue({}), })); describe('healthRecordController', () => { @@ -24,14 +21,14 @@ describe('healthRecordController', () => { it('should return 200 when a health record is complete', async () => { const nhsNumber = '1234567890'; const conversationId = uuid(); - getHealthRecordStatus.mockResolvedValueOnce(HealthRecordStatus.COMPLETE); + getConversationStatus.mockResolvedValueOnce(HealthRecordStatus.COMPLETE); const res = await request(app) .get(`/patients/${nhsNumber}/health-records/${conversationId}`) .set('Authorization', authorizationKeys); expect(res.status).toEqual(200); - expect(getHealthRecordStatus).toHaveBeenCalledWith(conversationId); + expect(getConversationStatus).toHaveBeenCalledWith(conversationId); }); }); @@ -39,40 +36,40 @@ describe('healthRecordController', () => { it('should return 404 when a health record is not complete', async () => { const nhsNumber = '1234567890'; const conversationId = uuid(); - getHealthRecordStatus.mockResolvedValueOnce(HealthRecordStatus.PENDING); + getConversationStatus.mockResolvedValueOnce(HealthRecordStatus.PENDING); const res = await request(app) .get(`/patients/${nhsNumber}/health-records/${conversationId}`) .set('Authorization', authorizationKeys); expect(res.status).toEqual(404); - expect(getHealthRecordStatus).toHaveBeenCalledWith(conversationId); + expect(getConversationStatus).toHaveBeenCalledWith(conversationId); }); it('should return 404 when a health record is not found', async () => { const nhsNumber = '1234567890'; const conversationId = uuid(); - getHealthRecordStatus.mockResolvedValueOnce(HealthRecordStatus.NOT_FOUND); + getConversationStatus.mockResolvedValueOnce(HealthRecordStatus.NOT_FOUND); const res = await request(app) .get(`/patients/${nhsNumber}/health-records/${conversationId}`) .set('Authorization', authorizationKeys); expect(res.status).toEqual(404); - expect(getHealthRecordStatus).toHaveBeenCalledWith(conversationId); + expect(getConversationStatus).toHaveBeenCalledWith(conversationId); }); it('should return 503 when there is an error retrieving the health record', async () => { const nhsNumber = '1234567890'; const conversationId = uuid(); - getHealthRecordStatus.mockRejectedValue(); + getConversationStatus.mockRejectedValue(); const res = await request(app) .get(`/patients/${nhsNumber}/health-records/${conversationId}`) .set('Authorization', authorizationKeys); expect(res.status).toEqual(503); - expect(getHealthRecordStatus).toHaveBeenCalledWith(conversationId); + expect(getConversationStatus).toHaveBeenCalledWith(conversationId); }); }); diff --git a/src/api/patients/health-record-controller.js b/src/api/patients/health-record-controller.js index 51a792f7..ecb3939f 100644 --- a/src/api/patients/health-record-controller.js +++ b/src/api/patients/health-record-controller.js @@ -1,10 +1,7 @@ import { param } from 'express-validator'; -import { - getHealthRecordStatus, - -} from '../../services/database/health-record-repository'; import { setCurrentSpanAttributes } from '../../config/tracing'; import { HealthRecordStatus } from "../../models/enums"; +import { getConversationStatus } from "../../services/database/ehr-conversation-repository"; export const healthRecordControllerValidation = [ param('conversationId').isUUID().withMessage("'conversationId' provided is not a UUID"), @@ -19,7 +16,7 @@ export const healthRecordController = async (req, res) => { try { setCurrentSpanAttributes({ conversationId: req.params.conversationId }); - const status = await getHealthRecordStatus(req.params.conversationId); + const status = await getConversationStatus(req.params.conversationId); switch (status) { case HealthRecordStatus.COMPLETE: res.sendStatus(200); From f434d6d5b271e5073d0b9cb067783124fa2d79a6 Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Fri, 8 Mar 2024 11:10:23 +0000 Subject: [PATCH 34/67] [PRMT-4567] minor clean up --- src/models/index.js | 2 -- .../database/dynamo-ehr-transfer-tracker.js | 17 ++++++++++++----- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/models/index.js b/src/models/index.js index 64c4c10d..1f1abb5a 100644 --- a/src/models/index.js +++ b/src/models/index.js @@ -29,8 +29,6 @@ class ModelFactory { } configure() { - return; - if (this.sequelize instanceof Sequelize) { this.sequelize.close(); } diff --git a/src/services/database/dynamo-ehr-transfer-tracker.js b/src/services/database/dynamo-ehr-transfer-tracker.js index fdbebb82..b64366f2 100644 --- a/src/services/database/dynamo-ehr-transfer-tracker.js +++ b/src/services/database/dynamo-ehr-transfer-tracker.js @@ -1,17 +1,20 @@ import { TransactWriteCommand, QueryCommand, - UpdateCommand, GetCommand, } from '@aws-sdk/lib-dynamodb'; -import { getUKTimestamp } from '../time'; import { logError, logInfo } from '../../middleware/logging'; -import { RecordType, ConversationStatus } from '../../models/enums'; +import { RecordType } from '../../models/enums'; import { getDynamodbClient } from './dynamodb-client'; -import { buildFragmentUpdateParams } from '../../models/fragment'; export class EhrTransferTracker { + /** + * An abstract layer for accessing the EhrTransferTracker table on dynamodb. + * + * Due to singleton nature, this class is NOT supposed to be instantiated by `new EhrTransferTracker()`. + * Please call the class method `getInstance()` instead. + */ constructor() { if (EhrTransferTracker._instance) { throw new Error("Singleton classes can't be instantiated more than once."); @@ -24,7 +27,7 @@ export class EhrTransferTracker { const isInDojo = process.env.DOJO_VERSION !== undefined; if (isInLocal && !isInDojo) { - // for running individual test with IDE + // for running integration test within IDE this.tableName = 'local-test-db'; } @@ -32,6 +35,10 @@ export class EhrTransferTracker { } static getInstance() { + /** + * Return the existing instance of this class. Create a new one if no instances was created before. + * This is the supposed way to use this class. + */ if (this._instance) { return this._instance; } From b0ed97226f3447546de782ee627d1984ded95ef7 Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Fri, 8 Mar 2024 11:10:47 +0000 Subject: [PATCH 35/67] [PRMT-4567] migrated controller delete-ehr-controller.js --- .../patients/__tests__/delete-ehr-controller.test.js | 12 ++++++------ src/api/patients/delete-ehr-controller.js | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/api/patients/__tests__/delete-ehr-controller.test.js b/src/api/patients/__tests__/delete-ehr-controller.test.js index 44aa01d7..fd925c9e 100644 --- a/src/api/patients/__tests__/delete-ehr-controller.test.js +++ b/src/api/patients/__tests__/delete-ehr-controller.test.js @@ -1,11 +1,11 @@ import request from 'supertest'; import app from '../../../app'; -import { markHealthRecordAsDeletedForPatient } from '../../../services/database/health-record-repository'; +import { markRecordAsSoftDeleteForPatient } from "../../../services/database/ehr-conversation-repository"; import { initializeConfig } from '../../../config'; import { logError, logWarning } from '../../../middleware/logging'; import { v4 as uuid } from 'uuid'; -jest.mock('../../../services/database/health-record-repository'); +jest.mock('../../../services/database/ehr-conversation-repository'); jest.mock('../../../middleware/logging'); jest.mock('../../../services/storage/get-signed-url'); jest.mock('../../../config', () => ({ @@ -23,14 +23,14 @@ describe('deleteEhrController', () => { describe('success', () => { it('should return 200 when controller invoked correctly', async () => { const conversationIds = [uuid(), uuid(), uuid()]; - markHealthRecordAsDeletedForPatient.mockResolvedValue(conversationIds); + markRecordAsSoftDeleteForPatient.mockResolvedValue(conversationIds); const res = await request(app) .delete(`/patients/${nhsNumber}`) .set('Authorization', authorizationKeys); expect(res.status).toBe(200); - expect(markHealthRecordAsDeletedForPatient).toHaveBeenCalledWith(nhsNumber); + expect(markRecordAsSoftDeleteForPatient).toHaveBeenCalledWith(nhsNumber); expect(res.body.data.id).toEqual(nhsNumber); expect(res.body.data.type).toEqual('patients'); expect(res.body.data.conversationIds).toEqual(conversationIds); @@ -39,7 +39,7 @@ describe('deleteEhrController', () => { describe('failure', () => { it('should return a 503 when an unexpected server error occurs', async () => { - markHealthRecordAsDeletedForPatient.mockRejectedValue({}); + markRecordAsSoftDeleteForPatient.mockRejectedValue({}); const res = await request(app) .delete(`/patients/${nhsNumber}`) .set('Authorization', authorizationKeys); @@ -49,7 +49,7 @@ describe('deleteEhrController', () => { }); it('should return a 404 when record is not found', async () => { - markHealthRecordAsDeletedForPatient.mockResolvedValue(undefined); + markRecordAsSoftDeleteForPatient.mockResolvedValue(undefined); const res = await request(app) .delete(`/patients/${nhsNumber}`) .set('Authorization', authorizationKeys); diff --git a/src/api/patients/delete-ehr-controller.js b/src/api/patients/delete-ehr-controller.js index 24a30859..98eaab1c 100644 --- a/src/api/patients/delete-ehr-controller.js +++ b/src/api/patients/delete-ehr-controller.js @@ -1,6 +1,6 @@ import { param } from 'express-validator'; import { logError, logInfo, logWarning } from '../../middleware/logging'; -import { markHealthRecordAsDeletedForPatient } from '../../services/database/health-record-repository'; +import { markRecordAsSoftDeleteForPatient } from "../../services/database/ehr-conversation-repository"; export const deleteEhrValidation = [ param('nhsNumber') .isNumeric() @@ -13,7 +13,7 @@ export const deleteEhrController = async (req, res) => { const { nhsNumber } = req.params; try { - const conversationIds = await markHealthRecordAsDeletedForPatient(nhsNumber); + const conversationIds = await markRecordAsSoftDeleteForPatient(nhsNumber); if (!conversationIds || conversationIds.length === 0) { logWarning('Could not find EHR record'); res.sendStatus(404); From 01d2c7eb017244b76c071cbff4937334c7ba9b6d Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Fri, 8 Mar 2024 11:58:42 +0000 Subject: [PATCH 36/67] [PRMT-4567] Add integration test for endpoint DELETE /patients/:nhsNumber --- src/__tests__/app.integration.test.js | 182 +++++++++++++++--- .../database/dynamo-ehr-transfer-tracker.js | 6 +- 2 files changed, 156 insertions(+), 32 deletions(-) diff --git a/src/__tests__/app.integration.test.js b/src/__tests__/app.integration.test.js index 272c2e7d..bb713668 100644 --- a/src/__tests__/app.integration.test.js +++ b/src/__tests__/app.integration.test.js @@ -10,11 +10,12 @@ import { modelName as healthRecordModelName } from '../models/health-record'; import { EhrTransferTracker } from '../services/database/dynamo-ehr-transfer-tracker'; import { getCoreByKey } from '../services/database/ehr-core-repository'; import { - cleanupRecordsForTest, cleanupRecordsForTestByNhsNumber, - createConversationForTest -} from "../utilities/integration-test-utilities"; + cleanupRecordsForTest, + cleanupRecordsForTestByNhsNumber, + createConversationForTest, +} from '../utilities/integration-test-utilities'; import { getConversationById } from '../services/database/ehr-conversation-repository'; -import { ConversationStatus } from '../models/enums'; +import { ConversationStatus, RecordType } from '../models/enums'; import { isCore } from '../models/core'; import { getFragmentByKey } from '../services/database/ehr-fragment-repository'; import { isFragment } from '../models/fragment'; @@ -25,6 +26,31 @@ describe('app', () => { const config = initializeConfig(); const authorizationKeys = 'correct-key'; + const createReqBodyForEhr = (messageId, conversationId, nhsNumber, fragmentMessageIds) => ({ + data: { + type: 'messages', + id: messageId, + attributes: { + conversationId, + messageType: MessageType.EHR_EXTRACT, + nhsNumber, + fragmentMessageIds, + }, + }, + }); + + const createReqBodyForFragment = (messageId, conversationId, fragmentMessageIds = []) => ({ + data: { + type: 'messages', + id: messageId, + attributes: { + conversationId, + messageType: MessageType.FRAGMENT, + fragmentMessageIds: fragmentMessageIds, + }, + }, + }); + beforeEach(() => { process.env.API_KEY_FOR_TEST_USER = authorizationKeys; logger.add(transportSpy); @@ -381,32 +407,6 @@ describe('app', () => { const nhsNumber = '1234567890'; let conversationId; let messageId; - - const createReqBodyForEhr = (messageId, conversationId, nhsNumber, fragmentMessageIds) => ({ - data: { - type: 'messages', - id: messageId, - attributes: { - conversationId, - messageType: MessageType.EHR_EXTRACT, - nhsNumber, - fragmentMessageIds, - }, - }, - }); - - const createReqBodyForFragment = (messageId, conversationId, fragmentMessageIds = []) => ({ - data: { - type: 'messages', - id: messageId, - attributes: { - conversationId, - messageType: MessageType.FRAGMENT, - fragmentMessageIds: fragmentMessageIds, - }, - }, - }); - beforeEach(() => { messageId = uuid(); conversationId = uuid(); @@ -577,4 +577,126 @@ describe('app', () => { }); }); }); + + describe('DELETE /patients/:nhsNumber', () => { + // ======================= SETUP & HELPERS ======================= + const nhsNumber = '9000000001'; + const db = EhrTransferTracker.getInstance(); + const createCompleteRecord = async ( + nhsNumber, + conversationId, + coreMessageId, + fragmentMessageIds = [] + ) => { + await createConversationForTest(conversationId, nhsNumber, { + TransferStatus: ConversationStatus.STARTED, + }); + await request(app) + .post(`/messages`) + .send(createReqBodyForEhr(coreMessageId, conversationId, nhsNumber, fragmentMessageIds)) + .set('Authorization', authorizationKeys); + await Promise.all( + fragmentMessageIds.map((fragmentId) => + request(app) + .post(`/messages`) + .send(createReqBodyForFragment(fragmentId, conversationId)) + .set('Authorization', authorizationKeys) + ) + ); + }; + + const callGetPatient = (nhsNumber) => { + return request(app).get(`/patients/${nhsNumber}`).set('Authorization', authorizationKeys); + }; + + afterEach(async () => { + await cleanupRecordsForTestByNhsNumber(nhsNumber); + }); + + it('should mark the whole health record for the given NHS number as deleted', async () => { + // given + const inboundConversationId = uuid(); + const coreMessageId = uuid(); + const fragmentMessageIds = [uuid(), uuid(), uuid()]; + await createCompleteRecord( + nhsNumber, + inboundConversationId, + coreMessageId, + fragmentMessageIds + ); + + const getPatientResponse = await callGetPatient(nhsNumber); + expect(getPatientResponse.status).toBe(200); + expect(getPatientResponse.body).toMatchObject({ + conversationIdFromEhrIn: inboundConversationId, + fragmentMessageIds: expect.arrayContaining(fragmentMessageIds), + }); + const timestampBeforeDelete = Math.floor(new Date() / 1000); + + // when + const deleteResponse = await request(app) + .delete(`/patients/${nhsNumber}`) + .set('Authorization', authorizationKeys); + + // then + expect(deleteResponse.status).toBe(200); + expect(deleteResponse.body.data).toMatchObject({ + conversationIds: [inboundConversationId], + id: nhsNumber, + }); + + const getPatientResponseAfterDeletion = await callGetPatient(nhsNumber); + expect(getPatientResponseAfterDeletion.status).toBe(404); + + const softDeletedRecords = await db.queryTableByConversationId( + inboundConversationId, + RecordType.ALL, + true + ); + expect(softDeletedRecords).toHaveLength(5); // Conversation + Core + 3 Fragments + + const timestampAfterDelete = Math.ceil(new Date() / 1000); + for (const item of softDeletedRecords) { + expect(item).toMatchObject({ + InboundConversationId: inboundConversationId, + DeletedAt: expect.any(Number), + }); + expect(item.DeletedAt).toBeGreaterThanOrEqual(timestampBeforeDelete); + expect(item.DeletedAt).toBeLessThanOrEqual(timestampAfterDelete); + } + }); + + + it('should delete all record if patient has more than one set of record in our storage', async () => { + // given + const inboundConversationId1 = uuid(); + const coreMessageId1 = uuid(); + const inboundConversationId2 = uuid(); + const coreMessageId2 = uuid(); + await createCompleteRecord(nhsNumber, inboundConversationId1, coreMessageId1); + await createCompleteRecord(nhsNumber, inboundConversationId2, coreMessageId2); + + const getPatientResponse = await callGetPatient(nhsNumber); + expect(getPatientResponse.status).toBe(200); + expect(getPatientResponse.body).toMatchObject({ + conversationIdFromEhrIn: inboundConversationId2, + fragmentMessageIds: [], + }); + + // when + const deleteResponse = await request(app) + .delete(`/patients/${nhsNumber}`) + .set('Authorization', authorizationKeys); + + // then + expect(deleteResponse.status).toBe(200); + expect(deleteResponse.body.data).toMatchObject({ + conversationIds: expect.arrayContaining([inboundConversationId1, inboundConversationId2]), + id: nhsNumber, + }); + + const getPatientResponseAfterDeletion = await callGetPatient(nhsNumber); + expect(getPatientResponseAfterDeletion.status).toBe(404); + }); + }); }); diff --git a/src/services/database/dynamo-ehr-transfer-tracker.js b/src/services/database/dynamo-ehr-transfer-tracker.js index b64366f2..57373a2c 100644 --- a/src/services/database/dynamo-ehr-transfer-tracker.js +++ b/src/services/database/dynamo-ehr-transfer-tracker.js @@ -77,7 +77,7 @@ export class EhrTransferTracker { await this.client.send(command); } - async queryTableByNhsNumber(nhsNumber) { + async queryTableByNhsNumber(nhsNumber, includeDeletedRecord = false) { const params = { TableName: this.tableName, IndexName: 'NhsNumberSecondaryIndex', @@ -89,7 +89,9 @@ export class EhrTransferTracker { }, KeyConditionExpression: '#NhsNumber = :nhsNumber', }; - + if (!includeDeletedRecord) { + params.FilterExpression = 'attribute_not_exists(DeletedAt)'; + } const command = new QueryCommand(params); const response = await this.client.send(command); From ab7a6ef0f6cf9d29746fdd8735d9e15324ca7de6 Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Fri, 8 Mar 2024 12:59:06 +0000 Subject: [PATCH 37/67] [PRMT-4567] Remove postgres db checking from health check endpoint --- src/__tests__/app.integration.test.js | 9 +++-- src/api/health-check/health-check.js | 1 - .../get-health-check.integration.test.js | 6 +++- .../__tests__/get-health-check.test.js | 33 +++++++++++-------- src/services/health-check/get-health-check.js | 8 ++--- tasks | 1 + 6 files changed, 34 insertions(+), 24 deletions(-) diff --git a/src/__tests__/app.integration.test.js b/src/__tests__/app.integration.test.js index bb713668..d1b0e8de 100644 --- a/src/__tests__/app.integration.test.js +++ b/src/__tests__/app.integration.test.js @@ -614,7 +614,7 @@ describe('app', () => { }); it('should mark the whole health record for the given NHS number as deleted', async () => { - // given + // ============================= given ================================== const inboundConversationId = uuid(); const coreMessageId = uuid(); const fragmentMessageIds = [uuid(), uuid(), uuid()]; @@ -633,12 +633,12 @@ describe('app', () => { }); const timestampBeforeDelete = Math.floor(new Date() / 1000); - // when + // ============================= when ================================== const deleteResponse = await request(app) .delete(`/patients/${nhsNumber}`) .set('Authorization', authorizationKeys); - // then + // ============================= then ================================== expect(deleteResponse.status).toBe(200); expect(deleteResponse.body.data).toMatchObject({ conversationIds: [inboundConversationId], @@ -674,6 +674,9 @@ describe('app', () => { const inboundConversationId2 = uuid(); const coreMessageId2 = uuid(); await createCompleteRecord(nhsNumber, inboundConversationId1, coreMessageId1); + + await new Promise(resolve => setTimeout(resolve, 1500)); // time buffer to ensure record 2 get a newer timestamp + await createCompleteRecord(nhsNumber, inboundConversationId2, coreMessageId2); const getPatientResponse = await callGetPatient(nhsNumber); diff --git a/src/api/health-check/health-check.js b/src/api/health-check/health-check.js index c30a26d1..2d7cd6a3 100644 --- a/src/api/health-check/health-check.js +++ b/src/api/health-check/health-check.js @@ -9,7 +9,6 @@ healthCheck.get('/', (req, res, next) => { .then((status) => { if ( status.details.filestore.writable && - status.details.database.writable && status.details.filestore.available ) { logInfo('Health check successful'); diff --git a/src/services/health-check/__tests__/get-health-check.integration.test.js b/src/services/health-check/__tests__/get-health-check.integration.test.js index f631438a..b270b01a 100644 --- a/src/services/health-check/__tests__/get-health-check.integration.test.js +++ b/src/services/health-check/__tests__/get-health-check.integration.test.js @@ -1,7 +1,11 @@ +/* @deprecated +// postgres-db specfic tests +// to be deleted PRMT-4568 +*/ import { getHealthCheck } from '../get-health-check'; import ModelFactory from '../../../models'; -describe('getHealthCheck', () => { +describe.skip('getHealthCheck', () => { it('should return successful db health check if db connection is healthy', () => { return getHealthCheck().then((result) => { const db = result.details['database']; diff --git a/src/services/health-check/__tests__/get-health-check.test.js b/src/services/health-check/__tests__/get-health-check.test.js index 9e8dcfbd..eb239033 100644 --- a/src/services/health-check/__tests__/get-health-check.test.js +++ b/src/services/health-check/__tests__/get-health-check.test.js @@ -72,18 +72,23 @@ describe('getHealthCheck', () => { }); }); - it('should return failed db health check if there is an unknown error', () => { - ModelFactory._overrideConfig('host', 'something'); - - return getHealthCheck().then((result) => { - const db = result.details['database']; - - return expect(db).toEqual({ - type: 'postgresql', - connection: false, - writable: false, - error: 'Unknown error (Error Code: ENOTFOUND)', - }); - }); - }); + /* @deprecated + // postgres-db related test + // to be deleted PRMT-4568 + // + // it('should return failed db health check if there is an unknown error', () => { + // ModelFactory._overrideConfig('host', 'something'); + // + // return getHealthCheck().then((result) => { + // const db = result.details['database']; + // + // return expect(db).toEqual({ + // type: 'postgresql', + // connection: false, + // writable: false, + // error: 'Unknown error (Error Code: ENOTFOUND)', + // }); + // }); + // }); + */ }); diff --git a/src/services/health-check/get-health-check.js b/src/services/health-check/get-health-check.js index 3b1dabb6..d763e097 100644 --- a/src/services/health-check/get-health-check.js +++ b/src/services/health-check/get-health-check.js @@ -1,5 +1,4 @@ import { S3Service } from '../storage'; -import { checkDbHealth } from '../database'; import { logInfo } from '../../middleware/logging'; import { initializeConfig } from '../../config'; @@ -9,15 +8,14 @@ export function getHealthCheck() { const s3Service = new S3Service(); - return Promise.all([s3Service.checkS3Health(), checkDbHealth()]).then(([s3, db]) => { - logInfo('Health check status', db, s3); + return s3Service.checkS3Health().then((s3HealthCheckResult) => { + logInfo('Health check status', s3HealthCheckResult); return { version: '1', description: 'Health of EHR Repo service', nhsEnvironment: config.nhsEnvironment, details: { - filestore: s3, - database: db, + filestore: s3HealthCheckResult, }, }; }); diff --git a/tasks b/tasks index 7ae20282..b271dd3f 100755 --- a/tasks +++ b/tasks @@ -411,6 +411,7 @@ case "${command}" in npm run test:integration ;; test_all) + configure_local_envs dojo -c Dojofile-itest "./tasks _test_all" ;; _test_performance) From f73316d8226155e8e6f5a894847a49253f3e20ff Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Fri, 8 Mar 2024 13:45:24 +0000 Subject: [PATCH 38/67] [PRMR-4567] Unplug postgres db from application, comment out redundant tests --- package.json | 6 +- scripts/wait-for-localstack.js | 2 +- scripts/wait-for-postgres.js | 43 +++++++------ src/__tests__/app.integration.test.js | 7 +-- src/config/database.js | 2 +- src/config/index.js | 8 +-- src/models/index.js | 3 +- src/models/models.js | 24 ++++---- .../check-db-health.integration.test.js | 11 +++- ...alth-record-repository.integration.test.js | 60 +++++++++---------- .../message-repository.integration.test.js | 34 +++++------ .../__tests__/get-health-check.test.js | 10 ++-- tasks | 16 ++--- 13 files changed, 116 insertions(+), 110 deletions(-) diff --git a/package.json b/package.json index edd9ae1d..0fe4b68d 100644 --- a/package.json +++ b/package.json @@ -19,9 +19,9 @@ "start": "node build/server.js", "start:local": "npm run db:migrate ; babel-node -r dotenv/config src/server.js", "start:nodemon": "nodemon --exec npm run start:local", - "db:wait": "babel-node scripts/wait-for-postgres.js", - "db:migrate": "npm run db:wait && sequelize-cli db:migrate", - "db:teardown": "sequelize-cli db:migrate:undo:all" + "db:wait": "echo postgresdb deprecated", + "db:migrate": "echo postgresdb deprecated", + "db:teardown": "echo postgresdb deprecated" }, "devDependencies": { "@babel/cli": "^7.23.0", diff --git a/scripts/wait-for-localstack.js b/scripts/wait-for-localstack.js index e1f7e5df..c72f4419 100644 --- a/scripts/wait-for-localstack.js +++ b/scripts/wait-for-localstack.js @@ -41,7 +41,7 @@ const checkIfConnected = async () => { console.log('Successfully connected to localstack'); return; } - await sleep(1000); + await sleep(2000); } console.log(`Connection to Localstack timedout after ${MAX_ATTEMPTS}`); diff --git a/scripts/wait-for-postgres.js b/scripts/wait-for-postgres.js index 92e77918..dc3ecc32 100644 --- a/scripts/wait-for-postgres.js +++ b/scripts/wait-for-postgres.js @@ -1,22 +1,27 @@ -import ModelFactory from '../src/models'; +/** + * @deprecated + * Postgres DB related stubs + * To be deleted PRMT-4568 + */ +// import ModelFactory from '../src/models'; -const INTERVAL = 1000; -let ATTEMPTS = 10; +// const INTERVAL = 1000; +// let ATTEMPTS = 10; -const connect = () => { - ModelFactory.sequelize - .authenticate() - .then(() => { - console.log('Successfully connected'); - }) - .catch(() => { - if (ATTEMPTS === 0) { - throw new Error('Maximum number of attempts have been reached, exiting.'); - } - console.log(`Attempt ${ATTEMPTS} to connect failed`); - ATTEMPTS--; - setTimeout(connect, INTERVAL); - }); -}; +// const connect = () => { + // ModelFactory.sequelize + // .authenticate() + // .then(() => { + // console.log('Successfully connected'); + // }) + // .catch(() => { + // if (ATTEMPTS === 0) { + // throw new Error('Maximum number of attempts have been reached, exiting.'); + // } + // console.log(`Attempt ${ATTEMPTS} to connect failed`); + // ATTEMPTS--; + // setTimeout(connect, INTERVAL); + // }); +// }; -connect(); +// connect(); diff --git a/src/__tests__/app.integration.test.js b/src/__tests__/app.integration.test.js index d1b0e8de..be1b138e 100644 --- a/src/__tests__/app.integration.test.js +++ b/src/__tests__/app.integration.test.js @@ -4,9 +4,6 @@ import app from '../app'; import { initializeConfig } from '../config'; import { logger } from '../config/logging'; import { expectStructuredLogToContain, transportSpy } from '../__builders__/logging-helper'; -import ModelFactory from '../models'; -import { MessageType, modelName as messageModelName } from '../models/message'; -import { modelName as healthRecordModelName } from '../models/health-record'; import { EhrTransferTracker } from '../services/database/dynamo-ehr-transfer-tracker'; import { getCoreByKey } from '../services/database/ehr-core-repository'; import { @@ -15,11 +12,10 @@ import { createConversationForTest, } from '../utilities/integration-test-utilities'; import { getConversationById } from '../services/database/ehr-conversation-repository'; -import { ConversationStatus, RecordType } from '../models/enums'; +import { ConversationStatus, MessageType, RecordType } from "../models/enums"; import { isCore } from '../models/core'; import { getFragmentByKey } from '../services/database/ehr-fragment-repository'; import { isFragment } from '../models/fragment'; -import { isInCompleteStatus } from '../models/conversation'; import { TIMESTAMP_REGEX } from '../services/time'; describe('app', () => { @@ -420,7 +416,6 @@ describe('app', () => { }); afterAll(async () => { - await ModelFactory.sequelize.close(); }); it('should save health record without fragments in the database and return 201', async () => { diff --git a/src/config/database.js b/src/config/database.js index 930fdd03..25a716ee 100644 --- a/src/config/database.js +++ b/src/config/database.js @@ -26,4 +26,4 @@ if (use_ssl) { }; } -module.exports = sequelizeConfig; +// module.exports = sequelizeConfig; diff --git a/src/config/index.js b/src/config/index.js index d27f36a5..2c4d23af 100644 --- a/src/config/index.js +++ b/src/config/index.js @@ -1,12 +1,12 @@ -import sequelizeConfig from './database'; - -export const portNumber = 3000; +// import sequelizeConfig from './database'; +// export const portNumber = 3000; +// export const initializeConfig = () => ({ ehrServiceUrl: process.env.SERVICE_URL || `http://127.0.0.1:${portNumber}`, awsS3BucketName: process.env.S3_BUCKET_NAME, localstackUrl: process.env.LOCALSTACK_URL, - sequelize: sequelizeConfig, + // sequelize: sequelizeConfig, nhsEnvironment: process.env.NHS_ENVIRONMENT || 'local', consumerApiKeys: loadConsumerKeys(), }); diff --git a/src/models/index.js b/src/models/index.js index 1f1abb5a..28599a99 100644 --- a/src/models/index.js +++ b/src/models/index.js @@ -108,4 +108,5 @@ class ModelFactory { } } -export default new ModelFactory(); +// export default new ModelFactory(); +export default {}; \ No newline at end of file diff --git a/src/models/models.js b/src/models/models.js index b6516659..3ed1e1da 100644 --- a/src/models/models.js +++ b/src/models/models.js @@ -1,12 +1,12 @@ -import HealthCheck from './health-check'; -import Message from './message'; -import HealthRecord from './health-record'; - -/** - * @deprecated - * Postgres DB related stubs - * To be deleted PRMT-4568 - */ -export { HealthCheck }; -export { Message }; -export { HealthRecord }; +// import HealthCheck from './health-check'; +// import Message from './message'; +// import HealthRecord from './health-record'; +// +// /** +// * @deprecated +// * Postgres DB related stubs +// * To be deleted PRMT-4568 +// */ +// export { HealthCheck }; +// export { Message }; +// export { HealthRecord }; diff --git a/src/services/database/__tests__/check-db-health.integration.test.js b/src/services/database/__tests__/check-db-health.integration.test.js index 37f1751d..b90950c0 100644 --- a/src/services/database/__tests__/check-db-health.integration.test.js +++ b/src/services/database/__tests__/check-db-health.integration.test.js @@ -1,9 +1,14 @@ +/** + * @deprecated + * Postgres DB related stubs + * To be deleted PRMT-4568 + */ import { checkDbHealth } from '../check-db-health'; -import ModelFactory from '../../../models'; +// import ModelFactory from '../../../models'; -describe('db', () => { +describe.skip('db', () => { afterAll(() => { - ModelFactory.sequelize.close(); + // ModelFactory.sequelize.close(); }); describe('checkDbHealth', () => { diff --git a/src/services/database/__tests__/health-record-repository.integration.test.js b/src/services/database/__tests__/health-record-repository.integration.test.js index 49c01546..a8600c9e 100644 --- a/src/services/database/__tests__/health-record-repository.integration.test.js +++ b/src/services/database/__tests__/health-record-repository.integration.test.js @@ -1,43 +1,43 @@ import { v4 as uuid } from 'uuid'; -import { - getHealthRecordStatus, - updateHealthRecordCompleteness, - getCurrentHealthRecordIdForPatient, - getHealthRecordMessageIds, - messageAlreadyReceived, - markHealthRecordAsDeletedForPatient, -} from '../health-record-repository'; -import ModelFactory from '../../../models'; -import { modelName as healthRecordModelName } from '../../../models/health-record'; -import { MessageType, modelName as messageModelName } from '../../../models/message'; +// import { +// getHealthRecordStatus, +// updateHealthRecordCompleteness, +// getCurrentHealthRecordIdForPatient, +// getHealthRecordMessageIds, +// messageAlreadyReceived, +// markHealthRecordAsDeletedForPatient, +// } from '../health-record-repository'; +// import ModelFactory from '../../../models'; +// import { modelName as healthRecordModelName } from '../../../models/health-record'; +// import { MessageType, modelName as messageModelName } from '../../../models/message'; import { logError } from '../../../middleware/logging'; import { HealthRecordStatus } from "../../../models/enums"; -jest.mock('../../../middleware/logging'); +// jest.mock('../../../middleware/logging'); -describe('healthRecordRepository', () => { +describe.skip('healthRecordRepository', () => { /** * @deprecated * All the tests here are migrated to new test suite of dynamodb-based implementation * To be deleted PRMT-4568 */ - // ========================= COMMON PROPERTIES ========================= - const HealthRecord = ModelFactory.getByName(healthRecordModelName); - const Message = ModelFactory.getByName(messageModelName); - // ===================================================================== - - // ========================= SET UP / TEAR DOWN ======================== - beforeEach(async () => { - await HealthRecord.truncate(); - await Message.truncate(); - await ModelFactory.sequelize.sync({ force: true }); - }); - - afterAll(async () => { - await ModelFactory.sequelize.close(); - }); - // ===================================================================== - + // // ========================= COMMON PROPERTIES ========================= + // const HealthRecord = ModelFactory.getByName(healthRecordModelName); + // const Message = ModelFactory.getByName(messageModelName); + // // ===================================================================== + // + // // ========================= SET UP / TEAR DOWN ======================== + // beforeEach(async () => { + // await HealthRecord.truncate(); + // await Message.truncate(); + // await ModelFactory.sequelize.sync({ force: true }); + // }); + // + // afterAll(async () => { + // await ModelFactory.sequelize.close(); + // }); + // // ===================================================================== + // describe('getHealthRecordStatus', () => { it("should return status 'complete' when health record 'completedAt' field is not null", async () => { const conversationId = uuid(); diff --git a/src/services/database/__tests__/message-repository.integration.test.js b/src/services/database/__tests__/message-repository.integration.test.js index 8ac0617d..2724cae2 100644 --- a/src/services/database/__tests__/message-repository.integration.test.js +++ b/src/services/database/__tests__/message-repository.integration.test.js @@ -1,13 +1,13 @@ import { v4 as uuid } from 'uuid'; -import { - updateFragmentAndCreateItsParts, - createEhrExtract, - fragmentExists, - createFragmentPart, -} from '../message-repository'; -import ModelFactory from '../../../models'; -import { MessageType, modelName as messageModelName } from '../../../models/message'; -import { modelName as healthRecordModelName } from '../../../models/health-record'; +// import { +// updateFragmentAndCreateItsParts, +// createEhrExtract, +// fragmentExists, +// createFragmentPart, +// } from '../message-repository'; +// import ModelFactory from '../../../models'; +// import { MessageType, modelName as messageModelName } from '../../../models/message'; +// import { modelName as healthRecordModelName } from '../../../models/health-record'; import { logError } from '../../../middleware/logging'; import { getNow } from '../../time'; import expect from 'expect'; @@ -16,15 +16,15 @@ import expect from 'expect'; jest.mock('../../../middleware/logging'); jest.mock('../../time'); -describe('messageRepository', () => { +describe.skip('messageRepository', () => { /** * @deprecated * All the tests here are migrated to new test suite of dynamodb-based implementation * To be deleted PRMT-4568 */ - const Message = ModelFactory.getByName(messageModelName); - const HealthRecord = ModelFactory.getByName(healthRecordModelName); - const ehrExtractType = MessageType.EHR_EXTRACT; + const Message = {}; // ModelFactory.getByName(messageModelName); + const HealthRecord = {}; // ModelFactory.getByName(healthRecordModelName); + // const ehrExtractType = MessageType.EHR_EXTRACT; const fragmentMessageId = uuid(); const fragmentMessageIds = [fragmentMessageId]; const nhsNumber = '1234567890'; @@ -32,13 +32,13 @@ describe('messageRepository', () => { beforeEach(async () => { getNow.mockReturnValue(now); - await HealthRecord.truncate(); - await Message.truncate(); - await ModelFactory.sequelize.sync({ force: true }); + // await HealthRecord.truncate(); + // await Message.truncate(); + // await ModelFactory.sequelize.sync({ force: true }); }); afterAll(async () => { - await ModelFactory.sequelize.close(); + // await ModelFactory.sequelize.close(); }); describe('createEhrExtract', () => { diff --git a/src/services/health-check/__tests__/get-health-check.test.js b/src/services/health-check/__tests__/get-health-check.test.js index eb239033..2858f663 100644 --- a/src/services/health-check/__tests__/get-health-check.test.js +++ b/src/services/health-check/__tests__/get-health-check.test.js @@ -1,6 +1,6 @@ import { getHealthCheck } from '../get-health-check'; import { S3 } from 'aws-sdk'; -import ModelFactory from '../../../models'; +// import ModelFactory from '../../../models'; import { initializeConfig } from '../../../config'; jest.mock('aws-sdk'); @@ -13,7 +13,7 @@ describe('getHealthCheck', () => { const error = 'some-error'; beforeEach(() => { - ModelFactory._resetConfig(); + // ModelFactory._resetConfig(); S3.mockImplementation(() => ({ putObject: mockPutObject, @@ -21,9 +21,9 @@ describe('getHealthCheck', () => { })); }); - afterAll(() => { - ModelFactory.sequelize.close(); - }); + // afterAll(() => { + // ModelFactory.sequelize.close(); + // }); it('should return successful s3 health check if s3 succeeds', () => { // when diff --git a/tasks b/tasks index b271dd3f..390aecc5 100755 --- a/tasks +++ b/tasks @@ -362,8 +362,8 @@ case "${command}" in ;; _setup_test_integration_local) configure_local_envs - npm run db:teardown - npm run db:migrate +# npm run db:teardown +# npm run db:migrate prepare_local_envs_for_ide ;; _test_lint) @@ -383,15 +383,15 @@ case "${command}" in _test_integration_local) configure_local_envs npm install - npm run db:teardown - npm run db:migrate +# npm run db:teardown +# npm run db:migrate npm run test:integration ;; _test_integration) npm install - npm run db:migrate - node scripts/wait-for-localstack.js +# npm run db:migrate sh scripts/create-dynamodb-table.sh +# node scripts/wait-for-localstack.js npm run test:integration ;; test_integration) @@ -407,7 +407,7 @@ case "${command}" in npm run lint npm run test:unit configure_local_envs - npm run db:migrate +# npm run db:migrate npm run test:integration ;; test_all) @@ -425,7 +425,7 @@ case "${command}" in ;; _test_coverage) npm install - npm run db:migrate +# npm run db:migrate npm run test:coverage ;; test_coverage) From 0733d835147bff346f72f7022e5c12e529808ecb Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Fri, 8 Mar 2024 13:59:32 +0000 Subject: [PATCH 39/67] [PRMT-4567] mark redundant tests as deprecated, fix unit tests --- src/api/health-check/__tests__/health-check.test.js | 7 ++++++- .../messages/__tests__/store-message-controller.test.js | 2 +- src/config/index.js | 4 ++-- src/middleware/__tests__/auth.test.js | 7 +++---- tasks | 2 +- 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/api/health-check/__tests__/health-check.test.js b/src/api/health-check/__tests__/health-check.test.js index b859ba1a..b8f5086e 100644 --- a/src/api/health-check/__tests__/health-check.test.js +++ b/src/api/health-check/__tests__/health-check.test.js @@ -78,7 +78,12 @@ describe('GET /health', () => { }); }); - describe('database is not writable', () => { + /** + * @deprecated + * postgres-db specific tests + * To be deleted PRMT-4568 + */ + describe.skip('database is not writable', () => { beforeEach(() => { getHealthCheck.mockReturnValue(Promise.resolve(expectedHealthCheckBase(true, true, false))); }); diff --git a/src/api/messages/__tests__/store-message-controller.test.js b/src/api/messages/__tests__/store-message-controller.test.js index 4892ee7b..094b0e33 100644 --- a/src/api/messages/__tests__/store-message-controller.test.js +++ b/src/api/messages/__tests__/store-message-controller.test.js @@ -107,7 +107,7 @@ describe('storeMessageController', () => { expect(res.status).toBe(201); expect(fragmentExistsInRecord).toHaveBeenCalledWith(nestedFragmentId); - expect(markFragmentAsReceivedAndCreateItsParts).toHaveBeenCalledWith(nestedFragmentId, conversationId); + expect(markFragmentAsReceivedAndCreateItsParts).toHaveBeenCalledWith(nestedFragmentId, conversationId, []); expect(updateConversationCompleteness).toHaveBeenCalledWith(conversationId); }); }); diff --git a/src/config/index.js b/src/config/index.js index 2c4d23af..e2f8b413 100644 --- a/src/config/index.js +++ b/src/config/index.js @@ -1,7 +1,7 @@ // import sequelizeConfig from './database'; -// export const portNumber = 3000; -// +export const portNumber = 3000; + export const initializeConfig = () => ({ ehrServiceUrl: process.env.SERVICE_URL || `http://127.0.0.1:${portNumber}`, awsS3BucketName: process.env.S3_BUCKET_NAME, diff --git a/src/middleware/__tests__/auth.test.js b/src/middleware/__tests__/auth.test.js index 3d412a13..9fe6c41f 100644 --- a/src/middleware/__tests__/auth.test.js +++ b/src/middleware/__tests__/auth.test.js @@ -3,16 +3,15 @@ import app from '../../app'; import { v4 as uuid } from 'uuid'; import { initializeConfig } from '../../config'; import { logInfo, logWarning } from '../logging'; -import { messageAlreadyReceived } from '../../services/database/health-record-repository'; +import { fragmentAlreadyReceived } from "../../services/database/ehr-fragment-repository"; jest.mock('../logging'); -jest.mock('../../services/database/health-record-repository'); +jest.mock('../../services/database/ehr-fragment-repository'); jest.mock('../../services/storage/get-signed-url', () => jest.fn().mockReturnValue(Promise.resolve('some-url')) ); jest.mock('../../config', () => ({ initializeConfig: jest.fn().mockReturnValue({ - sequelize: { dialect: 'postgres' }, consumerApiKeys: { TEST_USER: 'correct-key', DUPLICATE_TEST_USER: 'correct-key', @@ -27,7 +26,7 @@ describe('auth', () => { describe('Authenticated successfully', () => { it('should return HTTP 200 when correctly authenticated', async () => { - messageAlreadyReceived.mockResolvedValueOnce(false); + fragmentAlreadyReceived.mockResolvedValueOnce(false); const res = await request(app) .get(`/messages/${conversationId}/${messageId}`) diff --git a/tasks b/tasks index 390aecc5..2b9554c2 100755 --- a/tasks +++ b/tasks @@ -391,7 +391,7 @@ case "${command}" in npm install # npm run db:migrate sh scripts/create-dynamodb-table.sh -# node scripts/wait-for-localstack.js + node scripts/wait-for-localstack.js npm run test:integration ;; test_integration) From a56548c8217234c936f62832e937a064cba5b72b Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Fri, 8 Mar 2024 14:14:45 +0000 Subject: [PATCH 40/67] [PRMT-4567] Update dtest.yml, remove postgresdb healthcheck from docker test --- docker-compose-dtest.yml | 6 ++++++ tasks | 3 +++ test/docker/health.test.js | 4 ---- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/docker-compose-dtest.yml b/docker-compose-dtest.yml index 91eb351a..801dc0f4 100644 --- a/docker-compose-dtest.yml +++ b/docker-compose-dtest.yml @@ -8,6 +8,7 @@ services: links: - db:db - localstack:localstack + - dynamodb-local:dynamodb-local ports: - 3000:3000 db: @@ -31,3 +32,8 @@ services: volumes: - ./scripts/create-bucket.sh:/docker-entrypoint-initaws.d/create-bucket.sh - /var/run/docker.sock:/var/run/docker.sock + dynamodb-local: + image: amazon/dynamodb-local + command: "-jar DynamoDBLocal.jar -sharedDb -inMemory" + ports: + - "4573:8000" \ No newline at end of file diff --git a/tasks b/tasks index 2b9554c2..8ecb863f 100755 --- a/tasks +++ b/tasks @@ -408,6 +408,7 @@ case "${command}" in npm run test:unit configure_local_envs # npm run db:migrate + sh scripts/create-dynamodb-table.sh npm run test:integration ;; test_all) @@ -426,6 +427,7 @@ case "${command}" in _test_coverage) npm install # npm run db:migrate + sh scripts/create-dynamodb-table.sh npm run test:coverage ;; test_coverage) @@ -450,6 +452,7 @@ case "${command}" in _test_docker) npm install ./scripts/migrate-db.sh + sh scripts/create-dynamodb-table.sh node scripts/wait-for-localstack.js npm run test:docker ;; diff --git a/test/docker/health.test.js b/test/docker/health.test.js index 18367e22..165ebc6e 100644 --- a/test/docker/health.test.js +++ b/test/docker/health.test.js @@ -14,10 +14,6 @@ describe('GET /health', () => { available: true, writable: true, }), - database: expect.objectContaining({ - connection: true, - writable: true, - }), }), }) ); From f489be24717daf25a9d2dea70fb9af8e9a0505e4 Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Fri, 8 Mar 2024 14:29:16 +0000 Subject: [PATCH 41/67] [PRMT-4567] comment out postgresdb migration command in ./tasks --- tasks | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasks b/tasks index 8ecb863f..1d7e7c77 100755 --- a/tasks +++ b/tasks @@ -451,7 +451,7 @@ case "${command}" in ;; _test_docker) npm install - ./scripts/migrate-db.sh +# ./scripts/migrate-db.sh sh scripts/create-dynamodb-table.sh node scripts/wait-for-localstack.js npm run test:docker From 9590886bae1bcf77ece09ccce335bb55ddf010c7 Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Fri, 8 Mar 2024 14:47:21 +0000 Subject: [PATCH 42/67] [PRMT-4567] minor bugfix --- .../ehr-fragment-repository.integration.test.js | 15 ++++++++++++--- .../database/dynamo-ehr-transfer-tracker.js | 1 + 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/services/database/__tests__/ehr-fragment-repository.integration.test.js b/src/services/database/__tests__/ehr-fragment-repository.integration.test.js index 1696651e..a7d43d7f 100644 --- a/src/services/database/__tests__/ehr-fragment-repository.integration.test.js +++ b/src/services/database/__tests__/ehr-fragment-repository.integration.test.js @@ -127,6 +127,7 @@ describe('ehr-fragment-repository', () => { describe('fragmentExistsInRecord', () => { it('should return true for a fragment existing in the database', async () => { + // given const conversationId = uuid(); const messageId = uuid(); @@ -137,7 +138,11 @@ describe('ehr-fragment-repository', () => { const db = EhrTransferTracker.getInstance(); await db.writeItemsInTransaction([fragment]); - expect(await fragmentExistsInRecord(conversationId, messageId)).toBe(true); + // when + const result = await fragmentExistsInRecord(conversationId, messageId); + + // then + expect(result).toBe(true); }); it('should return false for a fragment that does not exist in the database', async () => { @@ -147,10 +152,14 @@ describe('ehr-fragment-repository', () => { }); it('should throw if database querying throws', async () => { - const messageId = 'not-valid'; + // given + mimicDynamodbFail(); + try { - await fragmentExistsInRecord(messageId); + // when + await fragmentExistsInRecord(uuid(), uuid()); } catch (err) { + // then expect(err).not.toBeNull(); expect(logError).toHaveBeenCalledWith('Querying database for fragment message failed', err); } diff --git a/src/services/database/dynamo-ehr-transfer-tracker.js b/src/services/database/dynamo-ehr-transfer-tracker.js index 57373a2c..334fe082 100644 --- a/src/services/database/dynamo-ehr-transfer-tracker.js +++ b/src/services/database/dynamo-ehr-transfer-tracker.js @@ -98,6 +98,7 @@ export class EhrTransferTracker { const items = response?.Items; if (!items) { logError('Received an empty response from dynamodb during query'); + return []; } return items; } From 95c3ad5fd19e79e395fe469f5e472691d31147f0 Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Fri, 8 Mar 2024 15:08:26 +0000 Subject: [PATCH 43/67] [PRMT-4567] Update README for the change related to database and integration tests --- README.md | 47 +++++++++++++++++------------------------------ 1 file changed, 17 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 94394da5..82d0bbe0 100644 --- a/README.md +++ b/README.md @@ -10,11 +10,6 @@ The stored the EHR is composed of the messages sent from its previous holder in - [Docker](https://docs.docker.com/install/) - [kudulab/dojo](https://github.com/kudulab/dojo#installation) -In order to run npm install locally on your host (outside of dojo), you'll need to install postgresql: -``` -brew install postgresql -``` - ### AWS helpers This repository imports shared AWS helpers from [prm-deductions-support-infra](https://github.com/nhsconnect/prm-deductions-support-infra/). @@ -31,45 +26,37 @@ If you would like to run the app locally outside `dojo`, you need to: ``` - `NHS_ENVIRONMENT` - should be set to current environment in which the container is deployed. The name must also exist in the `database.json` file. - `S3_BUCKET_NAME` - the name of S3 bucket to store the EHR fragments in. -- `DATABASE_USER` - username for the database -- `DATABASE_PASSWORD` - password to the database -- `DATABASE_NAME` - name of the database on server. -- `DATABASE_HOST` - database server hostname to connect with. -- `LOCALSTACK_URL` - (Test) the location of localstack, only used for tests +- `LOCALSTACK_URL` - (Test) the location of localstack, only used for s3 related tests +- `DYNAMODB_NAME` - The table name of the dynamodb table (ehr-transfer-tracker) used ``` ## Running the tests -Run the unit tests with - -by entering the `dojo` container and running `./tasks _test_unit` +Run the unit tests by entering the `dojo` container and running `./tasks _test_unit` or on your machine with `npm run test:unit` Run the integration tests within a Dojo container -1. Run `dojo -c Dojofile-itest` which will spin up the testing container +1. Run `./tasks test_integration_shell` which will spin up the testing container 2. Run `./tasks _test_integration` -You can also run them with `npm run test:integration` but that will require some additional manual set-up: +You can also run them with `./tasks test_integration` from out of dojo. -```bash -# Brings up the local test environment -docker-compose up & - -# Alternative with node-dojo (interactive) -# Requires changes to Environment Variables: -# DATABASE_HOST=db -# LOCALSTACK_URL=http://localstack:4572 -dojo -c Dojofile-itest +You can also run each individual integration test separately in an IDE (assuming IntelliJ), +but that will require some additional manual set-up: -npm run test-local - -# This is equivalent of: -sequelize-cli db:migrate # Runs the migration +```bash +# Config env var, spin up docker containers and enter interactive dojo environment +./tasks test_integration_shell -npm test +# If things work as expected your prompts should looks like `dojo@xxxx(node-dojo):/dojo/work$` +# inside dojo, run the below script to create a dynamodb table for integration test +scripts/create-dynamodb-table.sh -sequelize-cli db:migrate:undo:all # Undoes the migration to leave clean env +# The above script will create a test table in dynamodb-local docker image. +# The dynamodb-local is accessible at endpoint http://dynamodb-local:8000 within docker, +# or at endpoint http://localhost:4573 from out of docker. +# This should allow you to run or debug db-related integration tests from Intellij's play button. ``` ## Run the coverage tests (unit test and integration test) From 8ba39db2bdb999e1e945f9367d87ff841a1dccd4 Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Fri, 8 Mar 2024 16:54:49 +0000 Subject: [PATCH 44/67] [PRMR-4567] improve configs about running integration test with dojo --- .../database/dynamo-ehr-transfer-tracker.js | 8 +++---- src/services/database/dynamodb-client.js | 14 ++++--------- src/utilities/integration-test-utilities.js | 21 ++++++++++--------- 3 files changed, 18 insertions(+), 25 deletions(-) diff --git a/src/services/database/dynamo-ehr-transfer-tracker.js b/src/services/database/dynamo-ehr-transfer-tracker.js index 334fe082..6c6b008f 100644 --- a/src/services/database/dynamo-ehr-transfer-tracker.js +++ b/src/services/database/dynamo-ehr-transfer-tracker.js @@ -7,6 +7,7 @@ import { import { logError, logInfo } from '../../middleware/logging'; import { RecordType } from '../../models/enums'; import { getDynamodbClient } from './dynamodb-client'; +import { IS_IN_LOCAL } from "../../utilities/integration-test-utilities"; export class EhrTransferTracker { /** @@ -23,12 +24,9 @@ export class EhrTransferTracker { this.tableName = process.env.DYNAMODB_NAME; - const isInLocal = process.env.NHS_ENVIRONMENT === 'local' || !process.env.NHS_ENVIRONMENT; - const isInDojo = process.env.DOJO_VERSION !== undefined; - - if (isInLocal && !isInDojo) { + if (IS_IN_LOCAL) { // for running integration test within IDE - this.tableName = 'local-test-db'; + this.tableName = this.tableName ?? 'local-test-db'; } this.client = getDynamodbClient(); diff --git a/src/services/database/dynamodb-client.js b/src/services/database/dynamodb-client.js index 66790621..df0eaec2 100644 --- a/src/services/database/dynamodb-client.js +++ b/src/services/database/dynamodb-client.js @@ -1,21 +1,15 @@ import { DynamoDBClient } from '@aws-sdk/client-dynamodb'; import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb'; +import { IS_IN_LOCAL } from "../../utilities/integration-test-utilities"; export const getDynamodbClient = () => { const clientConfig = { region: process.env.AWS_DEFAULT_REGION ?? 'eu-west-2', }; - const isInLocal = process.env.NHS_ENVIRONMENT === 'local' || !process.env.NHS_ENVIRONMENT; - const isInDojo = process.env.DOJO_VERSION !== undefined; - - if (isInLocal) { - // for running whole integration test suite in dojo - clientConfig.endpoint = process.env.DYNAMODB_LOCAL_ENDPOINT; - } - if (isInLocal && !isInDojo) { - // for running individual test with IDE - clientConfig.endpoint = 'http://localhost:4573'; + if (IS_IN_LOCAL) { + // for running integration test in dojo or IDE + clientConfig.endpoint = process.env.DYNAMODB_LOCAL_ENDPOINT ?? 'http://localhost:4573'; } const baseClient = new DynamoDBClient(clientConfig); diff --git a/src/utilities/integration-test-utilities.js b/src/utilities/integration-test-utilities.js index be8c7f95..34f26d9a 100644 --- a/src/utilities/integration-test-utilities.js +++ b/src/utilities/integration-test-utilities.js @@ -2,7 +2,7 @@ import { v4 as uuidv4 } from 'uuid'; import { getUKTimestamp } from '../services/time'; import { EhrTransferTracker } from '../services/database/dynamo-ehr-transfer-tracker'; import { TransactWriteCommand } from '@aws-sdk/lib-dynamodb'; -import { RecordType } from "../models/enums"; +import { RecordType } from '../models/enums'; export const generateRandomNhsNumber = () => (Math.floor(Math.random() * 9e9) + 1e9).toString(); @@ -18,8 +18,7 @@ export const createConversationForTest = async (conversationId, nhsNumber, overr // This method is only meant for testing purpose. // the inbound conversation record is supposed to be created by other service. - const isInLocal = process.env.NHS_ENVIRONMENT === 'local' || !process.env.NHS_ENVIRONMENT; - if (!isInLocal) { + if (!IS_IN_LOCAL) { throw new Error('Unexpected call to createConversationForTest method in non-local environment'); } @@ -32,7 +31,7 @@ export const createConversationForTest = async (conversationId, nhsNumber, overr NhsNumber: nhsNumber, CreatedAt: timestamp, UpdatedAt: timestamp, - ...overrides + ...overrides, }; await db.writeItemsInTransaction([item]); @@ -41,8 +40,7 @@ export const createConversationForTest = async (conversationId, nhsNumber, overr export const cleanupRecordsForTest = async (conversationId) => { // This method is only meant for testing purpose - const isInLocal = process.env.NHS_ENVIRONMENT === 'local' || !process.env.NHS_ENVIRONMENT; - if (!isInLocal) { + if (!IS_IN_LOCAL) { throw new Error('Unexpected call to cleanupRecordsForTest method in non-local environment'); } @@ -63,11 +61,14 @@ export const cleanupRecordsForTest = async (conversationId) => { await db.client.send(deleteCommand); }; - export const cleanupRecordsForTestByNhsNumber = async (nhsNumber) => { // This method is only meant for testing purpose - const db = EhrTransferTracker.getInstance() + const db = EhrTransferTracker.getInstance(); const allConversations = await db.queryTableByNhsNumber(nhsNumber); - const removeAllRecords = allConversations.map(item => cleanupRecordsForTest(item.InboundConversationId)); + const removeAllRecords = allConversations.map((item) => + cleanupRecordsForTest(item.InboundConversationId) + ); return Promise.all(removeAllRecords); -} +}; + +export const IS_IN_LOCAL = process.env.NHS_ENVIRONMENT === 'local' || !process.env.NHS_ENVIRONMENT; \ No newline at end of file From a493867d5b9222cc2820cba0e27849b116a3abe2 Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Mon, 11 Mar 2024 10:19:45 +0000 Subject: [PATCH 45/67] [PRMT-4567] Fix test related to DeletedAt timestamp --- src/__tests__/app.integration.test.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/__tests__/app.integration.test.js b/src/__tests__/app.integration.test.js index be1b138e..d1eef960 100644 --- a/src/__tests__/app.integration.test.js +++ b/src/__tests__/app.integration.test.js @@ -651,13 +651,15 @@ describe('app', () => { expect(softDeletedRecords).toHaveLength(5); // Conversation + Core + 3 Fragments const timestampAfterDelete = Math.ceil(new Date() / 1000); + const eightWeeksInSeconds = 60 * 60 * 24 * 7 * 8; + for (const item of softDeletedRecords) { expect(item).toMatchObject({ InboundConversationId: inboundConversationId, DeletedAt: expect.any(Number), }); - expect(item.DeletedAt).toBeGreaterThanOrEqual(timestampBeforeDelete); - expect(item.DeletedAt).toBeLessThanOrEqual(timestampAfterDelete); + expect(item.DeletedAt).toBeGreaterThanOrEqual(timestampBeforeDelete + eightWeeksInSeconds); + expect(item.DeletedAt).toBeLessThanOrEqual(timestampAfterDelete + eightWeeksInSeconds); } }); From 515de1d3ac623b0bda121321483089887e9cb6cd Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Mon, 11 Mar 2024 10:31:07 +0000 Subject: [PATCH 46/67] [PRMT-4567] Amend Layer sort key prefix to be all capital case --- src/models/conversation.js | 6 +++--- src/models/core.js | 6 +++--- src/models/enums.js | 6 +++--- src/models/fragment.js | 7 ++++--- .../dynamo-ehr-transfer-tracker.integration.test.js | 2 +- .../__tests__/ehr-core-repository.integration.test.js | 4 ++-- .../__tests__/ehr-fragment-repository.integration.test.js | 2 +- src/utilities/integration-test-utilities.js | 2 +- 8 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/models/conversation.js b/src/models/conversation.js index 93edd8ce..e2d33427 100644 --- a/src/models/conversation.js +++ b/src/models/conversation.js @@ -1,6 +1,6 @@ import { getUKTimestamp } from '../services/time'; import { addChangesToUpdateParams } from '../utilities/dynamodb-helper'; -import { ConversationStatus } from "./enums"; +import { ConversationStatus, RecordType } from "./enums"; const fieldsAllowedToUpdate = ['TransferStatus', 'FailureCode', 'DeletedAt']; @@ -8,7 +8,7 @@ export const buildConversationUpdateParams = (conversationId, changes) => { const baseParams = { Key: { InboundConversationId: conversationId, - Layer: 'Conversation', + Layer: RecordType.CONVERSATION, }, UpdateExpression: 'set UpdatedAt = :now', ExpressionAttributeValues: { @@ -20,7 +20,7 @@ export const buildConversationUpdateParams = (conversationId, changes) => { }; export const isConversation = (item) => { - return item.Layer === 'Conversation'; + return item.Layer === RecordType.CONVERSATION; }; export const isInCompleteStatus = (conversation) => { diff --git a/src/models/core.js b/src/models/core.js index 3d45e888..b152b069 100644 --- a/src/models/core.js +++ b/src/models/core.js @@ -1,5 +1,5 @@ import { getUKTimestamp } from '../services/time'; -import { CoreStatus } from './enums'; +import { CoreStatus, RecordType } from "./enums"; import { validateIds } from '../utilities/dynamodb-helper'; export const buildCore = (inboundConversationId, messageId) => { @@ -9,7 +9,7 @@ export const buildCore = (inboundConversationId, messageId) => { return { InboundConversationId: inboundConversationId, - Layer: `Core#${messageId}`, + Layer: [RecordType.CORE, messageId].join('#'), InboundMessageId: messageId, CreatedAt: timestamp, ReceivedAt: timestamp, @@ -19,5 +19,5 @@ export const buildCore = (inboundConversationId, messageId) => { }; export const isCore = (dynamoDbItem) => { - return dynamoDbItem?.Layer?.startsWith('Core'); + return dynamoDbItem?.Layer?.startsWith(RecordType.CORE); }; diff --git a/src/models/enums.js b/src/models/enums.js index 0664cbb9..ff33a8b3 100644 --- a/src/models/enums.js +++ b/src/models/enums.js @@ -1,8 +1,8 @@ export const RecordType = { ALL: 'ALL', - CONVERSATION: 'Conversation', - CORE: 'Core', - FRAGMENT: 'Fragment', + CONVERSATION: 'CONVERSATION', + CORE: 'CORE', + FRAGMENT: 'FRAGMENT', }; export const ConversationStatus = { diff --git a/src/models/fragment.js b/src/models/fragment.js index cd8d574f..a524cf6d 100644 --- a/src/models/fragment.js +++ b/src/models/fragment.js @@ -1,5 +1,6 @@ import { getUKTimestamp } from '../services/time'; import { addChangesToUpdateParams, validateIds } from "../utilities/dynamodb-helper"; +import { RecordType } from "./enums"; const fieldsAllowedToUpdate = ['TransferStatus', 'ParentId', 'ReceivedAt', 'DeletedAt']; @@ -10,7 +11,7 @@ export const buildFragment = ({ inboundConversationId, fragmentMessageId, parent return { InboundConversationId: inboundConversationId, - Layer: `Fragment#${fragmentMessageId}`, + Layer: [RecordType.FRAGMENT, fragmentMessageId].join('#'), InboundMessageId: fragmentMessageId, ParentId: parentMessageId, CreatedAt: timestamp, @@ -37,7 +38,7 @@ export const buildFragmentUpdateParams = (conversationId, messageId, changes) => const params = { Key: { InboundConversationId: conversationId, - Layer: `Fragment#${messageId}`, + Layer: [RecordType.FRAGMENT, messageId].join('#'), }, UpdateExpression: `set CreatedAt = if_not_exists(CreatedAt, :now), \ InboundMessageId = if_not_exists(InboundMessageId, :messageId), \ @@ -52,5 +53,5 @@ export const buildFragmentUpdateParams = (conversationId, messageId, changes) => }; export const isFragment = (dynamoDbItem) => { - return dynamoDbItem?.Layer?.startsWith('Fragment'); + return dynamoDbItem?.Layer?.startsWith(RecordType.FRAGMENT); }; diff --git a/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js b/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js index 3048b5a9..11fe2f78 100644 --- a/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js +++ b/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js @@ -37,7 +37,7 @@ describe('EhrTransferTracker', () => { expect(actual[0]).toMatchObject({ InboundConversationId: testConversationId, InboundMessageId: testMessageId, - Layer: `Core#${testMessageId}`, + Layer: `CORE#${testMessageId}`, ReceivedAt: expect.any(String), CreatedAt: expect.any(String), UpdatedAt: expect.any(String), diff --git a/src/services/database/__tests__/ehr-core-repository.integration.test.js b/src/services/database/__tests__/ehr-core-repository.integration.test.js index cdcc1aa8..8ae971b2 100644 --- a/src/services/database/__tests__/ehr-core-repository.integration.test.js +++ b/src/services/database/__tests__/ehr-core-repository.integration.test.js @@ -30,7 +30,7 @@ describe('ehr-core-repository', () => { expect(actualMessage.InboundMessageId).toBe(messageId); expect(actualMessage.InboundConversationId).toBe(conversationId); - expect(actualMessage.Layer).toBe(`Core#${messageId}`); + expect(actualMessage.Layer).toBe(`CORE#${messageId}`); expect(actualMessage.ReceivedAt).toEqual(expectedTimestamp); }); @@ -51,7 +51,7 @@ describe('ehr-core-repository', () => { // then const fragmentMessage = await getFragmentByKey(conversationId, fragmentMessageId); expect(fragmentMessage.InboundConversationId).toBe(conversationId); - expect(fragmentMessage.Layer).toBe(`Fragment#${fragmentMessageId}`); + expect(fragmentMessage.Layer).toBe(`FRAGMENT#${fragmentMessageId}`); expect(fragmentMessage.InboundMessageId).toBe(fragmentMessageId); expect(fragmentMessage.ParentId).toBe(messageId); expect(fragmentMessage.ReceivedAt).toBeUndefined(); diff --git a/src/services/database/__tests__/ehr-fragment-repository.integration.test.js b/src/services/database/__tests__/ehr-fragment-repository.integration.test.js index a7d43d7f..7e2f3e80 100644 --- a/src/services/database/__tests__/ehr-fragment-repository.integration.test.js +++ b/src/services/database/__tests__/ehr-fragment-repository.integration.test.js @@ -177,7 +177,7 @@ describe('ehr-fragment-repository', () => { expect(fragment.InboundConversationId).toEqual(conversationId); expect(fragment.ReceivedAt).toEqual(expectedTimestamp); - expect(fragment.Layer).toEqual(`Fragment#${messageId}`); + expect(fragment.Layer).toEqual(`FRAGMENT#${messageId}`); expect(fragment.ParentId).toBeUndefined(); }); diff --git a/src/utilities/integration-test-utilities.js b/src/utilities/integration-test-utilities.js index 34f26d9a..b927e358 100644 --- a/src/utilities/integration-test-utilities.js +++ b/src/utilities/integration-test-utilities.js @@ -27,7 +27,7 @@ export const createConversationForTest = async (conversationId, nhsNumber, overr const item = { InboundConversationId: conversationId, - Layer: 'Conversation', + Layer: RecordType.CONVERSATION, NhsNumber: nhsNumber, CreatedAt: timestamp, UpdatedAt: timestamp, From 5c021a262e3c62836172839dc1c11fa339fc1294 Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Mon, 11 Mar 2024 10:35:49 +0000 Subject: [PATCH 47/67] [PRMT-4567] use the string "STORED_IN_REPOSITORY" for complete status of CORE and FRAGMENT --- src/models/conversation.js | 6 +++--- src/models/enums.js | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/models/conversation.js b/src/models/conversation.js index e2d33427..a780bacd 100644 --- a/src/models/conversation.js +++ b/src/models/conversation.js @@ -1,6 +1,6 @@ import { getUKTimestamp } from '../services/time'; import { addChangesToUpdateParams } from '../utilities/dynamodb-helper'; -import { ConversationStatus, RecordType } from "./enums"; +import { ConversationStatus, RecordType } from './enums'; const fieldsAllowedToUpdate = ['TransferStatus', 'FailureCode', 'DeletedAt']; @@ -25,5 +25,5 @@ export const isConversation = (item) => { export const isInCompleteStatus = (conversation) => { const status = conversation?.TransferStatus; - return status === ConversationStatus.COMPLETE || status?.startsWith('Outbound') -} \ No newline at end of file + return status === ConversationStatus.COMPLETE || status?.startsWith('Outbound'); +}; diff --git a/src/models/enums.js b/src/models/enums.js index ff33a8b3..65f16136 100644 --- a/src/models/enums.js +++ b/src/models/enums.js @@ -15,11 +15,11 @@ export const ConversationStatus = { }; export const CoreStatus = { - COMPLETE: 'INBOUND_COMPLETE', + COMPLETE: 'STORED_IN_REPOSITORY', }; export const FragmentStatus = { - COMPLETE: 'INBOUND_COMPLETE', + COMPLETE: 'STORED_IN_REPOSITORY', }; export const HealthRecordStatus = { From 6d1ffacffb8befdc0f2824e3a90fcc724850dd68 Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Mon, 11 Mar 2024 11:28:01 +0000 Subject: [PATCH 48/67] [PRMT-4567] Fixed an issue related to DeletedAt time and DST --- src/__tests__/app.integration.test.js | 13 +++++++------ src/utilities/dynamodb-helper.js | 4 ++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/__tests__/app.integration.test.js b/src/__tests__/app.integration.test.js index d1eef960..97209898 100644 --- a/src/__tests__/app.integration.test.js +++ b/src/__tests__/app.integration.test.js @@ -16,7 +16,8 @@ import { ConversationStatus, MessageType, RecordType } from "../models/enums"; import { isCore } from '../models/core'; import { getFragmentByKey } from '../services/database/ehr-fragment-repository'; import { isFragment } from '../models/fragment'; -import { TIMESTAMP_REGEX } from '../services/time'; +import { getEpochTimeInSecond, TIMESTAMP_REGEX } from "../services/time"; +import moment from "moment-timezone"; describe('app', () => { const config = initializeConfig(); @@ -626,7 +627,7 @@ describe('app', () => { conversationIdFromEhrIn: inboundConversationId, fragmentMessageIds: expect.arrayContaining(fragmentMessageIds), }); - const timestampBeforeDelete = Math.floor(new Date() / 1000); + const timestampBeforeDelete = getEpochTimeInSecond(moment().add(8, 'week')); // ============================= when ================================== const deleteResponse = await request(app) @@ -650,16 +651,16 @@ describe('app', () => { ); expect(softDeletedRecords).toHaveLength(5); // Conversation + Core + 3 Fragments - const timestampAfterDelete = Math.ceil(new Date() / 1000); - const eightWeeksInSeconds = 60 * 60 * 24 * 7 * 8; + const timestampAfterDelete = getEpochTimeInSecond(moment().add(8, 'week')); + for (const item of softDeletedRecords) { expect(item).toMatchObject({ InboundConversationId: inboundConversationId, DeletedAt: expect.any(Number), }); - expect(item.DeletedAt).toBeGreaterThanOrEqual(timestampBeforeDelete + eightWeeksInSeconds); - expect(item.DeletedAt).toBeLessThanOrEqual(timestampAfterDelete + eightWeeksInSeconds); + expect(item.DeletedAt).toBeGreaterThanOrEqual(timestampBeforeDelete); + expect(item.DeletedAt).toBeLessThanOrEqual(timestampAfterDelete); } }); diff --git a/src/utilities/dynamodb-helper.js b/src/utilities/dynamodb-helper.js index c6f5ebff..34038642 100644 --- a/src/utilities/dynamodb-helper.js +++ b/src/utilities/dynamodb-helper.js @@ -1,7 +1,7 @@ import { logError } from '../middleware/logging'; import { validate } from 'uuid'; import { getEpochTimeInSecond, getUKTimestamp } from '../services/time'; -import moment from "moment-timezone"; +import moment from 'moment-timezone'; export const validateIds = (conversationId, messageId) => { const uuidsAreValid = validate(conversationId) && validate(messageId); @@ -28,7 +28,7 @@ export const addChangesToUpdateParams = (params, changes, fieldsAllowedToUpdate) }; export const buildSoftDeleteUpdateParams = (item) => { - const eightWeeksAfter = moment().add(8, 'week'); + const eightWeeksAfter = moment().add({ weeks: 8, hour: 0 }); // hour: 0 for enforce correct precise time diff related to DST return { Key: { From e349466f5a0cf4d5be36c67784834b0ff6f85458 Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Mon, 11 Mar 2024 10:01:41 +0000 Subject: [PATCH 49/67] [PRMT-4568] Remove postgres-db related code --- scripts/wait-for-postgres.js | 27 -- src/api/fragments/get-fragment-controller.js | 29 -- .../__tests__/health-check.test.js | 36 -- .../messages/message-location-controller.js | 24 -- src/api/messages/store-message-controller.js | 39 -- .../patients/patient-details-controller.js | 39 -- src/config/database.js | 29 -- src/models/health-check.js | 32 -- src/models/health-record.js | 49 --- src/models/index.js | 112 ----- src/models/message.js | 64 --- src/models/models.js | 12 - src/models/parameters.js | 26 -- .../check-db-health.integration.test.js | 25 -- ...alth-record-repository.integration.test.js | 397 ------------------ .../message-repository.integration.test.js | 282 ------------- src/services/database/check-db-health.js | 59 --- .../database/health-record-repository.js | 201 --------- src/services/database/message-repository.js | 135 ------ src/services/database/pg-error-codes.js | 12 - .../get-health-check.integration.test.js | 34 -- .../__tests__/get-health-check.test.js | 25 -- 22 files changed, 1688 deletions(-) delete mode 100644 scripts/wait-for-postgres.js delete mode 100644 src/config/database.js delete mode 100644 src/models/health-check.js delete mode 100644 src/models/health-record.js delete mode 100644 src/models/index.js delete mode 100644 src/models/message.js delete mode 100644 src/models/models.js delete mode 100644 src/models/parameters.js delete mode 100644 src/services/database/__tests__/check-db-health.integration.test.js delete mode 100644 src/services/database/__tests__/health-record-repository.integration.test.js delete mode 100644 src/services/database/__tests__/message-repository.integration.test.js delete mode 100644 src/services/database/check-db-health.js delete mode 100644 src/services/database/health-record-repository.js delete mode 100644 src/services/database/message-repository.js delete mode 100644 src/services/database/pg-error-codes.js delete mode 100644 src/services/health-check/__tests__/get-health-check.integration.test.js diff --git a/scripts/wait-for-postgres.js b/scripts/wait-for-postgres.js deleted file mode 100644 index dc3ecc32..00000000 --- a/scripts/wait-for-postgres.js +++ /dev/null @@ -1,27 +0,0 @@ -/** - * @deprecated - * Postgres DB related stubs - * To be deleted PRMT-4568 - */ -// import ModelFactory from '../src/models'; - -// const INTERVAL = 1000; -// let ATTEMPTS = 10; - -// const connect = () => { - // ModelFactory.sequelize - // .authenticate() - // .then(() => { - // console.log('Successfully connected'); - // }) - // .catch(() => { - // if (ATTEMPTS === 0) { - // throw new Error('Maximum number of attempts have been reached, exiting.'); - // } - // console.log(`Attempt ${ATTEMPTS} to connect failed`); - // ATTEMPTS--; - // setTimeout(connect, INTERVAL); - // }); -// }; - -// connect(); diff --git a/src/api/fragments/get-fragment-controller.js b/src/api/fragments/get-fragment-controller.js index d2733082..c3577ead 100644 --- a/src/api/fragments/get-fragment-controller.js +++ b/src/api/fragments/get-fragment-controller.js @@ -3,41 +3,12 @@ import { param } from 'express-validator'; import { logError, logInfo } from '../../middleware/logging'; import { setCurrentSpanAttributes } from '../../config/tracing'; import { fragmentAlreadyReceived } from "../../services/database/ehr-fragment-repository"; -// import { messageAlreadyReceived } from '../../services/database/health-record-repository'; export const getFragmentControllerValidation = [ param('conversationId').isUUID().withMessage("'conversationId' provided is not a UUID"), param('messageId').isUUID().withMessage("'messageId' provided is not a UUID"), ]; -// export const getFragmentController = async (req, res) => { -// /** -// * @deprecated -// * To be deleted PRMT-4568 -// */ -// const { conversationId, messageId } = req.params; -// setCurrentSpanAttributes({ conversationId, messageId }); -// const operation = 'getObject'; -// -// try { -// const noFragmentRecordFound = !(await messageAlreadyReceived(messageId)); -// if (noFragmentRecordFound) { -// logInfo('NO FRAGMENT RECORD: Could not find message Id: ' + messageId + ' in the database'); -// res.sendStatus(404); -// return; -// } -// -// logInfo(`Retrieving presigned url to download the fragment with message id: ${messageId}`); -// -// const presignedUrl = await getSignedUrl(conversationId, messageId, operation); -// res.status(200).send(presignedUrl); -// logInfo('Presigned URL sent successfully'); -// } catch (err) { -// logError('Failed to retrieve pre-signed url', err); -// res.sendStatus(503); -// } -// }; - export const getFragmentController = async (req, res) => { const { conversationId, messageId } = req.params; setCurrentSpanAttributes({ conversationId, messageId }); diff --git a/src/api/health-check/__tests__/health-check.test.js b/src/api/health-check/__tests__/health-check.test.js index b8f5086e..26583af2 100644 --- a/src/api/health-check/__tests__/health-check.test.js +++ b/src/api/health-check/__tests__/health-check.test.js @@ -78,42 +78,6 @@ describe('GET /health', () => { }); }); - /** - * @deprecated - * postgres-db specific tests - * To be deleted PRMT-4568 - */ - describe.skip('database is not writable', () => { - beforeEach(() => { - getHealthCheck.mockReturnValue(Promise.resolve(expectedHealthCheckBase(true, true, false))); - }); - - it('should return 503 status if db writable is false', (done) => { - request(app).get('/health').expect(503).end(done); - }); - - it('should return details of the response from getHealthCheck when the database writable is false', (done) => { - request(app) - .get('/health') - .expect((res) => { - expect(res.body).toEqual(expectedHealthCheckBase(true, true, false)); - }) - .end(done); - }); - - it('should call logError with the health check result if db writable is false', (done) => { - request(app) - .get('/health') - .expect(() => { - expect(logError).toHaveBeenCalledWith( - 'Health check failed', - expectedHealthCheckBase(true, true, false) - ); - }) - .end(done); - }); - }); - describe('s3 is not available', () => { beforeEach(() => { getHealthCheck.mockReturnValue(Promise.resolve(expectedHealthCheckBase(true, false))); diff --git a/src/api/messages/message-location-controller.js b/src/api/messages/message-location-controller.js index 81b42912..dd7e1a06 100644 --- a/src/api/messages/message-location-controller.js +++ b/src/api/messages/message-location-controller.js @@ -10,30 +10,6 @@ export const messageLocationControllerValidation = [ param('messageId').isUUID().withMessage("'messageId' provided is not a UUID"), ]; -// @deprecated -// to be deleted PRMT-4568 -// -// export const messageLocationController = async (req, res) => { -// const { conversationId, messageId } = req.params; -// setCurrentSpanAttributes({ conversationId, messageId }); -// const operation = 'putObject'; -// -// try { -// if (await messageAlreadyReceived(messageId)) { -// logInfo('DUPLICATE: Message Id: ' + messageId + ' already received in the database'); -// res.sendStatus(409); -// return; -// } -// logInfo(`Retrieving presigned url to upload the fragment with message id: ${messageId}`); -// const presignedUrl = await getSignedUrl(conversationId, messageId, operation); -// res.status(200).send(presignedUrl); -// logInfo('Presigned URL sent successfully'); -// } catch (err) { -// logError('Failed to retrieve pre-signed url', err); -// res.sendStatus(503); -// } -// }; - export const messageLocationController = async (req, res) => { const { conversationId, messageId } = req.params; setCurrentSpanAttributes({ conversationId, messageId }); diff --git a/src/api/messages/store-message-controller.js b/src/api/messages/store-message-controller.js index 801973dc..8fe166eb 100644 --- a/src/api/messages/store-message-controller.js +++ b/src/api/messages/store-message-controller.js @@ -53,45 +53,6 @@ export const storeMessageControllerValidation = [ .withMessage("'fragmentMessageIds' should be an array"), ]; -// @deprecated -// to be deleted PRMT-4568 -// -// export const storeMessageController = async (req, res) => { -// const { id, attributes } = req.body.data; -// const { conversationId, messageType, nhsNumber, fragmentMessageIds } = attributes; -// setCurrentSpanAttributes({ conversationId, messageId: id }); -// -// try { -// if (messageType === MessageType.EHR_EXTRACT) { -// await createEhrExtract({ -// messageId: id, -// conversationId, -// nhsNumber, -// fragmentMessageIds, -// }); -// } -// if (messageType === MessageType.FRAGMENT) { -// if (await fragmentExists(id)) { -// await updateFragmentAndCreateItsParts(id, conversationId, fragmentMessageIds); -// } else { -// logWarning( -// `Fragment message ${id} did not arrive in order. Fragment parts: ${JSON.stringify( -// fragmentMessageIds -// )}` -// ); -// await createFragmentPart(id, conversationId); -// } -// } -// await updateHealthRecordCompleteness(conversationId); -// const healthRecordStatus = await getHealthRecordStatus(conversationId); -// -// logInfo('Health record status for fragments: ' + healthRecordStatus); -// res.status(201).json({ healthRecordStatus }); -// } catch (e) { -// logError('Returned 503 due to error while saving message', e); -// res.sendStatus(503); -// } -// }; export const storeMessageController = async (req, res) => { const { id: messageId, attributes } = req.body.data; const { conversationId, messageType, fragmentMessageIds } = attributes; diff --git a/src/api/patients/patient-details-controller.js b/src/api/patients/patient-details-controller.js index 121c1891..2fdf4d22 100644 --- a/src/api/patients/patient-details-controller.js +++ b/src/api/patients/patient-details-controller.js @@ -20,45 +20,6 @@ export const patientDetailsValidation = [ .withMessage("'nhsNumber' provided is not 10 characters"), ]; -// @deprecated -// to be deleted PRMT-4568 -// -// export const patientDetailsController = async (req, res) => { -// const { nhsNumber } = req.params; -// addConversationIdToLogContext(req.get('conversationId')); -// -// try { -// const currentHealthRecordConversationId = await getCurrentHealthRecordIdForPatient(nhsNumber); -// if (!currentHealthRecordConversationId) { -// logInfo('Did not find a complete patient health record'); -// res.sendStatus(404); -// return; -// } -// -// logInfo('Getting fragment message ids'); -// const { coreMessageId, fragmentMessageIds } = await getHealthRecordMessageIds( -// currentHealthRecordConversationId -// ); -// -// const getOperation = 'getObject'; -// const coreMessageUrl = await getSignedUrl( -// currentHealthRecordConversationId, -// coreMessageId, -// getOperation -// ); -// -// const responseBody = { -// coreMessageUrl, -// fragmentMessageIds, -// conversationIdFromEhrIn: currentHealthRecordConversationId, -// }; -// -// res.status(200).json(responseBody); -// } catch (err) { -// logError('Could not retrieve patient health record', err); -// res.sendStatus(503); -// } -// }; export const patientDetailsController = async (req, res) => { const { nhsNumber } = req.params; addConversationIdToLogContext(req.get('conversationId')); diff --git a/src/config/database.js b/src/config/database.js deleted file mode 100644 index 25a716ee..00000000 --- a/src/config/database.js +++ /dev/null @@ -1,29 +0,0 @@ -const use_ssl = process.env.USE_SSL_FOR_DB === 'true'; -const use_rds_credentials = process.env.USE_AWS_RDS_CREDENTIALS === 'true'; - -/** - * @deprecated - * Postgres DB related stubs - * To be deleted PRMT-4568 - */ -const sequelizeConfig = { - username: process.env.DATABASE_USER, - password: process.env.DATABASE_PASSWORD, - database: process.env.DATABASE_NAME, - host: process.env.DATABASE_HOST, - dialect: 'postgres', - logging: false, - use_rds_credentials, -}; - -if (use_ssl) { - sequelizeConfig.ssl = use_ssl; - sequelizeConfig.dialectOptions = { - // see https://node-postgres.com/features/ssl - ssl: { - rejectUnauthorized: false, - }, - }; -} - -// module.exports = sequelizeConfig; diff --git a/src/models/health-check.js b/src/models/health-check.js deleted file mode 100644 index dcbc9218..00000000 --- a/src/models/health-check.js +++ /dev/null @@ -1,32 +0,0 @@ -import getParameters from './parameters'; - -/** - * @deprecated - * Postgres DB related stubs - * To be deleted PRMT-4568 - */ -export const modelName = 'HealthCheck'; -const tableName = 'health_checks'; - -const model = (dataType) => ({ - id: { - type: dataType.UUID, - primaryKey: true, - defaultValue: dataType.UUIDV4, - }, - created_at: { - type: dataType.DATE, - allowNull: false, - }, - updated_at: { - type: dataType.DATE, - allowNull: false, - }, - deleted_at: dataType.DATE, -}); - -export default (sequelize, DataTypes) => { - return sequelize.define(modelName, model(DataTypes), { - ...getParameters(tableName), - }); -}; diff --git a/src/models/health-record.js b/src/models/health-record.js deleted file mode 100644 index 8657d3ac..00000000 --- a/src/models/health-record.js +++ /dev/null @@ -1,49 +0,0 @@ -import { getParametersRefactored } from './parameters'; - -/** - * @deprecated - * Postgres DB related stubs - * To be deleted PRMT-4568 - */ -export const modelName = 'HealthRecord'; -const tableName = 'health_records'; - -const model = (dataType) => ({ - conversationId: { - field: 'conversation_id', - type: dataType.UUID, - primaryKey: true, - defaultValue: dataType.UUIDV4, - }, - nhsNumber: { - field: 'nhs_number', - type: dataType.CHAR(10), - validate: { - isNumeric: true, - len: 10, - }, - allowNull: false, - }, - completedAt: { - field: 'completed_at', - type: dataType.DATE, - }, - createdAt: { - field: 'created_at', - type: dataType.DATE, - allowNull: false, - }, - updatedAt: { - field: 'updated_at', - type: dataType.DATE, - allowNull: false, - }, - deletedAt: { - field: 'deleted_at', - type: dataType.DATE, - }, -}); - -export default (sequelize, DataTypes) => { - return sequelize.define(modelName, model(DataTypes), getParametersRefactored(tableName)); -}; diff --git a/src/models/index.js b/src/models/index.js deleted file mode 100644 index 28599a99..00000000 --- a/src/models/index.js +++ /dev/null @@ -1,112 +0,0 @@ -import Sequelize from 'sequelize'; -import { initializeConfig } from '../config'; -import * as models from './models'; -import { Signer } from 'aws-sdk/clients/rds'; -import AWS from 'aws-sdk'; -import { logError, logInfo } from '../middleware/logging'; - -class ModelFactory { - /** - * @deprecated - * Postgres DB related stubs - * To be deleted PRMT-4568 - */ - constructor() { - this.db = {}; - this.sequelize = {}; - this.config = initializeConfig().sequelize; - this._resetConfig(); - } - - _overrideConfig(key, value) { - this.base_config[key] = value; - this.configure(); - } - - _resetConfig() { - this.base_config = this.config; - this.configure(); - } - - configure() { - if (this.sequelize instanceof Sequelize) { - this.sequelize.close(); - } - - let signer, getAuthTokenAsync; - if (this.base_config.use_rds_credentials) { - signer = new Signer({ - credentials: new AWS.RemoteCredentials({ - httpOptions: { timeout: 5000 }, // 5 second timeout - maxRetries: 10, // retry 10 times - retryDelayOptions: { base: 200 }, // see AWS.Config for information - }), - region: 'eu-west-2', - username: this.base_config.username, - hostname: this.base_config.host, - port: 5432, - }); - - getAuthTokenAsync = () => - new Promise((resolve, reject) => { - signer.getAuthToken((err, token) => { - if (err) { - reject(err); - } else { - resolve(token); - } - }); - }); - } - - this.sequelize = new Sequelize( - this.base_config.database, - this.base_config.username, - this.base_config.password, - this.base_config - ); - - if (this.base_config.use_rds_credentials) { - this.sequelize.beforeConnect(async (config) => { - logInfo('Obtaining new RDS DB Auth token'); - try { - config.password = await getAuthTokenAsync(); - } catch (err) { - logError('Error while retrieving auth token for RDS ', err); - } - }); - } - - this.sequelize - .authenticate() - .then(() => logInfo('DB Connection has been established successfully.')) - .catch((e) => logError('Unable to connect to the database:', e)); - - this.reload_models(); - } - - reload_models() { - this.db = {}; - - for (const m in models) { - const model = models[m](this.sequelize, Sequelize.DataTypes); - this.db[model.name] = model; - } - - Object.keys(this.db).forEach((modelName) => { - if (this.db[modelName].associate) { - this.db[modelName].associate(this.db); - } - }); - - this.db.sequelize = this.sequelize; - this.db.Sequelize = Sequelize; - } - - getByName(moduleName) { - return this.db[moduleName]; - } -} - -// export default new ModelFactory(); -export default {}; \ No newline at end of file diff --git a/src/models/message.js b/src/models/message.js deleted file mode 100644 index 92dadff5..00000000 --- a/src/models/message.js +++ /dev/null @@ -1,64 +0,0 @@ -import { getParametersRefactored } from './parameters'; - -/** - * @deprecated - * Postgres DB related stubs - * To be deleted PRMT-4568 - */ -export const modelName = 'Message'; -const tableName = 'messages'; - -export const MessageType = { - EHR_EXTRACT: 'ehrExtract', - FRAGMENT: 'fragment', -}; - -Object.freeze(MessageType); - -const model = (dataType) => ({ - messageId: { - field: 'message_id', - type: dataType.UUID, - primaryKey: true, - defaultValue: dataType.UUIDV4, - }, - conversationId: { - field: 'conversation_id', - type: dataType.UUID, - allowNull: false, - }, - parentId: { - field: 'parent_id', - type: dataType.UUID, - }, - type: { - field: 'type', - type: dataType.STRING, - validate: { - isIn: [Object.values(MessageType)], - }, - allowNull: false, - }, - receivedAt: { - field: 'received_at', - type: dataType.DATE, - }, - createdAt: { - field: 'created_at', - type: dataType.DATE, - allowNull: false, - }, - updatedAt: { - field: 'updated_at', - type: dataType.DATE, - allowNull: false, - }, - deletedAt: { - field: 'deleted_at', - type: dataType.DATE, - }, -}); - -export default (sequelize, DataTypes) => { - return sequelize.define(modelName, model(DataTypes), getParametersRefactored(tableName)); -}; diff --git a/src/models/models.js b/src/models/models.js deleted file mode 100644 index 3ed1e1da..00000000 --- a/src/models/models.js +++ /dev/null @@ -1,12 +0,0 @@ -// import HealthCheck from './health-check'; -// import Message from './message'; -// import HealthRecord from './health-record'; -// -// /** -// * @deprecated -// * Postgres DB related stubs -// * To be deleted PRMT-4568 -// */ -// export { HealthCheck }; -// export { Message }; -// export { HealthRecord }; diff --git a/src/models/parameters.js b/src/models/parameters.js deleted file mode 100644 index 2ef58ca4..00000000 --- a/src/models/parameters.js +++ /dev/null @@ -1,26 +0,0 @@ -/** - * @deprecated - * Postgres DB related stubs - * To be deleted PRMT-4568 - */ -const getParameters = (tableName) => ({ - tableName: tableName, - createdAt: 'created_at', - updatedAt: 'updated_at', - deletedAt: 'deleted_at', - timestamps: true, - schema: 'public', - paranoid: true, -}); - -export const getParametersRefactored = (tableName) => ({ - tableName: tableName, - createdAt: 'createdAt', - updatedAt: 'updatedAt', - deletedAt: 'deletedAt', - timestamps: true, - schema: 'public', - paranoid: true, -}); - -export default getParameters; diff --git a/src/services/database/__tests__/check-db-health.integration.test.js b/src/services/database/__tests__/check-db-health.integration.test.js deleted file mode 100644 index b90950c0..00000000 --- a/src/services/database/__tests__/check-db-health.integration.test.js +++ /dev/null @@ -1,25 +0,0 @@ -/** - * @deprecated - * Postgres DB related stubs - * To be deleted PRMT-4568 - */ -import { checkDbHealth } from '../check-db-health'; -// import ModelFactory from '../../../models'; - -describe.skip('db', () => { - afterAll(() => { - // ModelFactory.sequelize.close(); - }); - - describe('checkDbHealth', () => { - it('should return the db health', () => { - return checkDbHealth().then((value) => { - expect(value).toEqual({ - type: 'postgresql', - connection: true, - writable: true, - }); - }); - }); - }); -}); diff --git a/src/services/database/__tests__/health-record-repository.integration.test.js b/src/services/database/__tests__/health-record-repository.integration.test.js deleted file mode 100644 index a8600c9e..00000000 --- a/src/services/database/__tests__/health-record-repository.integration.test.js +++ /dev/null @@ -1,397 +0,0 @@ -import { v4 as uuid } from 'uuid'; -// import { -// getHealthRecordStatus, -// updateHealthRecordCompleteness, -// getCurrentHealthRecordIdForPatient, -// getHealthRecordMessageIds, -// messageAlreadyReceived, -// markHealthRecordAsDeletedForPatient, -// } from '../health-record-repository'; -// import ModelFactory from '../../../models'; -// import { modelName as healthRecordModelName } from '../../../models/health-record'; -// import { MessageType, modelName as messageModelName } from '../../../models/message'; -import { logError } from '../../../middleware/logging'; -import { HealthRecordStatus } from "../../../models/enums"; - -// jest.mock('../../../middleware/logging'); - -describe.skip('healthRecordRepository', () => { - /** - * @deprecated - * All the tests here are migrated to new test suite of dynamodb-based implementation - * To be deleted PRMT-4568 - */ - // // ========================= COMMON PROPERTIES ========================= - // const HealthRecord = ModelFactory.getByName(healthRecordModelName); - // const Message = ModelFactory.getByName(messageModelName); - // // ===================================================================== - // - // // ========================= SET UP / TEAR DOWN ======================== - // beforeEach(async () => { - // await HealthRecord.truncate(); - // await Message.truncate(); - // await ModelFactory.sequelize.sync({ force: true }); - // }); - // - // afterAll(async () => { - // await ModelFactory.sequelize.close(); - // }); - // // ===================================================================== - // - describe('getHealthRecordStatus', () => { - it("should return status 'complete' when health record 'completedAt' field is not null", async () => { - const conversationId = uuid(); - const nhsNumber = '1234567890'; - - await HealthRecord.create({ conversationId, nhsNumber, completedAt: new Date() }); - const status = await getHealthRecordStatus(conversationId); - - expect(status).toEqual(HealthRecordStatus.COMPLETE); - }); - - it("should return status 'pending' when health record 'completedAt' field is null", async () => { - const conversationId = uuid(); - const nhsNumber = '1234567890'; - - await HealthRecord.create({ conversationId, nhsNumber, completedAt: null }); - const status = await getHealthRecordStatus(conversationId); - - expect(status).toEqual(HealthRecordStatus.PENDING); - }); - - it("should return status 'notFound' when health record is not found", async () => { - const conversationId = uuid(); - const status = await getHealthRecordStatus(conversationId); - - expect(status).toEqual(HealthRecordStatus.NOT_FOUND); - }); - - it('should throw error if there is a problem retrieving health record from database', async () => { - const conversationId = 'not-a-uuid'; - try { - await getHealthRecordStatus(conversationId); - } catch (err) { - expect(err).not.toBeNull(); - expect(logError).toHaveBeenCalledWith( - 'Health Record could not be retrieved from database', - err - ); - } - }); - }); - - describe('updateHealthRecordCompleteness', () => { - it("should set 'completedAt' property for a small health record", async () => { - const conversationId = uuid(); - const messageId = uuid(); - const nhsNumber = '1234567890'; - - await HealthRecord.create({ conversationId, nhsNumber, completedAt: null }); - await Message.create({ - conversationId, - messageId, - type: MessageType.EHR_EXTRACT, - receivedAt: new Date(), - }); - - await updateHealthRecordCompleteness(conversationId); - const healthRecord = await HealthRecord.findByPk(conversationId); - - expect(healthRecord.completedAt).not.toBeNull(); - }); - - it("should not set 'completedAt' property when there are still messages to be received", async () => { - const conversationId = uuid(); - const messageId = uuid(); - const fragmentMessageId = uuid(); - const nhsNumber = '1234567890'; - - await HealthRecord.create({ conversationId, nhsNumber, completedAt: null }); - await Message.create({ - conversationId, - messageId, - type: MessageType.EHR_EXTRACT, - receivedAt: new Date(), - }); - await Message.create({ - conversationId, - messageId: fragmentMessageId, - type: MessageType.FRAGMENT, - receivedAt: null, - }); - - await updateHealthRecordCompleteness(conversationId); - const healthRecord = await HealthRecord.findByPk(conversationId); - - expect(healthRecord.completedAt).toBeNull(); - }); - - it('should throw an error when database query fails', async () => { - const conversationId = 'not-valid'; - try { - await updateHealthRecordCompleteness(conversationId); - } catch (err) { - expect(err).not.toBeNull(); - expect(logError).toHaveBeenCalledWith('Failed to update health record completeness', err); - } - }); - }); - - describe('getCurrentHealthRecordIdForPatient', () => { - it('should return most recent complete health record conversation id', async () => { - const nhsNumber = '9876543210'; - const previousHealthRecordConversationId = uuid(); - const incompleteHealthRecordConversationId = uuid(); - const currentHealthRecordConversationId = uuid(); - - await HealthRecord.create({ - conversationId: previousHealthRecordConversationId, - nhsNumber, - completedAt: new Date(), - }); - await HealthRecord.create({ - conversationId: incompleteHealthRecordConversationId, - nhsNumber, - completedAt: null, - }); - await HealthRecord.create({ - conversationId: currentHealthRecordConversationId, - nhsNumber, - completedAt: new Date(), - }); - - const currentHealthRecordId = await getCurrentHealthRecordIdForPatient(nhsNumber); - - expect(currentHealthRecordId).toEqual(currentHealthRecordConversationId); - }); - - it('should return undefined if no complete health record is found', async () => { - const nhsNumber = '9876543211'; - const incompleteHealthRecordConversationId = uuid(); - - await HealthRecord.create({ - conversationId: incompleteHealthRecordConversationId, - nhsNumber, - completedAt: null, - }); - - const currentHealthRecordId = await getCurrentHealthRecordIdForPatient(nhsNumber); - - expect(currentHealthRecordId).toBeUndefined(); - }); - - it('should return undefined when cannot find any health record', async () => { - const nhsNumber = '1111111112'; - const currentHealthRecordId = await getCurrentHealthRecordIdForPatient(nhsNumber); - - expect(currentHealthRecordId).toBeUndefined(); - }); - }); - - describe('getHealthRecordMessageIds', () => { - it('should throw a meaningful error if there are no undeleted messages associated with conversation id', async () => { - const conversationId = uuid(); - await Message.create({ - messageId: uuid(), - conversationId, - type: MessageType.EHR_EXTRACT, - receivedAt: new Date(), - deletedAt: new Date(), // nb magical sequelize "paranoid" deletion - }); - - try { - await getHealthRecordMessageIds(conversationId); - fail('should have thrown'); - } catch (e) { - expect(e.message).toEqual( - 'There were no undeleted messages associated with conversation id' - ); - } - }); - - it('should return health record extract message id given a conversation id for a small health record', async () => { - const messageId = uuid(); - const conversationId = uuid(); - - await Message.create({ - messageId, - conversationId, - type: MessageType.EHR_EXTRACT, - receivedAt: new Date(), - }); - const { coreMessageId, fragmentMessageIds } = await getHealthRecordMessageIds(conversationId); - - expect(coreMessageId).toEqual(messageId); - expect(fragmentMessageIds).toEqual([]); - }); - - it('should return health record extract message id and fragment message ids given singular fragment', async () => { - const messageId = uuid(); - const conversationId = uuid(); - const fragmentMessageId = uuid(); - - await Message.create({ - messageId, - conversationId, - type: MessageType.EHR_EXTRACT, - receivedAt: new Date(), - }); - - await Message.create({ - messageId: fragmentMessageId, - conversationId, - type: MessageType.FRAGMENT, - receivedAt: new Date(), - parentId: messageId, - }); - const { coreMessageId, fragmentMessageIds } = await getHealthRecordMessageIds(conversationId); - - expect(coreMessageId).toEqual(messageId); - expect(fragmentMessageIds).toEqual([fragmentMessageId]); - }); - - it('should return health record extract message id and fragment message ids given nested fragments', async () => { - const messageId = uuid(); - const conversationId = uuid(); - const fragmentMessageId = uuid(); - const nestedFragmentId = uuid(); - - await Message.create({ - messageId, - conversationId, - type: MessageType.EHR_EXTRACT, - receivedAt: new Date(), - }); - - await Message.create({ - messageId: fragmentMessageId, - conversationId, - type: MessageType.FRAGMENT, - receivedAt: new Date(), - parentId: messageId, - }); - - await Message.create({ - messageId: nestedFragmentId, - conversationId, - type: MessageType.FRAGMENT, - receivedAt: new Date(), - parentId: fragmentMessageId, - }); - - const { coreMessageId, fragmentMessageIds } = await getHealthRecordMessageIds(conversationId); - - expect(coreMessageId).toEqual(messageId); - expect(fragmentMessageIds).toEqual([fragmentMessageId, nestedFragmentId]); - }); - }); - - describe('healthRecordExists', () => { - it("should return false if 'messageId' is not found in db", async () => { - const messageId = uuid(); - const result = await messageAlreadyReceived(messageId); - expect(result).toEqual(false); - }); - - it("should return true if 'messageId' is found in db", async () => { - const conversationId = uuid(); - const messageId = uuid(); - const nhsNumber = '9876543211'; - - await HealthRecord.create({ - conversationId, - nhsNumber, - }); - - await Message.create({ - messageId, - conversationId, - type: MessageType.EHR_EXTRACT, - receivedAt: new Date(), - }); - - const result = await messageAlreadyReceived(messageId); - expect(result).toEqual(true); - }); - - it('should throw if database querying throws', async () => { - const messageId = 'not-valid'; - try { - await messageAlreadyReceived(messageId); - } catch (err) { - expect(err).not.toBeNull(); - expect(logError).toHaveBeenCalledWith('Querying database for health record failed', err); - } - }); - }); - - describe('markHealthRecordAsDeletedForPatient', () => { - async function createHealthRecordAndMessage(nhsNumber, conversationId, messageId) { - await HealthRecord.create({ - conversationId, - nhsNumber, - completedAt: new Date(), - }); - await Message.create({ - conversationId, - messageId, - type: MessageType.EHR_EXTRACT, - receivedAt: new Date(), - }); - } - - it('should return conversation id for the patient marked as deleted', async () => { - const nhsNumber = '9898989898'; - const messageId = uuid(); - const conversationId = uuid(); - - await createHealthRecordAndMessage(nhsNumber, conversationId, messageId); - - const result = await markHealthRecordAsDeletedForPatient(nhsNumber); - - const healthRecordMarkedAsDeleted = await HealthRecord.findAll({ - where: { nhsNumber }, - paranoid: false, - }); - - const messagesMarkedAsDeleted = await Message.findAll({ - where: { conversationId }, - paranoid: false, - }); - - const healthRecordStatusAfterwards = await getHealthRecordStatus(conversationId); - - expect(result).toEqual([conversationId]); - expect(healthRecordStatusAfterwards).toEqual(HealthRecordStatus.NOT_FOUND); - expect(healthRecordMarkedAsDeleted[0].deletedAt).not.toBeNull(); - expect(messagesMarkedAsDeleted[0].deletedAt).not.toBeNull(); - }); - - it('should return conversation id for the patient marked as deleted when the patient has several health records', async () => { - const nhsNumber = '6767676767'; - const firstMessageId = uuid(); - const secondMessageId = uuid(); - const firstConversationId = uuid(); - const secondConversationId = uuid(); - - await createHealthRecordAndMessage(nhsNumber, firstConversationId, firstMessageId); - await createHealthRecordAndMessage(nhsNumber, secondConversationId, secondMessageId); - - const result = await markHealthRecordAsDeletedForPatient(nhsNumber); - - const healthRecordMarkedAsDeleted = await HealthRecord.findAll({ - where: { nhsNumber }, - paranoid: false, - }); - - const messagesMarkedAsDeleted = await Message.findAll({ - where: { conversationId: [firstConversationId, secondConversationId] }, - paranoid: false, - }); - - expect(result).toEqual([firstConversationId, secondConversationId]); - expect(healthRecordMarkedAsDeleted).not.toHaveProperty('deletedAt', null); - expect(messagesMarkedAsDeleted).not.toHaveProperty('deletedAt', null); - }); - }); -}); diff --git a/src/services/database/__tests__/message-repository.integration.test.js b/src/services/database/__tests__/message-repository.integration.test.js deleted file mode 100644 index 2724cae2..00000000 --- a/src/services/database/__tests__/message-repository.integration.test.js +++ /dev/null @@ -1,282 +0,0 @@ -import { v4 as uuid } from 'uuid'; -// import { -// updateFragmentAndCreateItsParts, -// createEhrExtract, -// fragmentExists, -// createFragmentPart, -// } from '../message-repository'; -// import ModelFactory from '../../../models'; -// import { MessageType, modelName as messageModelName } from '../../../models/message'; -// import { modelName as healthRecordModelName } from '../../../models/health-record'; -import { logError } from '../../../middleware/logging'; -import { getNow } from '../../time'; -import expect from 'expect'; - -// Mocking -jest.mock('../../../middleware/logging'); -jest.mock('../../time'); - -describe.skip('messageRepository', () => { - /** - * @deprecated - * All the tests here are migrated to new test suite of dynamodb-based implementation - * To be deleted PRMT-4568 - */ - const Message = {}; // ModelFactory.getByName(messageModelName); - const HealthRecord = {}; // ModelFactory.getByName(healthRecordModelName); - // const ehrExtractType = MessageType.EHR_EXTRACT; - const fragmentMessageId = uuid(); - const fragmentMessageIds = [fragmentMessageId]; - const nhsNumber = '1234567890'; - const now = new Date(); - - beforeEach(async () => { - getNow.mockReturnValue(now); - // await HealthRecord.truncate(); - // await Message.truncate(); - // await ModelFactory.sequelize.sync({ force: true }); - }); - - afterAll(async () => { - // await ModelFactory.sequelize.close(); - }); - - describe('createEhrExtract', () => { - it('should create message in db', async () => { - const conversationId = uuid(); - const messageId = uuid(); - const ehrExtract = { messageId, conversationId, nhsNumber, fragmentMessageIds: [] }; - await createEhrExtract(ehrExtract); - - const actualMessage = await Message.findByPk(messageId); - expect(actualMessage.messageId).toBe(messageId); - expect(actualMessage.conversationId).toBe(conversationId); - expect(actualMessage.type).toBe(ehrExtractType); - expect(actualMessage.receivedAt).toEqual(now); - }); - - it('should create health record in db', async () => { - const conversationId = uuid(); - const messageId = uuid(); - const ehrExtract = { messageId, conversationId, nhsNumber, fragmentMessageIds: [] }; - await createEhrExtract(ehrExtract); - - const actualHealthRecord = await HealthRecord.findByPk(conversationId); - expect(actualHealthRecord.nhsNumber).toBe(nhsNumber); - }); - - it('should create fragments message in db when health record has fragments', async () => { - const conversationId = uuid(); - const messageId = uuid(); - const ehrExtract = { messageId, conversationId, nhsNumber, fragmentMessageIds }; - await createEhrExtract(ehrExtract); - - const fragmentMessage = await Message.findByPk(fragmentMessageId); - expect(fragmentMessage.conversationId).toBe(conversationId); - expect(fragmentMessage.type).toBe(MessageType.FRAGMENT); - expect(fragmentMessage.parentId).toBe(messageId); - expect(fragmentMessage.receivedAt).toBeNull(); - }); - - it('should not save message or health record with wrong type', async () => { - const conversationId = uuid(); - const messageId = uuid(); - const ehrExtract = { - messageId: 'not-a-valid-message-id', - conversationId, - nhsNumber, - fragmentMessageIds: [], - }; - - try { - await createEhrExtract(ehrExtract); - } catch (err) { - expect(err).not.toBeNull(); - expect(logError).toHaveBeenCalledWith('Message could not be stored', err); - } - const actualMessage = await Message.findByPk(messageId); - const actualHealthRecord = await HealthRecord.findByPk(conversationId); - expect(actualMessage).toBeNull(); - expect(actualHealthRecord).toBeNull(); - }); - - it('should not save message or health record with wrong nhs number', async () => { - const conversationId = uuid(); - const messageId = uuid(); - const ehrExtract = { - messageId, - conversationId, - type: ehrExtractType, - nhsNumber: 'not-valid', - fragmentMessageIds: [], - }; - - try { - await createEhrExtract(ehrExtract); - } catch (err) { - expect(err).not.toBeNull(); - expect(logError).toHaveBeenCalledWith('Message could not be stored', err); - } - const actualMessage = await Message.findByPk(messageId); - const actualHealthRecord = await HealthRecord.findByPk(conversationId); - expect(actualMessage).toBeNull(); - expect(actualHealthRecord).toBeNull(); - }); - }); - - describe('updateFragmentAndCreateItsParts', () => { - it('should update receivedAt for a fragment with current date', async () => { - const conversationId = uuid(); - const ehrMessageId = uuid(); - const fragmentMessageId = uuid(); - await HealthRecord.create({ conversationId, nhsNumber }); - await Message.create({ - conversationId, - messageId: ehrMessageId, - type: MessageType.EHR_EXTRACT, - receivedAt: new Date(), - }); - await Message.create({ - conversationId, - messageId: fragmentMessageId, - type: MessageType.FRAGMENT, - receivedAt: null, - }); - await updateFragmentAndCreateItsParts(fragmentMessageId, conversationId, []); - const fragment = await Message.findByPk(fragmentMessageId); - - expect(fragment.receivedAt).toEqual(now); - }); - - it('should not update receivedAt for a given fragment if database update query throws', async () => { - const conversationId = uuid(); - try { - await updateFragmentAndCreateItsParts('not-valid', conversationId, []); - } catch (err) { - expect(err).not.toBeNull(); - expect(logError).toHaveBeenCalledWith('Message could not be stored', err); - } - }); - - it('should create messages for nested fragments', async () => { - const conversationId = uuid(); - const ehrMessageId = uuid(); - const fragmentMessageId = uuid(); - const nestedFragmentMessageId = uuid(); - - await HealthRecord.create({ conversationId, nhsNumber }); - await Message.create({ - conversationId, - messageId: ehrMessageId, - type: MessageType.EHR_EXTRACT, - receivedAt: new Date(), - }); - await Message.create({ - conversationId, - messageId: fragmentMessageId, - type: MessageType.FRAGMENT, - receivedAt: null, - }); - await updateFragmentAndCreateItsParts(fragmentMessageId, conversationId, [ - nestedFragmentMessageId, - ]); - const nestedFragmentMessage = await Message.findByPk(nestedFragmentMessageId); - - expect(nestedFragmentMessage.receivedAt).toEqual(null); - expect(nestedFragmentMessage.parentId).toEqual(fragmentMessageId); - expect(nestedFragmentMessage.conversationId).toEqual(conversationId); - }); - - it('should update parentId for a nested fragment already existing in the DB', async () => { - const conversationId = uuid(); - const ehrMessageId = uuid(); - const fragmentMessageId = uuid(); - const nestedFragmentMessageId = uuid(); - - await HealthRecord.create({ conversationId, nhsNumber }); - await Message.create({ - conversationId, - messageId: ehrMessageId, - type: MessageType.EHR_EXTRACT, - receivedAt: new Date(), - }); - await Message.create({ - conversationId, - messageId: fragmentMessageId, - type: MessageType.FRAGMENT, - receivedAt: null, - }); - await Message.create({ - conversationId, - messageId: nestedFragmentMessageId, - type: MessageType.FRAGMENT, - receivedAt: new Date(), - parentId: null, - }); - - await updateFragmentAndCreateItsParts(fragmentMessageId, conversationId, [ - nestedFragmentMessageId, - ]); - - const nestedFragmentMessage = await Message.findByPk(nestedFragmentMessageId); - - expect(nestedFragmentMessage.parentId).toEqual(fragmentMessageId); - }); - }); - - describe('fragmentExists', () => { - it('should return true for a fragment existing in the database', async () => { - const conversationId = uuid(); - const messageId = uuid(); - await Message.create({ - conversationId, - messageId: messageId, - type: MessageType.FRAGMENT, - receivedAt: null, - }); - - expect(await fragmentExists(messageId)).toBe(true); - }); - - it('should return false for a fragment that does not exist in the database', async () => { - const messageId = uuid(); - expect(await fragmentExists(messageId)).toBe(false); - }); - - it('should throw if database querying throws', async () => { - const messageId = 'not-valid'; - try { - await fragmentExists(messageId); - } catch (err) { - expect(err).not.toBeNull(); - expect(logError).toHaveBeenCalledWith('Querying database for fragment message failed', err); - } - }); - }); - - describe('createFragmentPart', () => { - it('should create fragment entry in the database', async () => { - const messageId = uuid(); - const conversationId = uuid(); - await createFragmentPart(messageId, conversationId); - - const fragment = await Message.findByPk(messageId); - - expect(fragment.conversationId).toEqual(conversationId); - expect(fragment.receivedAt).toEqual(now); - expect(fragment.type).toEqual(MessageType.FRAGMENT); - expect(fragment.parentId).toBeNull(); - }); - - it('should throw if database creation query throws', async () => { - const conversationId = uuid(); - const messageId = 'not-valid'; - try { - await createFragmentPart(messageId, conversationId); - } catch (err) { - expect(err).not.toBeNull(); - expect(logError).toHaveBeenCalledWith('Creating fragment database entry failed', err); - } - }); - }); -}); diff --git a/src/services/database/check-db-health.js b/src/services/database/check-db-health.js deleted file mode 100644 index 06ef614b..00000000 --- a/src/services/database/check-db-health.js +++ /dev/null @@ -1,59 +0,0 @@ -import ModelFactory from '../../models'; -import { ERROR_CODES } from './pg-error-codes'; -import { modelName } from '../../models/health-check'; - -export const checkDbHealth = () => { - /** - * @deprecated - * It is decided we will not carry out health check by read/write into database anymore - * To be deleted PRMT-4568 - */ - const HealthCheck = ModelFactory.getByName(modelName); - - return HealthCheck.create() - .then(() => ({ - type: 'postgresql', - connection: true, - writable: true, - })) - .catch((err) => { - if (err.parent?.code) { - return parseHealthCheckError(err.parent.code); - } - - return { - type: 'postgresql', - connection: false, - writable: false, - error: `Sequelize error (Message: ${err.errors[0].message})`, - }; - }); -}; - -const parseHealthCheckError = (code) => { - switch (code) { - case ERROR_CODES.INVALID_USER_PASSWORD: - case ERROR_CODES.INVALID_CREDENTIALS: - return { - type: 'postgresql', - connection: true, - writable: false, - error: `Authorization error (Error Code: ${code})`, - }; - case ERROR_CODES.CONNECTION_REFUSED: - case ERROR_CODES.INVALID_DATABASE: - return { - type: 'postgresql', - connection: false, - writable: false, - error: `Connection error (Error Code: ${code})`, - }; - default: - return { - type: 'postgresql', - connection: false, - writable: false, - error: `Unknown error (Error Code: ${code})`, - }; - } -}; diff --git a/src/services/database/health-record-repository.js b/src/services/database/health-record-repository.js deleted file mode 100644 index b0840cdd..00000000 --- a/src/services/database/health-record-repository.js +++ /dev/null @@ -1,201 +0,0 @@ -import { MessageType, modelName as messageModelName } from "../../models/message"; -import { modelName as healthRecordModelName } from "../../models/health-record"; -import { logError, logInfo } from "../../middleware/logging"; -import Sequelize from "sequelize"; -import ModelFactory from "../../models"; -import { getNow } from "../time"; -import { HealthRecordStatus } from "../../models/enums"; -import { markRecordAsSoftDeleteForPatient } from "./ehr-conversation-repository"; - -Object.freeze(HealthRecordStatus); - -export const getHealthRecordStatus = async (conversationId) => { - /** - * @deprecated - * Replaced by new method `getConversationStatus` - * To be deleted PRMT-4568 - */ - const HealthRecord = ModelFactory.getByName(healthRecordModelName); - try { - const healthRecord = await HealthRecord.findByPk(conversationId); - if (!healthRecord) { - logInfo('Health Record not found'); - return HealthRecordStatus.NOT_FOUND; - } - - logInfo('Health Record retrieved from the database'); - - if (healthRecord.completedAt) { - return HealthRecordStatus.COMPLETE; - } else { - return HealthRecordStatus.PENDING; - } - } catch (err) { - logError('Health Record could not be retrieved from database', err); - throw err; - } -}; - -export const updateHealthRecordCompleteness = async (conversationId) => { - /** - * @deprecated - * Replaced by new method `updateConversationCompleteness` - * To be deleted PRMT-4568 - */ - const HealthRecord = ModelFactory.getByName(healthRecordModelName); - const Message = ModelFactory.getByName(messageModelName); - const sequelize = ModelFactory.sequelize; - const t = await sequelize.transaction(); - - try { - const pendingMessages = await Message.findAll({ - where: { - conversationId, - receivedAt: null, - }, - }); - if (pendingMessages.length === 0) { - await HealthRecord.update( - { completedAt: getNow() }, - { where: { conversationId }, transaction: t } - ); - } - } catch (err) { - logError('Failed to update health record completeness', err); - await t.rollback(); - throw err; - } - - await t.commit(); -}; - -export const getCurrentHealthRecordIdForPatient = async (nhsNumber) => { - /** - * @deprecated - * Replaced by new method `getCurrentConversationIdForPatient` - * To be deleted PRMT-4568 - */ - try { - const Op = Sequelize.Op; - const HealthRecord = ModelFactory.getByName(healthRecordModelName); - - const healthRecords = await HealthRecord.findAll({ - where: { - nhsNumber, - completedAt: { - [Op.ne]: null, - }, - }, - order: [['completedAt', 'DESC']], - }); - - if (!healthRecords.length) { - return undefined; - } - - const currentHealthRecord = healthRecords[0]; - - return currentHealthRecord.conversationId; - } catch (e) { - let error = { message: 'Error retrieving health record from database', error: e.message }; - logError(error); - throw error; - } -}; - -export const markHealthRecordAsDeletedForPatient = async (nhsNumber) => { - /** - * @deprecated - * Replaced by new method `markRecordAsSoftDeleteForPatient` - * To be deleted PRMT-4568 - */ - const HealthRecord = ModelFactory.getByName(healthRecordModelName); - const Message = ModelFactory.getByName(messageModelName); - const sequelize = ModelFactory.sequelize; - const Op = Sequelize.Op; - const t = await sequelize.transaction(); - - const healthRecords = await HealthRecord.findAll({ - where: { - nhsNumber, - completedAt: { - [Op.ne]: null, - }, - }, - transaction: t, - }) - .then((healthRecords) => healthRecords) - .catch((error) => { - logError(error); - throw error; - }); - - if (!healthRecords || healthRecords.length === 0) { - await t.rollback(); - return []; - } - - await HealthRecord.update({ deletedAt: getNow() }, { where: { nhsNumber }, transaction: t }); - - for (const hr of healthRecords) { - await Message.update( - { deletedAt: getNow() }, - { where: { conversationId: hr.conversationId }, transaction: t } - ); - } - - await t.commit(); - return healthRecords.map((hr) => hr.conversationId); -}; - -export const getHealthRecordMessageIds = async (conversationId) => { - /** - * @deprecated - * Replaced by new method `getMessageIdsForConversation` - * To be deleted PRMT-4568 - */ - const Message = ModelFactory.getByName(messageModelName); - - logInfo('finding messages for conversation id ' + conversationId); - const messages = await Message.findAll({ - where: { - conversationId, - }, - }); - - if (messages.length === 0) { - throw new Error('There were no undeleted messages associated with conversation id'); - } - - logInfo('finding which message by index is the core in ' + messages.length + ' messages'); - const healthRecordExtractIndex = messages.findIndex( - (message) => message.type === MessageType.EHR_EXTRACT - ); - logInfo('core message index is ' + healthRecordExtractIndex); - const coreMessageId = messages[healthRecordExtractIndex].messageId; - - messages.splice(healthRecordExtractIndex, 1); - const fragmentMessageIds = messages.map((message) => message.messageId); - - return { coreMessageId, fragmentMessageIds }; -}; - -export const messageAlreadyReceived = async (messageId) => { - /** - * @deprecated - * Replaced by new method `fragmentAlreadyReceived` - * To be deleted PRMT-4568 - */ - const Message = ModelFactory.getByName(messageModelName); - try { - const message = await Message.findByPk(messageId); - - if (!message) { - return false; - } - return message.receivedAt !== null; - } catch (e) { - logError('Querying database for health record failed', e); - throw e; - } -}; diff --git a/src/services/database/message-repository.js b/src/services/database/message-repository.js deleted file mode 100644 index 1ce8c80f..00000000 --- a/src/services/database/message-repository.js +++ /dev/null @@ -1,135 +0,0 @@ -import { MessageType, modelName as messageModelName } from '../../models/message'; -import { modelName as healthRecordModelName } from '../../models/health-record'; -import { logError } from '../../middleware/logging'; -import ModelFactory from '../../models'; -import { getNow } from '../time'; - -export const createEhrExtract = async (ehrExtract) => { - /** - * @deprecated - * Replaced by new method `createCore` - * To be deleted PRMT-4568 - */ - const Message = ModelFactory.getByName(messageModelName); - const HealthRecord = ModelFactory.getByName(healthRecordModelName); - const { conversationId, messageId, nhsNumber, fragmentMessageIds } = ehrExtract; - const healthRecord = { conversationId, nhsNumber }; - const message = { - conversationId, - messageId, - type: MessageType.EHR_EXTRACT, - receivedAt: getNow(), - }; - - const sequelize = ModelFactory.sequelize; - const t = await sequelize.transaction(); - - try { - await Message.create(message, { transaction: t }); - await HealthRecord.create(healthRecord, { transaction: t }); - - for (const fragmentMessageId of fragmentMessageIds) { - const fragmentMessage = { - messageId: fragmentMessageId, - parentId: messageId, - type: MessageType.FRAGMENT, - conversationId, - }; - await Message.create(fragmentMessage, { transaction: t }); - } - } catch (e) { - logError('Message could not be stored', e); - await t.rollback(); - throw e; - } - await t.commit(); -}; - -export const updateFragmentAndCreateItsParts = async ( - messageId, - conversationId, - remainingPartsIds -) => { - /** - * @deprecated - * Replaced by new method `markFragmentAsReceivedAndCreateItsParts` - * To be deleted PRMT-4568 - */ - const Message = ModelFactory.getByName(messageModelName); - const sequelize = ModelFactory.sequelize; - const t = await sequelize.transaction(); - try { - await Message.update( - { receivedAt: getNow() }, - { where: { messageId: messageId }, transaction: t } - ); - - for (const fragmentPartId of remainingPartsIds) { - const fragmentPartMessage = { - messageId: fragmentPartId, - parentId: messageId, - type: MessageType.FRAGMENT, - conversationId, - }; - - const fragmentPartExists = !!(await Message.findByPk(fragmentPartId)); - if (fragmentPartExists) { - await Message.update( - { parentId: messageId }, - { where: { messageId: fragmentPartId }, transaction: t } - ); - } else { - await Message.create(fragmentPartMessage, { transaction: t }); - } - } - } catch (e) { - logError('Message could not be stored', e); - await t.rollback(); - throw e; - } - await t.commit(); -}; - -export const fragmentExists = async (id) => { - /** - * @deprecated - * Replaced by new method `fragmentExistsInRecord` - * To be deleted PRMT-4568 - */ - const Message = ModelFactory.getByName(messageModelName); - - try { - const fragment = await Message.findByPk(id); - - return !!fragment; - } catch (e) { - logError('Querying database for fragment message failed', e); - throw e; - } -}; - -export const createFragmentPart = async (id, conversationId) => { - /** - * @deprecated - * To be replaced by new method `markFragmentAsReceivedAndCreateItsParts` - */ - const Message = ModelFactory.getByName(messageModelName); - const sequelize = ModelFactory.sequelize; - const t = await sequelize.transaction(); - const fragment = { - messageId: id, - conversationId, - type: MessageType.FRAGMENT, - receivedAt: getNow(), - }; - - try { - await Message.create(fragment, { transaction: t }); - } catch (e) { - logError('Creating fragment database entry failed', e); - await t.rollback(); - throw e; - } - - await t.commit(); -}; diff --git a/src/services/database/pg-error-codes.js b/src/services/database/pg-error-codes.js deleted file mode 100644 index 16673b6d..00000000 --- a/src/services/database/pg-error-codes.js +++ /dev/null @@ -1,12 +0,0 @@ - -export const ERROR_CODES = Object.freeze({ - /** - * @deprecated - * Postgres DB related stubs - * To be deleted PRMT-4568 - */ - INVALID_CREDENTIALS: '28000', - INVALID_USER_PASSWORD: '28P01', - INVALID_DATABASE: '3D000', - CONNECTION_REFUSED: 'ECONNREFUSED', -}); diff --git a/src/services/health-check/__tests__/get-health-check.integration.test.js b/src/services/health-check/__tests__/get-health-check.integration.test.js deleted file mode 100644 index b270b01a..00000000 --- a/src/services/health-check/__tests__/get-health-check.integration.test.js +++ /dev/null @@ -1,34 +0,0 @@ -/* @deprecated -// postgres-db specfic tests -// to be deleted PRMT-4568 -*/ -import { getHealthCheck } from '../get-health-check'; -import ModelFactory from '../../../models'; - -describe.skip('getHealthCheck', () => { - it('should return successful db health check if db connection is healthy', () => { - return getHealthCheck().then((result) => { - const db = result.details['database']; - return expect(db).toEqual({ - type: 'postgresql', - connection: true, - writable: true, - }); - }); - }); - - it('should return failed db health check if username is incorrect', () => { - ModelFactory._overrideConfig('username', 'wrong-username'); - - return getHealthCheck().then((result) => { - const db = result.details['database']; - - return expect(db).toEqual({ - type: 'postgresql', - connection: true, - writable: false, - error: 'Authorization error (Error Code: 28P01)', - }); - }); - }); -}); diff --git a/src/services/health-check/__tests__/get-health-check.test.js b/src/services/health-check/__tests__/get-health-check.test.js index 2858f663..4a0af3cf 100644 --- a/src/services/health-check/__tests__/get-health-check.test.js +++ b/src/services/health-check/__tests__/get-health-check.test.js @@ -1,6 +1,5 @@ import { getHealthCheck } from '../get-health-check'; import { S3 } from 'aws-sdk'; -// import ModelFactory from '../../../models'; import { initializeConfig } from '../../../config'; jest.mock('aws-sdk'); @@ -21,10 +20,6 @@ describe('getHealthCheck', () => { })); }); - // afterAll(() => { - // ModelFactory.sequelize.close(); - // }); - it('should return successful s3 health check if s3 succeeds', () => { // when mockPutObjectPromise.mockReturnValueOnce(Promise.resolve()); @@ -71,24 +66,4 @@ describe('getHealthCheck', () => { }); }); }); - - /* @deprecated - // postgres-db related test - // to be deleted PRMT-4568 - // - // it('should return failed db health check if there is an unknown error', () => { - // ModelFactory._overrideConfig('host', 'something'); - // - // return getHealthCheck().then((result) => { - // const db = result.details['database']; - // - // return expect(db).toEqual({ - // type: 'postgresql', - // connection: false, - // writable: false, - // error: 'Unknown error (Error Code: ENOTFOUND)', - // }); - // }); - // }); - */ }); From 0bba6ef07450d907d4768cf5c6e651c604639962 Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Mon, 11 Mar 2024 10:05:32 +0000 Subject: [PATCH 50/67] [PRMT-4568] Continue removing postgres-db related code --- ...10302112415-create-health-records-table.js | 50 --------------- .../20210302112617-create-message-table.js | 61 ------------------- ...210302113224-create-health-checks-table.js | 30 --------- scripts/grant-db-permissions.sql | 7 --- scripts/migrate-db.sh | 34 ----------- .../__tests__/delete-ehr-controller.test.js | 2 +- .../__tests__/get-health-check.test.js | 2 - 7 files changed, 1 insertion(+), 185 deletions(-) delete mode 100644 database/migrations/20210302112415-create-health-records-table.js delete mode 100644 database/migrations/20210302112617-create-message-table.js delete mode 100644 database/migrations/20210302113224-create-health-checks-table.js delete mode 100644 scripts/grant-db-permissions.sql delete mode 100755 scripts/migrate-db.sh diff --git a/database/migrations/20210302112415-create-health-records-table.js b/database/migrations/20210302112415-create-health-records-table.js deleted file mode 100644 index 9b0ba5ec..00000000 --- a/database/migrations/20210302112415-create-health-records-table.js +++ /dev/null @@ -1,50 +0,0 @@ -'use strict'; - -const tableName = 'health_records'; - -const model = dataType => { - return { - conversationId: { - field: 'conversation_id', - type: dataType.UUID, - primaryKey: true, - defaultValue: dataType.UUIDV4 - }, - nhsNumber: { - field: 'nhs_number', - type: dataType.CHAR(10), - validate: { - isNumeric: true, - len: 10 - }, - allowNull: false - }, - completedAt: { - field: 'completed_at', - type: dataType.DATE - }, - createdAt: { - field: 'created_at', - type: dataType.DATE, - allowNull: false - }, - updatedAt: { - field: 'updated_at', - type: dataType.DATE, - allowNull: false - }, - deletedAt: { - field: 'deleted_at', - type: dataType.DATE - } - } -}; - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.createTable(tableName, model(Sequelize)); - }, - down: queryInterface => { - return queryInterface.dropTable(tableName); - } -}; \ No newline at end of file diff --git a/database/migrations/20210302112617-create-message-table.js b/database/migrations/20210302112617-create-message-table.js deleted file mode 100644 index 04603766..00000000 --- a/database/migrations/20210302112617-create-message-table.js +++ /dev/null @@ -1,61 +0,0 @@ -'use strict'; - -const MessageType = { - EHR_EXTRACT: 'ehrExtract', - FRAGMENT: 'fragment' -}; - -const tableName = 'messages'; - -const model = dataType => { - return { - messageId: { - field: 'message_id', - type: dataType.UUID, - primaryKey: true, - defaultValue: dataType.UUIDV4 - }, - conversationId: { - field: 'conversation_id', - type: dataType.UUID, - allowNull: false - }, - parentId: { - field: 'parent_id', - type: dataType.UUID - }, - type: { - field: 'type', - type: dataType.STRING, - isIn: [Object.values(MessageType)], - allowNull: false - }, - receivedAt: { - field: 'received_at', - type: dataType.DATE - }, - createdAt: { - field: 'created_at', - type: dataType.DATE, - allowNull: false - }, - updatedAt: { - field: 'updated_at', - type: dataType.DATE, - allowNull: false - }, - deletedAt: { - field: 'deleted_at', - type: dataType.DATE - } - } -}; - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.createTable(tableName, model(Sequelize)); - }, - down: queryInterface => { - return queryInterface.dropTable(tableName); - } -}; diff --git a/database/migrations/20210302113224-create-health-checks-table.js b/database/migrations/20210302113224-create-health-checks-table.js deleted file mode 100644 index c24a62e7..00000000 --- a/database/migrations/20210302113224-create-health-checks-table.js +++ /dev/null @@ -1,30 +0,0 @@ -'use strict'; - -const tableName = 'health_checks'; - -const model = dataType => { - return { - id: { - type: dataType.UUID, - primaryKey: true - }, - created_at: { - type: dataType.DATE, - allowNull: false - }, - updated_at: { - type: dataType.DATE, - allowNull: false - }, - deleted_at: dataType.DATE - }; -}; -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.createTable(tableName, model(Sequelize)); - }, - - down: (queryInterface, Sequelize) => { - return queryInterface.dropTable(tableName); - } -}; diff --git a/scripts/grant-db-permissions.sql b/scripts/grant-db-permissions.sql deleted file mode 100644 index 03e24b30..00000000 --- a/scripts/grant-db-permissions.sql +++ /dev/null @@ -1,7 +0,0 @@ --- Give just enough permissions for migration role to change data in the SequelizeMeta table -GRANT SELECT, INSERT ON "SequelizeMeta" TO migration_role; - --- Needs to execute after database was migrated -GRANT SELECT, INSERT, UPDATE, DELETE ON health_checks TO application_role; -GRANT SELECT, INSERT, UPDATE, DELETE ON health_records TO application_role; -GRANT SELECT, INSERT, UPDATE, DELETE ON messages TO application_role; \ No newline at end of file diff --git a/scripts/migrate-db.sh b/scripts/migrate-db.sh deleted file mode 100755 index 7122783f..00000000 --- a/scripts/migrate-db.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/bin/bash - -NHS_SERVICE=ehr-repo -DB_CONNECTION_TIMEOUT=30 - -timestamp() { - date +"%Y-%m-%d %H:%M:%S" -} -function jsonPrettify { - echo "{message: $1, level: $2, timestamp: `timestamp`, service: ${NHS_SERVICE}, environment: ${NHS_ENVIRONMENT} } " -} - -if [ "${SKIP_DB_MIGRATION}" == "true" ]; then - jsonPrettify "SKIP_DB_MIGRATION is set to true. Skipping DB migration" INFO -else - jsonPrettify "Waiting for DB port to become open 5432" INFO - count=0 - while ! pg_isready -h ${DATABASE_HOST}; do - jsonPrettify "Waiting for ${DATABASE_HOST}:5432" INFO - sleep 1 - ((count++)) - if [ "${DB_CONNECTION_TIMEOUT}" -le $count ]; then - jsonPrettify "Timed-out waiting for DB connection at ${DATABASE_HOST}:5432" WARN - exit 5 - fi - done - jsonPrettify "DB connection at ${DATABASE_HOST}:5432 is available" INFO - jsonPrettify "Trying to create a database, if not exists. 'Already exists' errors are safe to ignore" INFO - PGPASSWORD="${DATABASE_PASSWORD}" createdb --host="${DATABASE_HOST}" --username="${DATABASE_USER}" $DATABASE_NAME || true - set -e - jsonPrettify "Migrating DB, will not migrate parts that have already been migrated (meta)" INFO && \ - npx sequelize-cli db:migrate - jsonPrettify "DB migration completed." INFO -fi diff --git a/src/api/patients/__tests__/delete-ehr-controller.test.js b/src/api/patients/__tests__/delete-ehr-controller.test.js index fd925c9e..c258c0d4 100644 --- a/src/api/patients/__tests__/delete-ehr-controller.test.js +++ b/src/api/patients/__tests__/delete-ehr-controller.test.js @@ -9,7 +9,7 @@ jest.mock('../../../services/database/ehr-conversation-repository'); jest.mock('../../../middleware/logging'); jest.mock('../../../services/storage/get-signed-url'); jest.mock('../../../config', () => ({ - initializeConfig: jest.fn().mockReturnValue({ sequelize: { dialect: 'postgres' } }), + initializeConfig: jest.fn().mockReturnValue({}), })); describe('deleteEhrController', () => { diff --git a/src/services/health-check/__tests__/get-health-check.test.js b/src/services/health-check/__tests__/get-health-check.test.js index 4a0af3cf..9ebb6f9e 100644 --- a/src/services/health-check/__tests__/get-health-check.test.js +++ b/src/services/health-check/__tests__/get-health-check.test.js @@ -12,8 +12,6 @@ describe('getHealthCheck', () => { const error = 'some-error'; beforeEach(() => { - // ModelFactory._resetConfig(); - S3.mockImplementation(() => ({ putObject: mockPutObject, headBucket: mockHeadBucket, From dea9bd42d87677388ed60f2e8c9faa6740a03461 Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Mon, 11 Mar 2024 10:06:21 +0000 Subject: [PATCH 51/67] [PRMT-4568] Remove redundant comments --- src/services/database/ehr-core-repository.js | 2 -- src/services/database/ehr-fragment-repository.js | 1 - 2 files changed, 3 deletions(-) diff --git a/src/services/database/ehr-core-repository.js b/src/services/database/ehr-core-repository.js index e82e22db..9fe7c6c2 100644 --- a/src/services/database/ehr-core-repository.js +++ b/src/services/database/ehr-core-repository.js @@ -5,8 +5,6 @@ import { RecordType } from '../../models/enums'; import { logError } from '../../middleware/logging'; export const createCore = async ({ conversationId, messageId, fragmentMessageIds = [] }) => { - // to replace the existing `createEhrExtract` method - try { const db = EhrTransferTracker.getInstance(); const itemsToWrite = [buildCore(conversationId, messageId)]; diff --git a/src/services/database/ehr-fragment-repository.js b/src/services/database/ehr-fragment-repository.js index 5790952a..37a14f73 100644 --- a/src/services/database/ehr-fragment-repository.js +++ b/src/services/database/ehr-fragment-repository.js @@ -30,7 +30,6 @@ export const markFragmentAsReceivedAndCreateItsParts = async ( }; export const getFragmentByKey = (inboundConversationId, inboundMessageId) => { - // to replace the findByPk default method from sequalize const db = EhrTransferTracker.getInstance(); return db.getItemByKey(inboundConversationId, inboundMessageId, RecordType.FRAGMENT); }; From 176538b74418cad0f9f4368687bfabc76cb3fab0 Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Mon, 11 Mar 2024 10:12:24 +0000 Subject: [PATCH 52/67] [PRMT-4568] Fix broken import of deprecated code --- src/api/messages/message-location-controller.js | 1 - src/api/messages/store-message-controller.js | 12 +----------- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/src/api/messages/message-location-controller.js b/src/api/messages/message-location-controller.js index dd7e1a06..52396d54 100644 --- a/src/api/messages/message-location-controller.js +++ b/src/api/messages/message-location-controller.js @@ -2,7 +2,6 @@ import { getSignedUrl } from '../../services/storage'; import { param } from 'express-validator'; import { logError, logInfo } from '../../middleware/logging'; import { setCurrentSpanAttributes } from '../../config/tracing'; -import { messageAlreadyReceived } from '../../services/database/health-record-repository'; import { fragmentAlreadyReceived } from "../../services/database/ehr-fragment-repository"; export const messageLocationControllerValidation = [ diff --git a/src/api/messages/store-message-controller.js b/src/api/messages/store-message-controller.js index 8fe166eb..e214a361 100644 --- a/src/api/messages/store-message-controller.js +++ b/src/api/messages/store-message-controller.js @@ -1,16 +1,6 @@ import { body } from 'express-validator'; -import { MessageType } from '../../models/message'; -import { - updateFragmentAndCreateItsParts, - createEhrExtract, - fragmentExists, - createFragmentPart, -} from '../../services/database/message-repository'; +import { MessageType } from '../../models/enums'; import { logError, logInfo, logWarning } from '../../middleware/logging'; -import { - updateHealthRecordCompleteness, - getHealthRecordStatus, -} from '../../services/database/health-record-repository'; import { setCurrentSpanAttributes } from '../../config/tracing'; import { createCore } from "../../services/database/ehr-core-repository"; import { From 1289881e8504ebf0956b09e73c0b56ffaea6598c Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Mon, 11 Mar 2024 10:12:45 +0000 Subject: [PATCH 53/67] [PRMT-4568] Remove postgres related packages from dependencies --- package-lock.json | 1044 +-------------------------------------------- package.json | 8 +- 2 files changed, 16 insertions(+), 1036 deletions(-) diff --git a/package-lock.json b/package-lock.json index 84359fe7..c1b941c3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,8 +22,6 @@ "lodash.merge": "^4.6.2", "moment": "^2.30.1", "moment-timezone": "^0.5.45", - "pg": "^8.7.3", - "pg-hstore": "^2.3.4", "sequelize": "^6.33.0", "sinon": "^15.1.2", "swagger-ui-express": "^4.3.0", @@ -48,7 +46,6 @@ "nodemon": "^2.0.19", "npm-audit-resolver": "3.0.0-7", "prettier": "^2.6.0", - "sequelize-cli": "^6.4.1", "supertest": "^6.3.3", "typescript": "^4.6.2" } @@ -4825,14 +4822,6 @@ "dev": true, "license": "MIT" }, - "node_modules/at-least-node": { - "version": "1.0.0", - "dev": true, - "license": "ISC", - "engines": { - "node": ">= 4.0.0" - } - }, "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", @@ -5140,20 +5129,6 @@ "node": ">=8" } }, - "node_modules/bindings": { - "version": "1.5.0", - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "file-uri-to-path": "1.0.0" - } - }, - "node_modules/bluebird": { - "version": "3.7.2", - "dev": true, - "license": "MIT" - }, "node_modules/body-parser": { "version": "1.19.2", "license": "MIT", @@ -5264,13 +5239,6 @@ "dev": true, "license": "MIT" }, - "node_modules/buffer-writer": { - "version": "2.0.0", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/bytes": { "version": "3.1.2", "license": "MIT", @@ -5394,31 +5362,6 @@ "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", "dev": true }, - "node_modules/cli-color": { - "version": "2.0.1", - "dev": true, - "license": "ISC", - "dependencies": { - "d": "^1.0.1", - "es5-ext": "^0.10.53", - "es6-iterator": "^2.0.3", - "memoizee": "^0.4.15", - "timers-ext": "^0.1.7" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/cliui": { - "version": "7.0.4", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, "node_modules/clone-deep": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", @@ -5534,20 +5477,6 @@ "typedarray": "^0.0.6" } }, - "node_modules/config-chain": { - "version": "1.1.13", - "dev": true, - "license": "MIT", - "dependencies": { - "ini": "^1.3.4", - "proto-list": "~1.2.1" - } - }, - "node_modules/config-chain/node_modules/ini": { - "version": "1.3.8", - "dev": true, - "license": "ISC" - }, "node_modules/content-disposition": { "version": "0.5.4", "license": "MIT", @@ -5630,12 +5559,6 @@ "url": "https://opencollective.com/core-js" } }, - "node_modules/core-util-is": { - "version": "1.0.2", - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/cross-spawn": { "version": "7.0.3", "dev": true, @@ -5649,15 +5572,6 @@ "node": ">= 8" } }, - "node_modules/d": { - "version": "1.0.1", - "dev": true, - "license": "ISC", - "dependencies": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - } - }, "node_modules/dayjs": { "version": "1.11.0", "license": "MIT" @@ -5825,39 +5739,6 @@ "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.6.tgz", "integrity": "sha512-iGCHkfUc5kFekGiqhe8B/mdaurD+lakO9txNnTvKtA6PISrw86LgqHvRzWYPyoE2Ph5aMIrCw9/uko6XHTKCwA==" }, - "node_modules/editorconfig": { - "version": "0.15.3", - "dev": true, - "license": "MIT", - "dependencies": { - "commander": "^2.19.0", - "lru-cache": "^4.1.5", - "semver": "^5.6.0", - "sigmund": "^1.0.1" - }, - "bin": { - "editorconfig": "bin/editorconfig" - } - }, - "node_modules/editorconfig/node_modules/commander": { - "version": "2.20.3", - "dev": true, - "license": "MIT" - }, - "node_modules/editorconfig/node_modules/lru-cache": { - "version": "4.1.5", - "dev": true, - "license": "ISC", - "dependencies": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "node_modules/editorconfig/node_modules/yallist": { - "version": "2.1.2", - "dev": true, - "license": "ISC" - }, "node_modules/ee-first": { "version": "1.1.1", "license": "MIT" @@ -5960,50 +5841,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es5-ext": { - "version": "0.10.59", - "dev": true, - "hasInstallScript": true, - "license": "ISC", - "dependencies": { - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.3", - "next-tick": "^1.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/es6-iterator": { - "version": "2.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "node_modules/es6-symbol": { - "version": "3.1.3", - "dev": true, - "license": "ISC", - "dependencies": { - "d": "^1.0.1", - "ext": "^1.1.2" - } - }, - "node_modules/es6-weak-map": { - "version": "2.0.3", - "dev": true, - "license": "ISC", - "dependencies": { - "d": "1", - "es5-ext": "^0.10.46", - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.1" - } - }, "node_modules/escalade": { "version": "3.1.1", "dev": true, @@ -6374,15 +6211,6 @@ "node": ">= 0.6" } }, - "node_modules/event-emitter": { - "version": "0.3.5", - "dev": true, - "license": "MIT", - "dependencies": { - "d": "1", - "es5-ext": "~0.10.14" - } - }, "node_modules/events": { "version": "1.1.1", "license": "MIT", @@ -6531,19 +6359,6 @@ ], "license": "MIT" }, - "node_modules/ext": { - "version": "1.6.0", - "dev": true, - "license": "ISC", - "dependencies": { - "type": "^2.5.0" - } - }, - "node_modules/ext/node_modules/type": { - "version": "2.6.0", - "dev": true, - "license": "ISC" - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -6640,12 +6455,6 @@ "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/file-uri-to-path": { - "version": "1.0.0", - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/fill-range": { "version": "7.0.1", "dev": true, @@ -6815,28 +6624,6 @@ "node": ">= 0.6" } }, - "node_modules/fs-extra": { - "version": "9.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/fs-extra/node_modules/universalify": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/fs-readdir-recursive": { "version": "1.1.0", "dev": true, @@ -7505,11 +7292,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-promise": { - "version": "2.2.2", - "dev": true, - "license": "MIT" - }, "node_modules/is-regex": { "version": "1.1.4", "dev": true, @@ -9375,39 +9157,6 @@ "node": ">= 0.6.0" } }, - "node_modules/js-beautify": { - "version": "1.14.0", - "dev": true, - "license": "MIT", - "dependencies": { - "config-chain": "^1.1.12", - "editorconfig": "^0.15.3", - "glob": "^7.1.3", - "nopt": "^5.0.0" - }, - "bin": { - "css-beautify": "js/bin/css-beautify.js", - "html-beautify": "js/bin/html-beautify.js", - "js-beautify": "js/bin/js-beautify.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/js-beautify/node_modules/nopt": { - "version": "5.0.0", - "dev": true, - "license": "ISC", - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -9455,25 +9204,6 @@ "dev": true, "license": "MIT" }, - "node_modules/jsonfile": { - "version": "6.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/jsonfile/node_modules/universalify": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/jsonlines": { "version": "0.1.1", "dev": true, @@ -9528,17 +9258,6 @@ "node": ">= 0.8.0" } }, - "node_modules/libpq": { - "version": "1.8.12", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "bindings": "1.5.0", - "nan": "^2.14.0" - } - }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -9603,14 +9322,6 @@ "yallist": "^3.0.2" } }, - "node_modules/lru-queue": { - "version": "0.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "es5-ext": "~0.10.2" - } - }, "node_modules/make-dir": { "version": "2.1.0", "dev": true, @@ -9639,21 +9350,6 @@ "node": ">= 0.6" } }, - "node_modules/memoizee": { - "version": "0.4.15", - "dev": true, - "license": "ISC", - "dependencies": { - "d": "^1.0.1", - "es5-ext": "^0.10.53", - "es6-weak-map": "^2.0.3", - "event-emitter": "^0.3.5", - "is-promise": "^2.2.2", - "lru-queue": "^0.1.0", - "next-tick": "^1.1.0", - "timers-ext": "^0.1.7" - } - }, "node_modules/merge-descriptors": { "version": "1.0.1", "license": "MIT" @@ -9786,12 +9482,6 @@ "dev": true, "license": "ISC" }, - "node_modules/nan": { - "version": "2.16.0", - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/nano-seconds": { "version": "1.2.2", "license": "MIT", @@ -9811,11 +9501,6 @@ "node": ">= 0.6" } }, - "node_modules/next-tick": { - "version": "1.1.0", - "dev": true, - "license": "ISC" - }, "node_modules/nise": { "version": "5.1.5", "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.5.tgz", @@ -10136,10 +9821,6 @@ "node": ">=6" } }, - "node_modules/packet-reader": { - "version": "1.0.0", - "license": "MIT" - }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -10227,141 +9908,11 @@ "node": ">=8" } }, - "node_modules/pg": { - "version": "8.7.3", - "license": "MIT", - "dependencies": { - "buffer-writer": "2.0.0", - "packet-reader": "1.0.0", - "pg-connection-string": "^2.5.0", - "pg-pool": "^3.5.1", - "pg-protocol": "^1.5.0", - "pg-types": "^2.1.0", - "pgpass": "1.x" - }, - "engines": { - "node": ">= 8.0.0" - }, - "peerDependencies": { - "pg-native": ">=2.0.0" - }, - "peerDependenciesMeta": { - "pg-native": { - "optional": true - } - } - }, "node_modules/pg-connection-string": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==" }, - "node_modules/pg-hstore": { - "version": "2.3.4", - "license": "MIT", - "dependencies": { - "underscore": "^1.13.1" - }, - "engines": { - "node": ">= 0.8.x" - } - }, - "node_modules/pg-int8": { - "version": "1.0.1", - "license": "ISC", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/pg-native": { - "version": "3.0.1", - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "libpq": "^1.8.10", - "pg-types": "^1.12.1", - "readable-stream": "1.0.31" - } - }, - "node_modules/pg-native/node_modules/isarray": { - "version": "0.0.1", - "license": "MIT", - "optional": true, - "peer": true - }, - "node_modules/pg-native/node_modules/pg-types": { - "version": "1.13.0", - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "pg-int8": "1.0.1", - "postgres-array": "~1.0.0", - "postgres-bytea": "~1.0.0", - "postgres-date": "~1.0.0", - "postgres-interval": "^1.1.0" - } - }, - "node_modules/pg-native/node_modules/postgres-array": { - "version": "1.0.3", - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pg-native/node_modules/readable-stream": { - "version": "1.0.31", - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "node_modules/pg-native/node_modules/string_decoder": { - "version": "0.10.31", - "license": "MIT", - "optional": true, - "peer": true - }, - "node_modules/pg-pool": { - "version": "3.5.1", - "license": "MIT", - "peerDependencies": { - "pg": ">=8.0" - } - }, - "node_modules/pg-protocol": { - "version": "1.5.0", - "license": "MIT" - }, - "node_modules/pg-types": { - "version": "2.2.0", - "license": "MIT", - "dependencies": { - "pg-int8": "1.0.1", - "postgres-array": "~2.0.0", - "postgres-bytea": "~1.0.0", - "postgres-date": "~1.0.4", - "postgres-interval": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pgpass": { - "version": "1.0.5", - "license": "MIT", - "dependencies": { - "split2": "^4.1.0" - } - }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -10469,37 +10020,6 @@ "node": ">=4" } }, - "node_modules/postgres-array": { - "version": "2.0.0", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/postgres-bytea": { - "version": "1.0.0", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postgres-date": { - "version": "1.0.7", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postgres-interval": { - "version": "1.2.0", - "license": "MIT", - "dependencies": { - "xtend": "^4.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -10574,11 +10094,6 @@ "node": ">= 6" } }, - "node_modules/proto-list": { - "version": "1.2.4", - "dev": true, - "license": "ISC" - }, "node_modules/proxy-addr": { "version": "2.0.7", "license": "MIT", @@ -10590,11 +10105,6 @@ "node": ">= 0.10" } }, - "node_modules/pseudomap": { - "version": "1.0.2", - "dev": true, - "license": "ISC" - }, "node_modules/pstree.remy": { "version": "1.1.8", "dev": true, @@ -11023,27 +10533,6 @@ } } }, - "node_modules/sequelize-cli": { - "version": "6.4.1", - "dev": true, - "license": "MIT", - "dependencies": { - "cli-color": "^2.0.1", - "fs-extra": "^9.1.0", - "js-beautify": "^1.14.0", - "lodash": "^4.17.21", - "resolve": "^1.20.0", - "umzug": "^2.3.0", - "yargs": "^16.2.0" - }, - "bin": { - "sequelize": "lib/sequelize", - "sequelize-cli": "lib/sequelize" - }, - "engines": { - "node": ">=10.0.0" - } - }, "node_modules/sequelize-pool": { "version": "7.1.0", "license": "MIT", @@ -11112,11 +10601,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/sigmund": { - "version": "1.0.1", - "dev": true, - "license": "ISC" - }, "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -11262,13 +10746,6 @@ "node": ">=4" } }, - "node_modules/split2": { - "version": "4.1.0", - "license": "ISC", - "engines": { - "node": ">= 10.x" - } - }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -11613,15 +11090,6 @@ "dev": true, "license": "MIT" }, - "node_modules/timers-ext": { - "version": "0.1.7", - "dev": true, - "license": "ISC", - "dependencies": { - "es5-ext": "~0.10.46", - "next-tick": "1" - } - }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -11696,11 +11164,6 @@ "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" } }, - "node_modules/type": { - "version": "1.2.0", - "dev": true, - "license": "ISC" - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -11761,17 +11224,6 @@ "node": ">=4.2.0" } }, - "node_modules/umzug": { - "version": "2.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "bluebird": "^3.7.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/unbox-primitive": { "version": "1.0.1", "dev": true, @@ -11791,10 +11243,6 @@ "dev": true, "license": "MIT" }, - "node_modules/underscore": { - "version": "1.13.1", - "license": "MIT" - }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", @@ -12139,61 +11587,29 @@ "node": ">=4.0.0" } }, - "node_modules/xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/xtend": { - "version": "4.0.2", - "license": "MIT", - "engines": { - "node": ">=0.4" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, - "node_modules/yargs": { - "version": "16.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", "engines": { - "node": ">=10" + "node": ">=4.0" } }, - "node_modules/yargs-parser": { - "version": "20.2.9", + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true, - "license": "ISC", "engines": { "node": ">=10" } }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, "node_modules/yargs-unparser": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", @@ -16018,10 +15434,6 @@ "version": "0.4.0", "dev": true }, - "at-least-node": { - "version": "1.0.0", - "dev": true - }, "audit-resolve-core": { "version": "3.0.0-3", "resolved": "https://registry.npmjs.org/audit-resolve-core/-/audit-resolve-core-3.0.0-3.tgz", @@ -16245,18 +15657,6 @@ "version": "2.2.0", "dev": true }, - "bindings": { - "version": "1.5.0", - "optional": true, - "peer": true, - "requires": { - "file-uri-to-path": "1.0.0" - } - }, - "bluebird": { - "version": "3.7.2", - "dev": true - }, "body-parser": { "version": "1.19.2", "requires": { @@ -16336,9 +15736,6 @@ "version": "1.1.2", "dev": true }, - "buffer-writer": { - "version": "2.0.0" - }, "bytes": { "version": "3.1.2" }, @@ -16405,26 +15802,6 @@ "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", "dev": true }, - "cli-color": { - "version": "2.0.1", - "dev": true, - "requires": { - "d": "^1.0.1", - "es5-ext": "^0.10.53", - "es6-iterator": "^2.0.3", - "memoizee": "^0.4.15", - "timers-ext": "^0.1.7" - } - }, - "cliui": { - "version": "7.0.4", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, "clone-deep": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", @@ -16515,20 +15892,6 @@ "typedarray": "^0.0.6" } }, - "config-chain": { - "version": "1.1.13", - "dev": true, - "requires": { - "ini": "^1.3.4", - "proto-list": "~1.2.1" - }, - "dependencies": { - "ini": { - "version": "1.3.8", - "dev": true - } - } - }, "content-disposition": { "version": "0.5.4", "requires": { @@ -16576,11 +15939,6 @@ "browserslist": "^4.22.2" } }, - "core-util-is": { - "version": "1.0.2", - "optional": true, - "peer": true - }, "cross-spawn": { "version": "7.0.3", "dev": true, @@ -16590,14 +15948,6 @@ "which": "^2.0.1" } }, - "d": { - "version": "1.0.1", - "dev": true, - "requires": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - } - }, "dayjs": { "version": "1.11.0" }, @@ -16711,34 +16061,6 @@ "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.6.tgz", "integrity": "sha512-iGCHkfUc5kFekGiqhe8B/mdaurD+lakO9txNnTvKtA6PISrw86LgqHvRzWYPyoE2Ph5aMIrCw9/uko6XHTKCwA==" }, - "editorconfig": { - "version": "0.15.3", - "dev": true, - "requires": { - "commander": "^2.19.0", - "lru-cache": "^4.1.5", - "semver": "^7.5.2", - "sigmund": "^1.0.1" - }, - "dependencies": { - "commander": { - "version": "2.20.3", - "dev": true - }, - "lru-cache": { - "version": "4.1.5", - "dev": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "yallist": { - "version": "2.1.2", - "dev": true - } - } - }, "ee-first": { "version": "1.1.1" }, @@ -16816,42 +16138,6 @@ "is-symbol": "^1.0.2" } }, - "es5-ext": { - "version": "0.10.59", - "dev": true, - "requires": { - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.3", - "next-tick": "^1.1.0" - } - }, - "es6-iterator": { - "version": "2.0.3", - "dev": true, - "requires": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "es6-symbol": { - "version": "3.1.3", - "dev": true, - "requires": { - "d": "^1.0.1", - "ext": "^1.1.2" - } - }, - "es6-weak-map": { - "version": "2.0.3", - "dev": true, - "requires": { - "d": "1", - "es5-ext": "^0.10.46", - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.1" - } - }, "escalade": { "version": "3.1.1", "dev": true @@ -17065,14 +16351,6 @@ "etag": { "version": "1.8.1" }, - "event-emitter": { - "version": "0.3.5", - "dev": true, - "requires": { - "d": "1", - "es5-ext": "~0.10.14" - } - }, "events": { "version": "1.1.1" }, @@ -17175,19 +16453,6 @@ "lodash": "^4.17.21" } }, - "ext": { - "version": "1.6.0", - "dev": true, - "requires": { - "type": "^2.5.0" - }, - "dependencies": { - "type": { - "version": "2.6.0", - "dev": true - } - } - }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -17259,11 +16524,6 @@ "flat-cache": "^3.0.4" } }, - "file-uri-to-path": { - "version": "1.0.0", - "optional": true, - "peer": true - }, "fill-range": { "version": "7.0.1", "dev": true, @@ -17377,22 +16637,6 @@ "fresh": { "version": "0.5.2" }, - "fs-extra": { - "version": "9.1.0", - "dev": true, - "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "dependencies": { - "universalify": { - "version": "2.0.0", - "dev": true - } - } - }, "fs-readdir-recursive": { "version": "1.1.0", "dev": true @@ -17808,10 +17052,6 @@ "isobject": "^3.0.1" } }, - "is-promise": { - "version": "2.2.2", - "dev": true - }, "is-regex": { "version": "1.1.4", "dev": true, @@ -19174,25 +18414,6 @@ "jmespath": { "version": "0.16.0" }, - "js-beautify": { - "version": "1.14.0", - "dev": true, - "requires": { - "config-chain": "^1.1.12", - "editorconfig": "^0.15.3", - "glob": "^7.1.3", - "nopt": "^5.0.0" - }, - "dependencies": { - "nopt": { - "version": "5.0.0", - "dev": true, - "requires": { - "abbrev": "1" - } - } - } - }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -19230,20 +18451,6 @@ "version": "1.0.1", "dev": true }, - "jsonfile": { - "version": "6.1.0", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^2.0.0" - }, - "dependencies": { - "universalify": { - "version": "2.0.0", - "dev": true - } - } - }, "jsonlines": { "version": "0.1.1", "dev": true @@ -19284,15 +18491,6 @@ "type-check": "~0.4.0" } }, - "libpq": { - "version": "1.8.12", - "optional": true, - "peer": true, - "requires": { - "bindings": "1.5.0", - "nan": "^2.14.0" - } - }, "lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -19347,13 +18545,6 @@ "yallist": "^3.0.2" } }, - "lru-queue": { - "version": "0.1.0", - "dev": true, - "requires": { - "es5-ext": "~0.10.2" - } - }, "make-dir": { "version": "2.1.0", "dev": true, @@ -19374,20 +18565,6 @@ "media-typer": { "version": "0.3.0" }, - "memoizee": { - "version": "0.4.15", - "dev": true, - "requires": { - "d": "^1.0.1", - "es5-ext": "^0.10.53", - "es6-weak-map": "^2.0.3", - "event-emitter": "^0.3.5", - "is-promise": "^2.2.2", - "lru-queue": "^0.1.0", - "next-tick": "^1.1.0", - "timers-ext": "^0.1.7" - } - }, "merge-descriptors": { "version": "1.0.1" }, @@ -19474,11 +18651,6 @@ "version": "0.0.8", "dev": true }, - "nan": { - "version": "2.16.0", - "optional": true, - "peer": true - }, "nano-seconds": { "version": "1.2.2" }, @@ -19489,10 +18661,6 @@ "negotiator": { "version": "0.6.3" }, - "next-tick": { - "version": "1.1.0", - "dev": true - }, "nise": { "version": "5.1.5", "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.5.tgz", @@ -19736,9 +18904,6 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, - "packet-reader": { - "version": "1.0.0" - }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -19792,105 +18957,11 @@ "version": "4.0.0", "dev": true }, - "pg": { - "version": "8.7.3", - "requires": { - "buffer-writer": "2.0.0", - "packet-reader": "1.0.0", - "pg-connection-string": "^2.5.0", - "pg-pool": "^3.5.1", - "pg-protocol": "^1.5.0", - "pg-types": "^2.1.0", - "pgpass": "1.x" - } - }, "pg-connection-string": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==" }, - "pg-hstore": { - "version": "2.3.4", - "requires": { - "underscore": "^1.13.1" - } - }, - "pg-int8": { - "version": "1.0.1" - }, - "pg-native": { - "version": "3.0.1", - "optional": true, - "peer": true, - "requires": { - "libpq": "^1.8.10", - "pg-types": "^1.12.1", - "readable-stream": "1.0.31" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "optional": true, - "peer": true - }, - "pg-types": { - "version": "1.13.0", - "optional": true, - "peer": true, - "requires": { - "pg-int8": "1.0.1", - "postgres-array": "~1.0.0", - "postgres-bytea": "~1.0.0", - "postgres-date": "~1.0.0", - "postgres-interval": "^1.1.0" - } - }, - "postgres-array": { - "version": "1.0.3", - "optional": true, - "peer": true - }, - "readable-stream": { - "version": "1.0.31", - "optional": true, - "peer": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "optional": true, - "peer": true - } - } - }, - "pg-pool": { - "version": "3.5.1", - "requires": {} - }, - "pg-protocol": { - "version": "1.5.0" - }, - "pg-types": { - "version": "2.2.0", - "requires": { - "pg-int8": "1.0.1", - "postgres-array": "~2.0.0", - "postgres-bytea": "~1.0.0", - "postgres-date": "~1.0.4", - "postgres-interval": "^1.1.0" - } - }, - "pgpass": { - "version": "1.0.5", - "requires": { - "split2": "^4.1.0" - } - }, "picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -19965,21 +19036,6 @@ } } }, - "postgres-array": { - "version": "2.0.0" - }, - "postgres-bytea": { - "version": "1.0.0" - }, - "postgres-date": { - "version": "1.0.7" - }, - "postgres-interval": { - "version": "1.2.0", - "requires": { - "xtend": "^4.0.0" - } - }, "prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -20027,10 +19083,6 @@ "sisteransi": "^1.0.5" } }, - "proto-list": { - "version": "1.2.4", - "dev": true - }, "proxy-addr": { "version": "2.0.7", "requires": { @@ -20038,10 +19090,6 @@ "ipaddr.js": "1.9.1" } }, - "pseudomap": { - "version": "1.0.2", - "dev": true - }, "pstree.remy": { "version": "1.1.8", "dev": true @@ -20309,19 +19357,6 @@ "wkx": "^0.5.0" } }, - "sequelize-cli": { - "version": "6.4.1", - "dev": true, - "requires": { - "cli-color": "^2.0.1", - "fs-extra": "^9.1.0", - "js-beautify": "^1.14.0", - "lodash": "^4.17.21", - "resolve": "^1.20.0", - "umzug": "^2.3.0", - "yargs": "^16.2.0" - } - }, "sequelize-pool": { "version": "7.1.0" }, @@ -20366,10 +19401,6 @@ "object-inspect": "^1.9.0" } }, - "sigmund": { - "version": "1.0.1", - "dev": true - }, "signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -20487,9 +19518,6 @@ } } }, - "split2": { - "version": "4.1.0" - }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -20717,14 +19745,6 @@ "version": "0.2.0", "dev": true }, - "timers-ext": { - "version": "0.1.7", - "dev": true, - "requires": { - "es5-ext": "~0.10.46", - "next-tick": "1" - } - }, "tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -20773,10 +19793,6 @@ "tslib": "^1.8.1" } }, - "type": { - "version": "1.2.0", - "dev": true - }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -20812,13 +19828,6 @@ "version": "4.6.2", "dev": true }, - "umzug": { - "version": "2.3.0", - "dev": true, - "requires": { - "bluebird": "^3.7.2" - } - }, "unbox-primitive": { "version": "1.0.1", "dev": true, @@ -20833,9 +19842,6 @@ "version": "2.0.5", "dev": true }, - "underscore": { - "version": "1.13.1" - }, "unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", @@ -21074,9 +20080,6 @@ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" }, - "xtend": { - "version": "4.0.2" - }, "y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -21089,23 +20092,6 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, - "yargs": { - "version": "16.2.0", - "dev": true, - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - } - }, - "yargs-parser": { - "version": "20.2.9", - "dev": true - }, "yargs-unparser": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", diff --git a/package.json b/package.json index 0fe4b68d..8a14865f 100644 --- a/package.json +++ b/package.json @@ -18,10 +18,7 @@ "build": "babel src -d build --ignore '**/*.test.js' --ignore '**/__mocks__/*' && cp src/*.json build", "start": "node build/server.js", "start:local": "npm run db:migrate ; babel-node -r dotenv/config src/server.js", - "start:nodemon": "nodemon --exec npm run start:local", - "db:wait": "echo postgresdb deprecated", - "db:migrate": "echo postgresdb deprecated", - "db:teardown": "echo postgresdb deprecated" + "start:nodemon": "nodemon --exec npm run start:local" }, "devDependencies": { "@babel/cli": "^7.23.0", @@ -40,7 +37,6 @@ "nodemon": "^2.0.19", "npm-audit-resolver": "3.0.0-7", "prettier": "^2.6.0", - "sequelize-cli": "^6.4.1", "supertest": "^6.3.3", "typescript": "^4.6.2" }, @@ -59,8 +55,6 @@ "lodash.merge": "^4.6.2", "moment": "^2.30.1", "moment-timezone": "^0.5.45", - "pg": "^8.7.3", - "pg-hstore": "^2.3.4", "sequelize": "^6.33.0", "sinon": "^15.1.2", "swagger-ui-express": "^4.3.0", From e2b731a8e88847997bb4298242a6036033a6db26 Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Mon, 11 Mar 2024 11:34:49 +0000 Subject: [PATCH 54/67] [PRMT-4568] Remove postgresdb from docker config, remove .sequelizerc --- .sequelizerc | 7 ------- Dockerfile | 7 +++---- docker-compose-dtest.yml | 8 -------- docker-compose-itest.yml | 10 ---------- docker-compose.yml | 11 ----------- 5 files changed, 3 insertions(+), 40 deletions(-) delete mode 100644 .sequelizerc diff --git a/.sequelizerc b/.sequelizerc deleted file mode 100644 index ea6fdf4c..00000000 --- a/.sequelizerc +++ /dev/null @@ -1,7 +0,0 @@ -const path = require('path'); - -module.exports = { - 'config': path.resolve('src', 'config', 'database.js'), - 'models-path': path.resolve('src', 'database', 'models'), - 'migrations-path': path.resolve('database', 'migrations') -} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 6999da8d..876c7cd5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,7 @@ FROM node:16.19.0-alpine AS builder # install python and postgres native requirements RUN apk update && \ - apk add --no-cache bash tini postgresql-client && \ + apk add --no-cache bash tini && \ rm -rf /var/cache/apk/* RUN apk add --no-cache \ @@ -14,7 +14,7 @@ RUN apk add --no-cache \ && rm -rf /var/cache/apk/* # Install sequelize postgress native dependencies -RUN apk add --no-cache postgresql-dev g++ make +RUN apk add --no-cache g++ make COPY package*.json /app/ @@ -34,7 +34,7 @@ COPY --from=builder /app /app # install python and postgres native requirements (again, as per builder) # add root CA from deductions team to trusted certificates RUN apk update && \ - apk add --no-cache openssl ca-certificates bash tini postgresql-client && \ + apk add --no-cache openssl ca-certificates bash tini && \ rm -rf /var/cache/apk/* RUN apk add --no-cache \ @@ -48,7 +48,6 @@ RUN apk add --no-cache \ COPY build/ /app/build COPY database/ /app/database COPY build/config/database.js /app/src/config/ -COPY .sequelizerc /app/ COPY scripts/load-api-keys.sh /app/scripts/load-api-keys.sh COPY scripts/run-server-with-db.sh /usr/bin/run-ehr-server diff --git a/docker-compose-dtest.yml b/docker-compose-dtest.yml index 801dc0f4..90bdb129 100644 --- a/docker-compose-dtest.yml +++ b/docker-compose-dtest.yml @@ -6,18 +6,10 @@ services: ehr-repo: image: ${REPOSITORY_URI}:${IMAGE_TAG} links: - - db:db - localstack:localstack - dynamodb-local:dynamodb-local ports: - 3000:3000 - db: - image: postgres:12.1 - environment: - POSTGRES_PASSWORD: ${DATABASE_PASSWORD} - POSTGRES_USER: ${DATABASE_USER} - POSTGRES_DB: ${DATABASE_NAME} - LC_ALL: C localstack: image: localstack/localstack:0.10.9 environment: diff --git a/docker-compose-itest.yml b/docker-compose-itest.yml index f4cd11ff..dcb216a7 100644 --- a/docker-compose-itest.yml +++ b/docker-compose-itest.yml @@ -2,20 +2,10 @@ version: '2' services: default: links: - - db:db - localstack:localstack - dynamodb-local:dynamodb-local ports: - "3000:3000" - db: - image: postgres:12.1 - environment: - POSTGRES_PASSWORD: ${DATABASE_PASSWORD} - POSTGRES_USER: ${DATABASE_USER} - POSTGRES_DB: ${DATABASE_NAME} - LC_ALL: C - ports: - - "5432:5432" localstack: image: localstack/localstack:0.10.9 environment: diff --git a/docker-compose.yml b/docker-compose.yml index 4ef9fd2b..9b2a301c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,16 +1,5 @@ version: '3.4' services: - postgres: - image: postgres:12.1 - ports: - - 5432:5432 - expose: - - 5432 - environment: - POSTGRES_USER: ${DATABASE_USER} - POSTGRES_PASSWORD: ${DATABASE_PASSWORD} - POSTGRES_DB: ${DATABASE_NAME} - LC_ALL: C localstack: image: localstack/localstack ports: From 025ccc884fa6f11ad17124773997b4a43091c8ec Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Mon, 11 Mar 2024 11:37:48 +0000 Subject: [PATCH 55/67] [PRMT-4568] Continue removing postgres-db related stuffs --- Dockerfile | 4 ++-- src/config/index.js | 3 --- src/services/database/__mocks__/check-db-health.js | 5 ----- 3 files changed, 2 insertions(+), 10 deletions(-) delete mode 100644 src/services/database/__mocks__/check-db-health.js diff --git a/Dockerfile b/Dockerfile index 876c7cd5..0176c141 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM node:16.19.0-alpine AS builder -# install python and postgres native requirements +# install python native requirements RUN apk update && \ apk add --no-cache bash tini && \ rm -rf /var/cache/apk/* @@ -31,7 +31,7 @@ COPY --from=builder /usr/local/bin/node /usr/local/bin # take native-install node modules COPY --from=builder /app /app -# install python and postgres native requirements (again, as per builder) +# install python native requirements (again, as per builder) # add root CA from deductions team to trusted certificates RUN apk update && \ apk add --no-cache openssl ca-certificates bash tini && \ diff --git a/src/config/index.js b/src/config/index.js index e2f8b413..6ebf87f7 100644 --- a/src/config/index.js +++ b/src/config/index.js @@ -1,12 +1,9 @@ -// import sequelizeConfig from './database'; - export const portNumber = 3000; export const initializeConfig = () => ({ ehrServiceUrl: process.env.SERVICE_URL || `http://127.0.0.1:${portNumber}`, awsS3BucketName: process.env.S3_BUCKET_NAME, localstackUrl: process.env.LOCALSTACK_URL, - // sequelize: sequelizeConfig, nhsEnvironment: process.env.NHS_ENVIRONMENT || 'local', consumerApiKeys: loadConsumerKeys(), }); diff --git a/src/services/database/__mocks__/check-db-health.js b/src/services/database/__mocks__/check-db-health.js deleted file mode 100644 index 76989343..00000000 --- a/src/services/database/__mocks__/check-db-health.js +++ /dev/null @@ -1,5 +0,0 @@ -export const checkDbHealth = () => ({ - type: 'postgresql', - connection: true, - writable: true, -}); From 420a672e714e596df651b8206d6b83376b3f162d Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Mon, 11 Mar 2024 12:06:49 +0000 Subject: [PATCH 56/67] [PRMT-4568] Remove postgres db related content from ./tasks (except terraform things) --- tasks | 67 ----------------------------------------------------------- 1 file changed, 67 deletions(-) diff --git a/tasks b/tasks index 1d7e7c77..f65ef0ad 100755 --- a/tasks +++ b/tasks @@ -83,9 +83,7 @@ function configure_service_url { } function prepare_local_envs_for_ide { - export DATABASE_HOST=localhost envs=$(printenv | grep "NODE_ENV" && \ - printenv | grep "DATABASE" && \ printenv | grep "AUTHORIZATION_KEYS" && \ printenv | grep "SERVICE_URL" ) @@ -101,14 +99,9 @@ function generate_secure_string { function configure_local_envs { export AUTHORIZATION_KEYS=$(generate_secure_string 10) - export DATABASE_NAME=deductions_test - export DATABASE_USER=$(generate_secure_string 20) - export DATABASE_PASSWORD=$(generate_secure_string 20) - export DATABASE_HOST=db export S3_BUCKET_NAME=test-bucket export LOCALSTACK_URL=http://localstack:4572 export REPOSITORY_URI=$IMAGE_REPO_NAME - export SKIP_DB_MIGRATION=false export NODE_ENV=local export DYNAMODB_NAME=local-test-db @@ -119,15 +112,6 @@ function configure_local_envs { configure_service_url } -function configure_db_migration_envs { - export SKIP_DB_MIGRATION=false - export DATABASE_HOST=$(_get_aws_ssm_secret "/repo/${NHS_ENVIRONMENT}/output/prm-deductions-ehr-repository/db-host") - export DATABASE_USER=$(_get_aws_ssm_secret "/repo/${NHS_ENVIRONMENT}/output/prm-deductions-ehr-repository/db-migration-user") - export DATABASE_NAME=$(_get_aws_ssm_secret "/repo/${NHS_ENVIRONMENT}/output/prm-deductions-ehr-repository/db-name") - export DATABASE_PASSWORD="$(aws rds generate-db-auth-token --hostname $DATABASE_HOST --port 5432 --region $AWS_DEFAULT_REGION --username $DATABASE_USER)" - export USE_SSL_FOR_DB="true" -} - function get_aws_account_id { AWS_ACCOUNT_ID=$(dojo -c Dojofile-infra "aws sts get-caller-identity | jq -r .Account") } @@ -251,18 +235,6 @@ function tf_apply_db_roles { # Script Functions # #################### -function get_db_credentials { - export DATABASE_PASSWORD=$(get_aws_ssm_secret "/repo/${NHS_ENVIRONMENT}/user-input/ehr-repo-db-password") - export DATABASE_USER=$(get_aws_ssm_secret "/repo/${NHS_ENVIRONMENT}/user-input/ehr-repo-db-username") - export DATABASE_HOST=$(get_aws_ssm_secret "/repo/${NHS_ENVIRONMENT}/output/prm-deductions-ehr-repository/db-host") - export DATABASE_NAME="deductions_db" -} - -function drop_db { - PGPASSWORD="${DATABASE_PASSWORD}" dropdb --host="${DATABASE_HOST}" --username="${DATABASE_USER}" $DATABASE_NAME - echo "Database $DATABASE_NAME was dropped successfully" -} - function create_secret_ssm_param { secret_id="$1" value="$2" @@ -362,8 +334,6 @@ case "${command}" in ;; _setup_test_integration_local) configure_local_envs -# npm run db:teardown -# npm run db:migrate prepare_local_envs_for_ide ;; _test_lint) @@ -383,13 +353,10 @@ case "${command}" in _test_integration_local) configure_local_envs npm install -# npm run db:teardown -# npm run db:migrate npm run test:integration ;; _test_integration) npm install -# npm run db:migrate sh scripts/create-dynamodb-table.sh node scripts/wait-for-localstack.js npm run test:integration @@ -407,7 +374,6 @@ case "${command}" in npm run lint npm run test:unit configure_local_envs -# npm run db:migrate sh scripts/create-dynamodb-table.sh npm run test:integration ;; @@ -426,7 +392,6 @@ case "${command}" in ;; _test_coverage) npm install -# npm run db:migrate sh scripts/create-dynamodb-table.sh npm run test:coverage ;; @@ -451,7 +416,6 @@ case "${command}" in ;; _test_docker) npm install -# ./scripts/migrate-db.sh sh scripts/create-dynamodb-table.sh node scripts/wait-for-localstack.js npm run test:docker @@ -481,14 +445,6 @@ case "${command}" in nslookup "ehr-repo.${DOMAIN_INFIX}.patient-deductions.nhs.uk" curl -i --fail "https://ehr-repo.${DOMAIN_INFIX}.patient-deductions.nhs.uk/health" ;; - _drop_db) - drop_db - ;; - drop_db) - check_env - get_db_credentials - dojo -c Dojofile-infra "./tasks _drop_db" - ;; _create_secrets) _assume_environment_role $NHS_ENVIRONMENT generate_username_ssm_param "/repo/${NHS_ENVIRONMENT}/user-input/ehr-repo-db-username" @@ -554,34 +510,11 @@ case "${command}" in check_env dojo -c Dojofile-infra "./tasks _tf_apply_db_roles" ;; - _run_db_migrations) - _assume_environment_role $NHS_ENVIRONMENT - configure_db_migration_envs - npm install - scripts/migrate-db.sh - ;; - run_db_migrations) - check_env - dojo -c Dojofile "./tasks _run_db_migrations" - ;; promote_docker_image) check_env set_image_tag promote_docker_image "$IMAGE_REPO_NAME:$IMAGE_TAG" "$NHS_ENVIRONMENT" ;; - _grant_db_permissions) - check_env - _assume_environment_role $NHS_ENVIRONMENT - db_host=$(_get_aws_ssm_secret "/repo/${NHS_ENVIRONMENT}/output/prm-deductions-ehr-repository/db-host") - db_username=$(_get_aws_ssm_secret "/repo/${NHS_ENVIRONMENT}/user-input/ehr-repo-db-username") - db_password=$(_get_aws_ssm_secret "/repo/${NHS_ENVIRONMENT}/user-input/ehr-repo-db-password") - db_name=$(_get_aws_ssm_secret "/repo/${NHS_ENVIRONMENT}/output/prm-deductions-ehr-repository/db-name") - export PGPASSWORD=$db_password - psql -h $db_host -U $db_username -d $db_name -f scripts/grant-db-permissions.sql - ;; - grant_db_permissions) - dojo "./tasks _grant_db_permissions" - ;; _wait_ecs) _assume_environment_role $NHS_ENVIRONMENT aws ecs wait services-stable \ From 1beb507b153e949754bb68ab5758c35c41ceb3b7 Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Mon, 11 Mar 2024 13:25:50 +0000 Subject: [PATCH 57/67] [PRMT-4568] Run linter and formatter --- src/__tests__/app.integration.test.js | 16 +++------- src/api/fragments/get-fragment-controller.js | 2 +- .../message-location-controller.test.js | 4 +-- .../store-message-controller.test.js | 32 +++++++++++-------- .../messages/message-location-controller.js | 2 +- src/api/messages/store-message-controller.js | 12 +++---- .../__tests__/delete-ehr-controller.test.js | 2 +- .../patient-details-controller.test.js | 6 ++-- src/api/patients/delete-ehr-controller.js | 2 +- src/api/patients/health-record-controller.js | 4 +-- .../patients/patient-details-controller.js | 2 +- src/errors/errors.js | 14 ++++---- src/middleware/__tests__/auth.test.js | 2 +- src/models/core.js | 2 +- src/models/fragment.js | 4 +-- ...o-ehr-transfer-tracker.integration.test.js | 2 -- ...onversation-repository.integration.test.js | 18 +++++++---- .../database/dynamo-ehr-transfer-tracker.js | 8 ++--- src/services/database/dynamodb-client.js | 2 +- .../database/ehr-fragment-repository.js | 6 ++-- src/services/time.js | 1 - src/utilities/integration-test-utilities.js | 2 +- 22 files changed, 70 insertions(+), 75 deletions(-) diff --git a/src/__tests__/app.integration.test.js b/src/__tests__/app.integration.test.js index 97209898..ab712726 100644 --- a/src/__tests__/app.integration.test.js +++ b/src/__tests__/app.integration.test.js @@ -12,12 +12,12 @@ import { createConversationForTest, } from '../utilities/integration-test-utilities'; import { getConversationById } from '../services/database/ehr-conversation-repository'; -import { ConversationStatus, MessageType, RecordType } from "../models/enums"; +import { ConversationStatus, MessageType, RecordType } from '../models/enums'; import { isCore } from '../models/core'; import { getFragmentByKey } from '../services/database/ehr-fragment-repository'; import { isFragment } from '../models/fragment'; -import { getEpochTimeInSecond, TIMESTAMP_REGEX } from "../services/time"; -import moment from "moment-timezone"; +import { getEpochTimeInSecond, TIMESTAMP_REGEX } from '../services/time'; +import moment from 'moment-timezone'; describe('app', () => { const config = initializeConfig(); @@ -85,8 +85,6 @@ describe('app', () => { const fragmentMessageId = v4(); const nhsNumber = '2345678901'; - const db = EhrTransferTracker.getInstance(); - it('should return presigned url when the fragment record exists', async () => { // setting up database const coreMessageResponse = await request(app) @@ -400,7 +398,6 @@ describe('app', () => { }); describe('POST /messages', () => { - const db = EhrTransferTracker.getInstance(); const nhsNumber = '1234567890'; let conversationId; let messageId; @@ -416,8 +413,7 @@ describe('app', () => { cleanupRecordsForTest(conversationId); }); - afterAll(async () => { - }); + afterAll(async () => {}); it('should save health record without fragments in the database and return 201', async () => { // when @@ -653,7 +649,6 @@ describe('app', () => { const timestampAfterDelete = getEpochTimeInSecond(moment().add(8, 'week')); - for (const item of softDeletedRecords) { expect(item).toMatchObject({ InboundConversationId: inboundConversationId, @@ -664,7 +659,6 @@ describe('app', () => { } }); - it('should delete all record if patient has more than one set of record in our storage', async () => { // given const inboundConversationId1 = uuid(); @@ -673,7 +667,7 @@ describe('app', () => { const coreMessageId2 = uuid(); await createCompleteRecord(nhsNumber, inboundConversationId1, coreMessageId1); - await new Promise(resolve => setTimeout(resolve, 1500)); // time buffer to ensure record 2 get a newer timestamp + await new Promise((resolve) => setTimeout(resolve, 1500)); // time buffer to ensure record 2 get a newer timestamp await createCompleteRecord(nhsNumber, inboundConversationId2, coreMessageId2); diff --git a/src/api/fragments/get-fragment-controller.js b/src/api/fragments/get-fragment-controller.js index c3577ead..b39c9a55 100644 --- a/src/api/fragments/get-fragment-controller.js +++ b/src/api/fragments/get-fragment-controller.js @@ -2,7 +2,7 @@ import { getSignedUrl } from '../../services/storage'; import { param } from 'express-validator'; import { logError, logInfo } from '../../middleware/logging'; import { setCurrentSpanAttributes } from '../../config/tracing'; -import { fragmentAlreadyReceived } from "../../services/database/ehr-fragment-repository"; +import { fragmentAlreadyReceived } from '../../services/database/ehr-fragment-repository'; export const getFragmentControllerValidation = [ param('conversationId').isUUID().withMessage("'conversationId' provided is not a UUID"), diff --git a/src/api/messages/__tests__/message-location-controller.test.js b/src/api/messages/__tests__/message-location-controller.test.js index 35bebf95..38d5ce51 100644 --- a/src/api/messages/__tests__/message-location-controller.test.js +++ b/src/api/messages/__tests__/message-location-controller.test.js @@ -4,10 +4,10 @@ import { getSignedUrl } from '../../../services/storage'; import { v4 as uuid } from 'uuid'; import { logError, logInfo } from '../../../middleware/logging'; import { initializeConfig } from '../../../config'; -import { fragmentAlreadyReceived } from "../../../services/database/ehr-fragment-repository"; +import { fragmentAlreadyReceived } from '../../../services/database/ehr-fragment-repository'; jest.mock('../../../services/storage'); -jest.mock("../../../services/database/ehr-fragment-repository"); +jest.mock('../../../services/database/ehr-fragment-repository'); jest.mock('../../../middleware/logging'); jest.mock('../../../config', () => ({ initializeConfig: jest.fn().mockReturnValue({}), diff --git a/src/api/messages/__tests__/store-message-controller.test.js b/src/api/messages/__tests__/store-message-controller.test.js index 094b0e33..60b573ea 100644 --- a/src/api/messages/__tests__/store-message-controller.test.js +++ b/src/api/messages/__tests__/store-message-controller.test.js @@ -3,20 +3,20 @@ import request from 'supertest'; import app from '../../../app'; import { initializeConfig } from '../../../config'; import { logError } from '../../../middleware/logging'; -import { MessageType } from "../../../models/enums"; +import { MessageType } from '../../../models/enums'; import { getConversationStatus, - updateConversationCompleteness -} from "../../../services/database/ehr-conversation-repository"; -import { createCore } from "../../../services/database/ehr-core-repository"; + updateConversationCompleteness, +} from '../../../services/database/ehr-conversation-repository'; +import { createCore } from '../../../services/database/ehr-core-repository'; import { fragmentExistsInRecord, - markFragmentAsReceivedAndCreateItsParts -} from "../../../services/database/ehr-fragment-repository"; + markFragmentAsReceivedAndCreateItsParts, +} from '../../../services/database/ehr-fragment-repository'; -jest.mock("../../../services/database/ehr-conversation-repository"); -jest.mock("../../../services/database/ehr-core-repository"); -jest.mock("../../../services/database/ehr-fragment-repository"); +jest.mock('../../../services/database/ehr-conversation-repository'); +jest.mock('../../../services/database/ehr-core-repository'); +jest.mock('../../../services/database/ehr-fragment-repository'); jest.mock('../../../middleware/logging'); jest.mock('../../../config', () => ({ initializeConfig: jest.fn().mockReturnValue({}), @@ -83,9 +83,11 @@ describe('storeMessageController', () => { expect(res.status).toBe(201); expect(createCore).not.toHaveBeenCalled(); - expect(markFragmentAsReceivedAndCreateItsParts).toHaveBeenCalledWith(messageId, conversationId, [ - nestedFragmentId, - ]); + expect(markFragmentAsReceivedAndCreateItsParts).toHaveBeenCalledWith( + messageId, + conversationId, + [nestedFragmentId] + ); expect(updateConversationCompleteness).toHaveBeenCalledWith(conversationId); }); @@ -107,7 +109,11 @@ describe('storeMessageController', () => { expect(res.status).toBe(201); expect(fragmentExistsInRecord).toHaveBeenCalledWith(nestedFragmentId); - expect(markFragmentAsReceivedAndCreateItsParts).toHaveBeenCalledWith(nestedFragmentId, conversationId, []); + expect(markFragmentAsReceivedAndCreateItsParts).toHaveBeenCalledWith( + nestedFragmentId, + conversationId, + [] + ); expect(updateConversationCompleteness).toHaveBeenCalledWith(conversationId); }); }); diff --git a/src/api/messages/message-location-controller.js b/src/api/messages/message-location-controller.js index 52396d54..12c36f21 100644 --- a/src/api/messages/message-location-controller.js +++ b/src/api/messages/message-location-controller.js @@ -2,7 +2,7 @@ import { getSignedUrl } from '../../services/storage'; import { param } from 'express-validator'; import { logError, logInfo } from '../../middleware/logging'; import { setCurrentSpanAttributes } from '../../config/tracing'; -import { fragmentAlreadyReceived } from "../../services/database/ehr-fragment-repository"; +import { fragmentAlreadyReceived } from '../../services/database/ehr-fragment-repository'; export const messageLocationControllerValidation = [ param('conversationId').isUUID().withMessage("'conversationId' provided is not a UUID"), diff --git a/src/api/messages/store-message-controller.js b/src/api/messages/store-message-controller.js index e214a361..b03e2dcc 100644 --- a/src/api/messages/store-message-controller.js +++ b/src/api/messages/store-message-controller.js @@ -2,15 +2,15 @@ import { body } from 'express-validator'; import { MessageType } from '../../models/enums'; import { logError, logInfo, logWarning } from '../../middleware/logging'; import { setCurrentSpanAttributes } from '../../config/tracing'; -import { createCore } from "../../services/database/ehr-core-repository"; +import { createCore } from '../../services/database/ehr-core-repository'; import { fragmentExistsInRecord, - markFragmentAsReceivedAndCreateItsParts -} from "../../services/database/ehr-fragment-repository"; + markFragmentAsReceivedAndCreateItsParts, +} from '../../services/database/ehr-fragment-repository'; import { getConversationStatus, - updateConversationCompleteness -} from "../../services/database/ehr-conversation-repository"; + updateConversationCompleteness, +} from '../../services/database/ehr-conversation-repository'; export const storeMessageControllerValidation = [ body('data.type').equals('messages'), @@ -57,7 +57,7 @@ export const storeMessageController = async (req, res) => { }); } if (messageType === MessageType.FRAGMENT) { - if (!await fragmentExistsInRecord(messageId)) { + if (!(await fragmentExistsInRecord(messageId))) { logWarning( `Fragment message ${messageId} did not arrive in order. Fragment parts: ${JSON.stringify( fragmentMessageIds diff --git a/src/api/patients/__tests__/delete-ehr-controller.test.js b/src/api/patients/__tests__/delete-ehr-controller.test.js index c258c0d4..2d160a59 100644 --- a/src/api/patients/__tests__/delete-ehr-controller.test.js +++ b/src/api/patients/__tests__/delete-ehr-controller.test.js @@ -1,6 +1,6 @@ import request from 'supertest'; import app from '../../../app'; -import { markRecordAsSoftDeleteForPatient } from "../../../services/database/ehr-conversation-repository"; +import { markRecordAsSoftDeleteForPatient } from '../../../services/database/ehr-conversation-repository'; import { initializeConfig } from '../../../config'; import { logError, logWarning } from '../../../middleware/logging'; import { v4 as uuid } from 'uuid'; diff --git a/src/api/patients/__tests__/patient-details-controller.test.js b/src/api/patients/__tests__/patient-details-controller.test.js index 51f243e3..2f9fc300 100644 --- a/src/api/patients/__tests__/patient-details-controller.test.js +++ b/src/api/patients/__tests__/patient-details-controller.test.js @@ -7,9 +7,9 @@ import { logError, logInfo, logWarning } from '../../../middleware/logging'; import getSignedUrl from '../../../services/storage/get-signed-url'; import { getCurrentConversationIdForPatient, - getMessageIdsForConversation -} from "../../../services/database/ehr-conversation-repository"; -import { HealthRecordNotFoundError } from "../../../errors/errors"; + getMessageIdsForConversation, +} from '../../../services/database/ehr-conversation-repository'; +import { HealthRecordNotFoundError } from '../../../errors/errors'; jest.mock('../../../services/database/ehr-conversation-repository'); jest.mock('../../../middleware/logging'); diff --git a/src/api/patients/delete-ehr-controller.js b/src/api/patients/delete-ehr-controller.js index 98eaab1c..d859caa4 100644 --- a/src/api/patients/delete-ehr-controller.js +++ b/src/api/patients/delete-ehr-controller.js @@ -1,6 +1,6 @@ import { param } from 'express-validator'; import { logError, logInfo, logWarning } from '../../middleware/logging'; -import { markRecordAsSoftDeleteForPatient } from "../../services/database/ehr-conversation-repository"; +import { markRecordAsSoftDeleteForPatient } from '../../services/database/ehr-conversation-repository'; export const deleteEhrValidation = [ param('nhsNumber') .isNumeric() diff --git a/src/api/patients/health-record-controller.js b/src/api/patients/health-record-controller.js index ecb3939f..8219a827 100644 --- a/src/api/patients/health-record-controller.js +++ b/src/api/patients/health-record-controller.js @@ -1,7 +1,7 @@ import { param } from 'express-validator'; import { setCurrentSpanAttributes } from '../../config/tracing'; -import { HealthRecordStatus } from "../../models/enums"; -import { getConversationStatus } from "../../services/database/ehr-conversation-repository"; +import { HealthRecordStatus } from '../../models/enums'; +import { getConversationStatus } from '../../services/database/ehr-conversation-repository'; export const healthRecordControllerValidation = [ param('conversationId').isUUID().withMessage("'conversationId' provided is not a UUID"), diff --git a/src/api/patients/patient-details-controller.js b/src/api/patients/patient-details-controller.js index 2fdf4d22..7009099b 100644 --- a/src/api/patients/patient-details-controller.js +++ b/src/api/patients/patient-details-controller.js @@ -45,7 +45,7 @@ export const patientDetailsController = async (req, res) => { // TODO: remove this duplicated `conversationIdFromEhrIn` field, // after updating ehr-out to use the field name "inboundConversationId" conversationIdFromEhrIn: currentHealthRecordConversationId, - inboundConversationId: currentHealthRecordConversationId + inboundConversationId: currentHealthRecordConversationId, }; res.status(200).json(responseBody); diff --git a/src/errors/errors.js b/src/errors/errors.js index df73eda1..f1a29742 100644 --- a/src/errors/errors.js +++ b/src/errors/errors.js @@ -1,22 +1,20 @@ -import { logError } from "../middleware/logging"; +import { logError } from '../middleware/logging'; export const errorMessages = { HealthRecordNotFound: 'No complete health record was found with given criteria', - MessageNotFound: 'There were no undeleted messages associated with conversation id' -} - + MessageNotFound: 'There were no undeleted messages associated with conversation id', +}; export class HealthRecordNotFoundError extends Error { constructor(error) { super(errorMessages.HealthRecordNotFound); logError(errorMessages.HealthRecordNotFound, error); - }; + } } - export class MessageNotFoundError extends Error { constructor(error) { super(errorMessages.MessageNotFound); logError(errorMessages.MessageNotFound, error); - }; -} \ No newline at end of file + } +} diff --git a/src/middleware/__tests__/auth.test.js b/src/middleware/__tests__/auth.test.js index 9fe6c41f..0a72672d 100644 --- a/src/middleware/__tests__/auth.test.js +++ b/src/middleware/__tests__/auth.test.js @@ -3,7 +3,7 @@ import app from '../../app'; import { v4 as uuid } from 'uuid'; import { initializeConfig } from '../../config'; import { logInfo, logWarning } from '../logging'; -import { fragmentAlreadyReceived } from "../../services/database/ehr-fragment-repository"; +import { fragmentAlreadyReceived } from '../../services/database/ehr-fragment-repository'; jest.mock('../logging'); jest.mock('../../services/database/ehr-fragment-repository'); diff --git a/src/models/core.js b/src/models/core.js index b152b069..cbe61dac 100644 --- a/src/models/core.js +++ b/src/models/core.js @@ -1,5 +1,5 @@ import { getUKTimestamp } from '../services/time'; -import { CoreStatus, RecordType } from "./enums"; +import { CoreStatus, RecordType } from './enums'; import { validateIds } from '../utilities/dynamodb-helper'; export const buildCore = (inboundConversationId, messageId) => { diff --git a/src/models/fragment.js b/src/models/fragment.js index a524cf6d..1ab5ea43 100644 --- a/src/models/fragment.js +++ b/src/models/fragment.js @@ -1,6 +1,6 @@ import { getUKTimestamp } from '../services/time'; -import { addChangesToUpdateParams, validateIds } from "../utilities/dynamodb-helper"; -import { RecordType } from "./enums"; +import { addChangesToUpdateParams, validateIds } from '../utilities/dynamodb-helper'; +import { RecordType } from './enums'; const fieldsAllowedToUpdate = ['TransferStatus', 'ParentId', 'ReceivedAt', 'DeletedAt']; diff --git a/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js b/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js index 11fe2f78..92ac7342 100644 --- a/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js +++ b/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js @@ -1,12 +1,10 @@ import { EhrTransferTracker } from '../dynamo-ehr-transfer-tracker'; import { v4 as uuid } from 'uuid'; import { RecordType } from '../../../models/enums'; -import { createCore } from '../ehr-core-repository'; import { cleanupRecordsForTest, createConversationForTest, } from '../../../utilities/integration-test-utilities'; -import { markFragmentAsReceivedAndCreateItsParts } from '../ehr-fragment-repository'; import { buildCore } from '../../../models/core'; describe('EhrTransferTracker', () => { diff --git a/src/services/database/__tests__/ehr-conversation-repository.integration.test.js b/src/services/database/__tests__/ehr-conversation-repository.integration.test.js index 12e9c5c7..438be114 100644 --- a/src/services/database/__tests__/ehr-conversation-repository.integration.test.js +++ b/src/services/database/__tests__/ehr-conversation-repository.integration.test.js @@ -15,13 +15,11 @@ import { } from '../../../utilities/integration-test-utilities'; import { createCore } from '../ehr-core-repository'; import { EhrTransferTracker } from '../dynamo-ehr-transfer-tracker'; -import { - markFragmentAsReceivedAndCreateItsParts, -} from '../ehr-fragment-repository'; +import { markFragmentAsReceivedAndCreateItsParts } from '../ehr-fragment-repository'; import { HealthRecordNotFoundError, MessageNotFoundError } from '../../../errors/errors'; import { buildCore } from '../../../models/core'; import { getEpochTimeInSecond } from '../../time'; -import moment from "moment-timezone"; +import moment from 'moment-timezone'; jest.mock('../../../middleware/logging'); @@ -274,7 +272,9 @@ describe('ehr-conversation-repository', () => { await createCore({ conversationId, messageId, fragmentMessageIds: [] }); // when - const { coreMessageId, fragmentMessageIds } = await getMessageIdsForConversation(conversationId); + const { coreMessageId, fragmentMessageIds } = await getMessageIdsForConversation( + conversationId + ); // then expect(coreMessageId).toEqual(messageId); @@ -291,7 +291,9 @@ describe('ehr-conversation-repository', () => { await markFragmentAsReceived(fragmentMessageId, conversationId); // when - const { coreMessageId, fragmentMessageIds } = await getMessageIdsForConversation(conversationId); + const { coreMessageId, fragmentMessageIds } = await getMessageIdsForConversation( + conversationId + ); // then expect(coreMessageId).toEqual(messageId); @@ -312,7 +314,9 @@ describe('ehr-conversation-repository', () => { await markFragmentAsReceived(nestedFragmentId, conversationId); // when - const { coreMessageId, fragmentMessageIds } = await getMessageIdsForConversation(conversationId); + const { coreMessageId, fragmentMessageIds } = await getMessageIdsForConversation( + conversationId + ); // then expect(coreMessageId).toEqual(messageId); diff --git a/src/services/database/dynamo-ehr-transfer-tracker.js b/src/services/database/dynamo-ehr-transfer-tracker.js index 6c6b008f..75b24e20 100644 --- a/src/services/database/dynamo-ehr-transfer-tracker.js +++ b/src/services/database/dynamo-ehr-transfer-tracker.js @@ -1,13 +1,9 @@ -import { - TransactWriteCommand, - QueryCommand, - GetCommand, -} from '@aws-sdk/lib-dynamodb'; +import { TransactWriteCommand, QueryCommand, GetCommand } from '@aws-sdk/lib-dynamodb'; import { logError, logInfo } from '../../middleware/logging'; import { RecordType } from '../../models/enums'; import { getDynamodbClient } from './dynamodb-client'; -import { IS_IN_LOCAL } from "../../utilities/integration-test-utilities"; +import { IS_IN_LOCAL } from '../../utilities/integration-test-utilities'; export class EhrTransferTracker { /** diff --git a/src/services/database/dynamodb-client.js b/src/services/database/dynamodb-client.js index df0eaec2..42c9e870 100644 --- a/src/services/database/dynamodb-client.js +++ b/src/services/database/dynamodb-client.js @@ -1,6 +1,6 @@ import { DynamoDBClient } from '@aws-sdk/client-dynamodb'; import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb'; -import { IS_IN_LOCAL } from "../../utilities/integration-test-utilities"; +import { IS_IN_LOCAL } from '../../utilities/integration-test-utilities'; export const getDynamodbClient = () => { const clientConfig = { diff --git a/src/services/database/ehr-fragment-repository.js b/src/services/database/ehr-fragment-repository.js index 37a14f73..250b3837 100644 --- a/src/services/database/ehr-fragment-repository.js +++ b/src/services/database/ehr-fragment-repository.js @@ -1,7 +1,7 @@ import { getUKTimestamp } from '../time'; import { EhrTransferTracker } from './dynamo-ehr-transfer-tracker'; import { buildFragmentUpdateParams } from '../../models/fragment'; -import { FragmentStatus, RecordType } from "../../models/enums"; +import { FragmentStatus, RecordType } from '../../models/enums'; import { logError } from '../../middleware/logging'; export const markFragmentAsReceivedAndCreateItsParts = async ( @@ -15,7 +15,7 @@ export const markFragmentAsReceivedAndCreateItsParts = async ( const currentFragmentParams = buildFragmentUpdateParams(conversationId, messageId, { ReceivedAt: timestamp, - TransferStatus: FragmentStatus.COMPLETE + TransferStatus: FragmentStatus.COMPLETE, }); const childFragmentsParams = remainingPartsIds.map((fragmentPartId) => { @@ -52,4 +52,4 @@ export const fragmentAlreadyReceived = async (conversationId, messageId) => { logError('Querying database for fragment message failed', e); throw e; } -} +}; diff --git a/src/services/time.js b/src/services/time.js index 149d45fe..bf86806a 100644 --- a/src/services/time.js +++ b/src/services/time.js @@ -10,4 +10,3 @@ export const TIMESTAMP_REGEX = /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\+\d{2}:00/; export const getEpochTimeInSecond = (datetime) => { return moment(datetime).unix(); }; - diff --git a/src/utilities/integration-test-utilities.js b/src/utilities/integration-test-utilities.js index b927e358..f656e548 100644 --- a/src/utilities/integration-test-utilities.js +++ b/src/utilities/integration-test-utilities.js @@ -71,4 +71,4 @@ export const cleanupRecordsForTestByNhsNumber = async (nhsNumber) => { return Promise.all(removeAllRecords); }; -export const IS_IN_LOCAL = process.env.NHS_ENVIRONMENT === 'local' || !process.env.NHS_ENVIRONMENT; \ No newline at end of file +export const IS_IN_LOCAL = process.env.NHS_ENVIRONMENT === 'local' || !process.env.NHS_ENVIRONMENT; From d1b5e7af5363abc0c32e74ecfb3bb42a1bb7956f Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Mon, 11 Mar 2024 13:40:10 +0000 Subject: [PATCH 58/67] [PRMT-4568] Remove commented out code --- src/api/patients/patient-details-controller.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/api/patients/patient-details-controller.js b/src/api/patients/patient-details-controller.js index 7009099b..f8f76c9f 100644 --- a/src/api/patients/patient-details-controller.js +++ b/src/api/patients/patient-details-controller.js @@ -1,8 +1,4 @@ import { param } from 'express-validator'; -// import { -// getCurrentHealthRecordIdForPatient, -// getHealthRecordMessageIds, -// } from '../../services/database/health-record-repository'; import { logError, logInfo, logWarning } from '../../middleware/logging'; import getSignedUrl from '../../services/storage/get-signed-url'; import { setCurrentSpanAttributes } from '../../config/tracing'; From 3066b8047249ea21f0b1d962ee818084fcd33dfb Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Mon, 11 Mar 2024 14:10:13 +0000 Subject: [PATCH 59/67] [PRMT-4568] remove sequelize from package.json --- package-lock.json | 208 +++------------------------------------------- package.json | 1 - 2 files changed, 12 insertions(+), 197 deletions(-) diff --git a/package-lock.json b/package-lock.json index c1b941c3..80756673 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,7 +22,6 @@ "lodash.merge": "^4.6.2", "moment": "^2.30.1", "moment-timezone": "^0.5.45", - "sequelize": "^6.33.0", "sinon": "^15.1.2", "swagger-ui-express": "^4.3.0", "traverse": "^0.6.6", @@ -4474,14 +4473,6 @@ "@babel/types": "^7.20.7" } }, - "node_modules/@types/debug": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", - "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", - "dependencies": { - "@types/ms": "*" - } - }, "node_modules/@types/graceful-fs": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", @@ -4520,13 +4511,9 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/ms": { - "version": "0.7.34", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", - "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==" - }, "node_modules/@types/node": { "version": "17.0.23", + "dev": true, "license": "MIT" }, "node_modules/@types/prettier": { @@ -4541,11 +4528,6 @@ "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", "dev": true }, - "node_modules/@types/validator": { - "version": "13.11.7", - "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.11.7.tgz", - "integrity": "sha512-q0JomTsJ2I5Mv7dhHhQLGjMvX0JJm5dyZ1DXQySIUzU1UlwzB8bt+R6+LODUbz0UDIOvEzGc28tk27gBJw2N8Q==" - }, "node_modules/@types/yargs": { "version": "17.0.32", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", @@ -5578,6 +5560,7 @@ }, "node_modules/debug": { "version": "4.3.4", + "dev": true, "license": "MIT", "dependencies": { "ms": "2.1.2" @@ -5734,11 +5717,6 @@ "node": ">=12" } }, - "node_modules/dottie": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.6.tgz", - "integrity": "sha512-iGCHkfUc5kFekGiqhe8B/mdaurD+lakO9txNnTvKtA6PISrw86LgqHvRzWYPyoE2Ph5aMIrCw9/uko6XHTKCwA==" - }, "node_modules/ee-first": { "version": "1.1.1", "license": "MIT" @@ -7047,14 +7025,6 @@ "node": ">=0.8.19" } }, - "node_modules/inflection": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.13.4.tgz", - "integrity": "sha512-6I/HUDeYFfuNCVS3td055BaXBwKYuzw7K3ExVMStBowKo9oOAMJIXIHvdyR3iboTCp1b+1i5DSkIZTcwIktuDw==", - "engines": [ - "node >= 0.4.0" - ] - }, "node_modules/inflight": { "version": "1.0.6", "dev": true, @@ -9908,11 +9878,6 @@ "node": ">=8" } }, - "node_modules/pg-connection-string": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", - "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==" - }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -10341,11 +10306,6 @@ "node": ">=10" } }, - "node_modules/retry-as-promised": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-7.0.4.tgz", - "integrity": "sha512-XgmCoxKWkDofwH8WddD0w85ZfqYz+ZHlr5yo+3YUCfycWawU56T5ckWXsScsj5B8tqUcIG67DxXByo3VUgiAdA==" - }, "node_modules/reusify": { "version": "1.0.4", "dev": true, @@ -10411,6 +10371,7 @@ "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, "dependencies": { "lru-cache": "^6.0.0" }, @@ -10423,6 +10384,7 @@ }, "node_modules/semver/node_modules/lru-cache": { "version": "6.0.0", + "dev": true, "license": "ISC", "dependencies": { "yallist": "^4.0.0" @@ -10433,6 +10395,7 @@ }, "node_modules/semver/node_modules/yallist": { "version": "4.0.0", + "dev": true, "license": "ISC" }, "node_modules/send": { @@ -10472,74 +10435,6 @@ "version": "2.1.3", "license": "MIT" }, - "node_modules/sequelize": { - "version": "6.35.2", - "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.35.2.tgz", - "integrity": "sha512-EdzLaw2kK4/aOnWQ7ed/qh3B6/g+1DvmeXr66RwbcqSm/+QRS9X0LDI5INBibsy4eNJHWIRPo3+QK0zL+IPBHg==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/sequelize" - } - ], - "dependencies": { - "@types/debug": "^4.1.8", - "@types/validator": "^13.7.17", - "debug": "^4.3.4", - "dottie": "^2.0.6", - "inflection": "^1.13.4", - "lodash": "^4.17.21", - "moment": "^2.29.4", - "moment-timezone": "^0.5.43", - "pg-connection-string": "^2.6.1", - "retry-as-promised": "^7.0.4", - "semver": "^7.5.4", - "sequelize-pool": "^7.1.0", - "toposort-class": "^1.0.1", - "uuid": "^8.3.2", - "validator": "^13.9.0", - "wkx": "^0.5.0" - }, - "engines": { - "node": ">=10.0.0" - }, - "peerDependenciesMeta": { - "ibm_db": { - "optional": true - }, - "mariadb": { - "optional": true - }, - "mysql2": { - "optional": true - }, - "oracledb": { - "optional": true - }, - "pg": { - "optional": true - }, - "pg-hstore": { - "optional": true - }, - "snowflake-sdk": { - "optional": true - }, - "sqlite3": { - "optional": true - }, - "tedious": { - "optional": true - } - } - }, - "node_modules/sequelize-pool": { - "version": "7.1.0", - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/serve-static": { "version": "1.14.2", "license": "MIT", @@ -11123,10 +11018,6 @@ "node": ">=0.6" } }, - "node_modules/toposort-class": { - "version": "1.0.1", - "license": "MIT" - }, "node_modules/touch": { "version": "3.1.0", "dev": true, @@ -11504,13 +11395,6 @@ "node": ">= 6.4.0" } }, - "node_modules/wkx": { - "version": "0.5.0", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/wrap-ansi": { "version": "7.0.0", "dev": true, @@ -15187,14 +15071,6 @@ "@babel/types": "^7.20.7" } }, - "@types/debug": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", - "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", - "requires": { - "@types/ms": "*" - } - }, "@types/graceful-fs": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", @@ -15232,13 +15108,9 @@ "version": "7.0.10", "dev": true }, - "@types/ms": { - "version": "0.7.34", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", - "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==" - }, "@types/node": { - "version": "17.0.23" + "version": "17.0.23", + "dev": true }, "@types/prettier": { "version": "2.7.3", @@ -15252,11 +15124,6 @@ "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", "dev": true }, - "@types/validator": { - "version": "13.11.7", - "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.11.7.tgz", - "integrity": "sha512-q0JomTsJ2I5Mv7dhHhQLGjMvX0JJm5dyZ1DXQySIUzU1UlwzB8bt+R6+LODUbz0UDIOvEzGc28tk27gBJw2N8Q==" - }, "@types/yargs": { "version": "17.0.32", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", @@ -15953,6 +15820,7 @@ }, "debug": { "version": "4.3.4", + "dev": true, "requires": { "ms": "2.1.2" } @@ -16056,11 +15924,6 @@ "version": "16.0.0", "dev": true }, - "dottie": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.6.tgz", - "integrity": "sha512-iGCHkfUc5kFekGiqhe8B/mdaurD+lakO9txNnTvKtA6PISrw86LgqHvRzWYPyoE2Ph5aMIrCw9/uko6XHTKCwA==" - }, "ee-first": { "version": "1.1.1" }, @@ -16908,11 +16771,6 @@ "version": "0.1.4", "dev": true }, - "inflection": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.13.4.tgz", - "integrity": "sha512-6I/HUDeYFfuNCVS3td055BaXBwKYuzw7K3ExVMStBowKo9oOAMJIXIHvdyR3iboTCp1b+1i5DSkIZTcwIktuDw==" - }, "inflight": { "version": "1.0.6", "dev": true, @@ -18957,11 +18815,6 @@ "version": "4.0.0", "dev": true }, - "pg-connection-string": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", - "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==" - }, "picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -19247,11 +19100,6 @@ "integrity": "sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ==", "dev": true }, - "retry-as-promised": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-7.0.4.tgz", - "integrity": "sha512-XgmCoxKWkDofwH8WddD0w85ZfqYz+ZHlr5yo+3YUCfycWawU56T5ckWXsScsj5B8tqUcIG67DxXByo3VUgiAdA==" - }, "reusify": { "version": "1.0.4", "dev": true @@ -19285,18 +19133,21 @@ "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, "requires": { "lru-cache": "^6.0.0" }, "dependencies": { "lru-cache": { "version": "6.0.0", + "dev": true, "requires": { "yallist": "^4.0.0" } }, "yallist": { - "version": "4.0.0" + "version": "4.0.0", + "dev": true } } }, @@ -19334,32 +19185,6 @@ } } }, - "sequelize": { - "version": "6.35.2", - "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.35.2.tgz", - "integrity": "sha512-EdzLaw2kK4/aOnWQ7ed/qh3B6/g+1DvmeXr66RwbcqSm/+QRS9X0LDI5INBibsy4eNJHWIRPo3+QK0zL+IPBHg==", - "requires": { - "@types/debug": "^4.1.8", - "@types/validator": "^13.7.17", - "debug": "^4.3.4", - "dottie": "^2.0.6", - "inflection": "^1.13.4", - "lodash": "^4.17.21", - "moment": "^2.29.4", - "moment-timezone": "^0.5.43", - "pg-connection-string": "^2.6.1", - "retry-as-promised": "^7.0.4", - "semver": "^7.5.2", - "sequelize-pool": "^7.1.0", - "toposort-class": "^1.0.1", - "uuid": "^8.3.2", - "validator": "^13.9.0", - "wkx": "^0.5.0" - } - }, - "sequelize-pool": { - "version": "7.1.0" - }, "serve-static": { "version": "1.14.2", "requires": { @@ -19767,9 +19592,6 @@ "toidentifier": { "version": "1.0.1" }, - "toposort-class": { - "version": "1.0.1" - }, "touch": { "version": "3.1.0", "dev": true, @@ -20017,12 +19839,6 @@ "triple-beam": "^1.3.0" } }, - "wkx": { - "version": "0.5.0", - "requires": { - "@types/node": "*" - } - }, "wrap-ansi": { "version": "7.0.0", "dev": true, diff --git a/package.json b/package.json index 8a14865f..2b783600 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,6 @@ "lodash.merge": "^4.6.2", "moment": "^2.30.1", "moment-timezone": "^0.5.45", - "sequelize": "^6.33.0", "sinon": "^15.1.2", "swagger-ui-express": "^4.3.0", "traverse": "^0.6.6", From 9140ce1518c2a7b0400f60279d0d679ca1d37e60 Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Tue, 12 Mar 2024 09:59:11 +0000 Subject: [PATCH 60/67] [PRMT-4568] Fix Dockerfile (remove references to deleted file) --- Dockerfile | 2 -- 1 file changed, 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 0176c141..e68670c0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -46,8 +46,6 @@ RUN apk add --no-cache \ && rm -rf /var/cache/apk/* COPY build/ /app/build -COPY database/ /app/database -COPY build/config/database.js /app/src/config/ COPY scripts/load-api-keys.sh /app/scripts/load-api-keys.sh COPY scripts/run-server-with-db.sh /usr/bin/run-ehr-server From e7181ad46b9dec0991d2f9db040ca93ac3d00435 Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Tue, 12 Mar 2024 15:36:15 +0000 Subject: [PATCH 61/67] [PRMT-4568] remove db:migrate from package.json --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 2b783600..5cd13e63 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "test:coverage": "jest src/ --maxWorkers=1 --coverage --detectOpenHandles --forceExit --setupFiles dotenv/config", "build": "babel src -d build --ignore '**/*.test.js' --ignore '**/__mocks__/*' && cp src/*.json build", "start": "node build/server.js", - "start:local": "npm run db:migrate ; babel-node -r dotenv/config src/server.js", + "start:local": "babel-node -r dotenv/config src/server.js", "start:nodemon": "nodemon --exec npm run start:local" }, "devDependencies": { @@ -41,7 +41,7 @@ "typescript": "^4.6.2" }, "dependencies": { - "@aws-sdk/client-dynamodb": "^3.525.0", + "@aws-sdk/client-dynamoDB": "^3.525.0", "@aws-sdk/lib-dynamodb": "^3.525.0", "@babel/runtime": "^7.17.8", "async-local-storage": "^2.3.1", From 5540d9c102940ec61dcf68b61e066be77eb77e43 Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Tue, 12 Mar 2024 15:40:15 +0000 Subject: [PATCH 62/67] [PRMT-4568] Use underscore instead of hyphen in terraform code --- terraform-db-roles/dynamodb.tf | 8 ++++---- terraform/data.tf | 2 +- terraform/ecs-task.tf | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/terraform-db-roles/dynamodb.tf b/terraform-db-roles/dynamodb.tf index 24139c6f..ca3913b0 100644 --- a/terraform-db-roles/dynamodb.tf +++ b/terraform-db-roles/dynamodb.tf @@ -1,4 +1,4 @@ -data "aws_iam_policy_document" "ehr-transfer-tracker-db-access" { +data "aws_iam_policy_document" "ehr_transfer_tracker_db_access" { statement { actions = [ "dynamodb:GetItem", @@ -12,13 +12,13 @@ data "aws_iam_policy_document" "ehr-transfer-tracker-db-access" { } } -resource "aws_iam_policy" "ehr-transfer-tracker-db-access" { +resource "aws_iam_policy" "ehr_transfer_tracker_db_access" { name = "${var.environment}-${var.component_name}-transfer-tracker-db-access" - policy = data.aws_iam_policy_document.ehr-transfer-tracker-db-access.json + policy = data.aws_iam_policy_document.ehr_transfer_tracker_db_access.json } # Grant ECS Task permissions to access the transfer tracker db resource "aws_iam_role_policy_attachment" "dynamodb_application_user_policy_attach" { role = "${var.environment}-${var.component_name}-EcsTaskRole" - policy_arn = aws_iam_policy.ehr-transfer-tracker-db-access.arn + policy_arn = aws_iam_policy.ehr_transfer_tracker_db_access.arn } diff --git a/terraform/data.tf b/terraform/data.tf index 3d10595d..b12360ff 100644 --- a/terraform/data.tf +++ b/terraform/data.tf @@ -20,6 +20,6 @@ data "aws_ssm_parameter" "db-password" { name = "/repo/${var.environment}/user-input/ehr-repo-db-password" } -data "aws_ssm_parameter" "dynamodb-name" { +data "aws_ssm_parameter" "dynamodb_name" { name = "/repo/${var.environment}/output/prm-deductions-infra/ehr-transfer-tracker-db-name" } \ No newline at end of file diff --git a/terraform/ecs-task.tf b/terraform/ecs-task.tf index 3b24d956..7f665b5e 100644 --- a/terraform/ecs-task.tf +++ b/terraform/ecs-task.tf @@ -15,7 +15,7 @@ locals { { name = "SKIP_DB_MIGRATION", value = "true" }, { name = "USE_SSL_FOR_DB", value = "true" }, { name = "LOG_LEVEL", value = var.log_level }, - { name = "DYNAMODB_NAME", value = data.aws_ssm_parameter.dynamodb-name.value }, + { name = "DYNAMODB_NAME", value = data.aws_ssm_parameter.dynamodb_name.value }, ] } From 0871dcd8019473e16ae79443011e53c4fceb3a23 Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Tue, 12 Mar 2024 15:45:05 +0000 Subject: [PATCH 63/67] [PRMT-4568] Remove trailing comma --- .prettierrc | 3 ++- src/services/database/dynamodb-client.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.prettierrc b/.prettierrc index 3b358f9e..5ebed2ed 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,4 +1,5 @@ { "singleQuote": true, - "printWidth": 100 + "printWidth": 100, + "trailingComma": "none" } \ No newline at end of file diff --git a/src/services/database/dynamodb-client.js b/src/services/database/dynamodb-client.js index 42c9e870..562f996b 100644 --- a/src/services/database/dynamodb-client.js +++ b/src/services/database/dynamodb-client.js @@ -4,7 +4,7 @@ import { IS_IN_LOCAL } from '../../utilities/integration-test-utilities'; export const getDynamodbClient = () => { const clientConfig = { - region: process.env.AWS_DEFAULT_REGION ?? 'eu-west-2', + region: process.env.AWS_DEFAULT_REGION ?? 'eu-west-2' }; if (IS_IN_LOCAL) { From d6a1d72235a7f424a769e336f14e9c1331f18f61 Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Tue, 12 Mar 2024 16:04:45 +0000 Subject: [PATCH 64/67] [PRMT-4568] Fix typo --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5cd13e63..56cfd386 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "typescript": "^4.6.2" }, "dependencies": { - "@aws-sdk/client-dynamoDB": "^3.525.0", + "@aws-sdk/client-dynamodb": "^3.525.0", "@aws-sdk/lib-dynamodb": "^3.525.0", "@babel/runtime": "^7.17.8", "async-local-storage": "^2.3.1", From e2f7fd93fa65eebf04a4f7b570af1b37b6fa9894 Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Wed, 13 Mar 2024 09:40:53 +0000 Subject: [PATCH 65/67] [PRMT-4568] fix typo (Outbound --> OUTBOUND) at TransferStatus --- src/models/conversation.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/models/conversation.js b/src/models/conversation.js index a780bacd..c017b5c9 100644 --- a/src/models/conversation.js +++ b/src/models/conversation.js @@ -25,5 +25,5 @@ export const isConversation = (item) => { export const isInCompleteStatus = (conversation) => { const status = conversation?.TransferStatus; - return status === ConversationStatus.COMPLETE || status?.startsWith('Outbound'); + return status === ConversationStatus.COMPLETE || status?.startsWith('OUTBOUND'); }; From 432319ea003fca9826982168e3789bd89b139f89 Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Wed, 13 Mar 2024 13:46:31 +0000 Subject: [PATCH 66/67] [PRMT-4568] Adapt to database schema change: sort key for core change from `CORE#{messageId}` to just `CORE` --- src/models/core.js | 2 +- .../__tests__/dynamo-ehr-transfer-tracker.integration.test.js | 2 +- .../database/__tests__/ehr-core-repository.integration.test.js | 3 ++- src/services/database/dynamo-ehr-transfer-tracker.js | 3 ++- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/models/core.js b/src/models/core.js index cbe61dac..940051d1 100644 --- a/src/models/core.js +++ b/src/models/core.js @@ -9,7 +9,7 @@ export const buildCore = (inboundConversationId, messageId) => { return { InboundConversationId: inboundConversationId, - Layer: [RecordType.CORE, messageId].join('#'), + Layer: RecordType.CORE, InboundMessageId: messageId, CreatedAt: timestamp, ReceivedAt: timestamp, diff --git a/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js b/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js index 92ac7342..715013be 100644 --- a/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js +++ b/src/services/database/__tests__/dynamo-ehr-transfer-tracker.integration.test.js @@ -35,7 +35,7 @@ describe('EhrTransferTracker', () => { expect(actual[0]).toMatchObject({ InboundConversationId: testConversationId, InboundMessageId: testMessageId, - Layer: `CORE#${testMessageId}`, + Layer: RecordType.CORE, ReceivedAt: expect.any(String), CreatedAt: expect.any(String), UpdatedAt: expect.any(String), diff --git a/src/services/database/__tests__/ehr-core-repository.integration.test.js b/src/services/database/__tests__/ehr-core-repository.integration.test.js index 8ae971b2..5f3e30f5 100644 --- a/src/services/database/__tests__/ehr-core-repository.integration.test.js +++ b/src/services/database/__tests__/ehr-core-repository.integration.test.js @@ -3,6 +3,7 @@ import { v4 as uuid } from 'uuid'; import { logError } from '../../../middleware/logging'; import { createCore, getCoreByKey } from '../ehr-core-repository'; import { getFragmentByKey } from '../ehr-fragment-repository'; +import { RecordType } from "../../../models/enums"; // Mocking jest.mock('../../../middleware/logging'); @@ -30,7 +31,7 @@ describe('ehr-core-repository', () => { expect(actualMessage.InboundMessageId).toBe(messageId); expect(actualMessage.InboundConversationId).toBe(conversationId); - expect(actualMessage.Layer).toBe(`CORE#${messageId}`); + expect(actualMessage.Layer).toBe(RecordType.CORE); expect(actualMessage.ReceivedAt).toEqual(expectedTimestamp); }); diff --git a/src/services/database/dynamo-ehr-transfer-tracker.js b/src/services/database/dynamo-ehr-transfer-tracker.js index 75b24e20..87cc5473 100644 --- a/src/services/database/dynamo-ehr-transfer-tracker.js +++ b/src/services/database/dynamo-ehr-transfer-tracker.js @@ -151,11 +151,12 @@ export class EhrTransferTracker { throw new Error('must be called with both conversationId and inboundMessageId'); } + const sortKey = recordType === RecordType.FRAGMENT ? `${recordType}#${inboundMessageId}` : recordType; const command = new GetCommand({ TableName: this.tableName, Key: { InboundConversationId: inboundConversationId, - Layer: `${recordType}#${inboundMessageId}`, + Layer: sortKey, }, }); From 4a9db22dda5b0c63a9cf2028d2a3bb96d09c1b90 Mon Sep 17 00:00:00 2001 From: Joe Fong Date: Thu, 14 Mar 2024 09:26:04 +0000 Subject: [PATCH 67/67] [PRMT-4568] address PR comments --- src/api/patients/patient-details-controller.js | 2 +- src/errors/errors.js | 2 +- src/services/database/dynamo-ehr-transfer-tracker.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/api/patients/patient-details-controller.js b/src/api/patients/patient-details-controller.js index f8f76c9f..949d6e64 100644 --- a/src/api/patients/patient-details-controller.js +++ b/src/api/patients/patient-details-controller.js @@ -39,7 +39,7 @@ export const patientDetailsController = async (req, res) => { coreMessageUrl, fragmentMessageIds, // TODO: remove this duplicated `conversationIdFromEhrIn` field, - // after updating ehr-out to use the field name "inboundConversationId" + // after updating ehr-out to use the field name "inboundConversationId" (planned in PRMT-4587) conversationIdFromEhrIn: currentHealthRecordConversationId, inboundConversationId: currentHealthRecordConversationId, }; diff --git a/src/errors/errors.js b/src/errors/errors.js index f1a29742..3b24a217 100644 --- a/src/errors/errors.js +++ b/src/errors/errors.js @@ -2,7 +2,7 @@ import { logError } from '../middleware/logging'; export const errorMessages = { HealthRecordNotFound: 'No complete health record was found with given criteria', - MessageNotFound: 'There were no undeleted messages associated with conversation id', + MessageNotFound: 'There were no existing messages associated with conversation id', }; export class HealthRecordNotFoundError extends Error { diff --git a/src/services/database/dynamo-ehr-transfer-tracker.js b/src/services/database/dynamo-ehr-transfer-tracker.js index 87cc5473..9e81226d 100644 --- a/src/services/database/dynamo-ehr-transfer-tracker.js +++ b/src/services/database/dynamo-ehr-transfer-tracker.js @@ -148,7 +148,7 @@ export class EhrTransferTracker { throw new Error('recordType has to be either Core or Fragment'); } if (!inboundConversationId && !inboundMessageId) { - throw new Error('must be called with both conversationId and inboundMessageId'); + throw new Error('must be called with both inboundConversationId and inboundMessageId'); } const sortKey = recordType === RecordType.FRAGMENT ? `${recordType}#${inboundMessageId}` : recordType;