diff --git a/.env.example b/.env.example index e57fba5..0d99961 100644 --- a/.env.example +++ b/.env.example @@ -9,7 +9,7 @@ NODE_ENV=development ################################################################################################# -####################################### ACCOUNT API SETUP ####################################### +####################################### NOTIFICATION API SETUP ####################################### ################################################################################################# # PORT_HTTP: Port used to listen for HTTP request @@ -61,17 +61,17 @@ SMTP_PASS=YOUR_SMTP_PASS ##################################### MONGO DATABASE SETUP ##################################### ################################################################################################# -# MONGODB_URI: Database connection URI used by the ACCOUNT service for connecting to +# MONGODB_URI: Database connection URI used by the NOTIFICATION service for connecting to # a MongoDB instance if the application is running in development or # production environment (NODE_ENV=development or NODE_ENV=production). -# default value: mongodb://localhost:27017/account-service -MONGODB_URI=mongodb://localhost:27017/account-service +# default value: mongodb://localhost:27017/notification-service +MONGODB_URI=mongodb://localhost:27017/notification-service -# MONGODB_URI_TEST: Database connection URI used by the ACCOUNT service for connecting to +# MONGODB_URI_TEST: Database connection URI used by the NOTIFICATION service for connecting to # a MongoDB instance if the application is running test environment # (NODE_ENV=test). -# default value: mongodb://localhost:27017/account-service-test -MONGODB_URI_TEST=mongodb://localhost:27017/account-service-test +# default value: mongodb://localhost:27017/notification-service-test +MONGODB_URI_TEST=mongodb://localhost:27017/notification-service-test # MONGODB_ENABLE_TLS: Enables/Disables connection to TLS # When TLS is used for connection, client certificates @@ -112,3 +112,19 @@ RABBITMQ_KEY_PATH=.certs/rabbitmq/key.pem # RABBITMQ_CA_PATH: RabbitMQ Certificate of the Authentication entity (CA) # default value: .certs/rabbitmq/ca.pem RABBITMQ_CA_PATH=.certs/rabbitmq/ca.pem + +################################################################################################# +##################################### GOOGLE FIREBASE SETUP ##################################### +################################################################################################# +# FIREBASE_ENABLE: Enables/Disables connection to Firebase admin +# When Firebase is used for notifications, your credentials +# file is required (FIREBASE_CREDENTIALS_PATH). +# +# default value: true +FIREBASE_ENABLE=true + +# FIREBASE_CREDENTIALS_PATH: +# The Google Firebase Config JSON file path for use firebase admin +# features. +# default value: /path/to/firebase_credentials_file.json +FIREBASE_CREDENTIALS_PATH=/path/to/firebase_credentials_file.json diff --git a/.travis.yml b/.travis.yml index 7825995..c2ea689 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,8 @@ language: node_js node_js: - - "12" - "13" + - "14" + - "15" env: - NODE_ENV=test RABBITMQ_URI=amqp://guest:guest@localhost:5672/haniot MONGODB_ENABLE_TLS=false MONGODB_URI_TEST=mongodb://localhost:27017/notification-test addons: @@ -17,6 +18,7 @@ before_script: - sudo rabbitmqctl set_permissions -p haniot guest ".*" ".*" ".*" - npm install && npm install coveralls mocha-lcov-reporter --save-dev script: + - npm run build - npm test after_success: - nyc report --reporter=text-lcov | coveralls diff --git a/Dockerfile b/Dockerfile index 5db90cf..e3b5e6f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ -FROM node:12-alpine -RUN apk --no-cache add bash curl grep +FROM node:14-alpine +RUN apk --no-cache add bash curl grep tzdata # Create app directory RUN mkdir -p /usr/src/ns diff --git a/README.md b/README.md index 032916a..23f2207 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,9 @@ Service for sending messages: **email**, **sms** or **push**. In this version on See the [documentation](https://github.com/haniot/notification/wiki) for more information. ## Prerequisites -- [Node 12.0.0+](https://nodejs.org/en/download/) +- [Node 13.0.0+](https://nodejs.org/en/download/) - [MongoDB Server 4.0.0+](https://www.mongodb.com/download-center/community) -- [RabbitMQ 3.7.0+](https://www.rabbitmq.com/download.html) +- [RabbitMQ 3.8.0+](https://www.rabbitmq.com/download.html) --- @@ -40,6 +40,8 @@ Application settings are defined by environment variables. To define the setting | `RABBITMQ_CERT_PATH` | RabbitMQ Certificate | `.certs/rabbitmq/cert.pem` | | `RABBITMQ_KEY_PATH` | RabbitMQ Key | `.certs/rabbitmq/key.pem` | | `RABBITMQ_CA_PATH` | RabbitMQ Certificate of the Authentication entity (CA). | `.certs/rabbitmq/ca.pem` | +| `FIREBASE_ENABLE` | Enables/Disables connection to Firebase admin when Firebase is used for notifications. | `true` | +| `FIREBASE_CREDENTIALS_PATH` | The Google Firebase Config JSON file path for use firebase admin features. | `/path/to/firebase_credentials_file.json` | ## Generate Certificates For development and testing environments the easiest and fastest way is to generate your own self-signed certificates. These certificates can be used to encrypt data as well as certificates signed by a CA, but users will receive a warning that the certificate is not trusted for their computer or browser. Therefore, self-signed certificates should only be used in non-production environments, that is, development and testing environments. To do this, run the `create-self-signed-certs.sh` script in the root of the repository. diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..33473c9 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,39 @@ +version: "3.8" +services: + mongo: + image: mongo + container_name: haniot-mongo + ports: + - 27017:27017 + restart: always + volumes: + - haniot-mongo-data:/data/db + networks: + - haniot-network + logging: + driver: json-file + options: + max-size: 100m + + rabbitmq: + image: rabbitmq:3-management-alpine + container_name: haniot-rabbitmq + restart: always + ports: + - 15672:15672 # Management UI + - 5672:5672 # AMQP Protocol + networks: + - haniot-network + logging: + driver: json-file + options: + max-size: 100m + +volumes: + haniot-mongo-data: + name: haniot-mongo + +networks: + haniot-network: + name: haniot-network + driver: bridge diff --git a/package-lock.json b/package-lock.json index 257c849..9e89b2d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,49 +1,49 @@ { "name": "notification", - "version": "1.2.2", + "version": "1.2.1", "lockfileVersion": 1, "requires": true, "dependencies": { "@babel/code-frame": { - "version": "7.10.3", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.3.tgz", - "integrity": "sha512-fDx9eNW0qz0WkUeqL6tXEXzVlPh6Y5aCDEZesl0xBGA8ndRukX91Uk44ZqnkECp01NAZUdCAl+aiQNGi0k88Eg==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", "dev": true, "requires": { - "@babel/highlight": "^7.10.3" + "@babel/highlight": "^7.10.4" } }, "@babel/core": { - "version": "7.10.3", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.10.3.tgz", - "integrity": "sha512-5YqWxYE3pyhIi84L84YcwjeEgS+fa7ZjK6IBVGTjDVfm64njkR2lfDhVR5OudLk8x2GK59YoSyVv+L/03k1q9w==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.3", - "@babel/generator": "^7.10.3", - "@babel/helper-module-transforms": "^7.10.1", - "@babel/helpers": "^7.10.1", - "@babel/parser": "^7.10.3", - "@babel/template": "^7.10.3", - "@babel/traverse": "^7.10.3", - "@babel/types": "^7.10.3", + "version": "7.12.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.3.tgz", + "integrity": "sha512-0qXcZYKZp3/6N2jKYVxZv0aNCsxTSVCiK72DTiTYZAu7sjg73W0/aynWjMbiGd87EQL4WyA8reiJVh92AVla9g==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.12.1", + "@babel/helper-module-transforms": "^7.12.1", + "@babel/helpers": "^7.12.1", + "@babel/parser": "^7.12.3", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.12.1", + "@babel/types": "^7.12.1", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.1", "json5": "^2.1.2", - "lodash": "^4.17.13", + "lodash": "^4.17.19", "resolve": "^1.3.2", "semver": "^5.4.1", "source-map": "^0.5.0" }, "dependencies": { "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, "ms": { @@ -52,6 +52,12 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -61,14 +67,13 @@ } }, "@babel/generator": { - "version": "7.10.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.10.3.tgz", - "integrity": "sha512-drt8MUHbEqRzNR0xnF8nMehbY11b1SDkRw03PSNH/3Rb2Z35oxkddVSi3rcaak0YJQ86PCuE7Qx1jSFhbLNBMA==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.5.tgz", + "integrity": "sha512-m16TQQJ8hPt7E+OS/XVQg/7U184MLXtvuGbCdA7na61vha+ImkyyNM/9DDA0unYCVZn3ZOhng+qz48/KBOT96A==", "dev": true, "requires": { - "@babel/types": "^7.10.3", + "@babel/types": "^7.12.5", "jsesc": "^2.5.1", - "lodash": "^4.17.13", "source-map": "^0.5.0" }, "dependencies": { @@ -81,175 +86,204 @@ } }, "@babel/helper-function-name": { - "version": "7.10.3", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.3.tgz", - "integrity": "sha512-FvSj2aiOd8zbeqijjgqdMDSyxsGHaMt5Tr0XjQsGKHD3/1FP3wksjnLAWzxw7lvXiej8W1Jt47SKTZ6upQNiRw==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", + "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.10.3", - "@babel/template": "^7.10.3", - "@babel/types": "^7.10.3" + "@babel/helper-get-function-arity": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/types": "^7.10.4" } }, "@babel/helper-get-function-arity": { - "version": "7.10.3", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.3.tgz", - "integrity": "sha512-iUD/gFsR+M6uiy69JA6fzM5seno8oE85IYZdbVVEuQaZlEzMO2MXblh+KSPJgsZAUx0EEbWXU0yJaW7C9CdAVg==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", + "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", "dev": true, "requires": { - "@babel/types": "^7.10.3" + "@babel/types": "^7.10.4" } }, "@babel/helper-member-expression-to-functions": { - "version": "7.10.3", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.10.3.tgz", - "integrity": "sha512-q7+37c4EPLSjNb2NmWOjNwj0+BOyYlssuQ58kHEWk1Z78K5i8vTUsteq78HMieRPQSl/NtpQyJfdjt3qZ5V2vw==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.1.tgz", + "integrity": "sha512-k0CIe3tXUKTRSoEx1LQEPFU9vRQfqHtl+kf8eNnDqb4AUJEy5pz6aIiog+YWtVm2jpggjS1laH68bPsR+KWWPQ==", "dev": true, "requires": { - "@babel/types": "^7.10.3" + "@babel/types": "^7.12.1" } }, "@babel/helper-module-imports": { - "version": "7.10.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.10.3.tgz", - "integrity": "sha512-Jtqw5M9pahLSUWA+76nhK9OG8nwYXzhQzVIGFoNaHnXF/r4l7kz4Fl0UAW7B6mqC5myoJiBP5/YQlXQTMfHI9w==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz", + "integrity": "sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==", "dev": true, "requires": { - "@babel/types": "^7.10.3" + "@babel/types": "^7.12.5" } }, "@babel/helper-module-transforms": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.10.1.tgz", - "integrity": "sha512-RLHRCAzyJe7Q7sF4oy2cB+kRnU4wDZY/H2xJFGof+M+SJEGhZsb+GFj5j1AD8NiSaVBJ+Pf0/WObiXu/zxWpFg==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz", + "integrity": "sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w==", "dev": true, "requires": { - "@babel/helper-module-imports": "^7.10.1", - "@babel/helper-replace-supers": "^7.10.1", - "@babel/helper-simple-access": "^7.10.1", - "@babel/helper-split-export-declaration": "^7.10.1", - "@babel/template": "^7.10.1", - "@babel/types": "^7.10.1", - "lodash": "^4.17.13" + "@babel/helper-module-imports": "^7.12.1", + "@babel/helper-replace-supers": "^7.12.1", + "@babel/helper-simple-access": "^7.12.1", + "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/helper-validator-identifier": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.12.1", + "@babel/types": "^7.12.1", + "lodash": "^4.17.19" } }, "@babel/helper-optimise-call-expression": { - "version": "7.10.3", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.3.tgz", - "integrity": "sha512-kT2R3VBH/cnSz+yChKpaKRJQJWxdGoc6SjioRId2wkeV3bK0wLLioFpJROrX0U4xr/NmxSSAWT/9Ih5snwIIzg==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz", + "integrity": "sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg==", "dev": true, "requires": { - "@babel/types": "^7.10.3" + "@babel/types": "^7.10.4" } }, "@babel/helper-replace-supers": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.10.1.tgz", - "integrity": "sha512-SOwJzEfpuQwInzzQJGjGaiG578UYmyi2Xw668klPWV5n07B73S0a9btjLk/52Mlcxa+5AdIYqws1KyXRfMoB7A==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.5.tgz", + "integrity": "sha512-5YILoed0ZyIpF4gKcpZitEnXEJ9UoDRki1Ey6xz46rxOzfNMAhVIJMoune1hmPVxh40LRv1+oafz7UsWX+vyWA==", "dev": true, "requires": { - "@babel/helper-member-expression-to-functions": "^7.10.1", - "@babel/helper-optimise-call-expression": "^7.10.1", - "@babel/traverse": "^7.10.1", - "@babel/types": "^7.10.1" + "@babel/helper-member-expression-to-functions": "^7.12.1", + "@babel/helper-optimise-call-expression": "^7.10.4", + "@babel/traverse": "^7.12.5", + "@babel/types": "^7.12.5" } }, "@babel/helper-simple-access": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.10.1.tgz", - "integrity": "sha512-VSWpWzRzn9VtgMJBIWTZ+GP107kZdQ4YplJlCmIrjoLVSi/0upixezHCDG8kpPVTBJpKfxTH01wDhh+jS2zKbw==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz", + "integrity": "sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA==", "dev": true, "requires": { - "@babel/template": "^7.10.1", - "@babel/types": "^7.10.1" + "@babel/types": "^7.12.1" } }, "@babel/helper-split-export-declaration": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.10.1.tgz", - "integrity": "sha512-UQ1LVBPrYdbchNhLwj6fetj46BcFwfS4NllJo/1aJsT+1dLTEnXJL0qHqtY7gPzF8S2fXBJamf1biAXV3X077g==", + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz", + "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==", "dev": true, "requires": { - "@babel/types": "^7.10.1" + "@babel/types": "^7.11.0" } }, "@babel/helper-validator-identifier": { - "version": "7.10.3", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.3.tgz", - "integrity": "sha512-bU8JvtlYpJSBPuj1VUmKpFGaDZuLxASky3LhaKj3bmpSTY6VWooSM8msk+Z0CZoErFye2tlABF6yDkT3FOPAXw==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", "dev": true }, "@babel/helpers": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.10.1.tgz", - "integrity": "sha512-muQNHF+IdU6wGgkaJyhhEmI54MOZBKsFfsXFhboz1ybwJ1Kl7IHlbm2a++4jwrmY5UYsgitt5lfqo1wMFcHmyw==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.12.5.tgz", + "integrity": "sha512-lgKGMQlKqA8meJqKsW6rUnc4MdUk35Ln0ATDqdM1a/UpARODdI4j5Y5lVfUScnSNkJcdCRAaWkspykNoFg9sJA==", "dev": true, "requires": { - "@babel/template": "^7.10.1", - "@babel/traverse": "^7.10.1", - "@babel/types": "^7.10.1" + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.12.5", + "@babel/types": "^7.12.5" } }, "@babel/highlight": { - "version": "7.10.3", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.3.tgz", - "integrity": "sha512-Ih9B/u7AtgEnySE2L2F0Xm0GaM729XqqLfHkalTsbjXGyqmf/6M0Cu0WpvqueUlW+xk88BHw9Nkpj49naU+vWw==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.10.3", + "@babel/helper-validator-identifier": "^7.10.4", "chalk": "^2.0.0", "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + } } }, "@babel/parser": { - "version": "7.10.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.10.3.tgz", - "integrity": "sha512-oJtNJCMFdIMwXGmx+KxuaD7i3b8uS7TTFYW/FNG2BT8m+fmGHoiPYoH0Pe3gya07WuFmM5FCDIr1x0irkD/hyA==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.5.tgz", + "integrity": "sha512-FVM6RZQ0mn2KCf1VUED7KepYeUWoVShczewOCfm3nzoBybaih51h+sYVVGthW9M6lPByEPTQf+xm27PBdlpwmQ==", "dev": true }, "@babel/runtime": { - "version": "7.10.3", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.10.3.tgz", - "integrity": "sha512-RzGO0RLSdokm9Ipe/YD+7ww8X2Ro79qiXZF3HU9ljrM+qnJmH1Vqth+hbiQZy761LnMJTMitHDuKVYTk3k4dLw==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.5.tgz", + "integrity": "sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==", "requires": { "regenerator-runtime": "^0.13.4" } }, "@babel/template": { - "version": "7.10.3", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.3.tgz", - "integrity": "sha512-5BjI4gdtD+9fHZUsaxPHPNpwa+xRkDO7c7JbhYn2afvrkDu5SfAAbi9AIMXw2xEhO/BR35TqiW97IqNvCo/GqA==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", + "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", "dev": true, "requires": { - "@babel/code-frame": "^7.10.3", - "@babel/parser": "^7.10.3", - "@babel/types": "^7.10.3" + "@babel/code-frame": "^7.10.4", + "@babel/parser": "^7.10.4", + "@babel/types": "^7.10.4" } }, "@babel/traverse": { - "version": "7.10.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.10.3.tgz", - "integrity": "sha512-qO6623eBFhuPm0TmmrUFMT1FulCmsSeJuVGhiLodk2raUDFhhTECLd9E9jC4LBIWziqt4wgF6KuXE4d+Jz9yug==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.5.tgz", + "integrity": "sha512-xa15FbQnias7z9a62LwYAA5SZZPkHIXpd42C6uW68o8uTuua96FHZy1y61Va5P/i83FAAcMpW8+A/QayntzuqA==", "dev": true, "requires": { - "@babel/code-frame": "^7.10.3", - "@babel/generator": "^7.10.3", - "@babel/helper-function-name": "^7.10.3", - "@babel/helper-split-export-declaration": "^7.10.1", - "@babel/parser": "^7.10.3", - "@babel/types": "^7.10.3", + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.12.5", + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/parser": "^7.12.5", + "@babel/types": "^7.12.5", "debug": "^4.1.0", "globals": "^11.1.0", - "lodash": "^4.17.13" + "lodash": "^4.17.19" }, "dependencies": { "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, "ms": { @@ -261,13 +295,13 @@ } }, "@babel/types": { - "version": "7.10.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.3.tgz", - "integrity": "sha512-nZxaJhBXBQ8HVoIcGsf9qWep3Oh3jCENK54V4mRF7qaJabVsAYdbTtmSD8WmAp1R6ytPiu5apMwSXyxB1WlaBA==", + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.10.3", - "lodash": "^4.17.13", + "@babel/helper-validator-identifier": "^7.10.4", + "lodash": "^4.17.19", "to-fast-properties": "^2.0.0" }, "dependencies": { @@ -289,6 +323,222 @@ "kuler": "^2.0.0" } }, + "@firebase/app-types": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.6.1.tgz", + "integrity": "sha512-L/ZnJRAq7F++utfuoTKX4CLBG5YR7tFO3PLzG1/oXXKEezJ0kRL3CMRoueBEmTCzVb/6SIs2Qlaw++uDgi5Xyg==" + }, + "@firebase/auth-interop-types": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.1.5.tgz", + "integrity": "sha512-88h74TMQ6wXChPA6h9Q3E1Jg6TkTHep2+k63OWg3s0ozyGVMeY+TTOti7PFPzq5RhszQPQOoCi59es4MaRvgCw==" + }, + "@firebase/component": { + "version": "0.1.19", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.1.19.tgz", + "integrity": "sha512-L0S3g8eqaerg8y0zox3oOHSTwn/FE8RbcRHiurnbESvDViZtP5S5WnhuAPd7FnFxa8ElWK0z1Tr3ikzWDv1xdQ==", + "requires": { + "@firebase/util": "0.3.2", + "tslib": "^1.11.1" + } + }, + "@firebase/database": { + "version": "0.6.13", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.6.13.tgz", + "integrity": "sha512-NommVkAPzU7CKd1gyehmi3lz0K78q0KOfiex7Nfy7MBMwknLm7oNqKovXSgQV1PCLvKXvvAplDSFhDhzIf9obA==", + "requires": { + "@firebase/auth-interop-types": "0.1.5", + "@firebase/component": "0.1.19", + "@firebase/database-types": "0.5.2", + "@firebase/logger": "0.2.6", + "@firebase/util": "0.3.2", + "faye-websocket": "0.11.3", + "tslib": "^1.11.1" + } + }, + "@firebase/database-types": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.5.2.tgz", + "integrity": "sha512-ap2WQOS3LKmGuVFKUghFft7RxXTyZTDr0Xd8y2aqmWsbJVjgozi0huL/EUMgTjGFrATAjcf2A7aNs8AKKZ2a8g==", + "requires": { + "@firebase/app-types": "0.6.1" + } + }, + "@firebase/logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.2.6.tgz", + "integrity": "sha512-KIxcUvW/cRGWlzK9Vd2KB864HlUnCfdTH0taHE0sXW5Xl7+W68suaeau1oKNEqmc3l45azkd4NzXTCWZRZdXrw==" + }, + "@firebase/util": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-0.3.2.tgz", + "integrity": "sha512-Dqs00++c8rwKky6KCKLLY2T1qYO4Q+X5t+lF7DInXDNF4ae1Oau35bkD+OpJ9u7l1pEv7KHowP6CUKuySCOc8g==", + "requires": { + "tslib": "^1.11.1" + } + }, + "@google-cloud/common": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-3.5.0.tgz", + "integrity": "sha512-10d7ZAvKhq47L271AqvHEd8KzJqGU45TY+rwM2Z3JHuB070FeTi7oJJd7elfrnKaEvaktw3hH2wKnRWxk/3oWQ==", + "optional": true, + "requires": { + "@google-cloud/projectify": "^2.0.0", + "@google-cloud/promisify": "^2.0.0", + "arrify": "^2.0.1", + "duplexify": "^4.1.1", + "ent": "^2.2.0", + "extend": "^3.0.2", + "google-auth-library": "^6.1.1", + "retry-request": "^4.1.1", + "teeny-request": "^7.0.0" + } + }, + "@google-cloud/firestore": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-4.7.1.tgz", + "integrity": "sha512-Qici+WKB6uRdDS1S3CaxGrIaCl4Bck70DYSzA5dZFkTU03Jj5DKXC4PYeUkfCAiB4haj7tzx+2ye7rhLxPclhQ==", + "optional": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "functional-red-black-tree": "^1.0.1", + "google-gax": "^2.9.2" + } + }, + "@google-cloud/paginator": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-3.0.5.tgz", + "integrity": "sha512-N4Uk4BT1YuskfRhKXBs0n9Lg2YTROZc6IMpkO/8DIHODtm5s3xY8K5vVBo23v/2XulY3azwITQlYWgT4GdLsUw==", + "optional": true, + "requires": { + "arrify": "^2.0.0", + "extend": "^3.0.2" + } + }, + "@google-cloud/projectify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-2.0.1.tgz", + "integrity": "sha512-ZDG38U/Yy6Zr21LaR3BTiiLtpJl6RkPS/JwoRT453G+6Q1DhlV0waNf8Lfu+YVYGIIxgKnLayJRfYlFJfiI8iQ==", + "optional": true + }, + "@google-cloud/promisify": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-2.0.3.tgz", + "integrity": "sha512-d4VSA86eL/AFTe5xtyZX+ePUjE8dIFu2T8zmdeNBSa5/kNgXPCx/o/wbFNHAGLJdGnk1vddRuMESD9HbOC8irw==", + "optional": true + }, + "@google-cloud/storage": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-5.5.0.tgz", + "integrity": "sha512-Pat83kHNnKJpEHUirtQtCoAJ2K3OlEo2ZcSlPjierJnEKnhbIQPyJ6mAbs/ovm3K3QDQhouKJ9QSONkFPEwQuA==", + "optional": true, + "requires": { + "@google-cloud/common": "^3.3.0", + "@google-cloud/paginator": "^3.0.0", + "@google-cloud/promisify": "^2.0.0", + "arrify": "^2.0.0", + "compressible": "^2.0.12", + "date-and-time": "^0.14.0", + "duplexify": "^4.0.0", + "extend": "^3.0.2", + "gaxios": "^4.0.0", + "gcs-resumable-upload": "^3.1.0", + "get-stream": "^6.0.0", + "hash-stream-validation": "^0.2.2", + "mime": "^2.2.0", + "mime-types": "^2.0.8", + "onetime": "^5.1.0", + "p-limit": "^3.0.1", + "pumpify": "^2.0.0", + "snakeize": "^0.1.0", + "stream-events": "^1.0.1", + "xdg-basedir": "^4.0.0" + }, + "dependencies": { + "p-limit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.0.2.tgz", + "integrity": "sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg==", + "optional": true, + "requires": { + "p-try": "^2.0.0" + } + } + } + }, + "@grpc/grpc-js": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.1.8.tgz", + "integrity": "sha512-64hg5rmEm6F/NvlWERhHmmgxbWU8nD2TMWE+9TvG7/WcOrFT3fzg/Uu631pXRFwmJ4aWO/kp9vVSlr8FUjBDLA==", + "optional": true, + "requires": { + "@grpc/proto-loader": "^0.6.0-pre14", + "@types/node": "^12.12.47", + "google-auth-library": "^6.0.0", + "semver": "^6.2.0" + }, + "dependencies": { + "@grpc/proto-loader": { + "version": "0.6.0-pre9", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.6.0-pre9.tgz", + "integrity": "sha512-oM+LjpEjNzW5pNJjt4/hq1HYayNeQT+eGrOPABJnYHv7TyNPDNzkQ76rDYZF86X5swJOa4EujEMzQ9iiTdPgww==", + "optional": true, + "requires": { + "@types/long": "^4.0.1", + "lodash.camelcase": "^4.3.0", + "long": "^4.0.0", + "protobufjs": "^6.9.0", + "yargs": "^15.3.1" + } + }, + "@types/node": { + "version": "12.19.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.19.4.tgz", + "integrity": "sha512-o3oj1bETk8kBwzz1WlO6JWL/AfAA3Vm6J1B3C9CsdxHYp7XgPiH7OEXPUbZTndHlRaIElrANkQfe6ZmfJb3H2w==", + "optional": true + }, + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "optional": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "optional": true, + "requires": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + } + } + } + }, + "@grpc/proto-loader": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.5.5.tgz", + "integrity": "sha512-WwN9jVNdHRQoOBo9FDH7qU+mgfjPc8GygPYms3M+y3fbQLfnCe/Kv/E01t7JRgnrsOHH8euvSbed3mIalXhwqQ==", + "optional": true, + "requires": { + "lodash.camelcase": "^4.3.0", + "protobufjs": "^6.8.6" + } + }, "@hapi/boom": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/@hapi/boom/-/boom-9.1.0.tgz", @@ -298,9 +548,9 @@ } }, "@hapi/hoek": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.0.4.tgz", - "integrity": "sha512-EwaJS7RjoXUZ2cXXKZZxZqieGtc7RbvQhUy8FwDoMQtxWVi14tFjeFCYPZAM1mBCpOpiBpyaZbb9NeHc7eGKgw==" + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.1.0.tgz", + "integrity": "sha512-i9YbZPN3QgfighY/1X1Pu118VUz2Fmmhd6b2n0/O8YVgGGfw0FbUYoA97k7FkpGJ+pLCFEDLUmAPPV4D1kpeFw==" }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", @@ -321,22 +571,6 @@ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true }, - "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" - } - }, - "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 - }, "resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -352,29 +586,30 @@ "dev": true }, "@ladjs/i18n": { - "version": "3.0.12", - "resolved": "https://registry.npmjs.org/@ladjs/i18n/-/i18n-3.0.12.tgz", - "integrity": "sha512-+jCZq0la/25VndOLYs0oi1hDnDFznfMCPEYDS8NPfLbw7aaJ0YSwi/rlB/GI/fHw87UgtvZ/rhSVADs0/c3hEQ==", + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/@ladjs/i18n/-/i18n-6.0.5.tgz", + "integrity": "sha512-l9Bkdo67dKcrWDyCkoEVpLEPMa8hdJbwe1gv0VWwD0fHIlvEAiNIAmka0ELF7H0ccNLqbKZx4vWKvkp0ufYFvw==", "requires": { "@hapi/boom": "^9.1.0", "boolean": "3.0.1", "country-language": "^0.1.7", - "debug": "^4.1.1", - "i18n": "^0.9.1", + "debug": "^4.2.0", + "i18n": "^0.13.2", "i18n-locales": "^0.0.4", - "lodash": "^4.17.15", - "moment": "^2.25.3", - "multimatch": "^4.0.0", + "lodash": "^4.17.20", + "multimatch": "^5.0.0", + "punycode": "^2.1.1", "qs": "^6.9.4", - "titleize": "^2.1.0" + "titleize": "^2.1.0", + "tlds": "^1.212.0" }, "dependencies": { "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, "ms": { @@ -389,15 +624,79 @@ } } }, + "@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78=", + "optional": true + }, + "@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "optional": true + }, + "@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "optional": true + }, + "@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A=", + "optional": true + }, + "@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=", + "optional": true, + "requires": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E=", + "optional": true + }, + "@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik=", + "optional": true + }, + "@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0=", + "optional": true + }, + "@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q=", + "optional": true + }, + "@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=", + "optional": true + }, "@sindresorhus/is": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-2.1.1.tgz", - "integrity": "sha512-/aPsuoj/1Dw/kzhkgz+ES6TxG0zfTMGLwuK2ZG00k/iJzYHTLCE8mVU8EPqEOp/lmxPoq1C1C9RYToRKb2KEfg==" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.0.0.tgz", + "integrity": "sha512-FyD2meJpDPjyNQejSjvnhpgI/azsQkA4lGbuu5BQZfjvJ9cbRZXzeWL2HceCekW4lixO9JPesIIQkSoLjeJHNQ==" }, "@sinonjs/commons": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.0.tgz", - "integrity": "sha512-wEj54PfsZ5jGSwMX68G8ZXFawcSglQSXqCftWX3ec8MDUzQdHgcKvw97awHbY0efQEL5iKUOAmmVtoYgmrSG4Q==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.1.tgz", + "integrity": "sha512-892K+kWUUi3cl+LlqEWIDrhvLgdL79tECi8JZUyq6IviKy/DNhuzCRlbHUjxK89f4ypPMMaFnFuR9Ie6DoIMsw==", "dev": true, "requires": { "type-detect": "4.0.8" @@ -439,10 +738,16 @@ "defer-to-connect": "^1.0.1" } }, + "@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "optional": true + }, "@types/babel-types": { - "version": "7.0.7", - "resolved": "https://registry.npmjs.org/@types/babel-types/-/babel-types-7.0.7.tgz", - "integrity": "sha512-dBtBbrc+qTHy1WdfHYjBwRln4+LWqASWakLHsWHR2NWHIFkv4W3O070IGoGLEBrJBvct3r0L1BUPuvURi7kYUQ==" + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/@types/babel-types/-/babel-types-7.0.9.tgz", + "integrity": "sha512-qZLoYeXSTgQuK1h7QQS16hqLGdmqtRmN8w/rl3Au/l5x/zkHx+a4VHrHyBsi1I1vtK2oBHxSzKIu0R5p6spdOA==" }, "@types/babylon": { "version": "6.16.5", @@ -463,24 +768,18 @@ } }, "@types/bson": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.0.2.tgz", - "integrity": "sha512-+uWmsejEHfmSjyyM/LkrP0orfE2m5Mx9Xel4tXNeqi1ldK5XMQcDsFkBmLDtuyKUbxj2jGDo0H240fbCRJZo7Q==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.0.3.tgz", + "integrity": "sha512-mVRvYnTOZJz3ccpxhr3wgxVmSeiYinW+zlzQz3SXWaJmD1DuL05Jeq7nKw3SnbKmbleW5qrLG5vdyWe/A9sXhw==", "dev": true, "requires": { "@types/node": "*" } }, "@types/chai": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.11.tgz", - "integrity": "sha512-t7uW6eFafjO+qJ3BIV2gGUyZs27egcNRkUdalkud+Qa3+kg//f129iuOFivHDXQ+vnU3fDXuwgv0cqMCbcE8sw==", - "dev": true - }, - "@types/color-name": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.14.tgz", + "integrity": "sha512-G+ITQPXkwTrslfG5L/BksmbLUA0M1iybEsmCWPqzSxsRRhJZimBKJkoMi8fr/CPygPTj4zO5pJH7I2/cm9M7SQ==", "dev": true }, "@types/connect": { @@ -493,9 +792,9 @@ } }, "@types/express": { - "version": "4.17.6", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.6.tgz", - "integrity": "sha512-n/mr9tZI83kd4azlPG5y997C/M4DNABK9yErhFM6hKdym4kkmd9j0vtsJyjFIwfRBxtrxZtAfGZCNRIBMFLK5w==", + "version": "4.17.9", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.9.tgz", + "integrity": "sha512-SDzEIZInC4sivGIFY4Sz1GG6J9UObPwCInYJjko2jzOf/Imx/dlpume6Xxwj1ORL82tBbmN4cPDIDkLbWHk9hw==", "dev": true, "requires": { "@types/body-parser": "*", @@ -505,9 +804,9 @@ } }, "@types/express-serve-static-core": { - "version": "4.17.8", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.8.tgz", - "integrity": "sha512-1SJZ+R3Q/7mLkOD9ewCBDYD2k0WyZQtWYqF/2VvoNN2/uhI49J9CDN4OAm+wGMA0DbArA4ef27xl4+JwMtGggw==", + "version": "4.17.13", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.13.tgz", + "integrity": "sha512-RgDi5a4nuzam073lRGKTUIaL3eF2+H7LJvJ8eUnCI0wA6SNjXc44DCmWNiTLs/AZ7QlsFWZiw/gTG3nSQGL0fA==", "dev": true, "requires": { "@types/node": "*", @@ -522,18 +821,24 @@ "dev": true }, "@types/helmet": { - "version": "0.0.47", - "resolved": "https://registry.npmjs.org/@types/helmet/-/helmet-0.0.47.tgz", - "integrity": "sha512-TcHA/djjdUtrMtq/QAayVLrsgjNNZ1Uhtz0KhfH01mrmjH44E54DA1A0HNbwW0H/NBFqV+tGMo85ACuEhMXcdg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/helmet/-/helmet-4.0.0.tgz", + "integrity": "sha512-ONIn/nSNQA57yRge3oaMQESef/6QhoeX7llWeDli0UZIfz8TQMkfNPTXA8VnnyeA1WUjG2pGqdjEIueYonMdfQ==", "dev": true, "requires": { - "@types/express": "*" + "helmet": "*" } }, + "@types/long": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz", + "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==", + "optional": true + }, "@types/mime": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.2.tgz", - "integrity": "sha512-4kPlzbljFcsttWEq6aBW0OZe6BDajAmyvr2xknBG92tejQnvdGtT9+kXSZ580DqpxY9qG2xeQVF9Dq0ymUTo5Q==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.3.tgz", + "integrity": "sha512-Jus9s4CDbqwocc5pOAnh8ShfrnMcPHuJYzVcSUU7lrh8Ni5HuIqX3oilL86p3dlTrk0LzHRCgA/GQ7uNCw6l2Q==", "dev": true }, "@types/minimatch": { @@ -542,15 +847,15 @@ "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==" }, "@types/mocha": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-7.0.2.tgz", - "integrity": "sha512-ZvO2tAcjmMi8V/5Z3JsyofMe3hasRcaw88cto5etSVMwVQfeivGAlEYmaQgceUSVYFofVjT+ioHsATjdWcFt1w==", + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.0.4.tgz", + "integrity": "sha512-M4BwiTJjHmLq6kjON7ZoI2JMlBvpY3BYSdiP6s/qCT3jb1s9/DeJF0JELpAxiVSIxXDzfNKe+r7yedMIoLbknQ==", "dev": true }, "@types/mongodb": { - "version": "3.5.25", - "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.5.25.tgz", - "integrity": "sha512-2H/Owt+pHCl9YmBOYnXc3VdnxejJEjVdH+QCWL5ZAfPehEn3evygKBX3/vKRv7aTwfNbUd0E5vjJdQklH/9a6w==", + "version": "3.5.33", + "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.5.33.tgz", + "integrity": "sha512-biMkaYFyUP8jNczbi2BgtvVfiEtPZ+NTf+Jz4LmsQjO+Zk+kx1WNjz8gNArWlA/5DXVwMVWgIFtbj6Tslt1yWw==", "dev": true, "requires": { "@types/bson": "*", @@ -558,9 +863,9 @@ } }, "@types/mongoose": { - "version": "5.7.29", - "resolved": "https://registry.npmjs.org/@types/mongoose/-/mongoose-5.7.29.tgz", - "integrity": "sha512-+7u1akCerciZ2MG66p6Vy+9Tg7dYcgSeNbDInxdOA5vB5xAZhNiIBj8HESnJmIqOBcWxQ90HpaPWtEwggBqXug==", + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/@types/mongoose/-/mongoose-5.10.0.tgz", + "integrity": "sha512-inJhRgmsYC8JWB3ABUjCreNmMoML/ffu/y8LtTYe7GAW6/B8rVLQU16am73kkceasKX75p41WjU8yquUx/CSAw==", "dev": true, "requires": { "@types/mongodb": "*", @@ -568,24 +873,23 @@ } }, "@types/morgan": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.1.tgz", - "integrity": "sha512-2j5IKrgJpEP6xw/uiVb2Xfga0W0sSVD9JP9t7EZLvpBENdB0OKgcnoKS8IsjNeNnZ/86robdZ61Orl0QCFGOXg==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.2.tgz", + "integrity": "sha512-edtGMEdit146JwwIeyQeHHg9yID4WSolQPxpEorHmN3KuytuCHyn2ELNr5Uxy8SerniFbbkmgKMrGM933am5BQ==", "dev": true, "requires": { "@types/node": "*" } }, "@types/node": { - "version": "14.0.14", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.14.tgz", - "integrity": "sha512-syUgf67ZQpaJj01/tRTknkMNoBBLWJOBODF0Zm4NrXmiSuxjymFrxnTu1QVYRubhVkRcZLYZG8STTwJRdVm/WQ==", - "dev": true + "version": "14.14.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.7.tgz", + "integrity": "sha512-Zw1vhUSQZYw+7u5dAwNbIA9TuTotpzY/OF7sJM9FqPOF3SPjKnxrjoTktXDZgUjybf4cWVBP7O8wvKdSaGHweg==" }, "@types/qs": { - "version": "6.9.3", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.3.tgz", - "integrity": "sha512-7s9EQWupR1fTc2pSMtXRQ9w9gLOcrJn+h7HOXw4evxyvVqMi4f+q7d2tnFe3ng3SNHjtK+0EzGMGFUQX4/AQRA==", + "version": "6.9.5", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.5.tgz", + "integrity": "sha512-/JHkVHtx/REVG0VVToGRGH2+23hsYLHdyG+GrvoUGlGAd0ErauXDyvHtRI/7H7mzLm+tBCKA7pfcpkQ1lf58iQ==", "dev": true }, "@types/range-parser": { @@ -595,13 +899,13 @@ "dev": true }, "@types/serve-static": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.4.tgz", - "integrity": "sha512-jTDt0o/YbpNwZbQmE/+2e+lfjJEJJR0I3OFaKQKPWkASkCoW3i6fsUnqudSMcNAfbtmADGu8f4MV4q+GqULmug==", + "version": "1.13.7", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.7.tgz", + "integrity": "sha512-3diZWucbR+xTmbDlU+FRRxBf+31OhFew7cJXML/zh9NmvSPTNoFecAwHB66BUqFgENJtqMiyl7JAwUE/siqdLw==", "dev": true, "requires": { - "@types/express-serve-static-core": "*", - "@types/mime": "*" + "@types/mime": "*", + "@types/node": "*" } }, "@types/swagger-ui-express": { @@ -614,12 +918,27 @@ "@types/serve-static": "*" } }, + "@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "dev": true + }, "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true }, + "abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "optional": true, + "requires": { + "event-target-shim": "^5.0.0" + } + }, "accepts": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", @@ -649,27 +968,42 @@ } } }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "optional": true, + "requires": { + "debug": "4" + }, + "dependencies": { + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "optional": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "optional": true + } + } + }, "aggregate-error": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz", - "integrity": "sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", "dev": true, "requires": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" } }, - "ajv": { - "version": "6.12.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz", - "integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==", - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, "align-text": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", @@ -719,6 +1053,12 @@ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", "dev": true }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", @@ -748,13 +1088,9 @@ } }, "ansi-colors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", - "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", - "dev": true, - "requires": { - "ansi-wrap": "^0.1.0" - } + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==" }, "ansi-gray": { "version": "0.1.1", @@ -766,17 +1102,31 @@ } }, "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" }, "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "requires": { - "color-convert": "^1.9.0" + "color-convert": "^2.0.1" + }, + "dependencies": { + "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==", + "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==" + } } }, "ansi-wrap": { @@ -793,6 +1143,17 @@ "requires": { "micromatch": "^3.1.4", "normalize-path": "^2.1.1" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } } }, "append-buffer": { @@ -804,6 +1165,11 @@ "buffer-equal": "^1.0.0" } }, + "append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha1-HjRA6RXwsSA9I3SOeO3XubW0PlY=" + }, "append-transform": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", @@ -971,18 +1337,6 @@ "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", "dev": true }, - "array.prototype.map": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array.prototype.map/-/array.prototype.map-1.0.2.tgz", - "integrity": "sha512-Az3OYxgsa1g7xDYp86l0nnN4bcmuEITGe1rbdEBVkrqkzMgDcbdQ2R7r41pNzti+4NMces3H8gMmuioZUilLgw==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1", - "es-array-method-boxes-properly": "^1.0.0", - "is-string": "^1.0.4" - } - }, "arrify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", @@ -993,19 +1347,6 @@ "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" }, - "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - }, "assertion-error": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", @@ -1053,7 +1394,14 @@ "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true }, "atob": { "version": "2.1.2", @@ -1061,16 +1409,6 @@ "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", "dev": true }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" - }, - "aws4": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.0.tgz", - "integrity": "sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA==" - }, "babel-runtime": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", @@ -1186,6 +1524,12 @@ } } }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "optional": true + }, "basic-auth": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", @@ -1194,13 +1538,11 @@ "safe-buffer": "5.1.2" } }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "requires": { - "tweetnacl": "^0.14.3" - } + "bignumber.js": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz", + "integrity": "sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA==", + "optional": true }, "binary-extensions": { "version": "1.13.1", @@ -1229,9 +1571,9 @@ } }, "bl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.0.tgz", - "integrity": "sha512-wbgvOpqopSr7uq6fJrLH8EsvYMJf9gzfo2jCsL2eTy75qXPukA4pCgHamOQkZtY5vmfVtjB+P3LNlMHW5CEZXA==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz", + "integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==", "requires": { "readable-stream": "^2.3.5", "safe-buffer": "^5.1.1" @@ -1319,99 +1661,11 @@ "widest-line": "^3.1.0" }, "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, "camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "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 - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "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 - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } } } }, @@ -1460,9 +1714,9 @@ "dev": true }, "bson": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.4.tgz", - "integrity": "sha512-S/yKGU1syOMzO86+dGpg2qGoDL0zvzcb262G+gqEy6TgP6rt6z6qxSFX/8X6vLC91P7G7C3nLs0+bvDzmvBA3Q==" + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.5.tgz", + "integrity": "sha512-kDuEzldR21lHciPQAIulLs1LZlCXdLziXI6Mb/TDkwXhb//UORJNPXgcRs2CuO4H0DcMkpfT3/ySsP3unoZjBg==" }, "buffer-equal": { "version": "1.0.0", @@ -1470,11 +1724,15 @@ "integrity": "sha1-WWFrSYME1Var1GaWayLu2j7KX74=", "dev": true }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" + }, "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", - "dev": true + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" }, "buffer-more-ints": { "version": "1.0.0", @@ -1487,6 +1745,26 @@ "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", "dev": true }, + "busboy": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz", + "integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=", + "requires": { + "dicer": "0.2.5", + "readable-stream": "1.1.x" + }, + "dependencies": { + "dicer": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz", + "integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=", + "requires": { + "readable-stream": "1.1.x", + "streamsearch": "0.1.2" + } + } + } + }, "bytes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", @@ -1525,9 +1803,9 @@ }, "dependencies": { "get-stream": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", - "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", "dev": true, "requires": { "pump": "^3.0.0" @@ -1538,16 +1816,6 @@ "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", "dev": true - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } } } }, @@ -1563,6 +1831,16 @@ "write-file-atomic": "^3.0.0" } }, + "call-bind": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.0.tgz", + "integrity": "sha512-AEXsYIyyDY3MCzbwdhzG3Jx1R0J2wetQyUynn6dYHAO+bg8l1k7jwZtRv4ryryFs7EP+NDlikJlVe59jr0cM2w==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.0" + } + }, "camelcase": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", @@ -1573,11 +1851,6 @@ "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz", "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=" }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" - }, "center-align": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", @@ -1602,13 +1875,30 @@ } }, "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "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" + } + } } }, "character-parser": { @@ -1626,26 +1916,16 @@ "dev": true }, "cheerio": { - "version": "0.22.0", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz", - "integrity": "sha1-qbqoYKP5tZWmuBsahocxIe06Jp4=", + "version": "1.0.0-rc.3", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.3.tgz", + "integrity": "sha512-0td5ijfUPuubwLUu0OBoe98gZj8C/AA+RW3v67GPlGOrvxWjZmBXiBCRU+I8VEiNyJzjth40POfHiz2RB3gImA==", "requires": { "css-select": "~1.2.0", - "dom-serializer": "~0.1.0", + "dom-serializer": "~0.1.1", "entities": "~1.1.1", "htmlparser2": "^3.9.1", - "lodash.assignin": "^4.0.9", - "lodash.bind": "^4.1.4", - "lodash.defaults": "^4.0.1", - "lodash.filter": "^4.4.0", - "lodash.flatten": "^4.2.0", - "lodash.foreach": "^4.3.0", - "lodash.map": "^4.4.0", - "lodash.merge": "^4.4.0", - "lodash.pick": "^4.2.1", - "lodash.reduce": "^4.4.0", - "lodash.reject": "^4.4.0", - "lodash.some": "^4.4.0" + "lodash": "^4.15.0", + "parse5": "^3.0.1" }, "dependencies": { "dom-serializer": { @@ -1677,14 +1957,6 @@ "path-is-absolute": "^1.0.0", "readdirp": "^2.2.1", "upath": "^1.1.1" - }, - "dependencies": { - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - } } }, "ci-info": { @@ -1731,9 +2003,9 @@ "dev": true }, "cli-boxes": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.0.tgz", - "integrity": "sha512-gpaBrMAizVEANOpfZp/EEUixTXDyGt7DFzdK5hU+UbWt/J0lB0w20ncZj59Z9a93xHb9u12zF5BS6i9RKbtg4w==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", + "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", "dev": true }, "cliui": { @@ -1866,9 +2138,9 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, "color-string": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", - "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.4.tgz", + "integrity": "sha512-57yF5yt8Xa3czSEW1jfQDE79Idk0+AkN/4KWad6tbdxUmAs3MvjxlWSWD4deYytcRfoZ9nhKyFl1kj5tBvidbw==", "requires": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" @@ -1898,14 +2170,15 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, "requires": { "delayed-stream": "~1.0.0" } }, "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==" }, "commondir": { "version": "1.0.1", @@ -1919,6 +2192,15 @@ "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", "dev": true }, + "compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "optional": true, + "requires": { + "mime-db": ">= 1.43.0 < 2" + } + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -1928,7 +2210,6 @@ "version": "1.6.2", "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "dev": true, "requires": { "buffer-from": "^1.0.0", "inherits": "^2.0.3", @@ -1939,14 +2220,12 @@ "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -1961,7 +2240,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, "requires": { "safe-buffer": "~5.1.0" } @@ -1972,7 +2250,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", - "dev": true, "requires": { "dot-prop": "^5.2.0", "graceful-fs": "^4.1.2", @@ -1983,11 +2260,11 @@ } }, "consolidate": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/consolidate/-/consolidate-0.15.1.tgz", - "integrity": "sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw==", + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/consolidate/-/consolidate-0.16.0.tgz", + "integrity": "sha512-Nhl1wzCslqXYTJVDyJCu3ODohy9OfBMB5uD2BiBTzd7w+QY0lBzafkR8y8755yMYHAaMD4NuzbAw03/xzfw+eQ==", "requires": { - "bluebird": "^3.1.1" + "bluebird": "^3.7.2" } }, "constantinople": { @@ -2080,22 +2357,31 @@ } }, "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "dependencies": { + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } } }, "crypto-random-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", - "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", - "dev": true + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==" }, "css-select": { "version": "1.2.0", @@ -2134,32 +2420,21 @@ "type": "^1.0.1" } }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "requires": { - "assert-plus": "^1.0.0" - } - }, "dasherize": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/dasherize/-/dasherize-2.0.0.tgz", "integrity": "sha1-bYCcnNDPe7iVLYD8hPoT1H3bEwg=" }, - "datauri": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/datauri/-/datauri-2.0.0.tgz", - "integrity": "sha512-zS2HSf9pI5XPlNZgIqJg/wCJpecgU/HA6E/uv2EfaWnW1EiTGLfy/EexTIsC9c99yoCOTXlqeeWk4FkCSuO3/g==", - "requires": { - "image-size": "^0.7.3", - "mimer": "^1.0.0" - } + "date-and-time": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/date-and-time/-/date-and-time-0.14.1.tgz", + "integrity": "sha512-M4RggEH5OF2ZuCOxgOU67R6Z9ohjKbxGvAQz48vj53wLmL0bAgumkBvycR32f30pK+Og9pIR+RFDyChbaE4oLA==", + "optional": true }, "dayjs": { - "version": "1.8.28", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.28.tgz", - "integrity": "sha512-ccnYgKC0/hPSGXxj7Ju6AV/BP4HUkXC2u15mikXT5mX9YorEaoi1bEKOmAqdkJHN4EEkmAf97SpH66Try5Mbeg==" + "version": "1.9.6", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.9.6.tgz", + "integrity": "sha512-HngNLtPEBWRo8EFVmHFmSXAjtCX8rGNqeXQI0Gh7wCTSqwaKgPIDqu9m07wABVopNwzvOeCb+2711vQhDlcIXw==" }, "debug": { "version": "2.6.9", @@ -2201,7 +2476,8 @@ "deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true }, "default-compare": { "version": "1.0.0", @@ -2308,7 +2584,8 @@ "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true }, "denque": { "version": "1.4.1", @@ -2331,6 +2608,14 @@ "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", "dev": true }, + "dicer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.3.0.tgz", + "integrity": "sha512-MdceRRWqltEG2dZqO769g27N/3PXfcKl04VhYnBlo2YhH7zPi88VebsjTKclaOyiuMaGU72hTfw3VkUitGcVCA==", + "requires": { + "streamsearch": "0.1.2" + } + }, "diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -2352,14 +2637,14 @@ }, "dependencies": { "domelementtype": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz", - "integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==" + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.2.tgz", + "integrity": "sha512-wFwTwCVebUrMgGeAwRL/NhZtHAUyT9n9yg4IMDwf10+6iCMxSkVq9MGCVEH+QZWo1nNidy8kNvwmv4zWHDTqvA==" }, "entities": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz", - "integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", + "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==" } } }, @@ -2391,10 +2676,9 @@ "integrity": "sha512-ZjI4zqTaxveH2/tTlzS1wFp+7ncxNZaIEWYg3lzZRHkKf5zPT/MnEG6WL0BhHMJUabkh8GeU5NL5j+rEUCb7Ug==" }, "dot-prop": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz", - "integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==", - "dev": true, + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", "requires": { "is-obj": "^2.0.0" } @@ -2411,45 +2695,41 @@ "dev": true }, "duplexify": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", - "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", - "dev": true, + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.1.tgz", + "integrity": "sha512-DY3xVEmVHTv1wSzKNbwoU6nVjzI369Y6sPoqfYr0/xlx3IdX2n94xIszTcjPO8W8ZIv0Wb0PXNcjuZyT4wiICA==", + "optional": true, "requires": { - "end-of-stream": "^1.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.0.0", + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", "stream-shift": "^1.0.0" }, "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "optional": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" } }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "optional": true + }, "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "optional": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "~5.2.0" } } } @@ -2464,13 +2744,12 @@ "object.defaults": "^1.1.0" } }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" + "safe-buffer": "^5.0.1" } }, "ee-first": { @@ -2479,29 +2758,29 @@ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "email-templates": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/email-templates/-/email-templates-7.0.5.tgz", - "integrity": "sha512-fkq/J7gW/+IU1PIl2R2udGaLYrTz7LKCpFkfCEYIzYRHP7e8LYfFYY1+kw/7YEPEvkizBEHEDIl+o3QJddBUug==", - "requires": { - "@ladjs/i18n": "^3.0.10", - "@sindresorhus/is": "^2.1.1", - "consolidate": "^0.15.1", - "debug": "^4.1.1", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/email-templates/-/email-templates-7.1.2.tgz", + "integrity": "sha512-+tp0oUj16WwkR+qo9dbrZN0H64g+ya6NrzqHi2PtJQNVrPQU1oSiERKfOQdmK52VBvpzpaawI2tHDuknvb7Czw==", + "requires": { + "@ladjs/i18n": "^6.0.5", + "@sindresorhus/is": "^4.0.0", + "consolidate": "^0.16.0", + "debug": "^4.2.0", "get-paths": "^0.0.7", "html-to-text": "^5.1.1", - "juice": "^6.0.0", - "lodash": "^4.17.15", - "nodemailer": "^6.4.6", + "juice": "^7.0.0", + "lodash": "^4.17.20", + "nodemailer": "^6.4.14", "pify": "^5.0.0", - "preview-email": "^2.0.1" + "preview-email": "^2.0.2" }, "dependencies": { "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, "ms": { @@ -2512,10 +2791,9 @@ } }, "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "enabled": { "version": "2.0.0", @@ -2536,11 +2814,16 @@ "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, "requires": { "once": "^1.4.0" } }, + "ent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", + "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=", + "optional": true + }, "entities": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", @@ -2563,80 +2846,21 @@ } } }, - "es-abstract": { - "version": "1.17.6", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", - "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", + "es5-ext": { + "version": "0.10.53", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", + "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", "dev": true, "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.0", - "is-regex": "^1.1.0", - "object-inspect": "^1.7.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.3", + "next-tick": "~1.0.0" } }, - "es-array-method-boxes-properly": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", - "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", - "dev": true - }, - "es-get-iterator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.0.tgz", - "integrity": "sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ==", - "dev": true, - "requires": { - "es-abstract": "^1.17.4", - "has-symbols": "^1.0.1", - "is-arguments": "^1.0.4", - "is-map": "^2.0.1", - "is-set": "^2.0.1", - "is-string": "^1.0.5", - "isarray": "^2.0.5" - }, - "dependencies": { - "isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true - } - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "es5-ext": { - "version": "0.10.53", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", - "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", - "dev": true, - "requires": { - "es6-iterator": "~2.0.3", - "es6-symbol": "~3.1.3", - "next-tick": "~1.0.0" - } - }, - "es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", "dev": true }, "es6-iterator": { @@ -2673,10 +2897,9 @@ } }, "escape-goat": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", - "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==", - "dev": true + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-3.0.0.tgz", + "integrity": "sha512-w3PwNZJwRxlp47QGzhuEBldEqVHHhh8/tIPcl6ecf2Bou99cdAt0knihBV0Ecc7CGxYduXVBDheH1K2oADRlvw==" }, "escape-html": { "version": "1.0.3", @@ -2684,9 +2907,10 @@ "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" }, "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true }, "esprima": { "version": "4.0.1", @@ -2704,6 +2928,12 @@ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" }, + "event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "optional": true + }, "expand-brackets": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", @@ -2795,9 +3025,9 @@ }, "dependencies": { "type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.0.0.tgz", - "integrity": "sha512-KBt58xCHry4Cejnc2ISQAF7QY+ORngsWfxezO68+12hKV6lQY8P/psIkcbjeHWn7MqcgciWJyCCevFMJdIXpow==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/type/-/type-2.1.0.tgz", + "integrity": "sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA==", "dev": true } } @@ -2899,11 +3129,6 @@ } } }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" - }, "fancy-log": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", @@ -2919,18 +3144,34 @@ "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==" + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "optional": true }, - "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==" + "fast-levenshtein": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-1.1.4.tgz", + "integrity": "sha1-5qdUzI8V5YmHqpy9J69m/W9OWvk=", + "dev": true }, "fast-safe-stringify": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==" }, + "fast-text-encoding": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.3.tgz", + "integrity": "sha512-dtm4QZH9nZtcDt8qJiOH9fcQd1NAgi+K1O2DbE6GG1PPCK/BWfOH3idCTRQ4ImXRUOyopDEgDEnVEE7Y/2Wrig==", + "optional": true + }, + "faye-websocket": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.3.tgz", + "integrity": "sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA==", + "requires": { + "websocket-driver": ">=0.5.1" + } + }, "feature-policy": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/feature-policy/-/feature-policy-0.3.0.tgz", @@ -3005,13 +3246,12 @@ } }, "find-up": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", - "dev": true, + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "requires": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" } }, "findup-sync": { @@ -3039,6 +3279,28 @@ "parse-filepath": "^1.0.1" } }, + "firebase-admin": { + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-9.4.1.tgz", + "integrity": "sha512-y9r2Mz2x1WTr60YrCDqz8Lw70DlwIvRIieVltP+UdRogkVpfnvyd+bi4D0KPlujW3teqcFPmxuzsXB+DP5vGfQ==", + "requires": { + "@firebase/database": "^0.6.10", + "@firebase/database-types": "^0.5.2", + "@google-cloud/firestore": "^4.5.0", + "@google-cloud/storage": "^5.3.0", + "@types/node": "^10.10.0", + "dicer": "^0.3.0", + "jsonwebtoken": "^8.5.1", + "node-forge": "^0.10.0" + }, + "dependencies": { + "@types/node": { + "version": "10.17.44", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.44.tgz", + "integrity": "sha512-vHPAyBX1ffLcy4fQHmDyIUMUb42gHZjPHU66nhvbMzAWJqHnySGZ6STwN3rwrnSd1FHB0DI/RWgGELgKSYRDmw==" + } + } + }, "flagged-respawn": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz", @@ -3046,21 +3308,10 @@ "dev": true }, "flat": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", - "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", - "dev": true, - "requires": { - "is-buffer": "~2.0.3" - }, - "dependencies": { - "is-buffer": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", - "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==", - "dev": true - } - } + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true }, "flush-write-stream": { "version": "1.1.1", @@ -3132,63 +3383,16 @@ "requires": { "cross-spawn": "^7.0.0", "signal-exit": "^3.0.2" - }, - "dependencies": { - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } } }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" - }, "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz", + "integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==", + "dev": true, "requires": { "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", + "combined-stream": "^1.0.8", "mime-types": "^2.1.12" } }, @@ -3218,20 +3422,21 @@ "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" }, "fromentries": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.2.0.tgz", - "integrity": "sha512-33X7H/wdfO99GdRLLgkjUrD4geAFdq/Uv0kl3HD4da6HDixd2GUg8Mw7dahLCV9r/EARkmtYBB6Tch4EEokFTQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", + "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", "dev": true }, "fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz", + "integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==", "dev": true, "requires": { + "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" + "jsonfile": "^6.0.1", + "universalify": "^1.0.0" } }, "fs-mkdirp-stream": { @@ -3264,20 +3469,77 @@ "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "optional": true + }, + "gaxios": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-4.0.1.tgz", + "integrity": "sha512-jOin8xRZ/UytQeBpSXFqIzqU7Fi5TqgPNLlUsSB8kjJ76+FiGBfImF8KJu++c6J4jOldfJUtt0YmkRj2ZpSHTQ==", + "optional": true, + "requires": { + "abort-controller": "^3.0.0", + "extend": "^3.0.2", + "https-proxy-agent": "^5.0.0", + "is-stream": "^2.0.0", + "node-fetch": "^2.3.0" + } + }, + "gcp-metadata": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-4.2.1.tgz", + "integrity": "sha512-tSk+REe5iq/N+K+SK1XjZJUrFPuDqGZVzCy2vocIHIGmPlTGsa8owXMJwGkrXr73NO0AzhPW4MF2DEHz7P2AVw==", + "optional": true, + "requires": { + "gaxios": "^4.0.0", + "json-bigint": "^1.0.0" + } + }, + "gcs-resumable-upload": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/gcs-resumable-upload/-/gcs-resumable-upload-3.1.1.tgz", + "integrity": "sha512-RS1osvAicj9+MjCc6jAcVL1Pt3tg7NK2C2gXM5nqD1Gs0klF2kj5nnAFSBy97JrtslMIQzpb7iSuxaG8rFWd2A==", + "optional": true, + "requires": { + "abort-controller": "^3.0.0", + "configstore": "^5.0.0", + "extend": "^3.0.2", + "gaxios": "^3.0.0", + "google-auth-library": "^6.0.0", + "pumpify": "^2.0.0", + "stream-events": "^1.0.4" + }, + "dependencies": { + "gaxios": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-3.2.0.tgz", + "integrity": "sha512-+6WPeVzPvOshftpxJwRi2Ozez80tn/hdtOUag7+gajDHRJvAblKxTFSSMPtr2hmnLy7p0mvYz0rMXLBl8pSO7Q==", + "optional": true, + "requires": { + "abort-controller": "^3.0.0", + "extend": "^3.0.2", + "https-proxy-agent": "^5.0.0", + "is-stream": "^2.0.0", + "node-fetch": "^2.3.0" + } + } + } }, "gensync": { - "version": "1.0.0-beta.1", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", - "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==", + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true }, "get-caller-file": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", - "dev": true + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" }, "get-func-name": { "version": "2.0.0", @@ -3285,6 +3547,17 @@ "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", "dev": true }, + "get-intrinsic": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.0.1.tgz", + "integrity": "sha512-ZnWP+AmS1VUaLgTRy47+zKtjTxz+0xMpx3I52i+aalBK1QP19ggLF3Db89KJX7kjfOfP2eoa01qc++GwPgufPg==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, "get-package-type": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", @@ -3307,25 +3580,10 @@ } }, "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - }, - "dependencies": { - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - } - } + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.0.tgz", + "integrity": "sha512-A1B3Bh1UmL0bidM/YX2NsCOTnGJePL9rO/M+Mw3m9f2gUpfokS0hi5Eah0WSUEWZdZhIZtMjkIYS7mDfOqNHbg==", + "optional": true }, "get-value": { "version": "2.0.6", @@ -3333,14 +3591,6 @@ "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", "dev": true }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "requires": { - "assert-plus": "^1.0.0" - } - }, "glob": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", @@ -3394,12 +3644,45 @@ "unique-stream": "^2.0.2" }, "dependencies": { + "duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "dev": true, + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true }, + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "dev": true, + "requires": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + } + }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", @@ -3427,9 +3710,9 @@ } }, "glob-watcher": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-5.0.3.tgz", - "integrity": "sha512-8tWsULNEPHKQ2MR4zXuzSmqbdyV5PtwwCaWSGQ1WwHsJ07ilNeN1JB8ntxhckbnpSHaf9dXFUHzIWvm1I13dsg==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-5.0.5.tgz", + "integrity": "sha512-zOZgGGEHPklZNjZQaZ9f41i7F2YwE+tS5ZHrDhbBCk3stwahn5vQxnFmBJZHoYdusR6R1bLSXeGUy/BhctwKzw==", "dev": true, "requires": { "anymatch": "^2.0.0", @@ -3437,6 +3720,7 @@ "chokidar": "^2.0.0", "is-negated-glob": "^1.0.0", "just-debounce": "^1.0.0", + "normalize-path": "^3.0.0", "object.defaults": "^1.1.0" } }, @@ -3488,6 +3772,50 @@ "sparkles": "^1.0.0" } }, + "google-auth-library": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-6.1.3.tgz", + "integrity": "sha512-m9mwvY3GWbr7ZYEbl61isWmk+fvTmOt0YNUfPOUY2VH8K5pZlAIWJjxEi0PqR3OjMretyiQLI6GURMrPSwHQ2g==", + "optional": true, + "requires": { + "arrify": "^2.0.0", + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "fast-text-encoding": "^1.0.0", + "gaxios": "^4.0.0", + "gcp-metadata": "^4.2.0", + "gtoken": "^5.0.4", + "jws": "^4.0.0", + "lru-cache": "^6.0.0" + } + }, + "google-gax": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-2.9.2.tgz", + "integrity": "sha512-Pve4osEzNKpBZqFXMfGKBbKCtgnHpUe5IQMh5Ou+Xtg8nLcba94L3gF0xgM5phMdGRRqJn0SMjcuEVmOYu7EBg==", + "optional": true, + "requires": { + "@grpc/grpc-js": "~1.1.1", + "@grpc/proto-loader": "^0.5.1", + "@types/long": "^4.0.0", + "abort-controller": "^3.0.0", + "duplexify": "^4.0.0", + "google-auth-library": "^6.1.3", + "is-stream-ended": "^0.1.4", + "node-fetch": "^2.6.1", + "protobufjs": "^6.9.0", + "retry-request": "^4.0.0" + } + }, + "google-p12-pem": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-3.0.3.tgz", + "integrity": "sha512-wS0ek4ZtFx/ACKYF3JhyGe5kzH7pgiQ7J5otlumqR9psmWMYc+U9cErKlCYVYHoUaidXHdZ2xbo34kB+S+24hA==", + "optional": true, + "requires": { + "node-forge": "^0.10.0" + } + }, "got": { "version": "9.6.0", "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", @@ -3512,14 +3840,22 @@ "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", "dev": true + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } } } }, "graceful-fs": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" }, "growl": { "version": "1.10.5", @@ -3527,6 +3863,18 @@ "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", "dev": true }, + "gtoken": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-5.1.0.tgz", + "integrity": "sha512-4d8N6Lk8TEAHl9vVoRVMh9BNOKWVgl2DdNtr3428O75r3QFrF/a5MMu851VmK0AA8+iSvbwRv69k5XnMLURGhg==", + "optional": true, + "requires": { + "gaxios": "^4.0.0", + "google-p12-pem": "^3.0.3", + "jws": "^4.0.0", + "mime": "^2.2.0" + } + }, "gulp": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/gulp/-/gulp-4.0.2.tgz", @@ -3539,6 +3887,21 @@ "vinyl-fs": "^3.0.0" }, "dependencies": { + "ansi-colors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", + "dev": true, + "requires": { + "ansi-wrap": "^0.1.0" + } + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, "camelcase": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", @@ -3556,6 +3919,12 @@ "wrap-ansi": "^2.0.0" } }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, "gulp-cli": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-2.3.0.tgz", @@ -3582,40 +3951,107 @@ "yargs": "^7.1.0" } }, - "yargs": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.1.tgz", - "integrity": "sha512-huO4Fr1f9PmiJJdll5kwoS2e4GqzGSsMT3PPMpOwoVkOK8ckqAewMTZyA6LXVQWflleb/Z8oPBEvNsMft0XE+g==", + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, "requires": { - "camelcase": "^3.0.0", - "cliui": "^3.2.0", - "decamelize": "^1.1.1", - "get-caller-file": "^1.0.1", - "os-locale": "^1.4.0", - "read-pkg-up": "^1.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^1.0.2", - "which-module": "^1.0.0", - "y18n": "^3.2.1", - "yargs-parser": "5.0.0-security.0" + "number-is-nan": "^1.0.0" } - } - } - }, - "gulp-nodemon": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/gulp-nodemon/-/gulp-nodemon-2.5.0.tgz", - "integrity": "sha512-vXfaP72xo2C6XOaXrNcLEM3QqDJ1x21S3x97U4YtzN2Rl2kH57++aFkAVxe6BafGRSTxs/xVfE/jNNlCv5Ym2Q==", - "dev": true, - "requires": { - "colors": "^1.2.1", - "gulp": "^4.0.0", - "nodemon": "^2.0.2" - } - }, + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", + "dev": true + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + } + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "dev": true + }, + "yargs": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.1.tgz", + "integrity": "sha512-huO4Fr1f9PmiJJdll5kwoS2e4GqzGSsMT3PPMpOwoVkOK8ckqAewMTZyA6LXVQWflleb/Z8oPBEvNsMft0XE+g==", + "dev": true, + "requires": { + "camelcase": "^3.0.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.2", + "which-module": "^1.0.0", + "y18n": "^3.2.1", + "yargs-parser": "5.0.0-security.0" + } + }, + "yargs-parser": { + "version": "5.0.0-security.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0-security.0.tgz", + "integrity": "sha512-T69y4Ps64LNesYxeYGYPvfoMTt/7y1XtfpIslUeK4um+9Hu7hlGoRtaDLvdXb7+/tfq4opVa2HRY5xGip022rQ==", + "dev": true, + "requires": { + "camelcase": "^3.0.0", + "object.assign": "^4.1.0" + } + } + } + }, + "gulp-nodemon": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/gulp-nodemon/-/gulp-nodemon-2.5.0.tgz", + "integrity": "sha512-vXfaP72xo2C6XOaXrNcLEM3QqDJ1x21S3x97U4YtzN2Rl2kH57++aFkAVxe6BafGRSTxs/xVfE/jNNlCv5Ym2Q==", + "dev": true, + "requires": { + "colors": "^1.2.1", + "gulp": "^4.0.0", + "nodemon": "^2.0.2" + } + }, "gulp-tslint": { "version": "8.1.4", "resolved": "https://registry.npmjs.org/gulp-tslint/-/gulp-tslint-8.1.4.tgz", @@ -3628,6 +4064,17 @@ "map-stream": "~0.0.7", "plugin-error": "1.0.1", "through": "~2.3.8" + }, + "dependencies": { + "ansi-colors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", + "dev": true, + "requires": { + "ansi-wrap": "^0.1.0" + } + } } }, "gulp-typescript": { @@ -3717,9 +4164,9 @@ }, "dependencies": { "uglify-js": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.10.0.tgz", - "integrity": "sha512-Esj5HG5WAyrLIdYU74Z3JdG2PxdIusvj6IWHMtlyESxc7kcDz7zYlYjpnSokn1UbpV0d/QX9fan7gkCNd/9BQA==", + "version": "3.11.6", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.11.6.tgz", + "integrity": "sha512-oASI1FOJ7BBFkSCNDZ446EgkSuHkOZBuqRFrwXIKWCoXw8ZXQETooTQjkAcBS03Acab7ubCKsXnwuV2svy061g==", "dev": true, "optional": true }, @@ -3731,25 +4178,10 @@ } } }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" - }, - "har-validator": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", - "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", - "requires": { - "ajv": "^6.5.5", - "har-schema": "^2.0.0" - } - }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, "requires": { "function-bind": "^1.1.1" } @@ -3757,7 +4189,8 @@ "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true }, "has-symbols": { "version": "1.0.1", @@ -3802,10 +4235,16 @@ "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==", "dev": true }, + "hash-stream-validation": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/hash-stream-validation/-/hash-stream-validation-0.2.4.tgz", + "integrity": "sha512-Gjzu0Xn7IagXVkSu9cSFuK1fqzwtLwFhNhVL8IFJijRNMgUttFbBSIAzKuSIrsFMO1+g1RlsoN49zPIbwPDMGQ==", + "optional": true + }, "hasha": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.0.tgz", - "integrity": "sha512-2W+jKdQbAdSIrggA8Q35Br8qKadTrqCTC8+XZvBWepKDK6m9XkX6Iz1a2yh2KP01kzAR/dpuMeUnocoLYDcskw==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", + "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", "dev": true, "requires": { "is-stream": "^2.0.0", @@ -3864,9 +4303,9 @@ "integrity": "sha512-Io1zA2yOA1YJslkr+AJlWSf2yWFkKjvkcL9Ni1XSUqnGLr/qRQe2UI3Cn/J9MsJht7yEVCe0SscY1HgVMujbgg==" }, "highlight.js": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.1.1.tgz", - "integrity": "sha512-b4L09127uVa+9vkMgPpdUQP78ickGbHEQTWeBrQFTJZ4/n2aihWOGS0ZoUqAwjVmfjhq/C76HRzkqwZhK4sBbg==", + "version": "10.3.2", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.3.2.tgz", + "integrity": "sha512-3jRT7OUYsVsKvukNKZCtnvRcFyCJqSEIuIMsEybAXRiFSwpt65qjPd/Pr+UOdYt7WJlt+lj3+ypUsHiySBp/Jw==", "dev": true }, "homedir-polyfill": { @@ -3984,14 +4423,37 @@ } } }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "http-parser-js": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.2.tgz", + "integrity": "sha512-opCO9ASqg5Wy2FNo7A0sxy71yGbbkJJXLdgMK04Tcypw9jr2MgWbyubb0+WdmDmGnFflO7fRbqbaihh/ENDlRQ==" + }, + "http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "optional": true, "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "dependencies": { + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "optional": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "optional": true + } } }, "http-status-codes": { @@ -3999,17 +4461,59 @@ "resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-1.4.0.tgz", "integrity": "sha512-JrT3ua+WgH8zBD3HEJYbeEgnuQaAnUeRRko/YojPAJjGmIfGD3KPU/asLdsLwKjfxOmQe5nXMQ0pt/7MyapVbQ==" }, + "https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "optional": true, + "requires": { + "agent-base": "6", + "debug": "4" + }, + "dependencies": { + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "optional": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "optional": true + } + } + }, "i18n": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/i18n/-/i18n-0.9.1.tgz", - "integrity": "sha512-ERo9WloOP2inRsJzAlzn4JDm3jvX7FW1+KB/JGXTzUVzi9Bsf4LNLXUQTMgM/aze4LNW/kvmxQX6bzg5UzqMJw==", + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/i18n/-/i18n-0.13.2.tgz", + "integrity": "sha512-PB65bHhQESMBIl/xVNChEAzoxZ5W6FrZ1H9Ma/YcPeSfE7VS9b0sqwBPusa0CfzSKUPSl+uMhRIgyv3jkE7XNw==", "requires": { - "debug": "*", - "make-plural": "^6.2.1", + "debug": "^4.1.1", + "make-plural": "^6.2.2", "math-interval-parser": "^2.0.1", "messageformat": "^2.3.0", "mustache": "^4.0.1", "sprintf-js": "^1.1.2" + }, + "dependencies": { + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } } }, "i18n-locales": { @@ -4034,11 +4538,6 @@ "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=", "dev": true }, - "image-size": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.7.5.tgz", - "integrity": "sha512-Hiyv+mXHfFEP7LzUL/llg9RwFxxY+o9N3JVLIeG5E7iFIFAalxvRU9UZthBdYDEVnzHMgjnKJPPpay5BWf1g9g==" - }, "import-lazy": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", @@ -4048,8 +4547,7 @@ "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" }, "indent-string": { "version": "4.0.0", @@ -4331,12 +4829,6 @@ "kind-of": "^3.0.2" } }, - "is-arguments": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", - "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", - "dev": true - }, "is-arrayish": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", @@ -4356,12 +4848,6 @@ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" }, - "is-callable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", - "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", - "dev": true - }, "is-ci": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", @@ -4371,6 +4857,14 @@ "ci-info": "^2.0.0" } }, + "is-core-module": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.1.0.tgz", + "integrity": "sha512-YcV7BgVMRFRua2FqQzKtTDMz8iCuLEyGKjr70q8Zm1yy2qKcurbFEd79PAdHV77oL3NrAaOVQIbMmiHQCHB7ZA==", + "requires": { + "has": "^1.0.3" + } + }, "is-data-descriptor": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", @@ -4380,12 +4874,6 @@ "kind-of": "^3.0.2" } }, - "is-date-object": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", - "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", - "dev": true - }, "is-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", @@ -4434,13 +4922,9 @@ "dev": true }, "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" }, "is-glob": { "version": "4.0.1", @@ -4461,12 +4945,6 @@ "is-path-inside": "^3.0.1" } }, - "is-map": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.1.tgz", - "integrity": "sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw==", - "dev": true - }, "is-negated-glob": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz", @@ -4491,8 +4969,7 @@ "is-obj": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", - "dev": true + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==" }, "is-path-inside": { "version": "3.0.2", @@ -4500,6 +4977,12 @@ "integrity": "sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==", "dev": true }, + "is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true + }, "is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", @@ -4515,9 +4998,9 @@ "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==" }, "is-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz", - "integrity": "sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", "requires": { "has-symbols": "^1.0.1" } @@ -4531,31 +5014,16 @@ "is-unc-path": "^1.0.0" } }, - "is-set": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.1.tgz", - "integrity": "sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA==", - "dev": true - }, "is-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" }, - "is-string": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", - "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", - "dev": true - }, - "is-symbol": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", - "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", - "dev": true, - "requires": { - "has-symbols": "^1.0.1" - } + "is-stream-ended": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-stream-ended/-/is-stream-ended-0.1.4.tgz", + "integrity": "sha512-xj0XPvmr7bQFTvirqnFr50o0hQIh6ZItDqloxt5aJrR4NQsYeSsyFQERYGCAzfindAcnKjINnwEEgLx4IqVzQw==", + "optional": true }, "is-typedarray": { "version": "1.0.0", @@ -4608,7 +5076,8 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true }, "isobject": { "version": "3.0.1", @@ -4616,11 +5085,6 @@ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "dev": true }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" - }, "istanbul-lib-coverage": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", @@ -4646,14 +5110,6 @@ "@istanbuljs/schema": "^0.1.2", "istanbul-lib-coverage": "^3.0.0", "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } } }, "istanbul-lib-processinfo": { @@ -4669,49 +5125,6 @@ "p-map": "^3.0.0", "rimraf": "^3.0.0", "uuid": "^3.3.3" - }, - "dependencies": { - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } } }, "istanbul-lib-report": { @@ -4732,9 +5145,9 @@ "dev": true }, "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "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" @@ -4754,12 +5167,12 @@ }, "dependencies": { "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, "ms": { @@ -4780,22 +5193,6 @@ "istanbul-lib-report": "^3.0.0" } }, - "iterate-iterator": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/iterate-iterator/-/iterate-iterator-1.0.1.tgz", - "integrity": "sha512-3Q6tudGN05kbkDQDI4CqjaBf4qf85w6W6GnuZDtUVYwKgtC1q8yxYX7CZed7N+tLzQqS6roujWvszf13T+n9aw==", - "dev": true - }, - "iterate-value": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/iterate-value/-/iterate-value-1.0.2.tgz", - "integrity": "sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ==", - "dev": true, - "requires": { - "es-get-iterator": "^1.0.2", - "iterate-iterator": "^1.0.1" - } - }, "js-stringify": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz", @@ -4808,53 +5205,42 @@ "dev": true }, "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", + "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", "dev": true, "requires": { "argparse": "^1.0.7", "esprima": "^4.0.0" } }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" - }, "jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true }, + "json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "optional": true, + "requires": { + "bignumber.js": "^9.0.0" + } + }, "json-buffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", "dev": true }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" - }, - "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==" - }, "json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" - }, "json5": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", @@ -4865,46 +5251,90 @@ } }, "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, "requires": { - "graceful-fs": "^4.1.6" - } - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + }, + "dependencies": { + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true + } } }, - "jstransformer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", - "integrity": "sha1-7Yvwkh4vPx7U1cGkT2hwntJHIsM=", + "jsonwebtoken": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", "requires": { - "is-promise": "^2.0.0", - "promise": "^7.0.1" - } - }, + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^5.6.0" + }, + "dependencies": { + "jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "requires": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } + } + }, + "jstransformer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", + "integrity": "sha1-7Yvwkh4vPx7U1cGkT2hwntJHIsM=", + "requires": { + "is-promise": "^2.0.0", + "promise": "^7.0.1" + } + }, "juice": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/juice/-/juice-6.0.0.tgz", - "integrity": "sha512-5T3JPgXYiw6A6axsb9E09Gzq46WbfJeDirY6nMrqY55iAdqEoPDxSr1GpXqYfoyndx4ujpBPXGLzBRzbiqOOaw==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/juice/-/juice-7.0.0.tgz", + "integrity": "sha512-AjKQX31KKN+uJs+zaf+GW8mBO/f/0NqSh2moTMyvwBY+4/lXIYTU8D8I2h6BAV3Xnz6GGsbalUyFqbYMe+Vh+Q==", "requires": { - "cheerio": "^0.22.0", - "commander": "^2.15.1", - "cross-spawn": "^6.0.5", - "deep-extend": "^0.6.0", + "cheerio": "^1.0.0-rc.3", + "commander": "^5.1.0", "mensch": "^0.3.4", "slick": "^1.12.2", - "web-resource-inliner": "^4.3.3" + "web-resource-inliner": "^5.0.0" } }, "just-debounce": { @@ -4914,11 +5344,32 @@ "dev": true }, "just-extend": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.1.0.tgz", - "integrity": "sha512-ApcjaOdVTJ7y4r08xI5wIqpvwS48Q0PBG4DJROcEkH1f8MdAiNFyFxz3xoL0LWAVwjrwPYZdVHHxhRHcx/uGLA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.1.1.tgz", + "integrity": "sha512-aWgeGFW67BP3e5181Ep1Fv2v8z//iBJfrvyTnq8wG86vEESwmonn1zPBJ0VfmT9CJq2FIT0VsETtrNFm2a+SHA==", "dev": true }, + "jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "optional": true, + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "optional": true, + "requires": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, "kareem": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.1.tgz", @@ -5035,22 +5486,22 @@ "integrity": "sha512-l+nePcPbIG1fNlqMzrh68MLkX/gTxk/+vdvAb388Ssi7UuUN31MI44w4Yf33mM3Cm4xDfw48mdf3rkdHszLNew==" }, "libmime": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/libmime/-/libmime-4.2.1.tgz", - "integrity": "sha512-09y7zjSc5im1aNsq815zgo4/G3DnIzym3aDOHsGq4Ee5vrX4PdgQRybAsztz9Rv0NhO+J5C0llEUloa3sUmjmA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/libmime/-/libmime-5.0.0.tgz", + "integrity": "sha512-2Bm96d5ktnE217Ib1FldvUaPAaOst6GtZrsxJCwnJgi9lnsoAKIHyU0sae8rNx6DNYbjdqqh8lv5/b9poD8qOg==", "requires": { "encoding-japanese": "1.0.30", - "iconv-lite": "0.5.0", + "iconv-lite": "0.6.2", "libbase64": "1.2.1", "libqp": "1.1.0" }, "dependencies": { "iconv-lite": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.0.tgz", - "integrity": "sha512-NnEhI9hIEKHOzJ4f697DMz9IQEXr/MMJ5w64vN2/4Ai+wRnvV7SBrL0KLoRlwaKVghOc7LQ5YkPLuX146b6Ydw==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz", + "integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==", "requires": { - "safer-buffer": ">= 2.1.2 < 3" + "safer-buffer": ">= 2.1.2 < 3.0.0" } } } @@ -5077,9 +5528,9 @@ } }, "linkify-it": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz", - "integrity": "sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.2.tgz", + "integrity": "sha512-gDBO4aHNZS6coiZCKVhSNh43F9ioIL4JwRjLZPkoLIY4yZFwg264Y5lu2x6rb1Js42Gh6Yqm2f6L2AJcnkzinQ==", "requires": { "uc.micro": "^1.0.1" } @@ -5109,40 +5560,20 @@ "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" } }, "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" - }, - "lodash.assignin": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz", - "integrity": "sha1-uo31+4QesKPoBEIysOJjqNxqKKI=" - }, - "lodash.bind": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz", - "integrity": "sha1-euMBfpOWIqwxt9fX3LGzTbFpDTU=" - }, - "lodash.defaults": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" - }, - "lodash.filter": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.filter/-/lodash.filter-4.6.0.tgz", - "integrity": "sha1-ZosdSYFgOuHMWm+nYBQ+SAtMSs4=" - }, - "lodash.flatten": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" + }, + "lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", + "optional": true }, "lodash.flattendeep": { "version": "4.4.0", @@ -5150,53 +5581,75 @@ "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", "dev": true }, - "lodash.foreach": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz", - "integrity": "sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM=" - }, - "lodash.map": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz", - "integrity": "sha1-dx7Hg540c9nEzeKLGTlMNWL09tM=" - }, - "lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + "lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=" }, - "lodash.pick": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", - "integrity": "sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM=" + "lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" }, - "lodash.reduce": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz", - "integrity": "sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs=" + "lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=" }, - "lodash.reject": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.reject/-/lodash.reject-4.6.0.tgz", - "integrity": "sha1-gNZJLcFHCGS79YNTO2UfQqn1JBU=" + "lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=" }, - "lodash.some": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz", - "integrity": "sha1-G7nzFO9ri63tE7VJFpsqlF62jk0=" + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" }, - "lodash.unescape": { + "lodash.isstring": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz", - "integrity": "sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw=" + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" + }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" }, "log-symbols": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", - "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", + "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", "dev": true, "requires": { - "chalk": "^2.4.2" + "chalk": "^4.0.0" + }, + "dependencies": { + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.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 + }, + "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" + } + } } }, "logform": { @@ -5224,6 +5677,12 @@ "integrity": "sha512-gKO5uExCXvSm6zbF562EvM+rd1kQDnB9AZBbiQVzf1ZmdDpxUSvpnAaVOP83N/31mRK8Ml8/VE8DMvsAZQ+7wg==", "dev": true }, + "long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", + "optional": true + }, "longest": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", @@ -5235,68 +5694,94 @@ "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", "dev": true }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "optional": true, + "requires": { + "yallist": "^4.0.0" + } + }, "lunr": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.8.tgz", - "integrity": "sha512-oxMeX/Y35PNFuZoHp+jUj5OSEmLCaIH4KTFJh7a93cHBoFmpw2IoPs22VIz7vyO2YUnx2Tn9dzIwO2P/4quIRg==", + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", "dev": true }, "mailparser": { - "version": "2.7.7", - "resolved": "https://registry.npmjs.org/mailparser/-/mailparser-2.7.7.tgz", - "integrity": "sha512-FcVkXYm+zIg59HNPINGQw99eMTvcAkmQZHmabF8aSeMZ6/vWkx0HdT6FpXApelfe5IKRk6nWEg+YAuuXZl9+Fg==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/mailparser/-/mailparser-2.8.1.tgz", + "integrity": "sha512-H/CYAO9dsw6SFNbEGGpZsejVSWDcFlyHjb1OkHUWg0wggUekva1tNc28trB155nSqM8rhtbwTKt//orX0AmJxQ==", "requires": { "encoding-japanese": "1.0.30", "he": "1.2.0", "html-to-text": "5.1.1", - "iconv-lite": "0.5.0", - "libmime": "4.2.1", - "linkify-it": "2.2.0", - "mailsplit": "4.6.2", - "nodemailer": "6.4.0", - "tlds": "1.207.0" + "iconv-lite": "0.6.2", + "libmime": "5.0.0", + "linkify-it": "3.0.2", + "mailsplit": "5.0.0", + "nodemailer": "6.4.11", + "tlds": "1.208.0" }, "dependencies": { "iconv-lite": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.0.tgz", - "integrity": "sha512-NnEhI9hIEKHOzJ4f697DMz9IQEXr/MMJ5w64vN2/4Ai+wRnvV7SBrL0KLoRlwaKVghOc7LQ5YkPLuX146b6Ydw==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz", + "integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==", "requires": { - "safer-buffer": ">= 2.1.2 < 3" + "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "nodemailer": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.4.0.tgz", - "integrity": "sha512-UBqPOfQGD1cM3HnjhuQe+0u3DWx47WWK7lBjG5UtPnGOysr7oDK5lNCzcjK6zzeBSdTk4m1tGx1xNbWFZQmMNA==" + "version": "6.4.11", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.4.11.tgz", + "integrity": "sha512-BVZBDi+aJV4O38rxsUh164Dk1NCqgh6Cm0rQSb9SK/DHGll/DrCMnycVDD7msJgZCnmVa8ASo8EZzR7jsgTukQ==" + }, + "tlds": { + "version": "1.208.0", + "resolved": "https://registry.npmjs.org/tlds/-/tlds-1.208.0.tgz", + "integrity": "sha512-6kbY7GJpRQXwBddSOAbVUZXjObbCGFXliWWN+kOSEoRWIOyRWLB6zdeKC/Tguwwenl/KsUx016XR50EdHYsxZw==" } } }, "mailsplit": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/mailsplit/-/mailsplit-4.6.2.tgz", - "integrity": "sha512-7Bw2R0QfORXexGGQCEK64EeShHacUNyU5kV5F5sj4jPQB3ITe2v9KRqxD40wpuue6W/sBJlSNBZ0AypIeTGQMQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/mailsplit/-/mailsplit-5.0.0.tgz", + "integrity": "sha512-HeXA0eyCKBtZqbr7uoeb3Nn2L7VV8Vm27x6/YBb0ZiNzRzLoNS2PqRgGYADwh0cBzLYtqddq40bSSirqLO2LGw==", "requires": { "libbase64": "1.2.1", "libmime": "4.2.1", "libqp": "1.1.0" + }, + "dependencies": { + "iconv-lite": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.0.tgz", + "integrity": "sha512-NnEhI9hIEKHOzJ4f697DMz9IQEXr/MMJ5w64vN2/4Ai+wRnvV7SBrL0KLoRlwaKVghOc7LQ5YkPLuX146b6Ydw==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "libmime": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/libmime/-/libmime-4.2.1.tgz", + "integrity": "sha512-09y7zjSc5im1aNsq815zgo4/G3DnIzym3aDOHsGq4Ee5vrX4PdgQRybAsztz9Rv0NhO+J5C0llEUloa3sUmjmA==", + "requires": { + "encoding-japanese": "1.0.30", + "iconv-lite": "0.5.0", + "libbase64": "1.2.1", + "libqp": "1.1.0" + } + } } }, "make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, "requires": { "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } } }, "make-error": { @@ -5323,9 +5808,9 @@ } }, "make-plural": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/make-plural/-/make-plural-6.2.1.tgz", - "integrity": "sha512-AmkruwJ9EjvyTv6AM8MBMK3TAeOJvhgTv5YQXzF0EP2qawhpvMjDpHvsdOIIT0Vn+BB0+IogmYZ1z+Ulm/m0Fg==" + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/make-plural/-/make-plural-6.2.2.tgz", + "integrity": "sha512-8iTuFioatnTTmb/YJjywkVIHLjcwkFD9Ms0JpxjEm9Mo8eQYkh1z+55dwv4yc1jQ8ftVBxWQbihvZL1DfzGGWA==" }, "map-cache": { "version": "0.2.2", @@ -5349,9 +5834,9 @@ } }, "marked": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-1.0.0.tgz", - "integrity": "sha512-Wo+L1pWTVibfrSr+TTtMuiMfNzmZWiOPeO7rZsQUY5bgsxpHesBEcIWJloWVTFnrMXnf/TL30eTFSGJddmQAng==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/marked/-/marked-1.2.4.tgz", + "integrity": "sha512-6x5TFGCTKSQBLTZtOburGxCxFEBJEGYVLwCMTBCxzvyuisGcC20UNzDSJhCr/cJ/Kmh6ulfJm10g6WWEAJ3kvg==", "dev": true }, "matchdep": { @@ -5480,9 +5965,9 @@ } }, "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz", + "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==" }, "mime-db": { "version": "1.44.0", @@ -5497,10 +5982,11 @@ "mime-db": "1.44.0" } }, - "mimer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/mimer/-/mimer-1.1.0.tgz", - "integrity": "sha512-y9dVfy2uiycQvDNiAYW6zp49ZhFlXDMr5wfdOiMbdzGM/0N5LNR6HTUn3un+WUQcM0koaw8FMTG1bt5EnHJdvQ==" + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "optional": true }, "mimic-response": { "version": "1.0.1", @@ -5546,56 +6032,58 @@ "version": "0.5.5", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, "requires": { "minimist": "^1.2.5" } }, "mocha": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.0.1.tgz", - "integrity": "sha512-vefaXfdYI8+Yo8nPZQQi0QO2o+5q9UIMX1jZ1XMmK3+4+CQjc7+B0hPdUeglXiTlr8IHMVRo63IhO9Mzt6fxOg==", + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.2.1.tgz", + "integrity": "sha512-cuLBVfyFfFqbNR0uUKbDGXKGk+UDFe6aR4os78XIrMQpZl/nv7JYHcvP5MFIAb374b2zFXsdgEGwmzMtP0Xg8w==", "dev": true, "requires": { + "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", - "chokidar": "3.3.1", - "debug": "3.2.6", + "chokidar": "3.4.3", + "debug": "4.2.0", "diff": "4.0.2", - "escape-string-regexp": "1.0.5", - "find-up": "4.1.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", "glob": "7.1.6", "growl": "1.10.5", "he": "1.2.0", - "js-yaml": "3.13.1", - "log-symbols": "3.0.0", + "js-yaml": "3.14.0", + "log-symbols": "4.0.0", "minimatch": "3.0.4", "ms": "2.1.2", - "object.assign": "4.1.0", - "promise.allsettled": "1.0.2", - "serialize-javascript": "3.0.0", - "strip-json-comments": "3.0.1", - "supports-color": "7.1.0", + "nanoid": "3.1.12", + "serialize-javascript": "5.0.1", + "strip-json-comments": "3.1.1", + "supports-color": "7.2.0", "which": "2.0.2", "wide-align": "1.1.3", - "workerpool": "6.0.0", + "workerpool": "6.0.2", "yargs": "13.3.2", "yargs-parser": "13.1.2", - "yargs-unparser": "1.6.0" + "yargs-unparser": "2.0.0" }, "dependencies": { - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true - }, "ansi-regex": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", "dev": true }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, "anymatch": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", @@ -5628,9 +6116,9 @@ "dev": true }, "chokidar": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.1.tgz", - "integrity": "sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz", + "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==", "dev": true, "requires": { "anymatch": "~3.1.1", @@ -5640,7 +6128,7 @@ "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", - "readdirp": "~3.3.0" + "readdirp": "~3.5.0" } }, "cliui": { @@ -5655,14 +6143,20 @@ } }, "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -5673,12 +6167,12 @@ } }, "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "requires": { - "locate-path": "^5.0.0", + "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, @@ -5689,12 +6183,6 @@ "dev": true, "optional": true }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, "glob-parent": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", @@ -5731,48 +6219,48 @@ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true + "p-limit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.0.2.tgz", + "integrity": "sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } }, "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==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "requires": { - "p-limit": "^2.0.0" + "p-limit": "^3.0.2" } }, - "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 - }, "readdirp": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.3.0.tgz", - "integrity": "sha512-zz0pAkSPOXXm1viEwygWIPSPkcBYjW1xU5j/JBh5t9bGCJwa6f9+BJa6VaB2g+b55yVrmXzqkyLf4xaWYM0IkQ==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", "dev": true, "requires": { - "picomatch": "^2.0.7" + "picomatch": "^2.2.1" } }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, "string-width": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", @@ -5794,15 +6282,15 @@ } }, "strip-json-comments": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", - "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", + "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 }, "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "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" @@ -5826,12 +6314,6 @@ "isexe": "^2.0.0" } }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, "wrap-ansi": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", @@ -5843,12 +6325,6 @@ "strip-ansi": "^5.0.0" } }, - "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", - "dev": true - }, "yargs": { "version": "13.3.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", @@ -5886,6 +6362,24 @@ "path-exists": "^3.0.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": "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, + "requires": { + "p-limit": "^2.0.0" + } + }, "path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", @@ -5907,16 +6401,16 @@ } }, "moment": { - "version": "2.27.0", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.27.0.tgz", - "integrity": "sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ==" + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" }, "mongodb": { - "version": "3.5.9", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.5.9.tgz", - "integrity": "sha512-vXHBY1CsGYcEPoVWhwgxIBeWqP3dSu9RuRDsoLRPTITrcrgm1f0Ubu1xqF9ozMwv53agmEiZm0YGo+7WL3Nbug==", + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.3.tgz", + "integrity": "sha512-rOZuR0QkodZiM+UbQE5kDsJykBqWi0CL4Ec2i1nrGrUI3KO11r6Fbxskqmq3JK2NH7aW4dcccBuUujAP0ERl5w==", "requires": { - "bl": "^2.2.0", + "bl": "^2.2.1", "bson": "^1.1.4", "denque": "^1.4.1", "require_optional": "^1.0.1", @@ -5925,19 +6419,19 @@ } }, "mongoose": { - "version": "5.9.20", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.9.20.tgz", - "integrity": "sha512-vRP6Csu2obzSl3ed7kTQMrolBNgweiRJ/eBU1PSe/rJfjqWS1oqDE2D1ZPGxkVOsKXs7Gyd84GAXerj8IB2UWg==", + "version": "5.10.15", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.10.15.tgz", + "integrity": "sha512-3QUWCpMRdFCPIBZkjG/B2OkfMY2WLkR+hv335o4T2mn3ta9kx8qVvXeUDojp3OHMxBZVUyCA+hDyyP4/aKmHuA==", "requires": { "bson": "^1.1.4", "kareem": "2.3.1", - "mongodb": "3.5.9", + "mongodb": "3.6.3", "mongoose-legacy-pluralize": "1.0.2", "mpath": "0.7.0", "mquery": "3.2.2", "ms": "2.1.2", "regexp-clone": "1.0.0", - "safe-buffer": "5.1.2", + "safe-buffer": "5.2.1", "sift": "7.0.1", "sliced": "1.0.1" }, @@ -5946,6 +6440,11 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" } } }, @@ -6010,10 +6509,25 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, + "multer": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.2.tgz", + "integrity": "sha512-xY8pX7V+ybyUpbYMxtjM9KAiD9ixtg5/JkeKUTD6xilfDv0vzzOFcCp4Ljb1UU3tSOM3VTZtKo63OmzOrGi3Cg==", + "requires": { + "append-field": "^1.0.0", + "busboy": "^0.2.11", + "concat-stream": "^1.5.2", + "mkdirp": "^0.5.1", + "object-assign": "^4.1.1", + "on-finished": "^2.3.0", + "type-is": "^1.6.4", + "xtend": "^4.0.0" + } + }, "multimatch": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-4.0.0.tgz", - "integrity": "sha512-lDmx79y1z6i7RNx0ZGCPq1bzJ6ZoDDKbvh7jxr9SJcWLkShMzXrHbYVpTdnhNM5MXpDUxCQ4DgqVttVXlBgiBQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-5.0.0.tgz", + "integrity": "sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA==", "requires": { "@types/minimatch": "^3.0.3", "array-differ": "^3.0.0", @@ -6034,12 +6548,18 @@ "dev": true }, "nan": { - "version": "2.14.1", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz", - "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==", + "version": "2.14.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", + "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==", "dev": true, "optional": true }, + "nanoid": { + "version": "3.1.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.12.tgz", + "integrity": "sha512-1qstj9z5+x491jfiC4Nelk+f8XBad7LN20PmyWINJEMRSf3wcAjAWysw1qaA8z6NSKe2sjq1hRSDpBH5paCb6A==", + "dev": true + }, "nanomatch": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", @@ -6073,9 +6593,9 @@ "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" }, "neo-async": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", - "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, "next-tick": { @@ -6084,11 +6604,6 @@ "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", "dev": true }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" - }, "nise": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/nise/-/nise-1.5.3.tgz", @@ -6127,6 +6642,16 @@ "resolved": "https://registry.npmjs.org/nocache/-/nocache-2.1.0.tgz", "integrity": "sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q==" }, + "node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" + }, + "node-forge": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", + "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==" + }, "node-preload": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", @@ -6137,14 +6662,14 @@ } }, "nodemailer": { - "version": "6.4.10", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.4.10.tgz", - "integrity": "sha512-j+pS9CURhPgk6r0ENr7dji+As2xZiHSvZeVnzKniLOw1eRAyM/7flP0u65tCnsapV8JFu+t0l/5VeHsCZEeh9g==" + "version": "6.4.16", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.4.16.tgz", + "integrity": "sha512-68K0LgZ6hmZ7PVmwL78gzNdjpj5viqBdFqKrTtr9bZbJYj6BRj5W6WGkxXrEnUl3Co3CBXi3CZBUlpV/foGnOQ==" }, "nodemon": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.4.tgz", - "integrity": "sha512-Ltced+hIfTmaS28Zjv1BM552oQ3dbwPqI4+zI0SLgq+wpJhSyqgYude/aZa/3i31VCQWMfXJVxvu86abcam3uQ==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.6.tgz", + "integrity": "sha512-4I3YDSKXg6ltYpcnZeHompqac4E6JeAMpGm8tJnB9Y3T0ehasLa4139dJOcCrB93HHrUMsCrKtoAlXTqT5n4AQ==", "dev": true, "requires": { "chokidar": "^3.2.2", @@ -6155,8 +6680,8 @@ "semver": "^5.7.1", "supports-color": "^5.5.0", "touch": "^3.1.0", - "undefsafe": "^2.0.2", - "update-notifier": "^4.0.0" + "undefsafe": "^2.0.3", + "update-notifier": "^4.1.0" }, "dependencies": { "anymatch": { @@ -6185,9 +6710,9 @@ } }, "chokidar": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.0.tgz", - "integrity": "sha512-aXAaho2VJtisB/1fg1+3nlLJqGOuewTzQpd/Tz0yTg2R0e4IGtshYvtjowyEumcBv2z+y4+kc75Mz7j5xJskcQ==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz", + "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==", "dev": true, "requires": { "anymatch": "~3.1.1", @@ -6197,7 +6722,7 @@ "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", - "readdirp": "~3.4.0" + "readdirp": "~3.5.0" } }, "debug": { @@ -6255,21 +6780,21 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, "readdirp": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz", - "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", "dev": true, "requires": { "picomatch": "^2.2.1" } }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -6300,16 +6825,21 @@ "resolve": "^1.10.0", "semver": "2 || 3 || 4 || 5", "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } } }, "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true }, "normalize-url": { "version": "4.5.0", @@ -6375,147 +6905,27 @@ "yargs": "^15.0.2" }, "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", "dev": true, "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" } }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.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 - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "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" - } - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "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 - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, "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 }, - "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", - "dev": true - }, "yargs": { - "version": "15.3.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz", - "integrity": "sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==", + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", "dev": true, "requires": { "cliui": "^6.0.0", @@ -6528,26 +6938,11 @@ "string-width": "^4.2.0", "which-module": "^2.0.0", "y18n": "^4.0.0", - "yargs-parser": "^18.1.1" - } - }, - "yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" + "yargs-parser": "^18.1.2" } } } }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" - }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -6580,12 +6975,6 @@ "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.0.3.tgz", "integrity": "sha512-JPKn0GMu+Fa3zt3Bmr66JhokJU5BaNBIh4ZeTlaCBzrBsOeXzwcKKAK1tbLiPKgvwmPXsDvvLHoWh5Bm7ofIYg==" }, - "object-inspect": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", - "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", - "dev": true - }, "object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", @@ -6602,15 +6991,15 @@ } }, "object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", "dev": true, "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" } }, "object.defaults": { @@ -6671,7 +7060,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, "requires": { "wrappy": "1" } @@ -6684,6 +7072,15 @@ "fn.name": "1.x.x" } }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "optional": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, "open": { "version": "6.4.0", "resolved": "https://registry.npmjs.org/open/-/open-6.4.0.tgz", @@ -6752,7 +7149,6 @@ "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" } @@ -6761,7 +7157,6 @@ "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" } @@ -6778,8 +7173,7 @@ "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 + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" }, "package-hash": { "version": "4.0.0", @@ -6803,14 +7197,6 @@ "registry-auth-token": "^4.0.0", "registry-url": "^5.0.0", "semver": "^6.2.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } } }, "parse-filepath": { @@ -6845,6 +7231,14 @@ "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", "dev": true }, + "parse5": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-3.0.3.tgz", + "integrity": "sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA==", + "requires": { + "@types/node": "*" + } + }, "parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -6863,13 +7257,9 @@ "dev": true }, "path-exists": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", - "dev": true, - "requires": { - "pinkie-promise": "^2.0.0" - } + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" }, "path-is-absolute": { "version": "1.0.1", @@ -6878,9 +7268,10 @@ "dev": true }, "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true }, "path-parse": { "version": "1.0.6", @@ -6932,11 +7323,6 @@ "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", "dev": true }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" - }, "picomatch": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", @@ -6970,24 +7356,6 @@ "dev": true, "requires": { "find-up": "^4.0.0" - }, - "dependencies": { - "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" - } - }, - "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 - } } }, "plugin-error": { @@ -7000,6 +7368,17 @@ "arr-diff": "^4.0.0", "arr-union": "^3.1.0", "extend-shallow": "^3.0.2" + }, + "dependencies": { + "ansi-colors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", + "dev": true, + "requires": { + "ansi-wrap": "^0.1.0" + } + } } }, "posix-character-classes": { @@ -7037,11 +7416,11 @@ }, "dependencies": { "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, "ms": { @@ -7084,17 +7463,33 @@ "asap": "~2.0.3" } }, - "promise.allsettled": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/promise.allsettled/-/promise.allsettled-1.0.2.tgz", - "integrity": "sha512-UpcYW5S1RaNKT6pd+s9jp9K9rlQge1UXKskec0j6Mmuq7UJCvlS2J2/s/yuPN8ehftf9HXMxWlKiPbGGUzpoRg==", - "dev": true, + "protobufjs": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.10.2.tgz", + "integrity": "sha512-27yj+04uF6ya9l+qfpH187aqEzfCF4+Uit0I9ZBQVqK09hk/SQzKa2MUqUpXaVa7LOFRg1TSSr3lVxGOk6c0SQ==", + "optional": true, "requires": { - "array.prototype.map": "^1.0.1", - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1", - "function-bind": "^1.1.1", - "iterate-value": "^1.0.0" + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/long": "^4.0.1", + "@types/node": "^13.7.0", + "long": "^4.0.0" + }, + "dependencies": { + "@types/node": { + "version": "13.13.30", + "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.30.tgz", + "integrity": "sha512-HmqFpNzp3TSELxU/bUuRK+xzarVOAsR00hzcvM0TXrMlt/+wcSLa5q6YhTb6/cA6wqDCZLDcfd8fSL95x5h7AA==", + "optional": true + } } }, "proxy-addr": { @@ -7106,11 +7501,6 @@ "ipaddr.js": "1.9.1" } }, - "psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" - }, "pstree.remy": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", @@ -7232,24 +7622,23 @@ "integrity": "sha512-GMu3M5nUL3fju4/egXwZO0XLi6fW/K3T3VTgFQ14GxNi8btlxgT5qZL//JwZFm/2Fa64J/PNS8AZeys3wiMkVA==" }, "pump": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", - "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", - "dev": true, + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "requires": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "pumpify": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", - "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", - "dev": true, + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-2.0.1.tgz", + "integrity": "sha512-m7KOje7jZxrmutanlkS1daj1dS6z6BgslzOXmcSEpIlCxM3VJH7lG5QLeck/6hgF6F4crFf01UtQmNsJfweTAw==", + "optional": true, "requires": { - "duplexify": "^3.6.0", + "duplexify": "^4.1.1", "inherits": "^2.0.3", - "pump": "^2.0.0" + "pump": "^3.0.0" } }, "punycode": { @@ -7258,12 +7647,20 @@ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" }, "pupa": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.0.1.tgz", - "integrity": "sha512-hEJH0s8PXLY/cdXh66tNEQGndDrIKNqNC5xmrysZy3i5C3oEoLna7YAOad+7u125+zH1HNXUmGEkrhb3c2VriA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", + "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", "dev": true, "requires": { "escape-goat": "^2.0.0" + }, + "dependencies": { + "escape-goat": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", + "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==", + "dev": true + } } }, "qs": { @@ -7272,14 +7669,23 @@ "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" }, "query-strings-parser": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/query-strings-parser/-/query-strings-parser-2.1.5.tgz", - "integrity": "sha512-taUgDFp4CzL5I8F9fsNuyAyRm25Ao6M6Cjy/7wf87rkVaHvTf1noYbNQP13iNlfyXG1id0t80+8ZYsu0/cfWEA==" + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/query-strings-parser/-/query-strings-parser-2.1.7.tgz", + "integrity": "sha512-XThlYdxdn7dnXGVuRJRqplJkf+9ZfoUXJsJx1xsi+Q8Z7mhTi+1hzjNXZ13rA6Rl+0rKBPlrTfn5YQjUII/iCQ==" }, "querystringify": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.1.tgz", - "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==" + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } }, "range-parser": { "version": "1.2.1", @@ -7328,6 +7734,27 @@ "requires": { "find-up": "^1.0.0", "read-pkg": "^1.0.0" + }, + "dependencies": { + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "^2.0.0" + } + } } }, "readable-stream": { @@ -7404,9 +7831,9 @@ "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" }, "regenerator-runtime": { - "version": "0.13.5", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz", - "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==" + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" }, "regex-not": { "version": "1.0.2", @@ -7424,9 +7851,9 @@ "integrity": "sha512-TuAasHQNamyyJ2hb97IuBEif4qBHGjPHBS64sZwytpLEqtBQ1gPJTnOaQ6qmpET16cK14kkjbazl6+p0RRv0yw==" }, "registry-auth-token": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.1.1.tgz", - "integrity": "sha512-9bKS7nTl9+/A1s7tnPeGrUpRcVY+LUh7bfFgzpndALdPfXQBfQV77rQVtqgUV3ti4vc/Ik81Ex8UJDWDQ12zQA==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz", + "integrity": "sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==", "dev": true, "requires": { "rc": "^1.2.8" @@ -7505,51 +7932,15 @@ "remove-trailing-separator": "^1.1.0" } }, - "request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "dependencies": { - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" - } - } - }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" }, "require-main-filename": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", - "dev": true + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" }, "require_optional": { "version": "1.0.1", @@ -7558,6 +7949,13 @@ "requires": { "resolve-from": "^2.0.0", "semver": "^5.1.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } } }, "requires-port": { @@ -7566,10 +7964,11 @@ "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" }, "resolve": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", - "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", "requires": { + "is-core-module": "^2.1.0", "path-parse": "^1.0.6" } }, @@ -7618,6 +8017,32 @@ "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", "dev": true }, + "retry-request": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-4.1.3.tgz", + "integrity": "sha512-QnRZUpuPNgX0+D1xVxul6DbJ9slvo4Rm6iV/dn63e048MvGbUZiKySVt6Tenp04JqmchxjiLltGerOJys7kJYQ==", + "optional": true, + "requires": { + "debug": "^4.1.1" + }, + "dependencies": { + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "optional": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "optional": true + } + } + }, "right-align": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", @@ -7664,9 +8089,9 @@ } }, "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" }, "semver-diff": { "version": "3.1.1", @@ -7675,14 +8100,6 @@ "dev": true, "requires": { "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } } }, "semver-greatest-satisfied-range": { @@ -7714,6 +8131,11 @@ "statuses": "~1.5.0" }, "dependencies": { + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, "ms": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", @@ -7722,10 +8144,13 @@ } }, "serialize-javascript": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-3.0.0.tgz", - "integrity": "sha512-skZcHYw2vEX4bw90nAr2iTTsz6x2SrHEnfxgKYmZlvJYBEZrvbKtobJWlQ20zczKb3bsHHXXTYt48zBA7ni9cw==", - "dev": true + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", + "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } }, "serve-static": { "version": "1.14.1", @@ -7741,8 +8166,7 @@ "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, "set-value": { "version": "2.0.1", @@ -7773,17 +8197,19 @@ "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" }, "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, "requires": { - "shebang-regex": "^1.0.0" + "shebang-regex": "^3.0.0" } }, "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true }, "shelljs": { "version": "0.8.4", @@ -7804,8 +8230,7 @@ "signal-exit": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", - "dev": true + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" }, "simple-swizzle": { "version": "0.2.2", @@ -7854,6 +8279,12 @@ "resolved": "https://registry.npmjs.org/slick/-/slick-1.12.2.tgz", "integrity": "sha1-vQSN23TefRymkV+qSldXCzVQwtc=" }, + "snakeize": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/snakeize/-/snakeize-0.1.0.tgz", + "integrity": "sha1-EMCI2LWOsHazIpu1oE4jLOEmQi0=", + "optional": true + }, "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", @@ -8063,9 +8494,9 @@ } }, "spdx-license-ids": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", - "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.6.tgz", + "integrity": "sha512-+orQK83kyMva3WyPf59k1+Y525csj5JejicWut55zeTWANuN17qSiSLUXWtzHeNWORSvT7GLDJ/E/XiIWoXBTw==", "dev": true }, "split-string": { @@ -8082,22 +8513,6 @@ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==" }, - "sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, "stack-trace": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", @@ -8129,6 +8544,15 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" }, + "stream-events": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", + "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==", + "optional": true, + "requires": { + "stubs": "^3.0.0" + } + }, "stream-exhaust": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/stream-exhaust/-/stream-exhaust-1.0.2.tgz", @@ -8138,38 +8562,21 @@ "stream-shift": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", - "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", - "dev": true + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==" }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "string.prototype.trimend": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", - "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - } + "streamsearch": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", + "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=" }, - "string.prototype.trimstart": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", - "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", - "dev": true, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" } }, "string_decoder": { @@ -8178,12 +8585,11 @@ "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" }, "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "^5.0.0" } }, "strip-bom": { @@ -8201,85 +8607,101 @@ "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true }, + "stubs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", + "integrity": "sha1-6NK6H6nJBXAwPAMLaQD31fiavls=", + "optional": true + }, "superagent": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.3.tgz", - "integrity": "sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-6.1.0.tgz", + "integrity": "sha512-OUDHEssirmplo3F+1HWKUrUjvnQuA+nZI6i/JJBdXb5eq9IyEQwPyPpqND+SSsxf6TygpBEkUjISVRN4/VOpeg==", "dev": true, "requires": { - "component-emitter": "^1.2.0", - "cookiejar": "^2.1.0", - "debug": "^3.1.0", - "extend": "^3.0.0", - "form-data": "^2.3.1", - "formidable": "^1.2.0", - "methods": "^1.1.1", - "mime": "^1.4.1", - "qs": "^6.5.1", - "readable-stream": "^2.3.5" + "component-emitter": "^1.3.0", + "cookiejar": "^2.1.2", + "debug": "^4.1.1", + "fast-safe-stringify": "^2.0.7", + "form-data": "^3.0.0", + "formidable": "^1.2.2", + "methods": "^1.1.2", + "mime": "^2.4.6", + "qs": "^6.9.4", + "readable-stream": "^3.6.0", + "semver": "^7.3.2" }, "dependencies": { "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, + "qs": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz", + "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==", + "dev": true + }, "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" } }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + }, + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "dev": true + }, "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dev": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "~5.2.0" } } } }, "supertest": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-4.0.2.tgz", - "integrity": "sha512-1BAbvrOZsGA3YTCWqbmh14L0YEq0EGICX/nBnfkfVJn7SrxQV1I3pMYjSzG9y/7ZU2V9dWqyqk2POwxlb09duQ==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-6.0.1.tgz", + "integrity": "sha512-8yDNdm+bbAN/jeDdXsRipbq9qMpVF7wRsbwLgsANHqdjPsCoecmlTuqEcLQMGpmojFBhxayZ0ckXmLXYq7e+0g==", "dev": true, "requires": { - "methods": "^1.1.2", - "superagent": "^3.8.3" + "methods": "1.1.2", + "superagent": "6.1.0" } }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, "requires": { "has-flag": "^3.0.0" } @@ -8295,9 +8717,9 @@ } }, "swagger-ui-dist": { - "version": "3.28.0", - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-3.28.0.tgz", - "integrity": "sha512-aPkfTzPv9djSiZI1NUkWr5HynCUsH+jaJ0WSx+/t19wq7MMGg9clHm9nGoIpAtqml1G51ofI+I75Ym72pukzFg==" + "version": "3.36.2", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-3.36.2.tgz", + "integrity": "sha512-jbxorhRC/FKk8yMx5zEbg1A1sXc/vsW2vrDTJ3clmaMr9F12zsy161kwnxjVt/vVkMglDOz+BC8ZMY01toxHwA==" }, "swagger-ui-express": { "version": "4.1.4", @@ -8307,10 +8729,31 @@ "swagger-ui-dist": "^3.18.1" } }, + "teeny-request": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-7.0.1.tgz", + "integrity": "sha512-sasJmQ37klOlplL4Ia/786M5YlOcoLGQyq2TE4WHSRupbAuDaQW0PfVxV4MtdBtRJ4ngzS+1qim8zP6Zp35qCw==", + "optional": true, + "requires": { + "http-proxy-agent": "^4.0.0", + "https-proxy-agent": "^5.0.0", + "node-fetch": "^2.6.1", + "stream-events": "^1.0.5", + "uuid": "^8.0.0" + }, + "dependencies": { + "uuid": { + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.1.tgz", + "integrity": "sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg==", + "optional": true + } + } + }, "term-size": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.0.tgz", - "integrity": "sha512-a6sumDlzyHVJWb8+YofY4TW112G6p2FCPEAFk+59gIYHv3XHRhm9ltVQ9kli4hNWeQBwSpe8cRN25x0ROunMOw==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz", + "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==", "dev": true }, "test-exclude": { @@ -8399,9 +8842,9 @@ "integrity": "sha512-m+apkYlfiQTKLW+sI4vqUkwMEzfgEUEYSqljx1voUE3Wz/z1ZsxyzSxvH2X8uKVrOp7QkByWt0rA6+gvhCKy6g==" }, "tlds": { - "version": "1.207.0", - "resolved": "https://registry.npmjs.org/tlds/-/tlds-1.207.0.tgz", - "integrity": "sha512-k7d7Q1LqjtAvhtEOs3yN14EabsNO8ZCoY6RESSJDB9lst3bTx3as/m1UuAeCKzYxiyhR1qq72ZPhpSf+qlqiwg==" + "version": "1.212.0", + "resolved": "https://registry.npmjs.org/tlds/-/tlds-1.212.0.tgz", + "integrity": "sha512-03rYYO1rGhOYpdYB+wlLY2d0xza6hdN/S67ol2ZpaH+CtFedMVAVhj8ft0rwxEkr90zatou8opBv7Xp6X4cK6g==" }, "to-absolute-glob": { "version": "2.0.2", @@ -8483,24 +8926,15 @@ "nopt": "~1.0.10" } }, - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - }, "triple-beam": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" }, "ts-node": { - "version": "8.10.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.10.2.tgz", - "integrity": "sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.0.0.tgz", + "integrity": "sha512-/TqB4SnererCDR/vb4S/QvSZvzQMJN8daAslg7MeaiHvD8rDZsSfXmNeNumyZZzMned72Xoq/isQljYSt8Ynfg==", "dev": true, "requires": { "arg": "^4.1.0", @@ -8511,15 +8945,14 @@ } }, "tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", - "dev": true + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, "tslint": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.2.tgz", - "integrity": "sha512-UyNrLdK3E0fQG/xWNqAFAC5ugtFyPO4JJR1KyyfQAyzR8W0fTRrC91A8Wej4BntFzcvETdCSDa/4PnNYJQLYiA==", + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.3.tgz", + "integrity": "sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", @@ -8533,8 +8966,48 @@ "mkdirp": "^0.5.3", "resolve": "^1.3.2", "semver": "^5.3.0", - "tslib": "^1.10.0", + "tslib": "^1.13.0", "tsutils": "^2.29.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } } }, "tsutils": { @@ -8546,19 +9019,6 @@ "tslib": "^1.8.1" } }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" - }, "type": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", @@ -8589,49 +9049,53 @@ "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", - "dev": true + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" }, "typedarray-to-buffer": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dev": true, "requires": { "is-typedarray": "^1.0.0" } }, "typedoc": { - "version": "0.17.8", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.17.8.tgz", - "integrity": "sha512-/OyrHCJ8jtzu+QZ+771YaxQ9s4g5Z3XsQE3Ma7q+BL392xxBn4UMvvCdVnqKC2T/dz03/VXSLVKOP3lHmDdc/w==", + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.19.2.tgz", + "integrity": "sha512-oDEg1BLEzi1qvgdQXc658EYgJ5qJLVSeZ0hQ57Eq4JXy6Vj2VX4RVo18qYxRWz75ifAaYuYNBUCnbhjd37TfOg==", "dev": true, "requires": { - "fs-extra": "^8.1.0", + "fs-extra": "^9.0.1", "handlebars": "^4.7.6", - "highlight.js": "^10.0.0", - "lodash": "^4.17.15", - "lunr": "^2.3.8", - "marked": "1.0.0", + "highlight.js": "^10.2.0", + "lodash": "^4.17.20", + "lunr": "^2.3.9", + "marked": "^1.1.1", "minimatch": "^3.0.0", "progress": "^2.0.3", + "semver": "^7.3.2", "shelljs": "^0.8.4", - "typedoc-default-themes": "^0.10.2" + "typedoc-default-themes": "^0.11.4" + }, + "dependencies": { + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "dev": true + } } }, "typedoc-default-themes": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/typedoc-default-themes/-/typedoc-default-themes-0.10.2.tgz", - "integrity": "sha512-zo09yRj+xwLFE3hyhJeVHWRSPuKEIAsFK5r2u47KL/HBKqpwdUSanoaz5L34IKiSATFrjG5ywmIu98hPVMfxZg==", - "dev": true, - "requires": { - "lunr": "^2.3.8" - } + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/typedoc-default-themes/-/typedoc-default-themes-0.11.4.tgz", + "integrity": "sha512-Y4Lf+qIb9NTydrexlazAM46SSLrmrQRqWiD52593g53SsmUFioAsMWt8m834J6qsp+7wHRjxCXSZeiiW5cMUdw==", + "dev": true }, "typescript": { - "version": "3.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.5.tgz", - "integrity": "sha512-hSAifV3k+i6lEoCJ2k6R2Z/rp/H3+8sdmcn5NrS3/3kE7+RyZXm9aqvxWqjEXHAd8b0pShatpcdMTvEdvAJltQ==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.5.tgz", + "integrity": "sha512-ywmr/VrTVCmNTJ6iV2LwIrfG1P+lv6luD8sUJs+2eI9NLGigaN+nUQc13iHqisq7bra9lnmUSYqbJvegraBOPQ==", "dev": true }, "uc.micro": { @@ -8688,9 +9152,9 @@ "integrity": "sha1-ByZx9I1oc1w0Ij/P72PmnlJ2zCs=" }, "undertaker": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/undertaker/-/undertaker-1.2.1.tgz", - "integrity": "sha512-71WxIzDkgYk9ZS+spIB8iZXchFhAdEo2YU8xYqBYJ39DIUIqziK78ftm26eecoIY49X0J2MLhG4hr18Yp6/CMA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/undertaker/-/undertaker-1.3.0.tgz", + "integrity": "sha512-/RXwi5m/Mu3H6IHQGww3GNt1PNXlbeCuclF2QYR14L/2CHPz3DFZkvB5hZ0N/QUkiXWCACML2jXViIQEQc2MLg==", "dev": true, "requires": { "arr-flatten": "^1.0.1", @@ -8698,6 +9162,7 @@ "bach": "^1.0.0", "collection-map": "^1.0.0", "es6-weak-map": "^2.0.1", + "fast-levenshtein": "^1.0.0", "last-run": "^1.1.0", "object.defaults": "^1.0.0", "object.reduce": "^1.0.0", @@ -8736,15 +9201,14 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", - "dev": true, "requires": { "crypto-random-string": "^2.0.0" } }, "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", + "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==", "dev": true }, "unpipe": { @@ -8805,9 +9269,9 @@ "dev": true }, "update-notifier": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-4.1.0.tgz", - "integrity": "sha512-w3doE1qtI0/ZmgeoDoARmI5fjDoT93IfKgEGqm26dGUOh8oNpaSTsGNdYRN/SjOuo10jcJGwkEL3mroKzktkew==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-4.1.3.tgz", + "integrity": "sha512-Yld6Z0RyCYGB6ckIjffGOSOmHXj1gMeE7aROz4MG+XMkmixBX4jUngrGXNYz7wPKBmtoD4MnBa2Anu7RSKht/A==", "dev": true, "requires": { "boxen": "^4.2.0", @@ -8823,66 +9287,6 @@ "pupa": "^2.0.1", "semver-diff": "^3.1.1", "xdg-basedir": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "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.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", - "requires": { - "punycode": "^2.1.0" } }, "urix": { @@ -8940,9 +9344,9 @@ } }, "valid-data-url": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/valid-data-url/-/valid-data-url-2.0.0.tgz", - "integrity": "sha512-dyCZnv3aCey7yfTgIqdZanKl7xWAEEKCbgmR7SKqyK6QT/Z07ROactrgD1eA37C69ODRj7rNOjzKWVPh0EUjBA==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/valid-data-url/-/valid-data-url-3.0.1.tgz", + "integrity": "sha512-jOWVmzVceKlVVdwjNSenT4PbGghU0SBIizAev8ofZVgivk/TVHXSbNL8LP6M3spZvkR9/QolkyJavGSX5Cs0UA==" }, "validate-npm-package-license": { "version": "3.0.4", @@ -8965,20 +9369,10 @@ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, "vinyl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.0.tgz", - "integrity": "sha512-MBH+yP0kC/GQ5GwBqrTPTzEfiiLjta7hTtvQtbxBgTeSXsmKQRQecjibMbxIXzVT3Y9KJK+drOz1/k+vsu8Nkg==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.1.tgz", + "integrity": "sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw==", "dev": true, "requires": { "clone": "^2.1.1", @@ -9014,12 +9408,45 @@ "vinyl-sourcemap": "^1.1.0" }, "dependencies": { + "duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "dev": true, + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true }, + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "dev": true, + "requires": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + } + }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", @@ -9059,6 +9486,17 @@ "now-and-later": "^2.0.0", "remove-bom-buffer": "^3.0.0", "vinyl": "^2.0.0" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } } }, "void-elements": { @@ -9067,48 +9505,55 @@ "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=" }, "web-resource-inliner": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/web-resource-inliner/-/web-resource-inliner-4.3.4.tgz", - "integrity": "sha512-agVAgRhOOi4GVlvKK34oM23tDgH8390HfLnZY2HZl8OFBwKNvUJkH7t89AT2iluQP8w9VHAAKX6Z8EN7/9tqKA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/web-resource-inliner/-/web-resource-inliner-5.0.0.tgz", + "integrity": "sha512-AIihwH+ZmdHfkJm7BjSXiEClVt4zUFqX4YlFAzjL13wLtDuUneSaFvDBTbdYRecs35SiU7iNKbMnN+++wVfb6A==", "requires": { - "async": "^3.1.0", - "chalk": "^2.4.2", - "datauri": "^2.0.0", + "ansi-colors": "^4.1.1", + "escape-goat": "^3.0.0", "htmlparser2": "^4.0.0", - "lodash.unescape": "^4.0.1", - "request": "^2.88.0", - "safer-buffer": "^2.1.2", - "valid-data-url": "^2.0.0", - "xtend": "^4.0.2" + "mime": "^2.4.6", + "node-fetch": "^2.6.0", + "valid-data-url": "^3.0.0" }, "dependencies": { + "dom-serializer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.1.0.tgz", + "integrity": "sha512-ox7bvGXt2n+uLWtCRLybYx60IrOlWL/aCebWJk1T0d4m3y2tzf4U3ij9wBMUb6YJZpz06HCCYuyCDveE2xXmzQ==", + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^3.0.0", + "entities": "^2.0.0" + } + }, "domelementtype": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz", - "integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==" + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.2.tgz", + "integrity": "sha512-wFwTwCVebUrMgGeAwRL/NhZtHAUyT9n9yg4IMDwf10+6iCMxSkVq9MGCVEH+QZWo1nNidy8kNvwmv4zWHDTqvA==" }, "domhandler": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-3.0.0.tgz", - "integrity": "sha512-eKLdI5v9m67kbXQbJSNn1zjh0SDzvzWVWtX+qEI3eMjZw8daH9k8rlj1FZY9memPwjiskQFbe7vHVVJIAqoEhw==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-3.3.0.tgz", + "integrity": "sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA==", "requires": { "domelementtype": "^2.0.1" } }, "domutils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.1.0.tgz", - "integrity": "sha512-CD9M0Dm1iaHfQ1R/TI+z3/JWp/pgub0j4jIQKH89ARR4ATAV2nbaOQS5XxU9maJP5jHaPdDDQSEHuE2UmpUTKg==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.4.2.tgz", + "integrity": "sha512-NKbgaM8ZJOecTZsIzW5gSuplsX2IWW2mIK7xVr8hTQF2v1CJWTmLZ1HOCh5sH+IzVPAGE5IucooOkvwBRAdowA==", "requires": { - "dom-serializer": "^0.2.1", + "dom-serializer": "^1.0.1", "domelementtype": "^2.0.1", - "domhandler": "^3.0.0" + "domhandler": "^3.3.0" } }, "entities": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz", - "integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", + "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==" }, "htmlparser2": { "version": "4.1.0", @@ -9123,19 +9568,34 @@ } } }, + "websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "requires": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + } + }, + "websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==" + }, "which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, "requires": { "isexe": "^2.0.0" } }, "which-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", - "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", - "dev": true + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" }, "wide-align": { "version": "1.1.3", @@ -9144,57 +9604,50 @@ "dev": true, "requires": { "string-width": "^1.0.2 || 2" - } - }, - "widest-line": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", - "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", - "dev": true, - "requires": { - "string-width": "^4.0.0" }, "dependencies": { "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", "dev": true }, "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" } }, "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "^5.0.0" + "ansi-regex": "^3.0.0" } } } }, + "widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "dev": true, + "requires": { + "string-width": "^4.0.0" + } + }, "window-size": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", @@ -9305,32 +9758,30 @@ "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=" }, "workerpool": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.0.tgz", - "integrity": "sha512-fU2OcNA/GVAJLLyKUoHkAgIhKb0JoCpSjLC/G2vYKxUjVmQwGbRVeoPJ1a8U4pnVofz4AQV5Y/NEw8oKqxEBtA==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.2.tgz", + "integrity": "sha512-DSNyvOpFKrNusaaUwk+ej6cBj1bmhLcBfj80elGk+ZIo5JSkq+unB1dLKEOcNfJDZgjGICfhQ0Q5TbP0PvF4+Q==", "dev": true }, "wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", - "dev": true, + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" } }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "write-file-atomic": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "dev": true, "requires": { "imurmurhash": "^0.1.4", "is-typedarray": "^1.0.0", @@ -9346,8 +9797,7 @@ "xdg-basedir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", - "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", - "dev": true + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==" }, "xtend": { "version": "4.0.2", @@ -9355,10 +9805,15 @@ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" }, "y18n": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", - "dev": true + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "optional": true }, "yargs": { "version": "3.10.0", @@ -9372,179 +9827,44 @@ } }, "yargs-parser": { - "version": "5.0.0-security.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0-security.0.tgz", - "integrity": "sha512-T69y4Ps64LNesYxeYGYPvfoMTt/7y1XtfpIslUeK4um+9Hu7hlGoRtaDLvdXb7+/tfq4opVa2HRY5xGip022rQ==", - "dev": true, + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", "requires": { - "camelcase": "^3.0.0", - "object.assign": "^4.1.0" + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" }, "dependencies": { "camelcase": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", - "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", - "dev": true + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" } } }, "yargs-unparser": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz", - "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==", + "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": { - "flat": "^4.1.0", - "lodash": "^4.17.15", - "yargs": "^13.3.0" + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" }, "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "dev": true, - "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - } - }, - "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, - "requires": { - "locate-path": "^3.0.0" - } - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "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, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "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, - "requires": { - "p-limit": "^2.0.0" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", "dev": true }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - } - }, - "y18n": { + "decamelize": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", "dev": true - }, - "yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", - "dev": true, - "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" - } - }, - "yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } } } }, diff --git a/package.json b/package.json index adaae8f..d4fbe70 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "notification", - "version": "1.2.1", + "version": "1.3.0", "description": "Microservice for sending messages: email, sms or push. In this version only the sending of email will be available.", "main": "dist/server.js", "scripts": { @@ -56,15 +56,17 @@ "amqp-client-node": "^1.0.10", "body-parser": "^1.19.0", "dotenv": "^8.2.0", - "email-templates": "^7.0.5", + "email-templates": "^7.1.2", "express": "^4.17.1", + "firebase-admin": "^9.4.1", "helmet": "^3.23.3", "inversify": "^5.0.1", "inversify-express-utils": "^6.3.2", - "mongoose": "^5.9.20", + "mongoose": "^5.10.15", "morgan": "^1.10.0", - "nodemailer": "^6.4.10", - "query-strings-parser": "^2.1.5", + "multer": "^1.4.2", + "nodemailer": "^6.4.16", + "query-strings-parser": "^2.1.7", "reflect-metadata": "^0.1.13", "swagger-ui-express": "^4.1.4", "winston": "^3.3.3", @@ -72,26 +74,26 @@ }, "devDependencies": { "@types/body-parser": "^1.19.0", - "@types/chai": "^4.2.11", - "@types/express": "^4.17.6", - "@types/helmet": "^0.0.47", - "@types/mocha": "^7.0.2", - "@types/mongoose": "^5.7.28", - "@types/morgan": "^1.9.1", + "@types/chai": "^4.2.14", + "@types/express": "^4.17.9", + "@types/helmet": "^4.0.0", + "@types/mocha": "^8.0.4", + "@types/mongoose": "^5.10.0", + "@types/morgan": "^1.9.2", "@types/swagger-ui-express": "^4.1.2", "chai": "^4.2.0", "gulp": "^4.0.2", "gulp-nodemon": "^2.5.0", "gulp-tslint": "^8.1.4", "gulp-typescript": "^5.0.1", - "mocha": "^8.0.1", + "mocha": "^8.2.1", "nyc": "^15.1.0", "sinon": "^7.5.0", "sinon-mongoose": "^2.3.0", - "supertest": "^4.0.2", - "ts-node": "^8.10.2", - "tslint": "^6.1.2", - "typedoc": "^0.17.8", - "typescript": "^3.9.5" + "supertest": "^6.0.1", + "ts-node": "^9.0.0", + "tslint": "^6.1.3", + "typedoc": "^0.19.2", + "typescript": "^4.0.5" } } diff --git a/src/app.ts b/src/app.ts index b42a61b..7cda1c7 100644 --- a/src/app.ts +++ b/src/app.ts @@ -100,10 +100,6 @@ export class App { stream: { write: (str: string) => this._logger.info(str) } } )) - - // app.use((err, req, res, next) => { - // next(err) - // }) }) this.express.use(inversifyExpress.build()) } diff --git a/src/application/domain/exception/firebase.client.exception.ts b/src/application/domain/exception/firebase.client.exception.ts new file mode 100644 index 0000000..b4d353d --- /dev/null +++ b/src/application/domain/exception/firebase.client.exception.ts @@ -0,0 +1,22 @@ +import { Exception } from './exception' + +/** + * Conflict data exception. + * + * @extends {Exception} + */ +export class FirebaseClientException extends Exception { + public code: number + + /** + * Creates an instance of ConflictException. + * + * @param code + * @param message Short message + * @param description Detailed message + */ + constructor(code: number, message: string, description?: string) { + super(message, description) + this.code = code + } +} diff --git a/src/application/domain/model/email.template.ts b/src/application/domain/model/email.template.ts new file mode 100644 index 0000000..f64fc93 --- /dev/null +++ b/src/application/domain/model/email.template.ts @@ -0,0 +1,77 @@ +import { IJSONSerializable } from '../utils/json.serializable.interface' +import { IJSONDeserializable } from '../utils/json.deserializable.interface' +import { JsonUtils } from '../utils/json.utils' +import { File } from './file' + +export class EmailTemplate implements IJSONSerializable, IJSONDeserializable { + private _type?: string + private _html?: File + private _subject?: File + private _text?: File + + get type(): string | undefined { + return this._type + } + + set type(value: string | undefined) { + this._type = value + } + + get html(): File | undefined { + return this._html + } + + set html(value: File | undefined) { + this._html = value + } + + get subject(): File | undefined { + return this._subject + } + + set subject(value: File | undefined) { + this._subject = value + } + + get text(): File | undefined { + return this._text + } + + set text(value: File | undefined) { + this._text = value + } + + public fromJSON(json: any): EmailTemplate { + if (!json) return this + if (JsonUtils.isJsonString(json)) { + json = JSON.parse(json) + } + if (json.type !== undefined) this.type = json.type + if (json.html !== undefined) this.html = new File().fromJSON(json.html) + if (json.subject !== undefined) this.subject = new File().fromJSON(json.subject) + if (json.text !== undefined) this.text = new File().fromJSON(json.text) + return this + } + + public toJSON(): any { + return { + type: this.type, + html: this.html?.toJSON(), + subject: this.subject?.toJSON(), + text: this.text?.toJSON() + } + } +} + +export enum EmailTemplateTypes { + WELCOME = 'welcome', + RESET_PASSWORD = 'reset-password', + UPDATED_PASSWORD = 'updated-password', + PILOT_STUDY_DATA = 'pilot-study-data' +} + +export enum EmailTemplateResources { + HTML = 'html', + SUBJECT = 'subject', + TEXT = 'text' +} diff --git a/src/application/domain/model/email.ts b/src/application/domain/model/email.ts index 3aaaf43..addb0f7 100644 --- a/src/application/domain/model/email.ts +++ b/src/application/domain/model/email.ts @@ -180,7 +180,3 @@ export class Email extends Entity implements IJSONSerializable, IJSONDeserializa return this } } - -export enum EmailTemplate { - DEFAULT = 'default' -} diff --git a/src/application/domain/model/file.ts b/src/application/domain/model/file.ts new file mode 100644 index 0000000..baee8f0 --- /dev/null +++ b/src/application/domain/model/file.ts @@ -0,0 +1,67 @@ +import { IJSONSerializable } from '../utils/json.serializable.interface' +import { IJSONDeserializable } from '../utils/json.deserializable.interface' +import { JsonUtils } from '../utils/json.utils' + +export class File implements IJSONSerializable, IJSONDeserializable { + private _filename?: string + private _buffer?: Buffer + private _mimetype?: string + private _download_link?: string + + get filename(): string | undefined { + return this._filename + } + + set filename(value: string | undefined) { + this._filename = value + } + + get buffer(): Buffer | undefined { + return this._buffer + } + + set buffer(value: Buffer | undefined) { + this._buffer = value + } + + get mimetype(): string | undefined { + return this._mimetype + } + + set mimetype(value: string | undefined) { + this._mimetype = value + } + + get download_link(): string | undefined { + return this._download_link + } + + set download_link(value: string | undefined) { + this._download_link = value + } + + public fromJSON(json: any): File { + if (!json) return this + if (JsonUtils.isJsonString(json)) { + json = JSON.parse(json) + } + if (json.filename !== undefined) this.filename = json.filename + if (json.buffer !== undefined) this.buffer = json.buffer + if (json.mimetype !== undefined) this.mimetype = json.mimetype + if (json.download_link !== undefined) this.download_link = json.download_link + return this + } + + public toJSON(): any { + return { + filename: this.filename, + buffer: this.buffer, + mimetype: this.mimetype, + download_link: this.download_link + } + } +} + +export enum FileFormatType { + OCTET_STREAM = 'application/octet-stream' +} diff --git a/src/application/domain/model/push.message.ts b/src/application/domain/model/push.message.ts new file mode 100644 index 0000000..fdfb1ad --- /dev/null +++ b/src/application/domain/model/push.message.ts @@ -0,0 +1,55 @@ +import { IJSONSerializable } from '../utils/json.serializable.interface' +import { IJSONDeserializable } from '../utils/json.deserializable.interface' +import { JsonUtils } from '../utils/json.utils' + +export class PushMessage implements IJSONSerializable, IJSONDeserializable { + private _type?: string + private _pt?: any + private _eng?: any + + get type(): string | undefined { + return this._type + } + + set type(value: string | undefined) { + this._type = value + } + + get pt(): any { + return this._pt + } + + set pt(value: any) { + this._pt = value + } + + get eng(): any { + return this._eng + } + + set eng(value: any) { + this._eng = value + } + + public fromJSON(json: any): PushMessage { + if (!json) return this + if (typeof json === 'string' && JsonUtils.isJsonString(json)) { + json = JSON.parse(json) + } + + if (json.type !== undefined) this.type = json.type + if (json.pt !== undefined) this.pt = json.pt + if (json.eng !== undefined) this.eng = json.eng + + return this + } + + public toJSON(): any { + return { + type: this.type, + pt: this.pt, + eng: this.eng + } + } +} + diff --git a/src/application/domain/model/push.token.ts b/src/application/domain/model/push.token.ts new file mode 100644 index 0000000..e8a7066 --- /dev/null +++ b/src/application/domain/model/push.token.ts @@ -0,0 +1,65 @@ +import { Entity } from './entity' +import { IJSONSerializable } from '../utils/json.serializable.interface' +import { IJSONDeserializable } from '../utils/json.deserializable.interface' +import { JsonUtils } from '../utils/json.utils' + +export class PushToken extends Entity implements IJSONSerializable, IJSONDeserializable { + private _user_id?: string + private _client_type?: string + private _token?: string + + constructor() { + super() + } + + get user_id(): string | undefined { + return this._user_id + } + + set user_id(value: string | undefined) { + this._user_id = value + } + + get client_type(): string | undefined { + return this._client_type + } + + set client_type(value: string | undefined) { + this._client_type = value + } + + get token(): string | undefined { + return this._token + } + + set token(value: string | undefined) { + this._token = value + } + + public fromJSON(json: any): PushToken { + if (!json) return this + if (typeof json === 'string' && JsonUtils.isJsonString(json)) { + json = JSON.parse(json) + } + + if (json.id !== undefined) super.id = json.id + if (json.user_id !== undefined) this.user_id = json.user_id + if (json.client_type !== undefined) this.client_type = json.client_type + if (json.token !== undefined) this.token = json.token + return this + } + + public toJSON(): any { + return { + id: super.id, + user_id: this.user_id, + client_type: this.client_type, + token: this.token + } + } +} + +export enum PushTokenClientTypes { + WEB = 'web', + MOBILE = 'mobile' +} diff --git a/src/application/domain/model/push.ts b/src/application/domain/model/push.ts new file mode 100644 index 0000000..86b26a8 --- /dev/null +++ b/src/application/domain/model/push.ts @@ -0,0 +1,90 @@ +import { Entity } from './entity' +import { IJSONSerializable } from '../utils/json.serializable.interface' +import { IJSONDeserializable } from '../utils/json.deserializable.interface' +import { PushMessage } from './push.message' +import { JsonUtils } from '../utils/json.utils' + +export class Push extends Entity implements IJSONSerializable, IJSONDeserializable { + private _type?: string + private _keep_it?: string + private _is_read?: string + private _to?: Array + private _message?: PushMessage + + constructor() { + super() + } + + get type(): string | undefined { + return this._type + } + + set type(value: string | undefined) { + this._type = value + } + + get keep_it(): string | undefined { + return this._keep_it + } + + set keep_it(value: string | undefined) { + this._keep_it = value + } + + get is_read(): string | undefined { + return this._is_read + } + + set is_read(value: string | undefined) { + this._is_read = value + } + + get to(): Array | undefined { + return this._to + } + + set to(value: Array | undefined) { + this._to = value + } + + get message(): PushMessage | undefined { + return this._message + } + + set message(value: PushMessage | undefined) { + this._message = value + } + + public fromJSON(json: any): Push { + if (!json) return this + + if (typeof json === 'string' && JsonUtils.isJsonString(json)) { + json = JSON.parse(json) + } + + if (json.id !== undefined) super.id = json.id + if (json.type !== undefined) this.type = json.type + if (json.keep_it !== undefined) this.keep_it = json.keep_it + if (json.is_read !== undefined) this.is_read = json.is_read + if (json.to !== undefined && json.to instanceof Array) this.to = json.to + if (json.message !== undefined) this.message = new PushMessage().fromJSON(json.message) + + return this + } + + public toJSON(): any { + return { + id: super.id, + type: this.type, + keep_it: this.keep_it, + is_read: this.is_read, + to: this.to?.length ? this.to : undefined, + message: this.message?.toJSON() + } + } +} + +export enum PushTypes { + DIRECT = 'direct', + TOPIC = 'topic' +} diff --git a/src/application/domain/model/User.ts b/src/application/domain/model/user.ts similarity index 100% rename from src/application/domain/model/User.ts rename to src/application/domain/model/user.ts diff --git a/src/application/domain/utils/choice.types.ts b/src/application/domain/utils/choice.types.ts new file mode 100644 index 0000000..2f0972e --- /dev/null +++ b/src/application/domain/utils/choice.types.ts @@ -0,0 +1,4 @@ +export enum ChoiceTypes { + YES = 'yes', + NO = 'no' +} diff --git a/src/application/domain/validator/email.template.validator.ts b/src/application/domain/validator/email.template.validator.ts new file mode 100644 index 0000000..36e149e --- /dev/null +++ b/src/application/domain/validator/email.template.validator.ts @@ -0,0 +1,32 @@ +import { ValidationException } from '../exception/validation.exception' +import { EmailTemplate, EmailTemplateTypes } from '../model/email.template' +import { EnumValuesValidator } from './enum.values.validator' + +export class EmailTemplateValidator { + public static validate(item: EmailTemplate): void | ValidationException { + const fields: Array = [] + const validate_item = file => { + const file_extension: string = file.filename?.split('.')[1] + if (file_extension !== 'pug') { + throw new ValidationException( + `Invalid extension from file: ${file.filename}`, + 'Only .pug files are allowed.') + } + } + + if (!item.type) fields.push('type') + else EnumValuesValidator.validate(item.type, 'type', EmailTemplateTypes) + if (!item.html || !item.html.filename || !item.html.buffer || !item.html.mimetype) fields.push('html') + else validate_item(item.html) + if (!item.subject || !item.subject.filename || !item.subject.buffer || !item.subject.mimetype) fields.push('subject') + else validate_item(item.subject) + if (!item.text || !item.text.filename || !item.text.buffer || !item.text.mimetype) fields.push('text') + else validate_item(item.text) + + if (fields.length > 0) { + throw new ValidationException( + 'Required fields were not provided...', + `EmailTemplate: ${fields.join(', ')} required.`) + } + } +} diff --git a/src/application/domain/validator/enum.values.validator.ts b/src/application/domain/validator/enum.values.validator.ts new file mode 100644 index 0000000..f3b024c --- /dev/null +++ b/src/application/domain/validator/enum.values.validator.ts @@ -0,0 +1,14 @@ +import { ValidationException } from '../exception/validation.exception' +import { Strings } from '../../../utils/strings' + +export class EnumValuesValidator { + public static validate(item: string, key: string, enumerate: object) { + const values: Array = Object.values(enumerate) + if (!(values.includes(item))) { + throw new ValidationException( + `${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED.replace('{0}', key)} ${item}`, + `${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED_DESC} ${values.join(', ')}.` + ) + } + } +} diff --git a/src/application/domain/validator/object.id.validator.ts b/src/application/domain/validator/object.id.validator.ts index 97d4e5d..6bd26fa 100644 --- a/src/application/domain/validator/object.id.validator.ts +++ b/src/application/domain/validator/object.id.validator.ts @@ -4,8 +4,8 @@ import { Strings } from '../../../utils/strings' export class ObjectIdValidator { public static validate(uuid: string, message?: string): void | ValidationException { if (!(/^[a-fA-F0-9]{24}$/.test(uuid))) { - throw new ValidationException(message ? message : Strings.ERROR_MESSAGE.UUID_NOT_VALID_FORMAT, - Strings.ERROR_MESSAGE.UUID_NOT_VALID_FORMAT_DESC) + throw new ValidationException(message ? message : Strings.ERROR_MESSAGE.VALIDATE.UUID_NOT_VALID_FORMAT, + Strings.ERROR_MESSAGE.VALIDATE.UUID_NOT_VALID_FORMAT_DESC) } } } diff --git a/src/application/domain/validator/push.token.validator.ts b/src/application/domain/validator/push.token.validator.ts new file mode 100644 index 0000000..dabacc0 --- /dev/null +++ b/src/application/domain/validator/push.token.validator.ts @@ -0,0 +1,24 @@ +import { PushToken, PushTokenClientTypes } from '../model/push.token' +import { ValidationException } from '../exception/validation.exception' +import { ObjectIdValidator } from './object.id.validator' +import { EnumValuesValidator } from './enum.values.validator' +import { Strings } from '../../../utils/strings' + +export class PushTokenValidator { + public static validate(item: PushToken): void | ValidationException { + const fields: Array = [] + + if (!item.user_id) fields.push('user_id') + else ObjectIdValidator.validate(item.user_id, Strings.USER.PARAM_ID_NOT_VALID_FORMAT) + if (!item.client_type) fields.push('client_type') + else EnumValuesValidator.validate(item.client_type, 'client_type', PushTokenClientTypes) + if (!item.token) fields.push('token') + + if (fields.length > 0) { + throw new ValidationException( + Strings.ERROR_MESSAGE.VALIDATE.REQUIRED_FIELDS, + Strings.ERROR_MESSAGE.VALIDATE.REQUIRED_FIELDS_DESC.replace('{0}', fields.join(', ')) + ) + } + } +} diff --git a/src/application/domain/validator/push.validator.ts b/src/application/domain/validator/push.validator.ts new file mode 100644 index 0000000..652da4d --- /dev/null +++ b/src/application/domain/validator/push.validator.ts @@ -0,0 +1,49 @@ +import { PushTypes, Push } from '../model/push' +import { ValidationException } from '../exception/validation.exception' +import { ObjectIdValidator } from './object.id.validator' +import { EnumValuesValidator } from './enum.values.validator' +import { ChoiceTypes } from '../utils/choice.types' +import { Strings } from '../../../utils/strings' + +export class PushValidator { + public static validate(item: Push): void | ValidationException { + let fields: Array = [] + + const getMessageMissedFields = (key: string, obj: any) => { + const missed_fields: Array = [] + if (!obj.title) missed_fields.push(`${key}.title`) + if (!obj.text) missed_fields.push(`${key}.text`) + return missed_fields + } + + if (!item.type) fields.push('type') + else EnumValuesValidator.validate(item.type, 'type', PushTypes) + if (!item.keep_it) fields.push('keep_it') + else EnumValuesValidator.validate(item.keep_it, 'keep_it', ChoiceTypes) + if (!item.to) fields.push('to') + else { + if (!item.to.length) { + throw new ValidationException( + Strings.ERROR_MESSAGE.VALIDATE.AT_LEAST_ONE_RECIPIENT, + Strings.ERROR_MESSAGE.VALIDATE.AT_LEAST_ONE_RECIPIENT_DESC + ) + } + if (item.type === PushTypes.DIRECT) item.to.map(user_id => ObjectIdValidator.validate(user_id)) + } + if (!item.message) fields.push('message') + else { + if (!item.message.type) fields.push('message.type') + if (!item.message.pt) fields.push('message.pt') + else fields = fields.concat(getMessageMissedFields('message.pt', item.message.pt)) + if (!item.message.eng) fields.push('message.eng') + else fields = fields.concat(getMessageMissedFields('message.eng', item.message.eng)) + } + + if (fields.length > 0) { + throw new ValidationException( + Strings.ERROR_MESSAGE.VALIDATE.REQUIRED_FIELDS, + Strings.ERROR_MESSAGE.VALIDATE.REQUIRED_FIELDS_DESC.replace('{0}', fields.join(', ')) + ) + } + } +} diff --git a/src/application/integration-event/event/user.validator.ts b/src/application/domain/validator/user.validator.ts similarity index 70% rename from src/application/integration-event/event/user.validator.ts rename to src/application/domain/validator/user.validator.ts index e7245d3..a3a713b 100644 --- a/src/application/integration-event/event/user.validator.ts +++ b/src/application/domain/validator/user.validator.ts @@ -1,6 +1,6 @@ -import { User } from '../../domain/model/User' -import { ValidationException } from '../../domain/exception/validation.exception' -import { ObjectIdValidator } from '../../domain/validator/object.id.validator' +import { User } from '../model/user' +import { ValidationException } from '../exception/validation.exception' +import { ObjectIdValidator } from './object.id.validator' export class UserValidator { public static validate(item: User): void | ValidationException { diff --git a/src/application/integration-event/event/integration.event.ts b/src/application/integration-event/event/integration.event.ts index 1ed0ac8..dd05e74 100644 --- a/src/application/integration-event/event/integration.event.ts +++ b/src/application/integration-event/event/integration.event.ts @@ -16,5 +16,6 @@ export abstract class IntegrationEvent implements IJSONSerializable { export enum EventType { EMAIL = 'emails', - USER = 'users' + USER = 'users', + PUSH = 'pushes' } diff --git a/src/application/integration-event/event/push.send.event.ts b/src/application/integration-event/event/push.send.event.ts new file mode 100644 index 0000000..e30d1c0 --- /dev/null +++ b/src/application/integration-event/event/push.send.event.ts @@ -0,0 +1,20 @@ +import { EventType, IntegrationEvent } from './integration.event' +import { Push } from '../../domain/model/push' + +export class PushSendEvent extends IntegrationEvent { + + public static readonly ROUTING_KEY: string = 'pushes.send' + public static readonly NAME: string = 'PushSendEvent' + + constructor(public timestamp?: Date, public push?: Push) { + super(PushSendEvent.NAME, EventType.PUSH, timestamp) + } + + public toJSON(): any { + if (!this.push) return {} + return { + ...super.toJSON(), + push: this.push.toJSON() + } + } +} diff --git a/src/application/integration-event/event/user.delete.event.ts b/src/application/integration-event/event/user.delete.event.ts index 8648f7b..1fca592 100644 --- a/src/application/integration-event/event/user.delete.event.ts +++ b/src/application/integration-event/event/user.delete.event.ts @@ -1,5 +1,5 @@ import { EventType, IntegrationEvent } from './integration.event' -import { User } from '../../domain/model/User' +import { User } from '../../domain/model/user' export class UserDeleteEvent extends IntegrationEvent { public static readonly ROUTING_KEY: string = 'users.delete' diff --git a/src/application/integration-event/handler/push.send.event.handler.ts b/src/application/integration-event/handler/push.send.event.handler.ts new file mode 100644 index 0000000..78efe7a --- /dev/null +++ b/src/application/integration-event/handler/push.send.event.handler.ts @@ -0,0 +1,37 @@ +import { inject } from 'inversify' +import { Identifier } from '../../../di/identifiers' +import { IIntegrationEventHandler } from './integration.event.handler.interface' +import { ILogger } from '../../../utils/custom.logger' +import { IPushService } from '../../port/push.service.interface' +import { PushSendEvent } from '../event/push.send.event' +import { Push } from '../../domain/model/push' + +export class PushSendEventHandler implements IIntegrationEventHandler { + /** + * Creates an instance of PushSendEventHandler. + * + * @param _pushService + * @param _logger + */ + constructor( + @inject(Identifier.PUSH_SERVICE) public readonly _pushService: IPushService, + @inject(Identifier.LOGGER) private readonly _logger: ILogger + ) { + } + + public async handle(event: PushSendEvent): Promise { + try { + const push: Push = new Push().fromJSON(event.push) + + // 1. Validate and send push + await this._pushService.send(push) + + // 3. If got here, it's because the action was successful. + this._logger.info(`Action for event ${event.event_name} successfully performed!`) + } catch (err) { + this._logger.warn(`An error occurred while attempting ` + .concat(`perform the operation with the ${event.event_name} name event. ${err.message}`) + .concat(err.description ? ' ' + err.description : '')) + } + } +} diff --git a/src/application/integration-event/handler/user.delete.event.handler.ts b/src/application/integration-event/handler/user.delete.event.handler.ts index f27be09..8cee97f 100644 --- a/src/application/integration-event/handler/user.delete.event.handler.ts +++ b/src/application/integration-event/handler/user.delete.event.handler.ts @@ -4,8 +4,8 @@ import { IIntegrationEventHandler } from './integration.event.handler.interface' import { ILogger } from '../../../utils/custom.logger' import { EmailEvent } from '../event/email.event' import { IEmailRepository } from '../../port/email.repository.interface' -import { User } from '../../domain/model/User' -import { UserValidator } from '../event/user.validator' +import { User } from '../../domain/model/user' +import { UserValidator } from '../../domain/validator/user.validator' import { UserDeleteEvent } from '../event/user.delete.event' export class UserDeleteEventHandler implements IIntegrationEventHandler { diff --git a/src/application/port/email.repository.interface.ts b/src/application/port/email.repository.interface.ts index 560cbf3..fa73690 100644 --- a/src/application/port/email.repository.interface.ts +++ b/src/application/port/email.repository.interface.ts @@ -1,5 +1,6 @@ import { Email } from '../domain/model/email' import { IRepository } from './repository.interface' +import { EmailTemplate } from '../domain/model/email.template' /** * Interface of the email repository. @@ -50,4 +51,23 @@ export interface IEmailRepository extends IRepository { * @throws {ValidationException | RepositoryException} */ removeAllFromUser(userId: string): Promise + + /** + * Find email template by type and resource. + * + * @param type Email template type. + * @param resource Email template resource. + * @return {Promise} + * @throws {Exception} + */ + findTemplateByTypeAndResource(type: string, resource: string): Promise + + /** + * Updates email template. + * + * @param emailTemplate Containing the data to be updated. + * @return {Promise} + * @throws {Exception} + */ + updateTemplate(emailTemplate: EmailTemplate): Promise } diff --git a/src/application/port/email.service.interface.ts b/src/application/port/email.service.interface.ts index 58fdf29..ba4095e 100644 --- a/src/application/port/email.service.interface.ts +++ b/src/application/port/email.service.interface.ts @@ -1,6 +1,7 @@ import { Email } from '../domain/model/email' import { IService } from './service.interface' import { IQuery } from './query.interface' +import { EmailTemplate } from '../domain/model/email.template' /** * Interface of the email service. @@ -39,4 +40,23 @@ export interface IEmailService extends IService { * @throws {RepositoryException} */ getByIdAndFromUser(emailId: string, userId: string, query: IQuery): Promise + + /** + * Find email template by type and resource. + * + * @param type Email template type. + * @param resource Email template resource. + * @return {Promise} + * @throws {ValidationException | RepositoryException} + */ + findTemplateByTypeAndResource(type: string, resource: string): Promise + + /** + * Updates email template. + * + * @param emailTemplate Containing the data to be updated. + * @return {Promise} + * @throws {ValidationException | RepositoryException} + */ + updateTemplate(emailTemplate: EmailTemplate): Promise } diff --git a/src/application/port/integration.event.repository.interface.ts b/src/application/port/integration.event.repository.interface.ts deleted file mode 100644 index c51a524..0000000 --- a/src/application/port/integration.event.repository.interface.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { IRepository } from './repository.interface' -import { IntegrationEvent } from '../integration-event/event/integration.event' - -/** - * Interface of the integration event repository. - * Must be implemented by the integration event repository at the infrastructure layer. - * - * @see {@link IntegrationEventRepository} for further information. - * @extends {IRepository} - */ -export interface IIntegrationEventRepository extends IRepository> { -} diff --git a/src/application/port/push.repository.interface.ts b/src/application/port/push.repository.interface.ts new file mode 100644 index 0000000..7659e80 --- /dev/null +++ b/src/application/port/push.repository.interface.ts @@ -0,0 +1,8 @@ +import { IRepository } from './repository.interface' +import { Push } from '../domain/model/push' + +export interface IPushRepository extends IRepository { + updateTokenReadStatus(id: string, is_read: string): Promise + + send(push: Push): Promise +} diff --git a/src/application/port/push.service.interface.ts b/src/application/port/push.service.interface.ts new file mode 100644 index 0000000..085714b --- /dev/null +++ b/src/application/port/push.service.interface.ts @@ -0,0 +1,11 @@ +import { Push } from '../domain/model/push' +import { IService } from './service.interface' +import { IQuery } from './query.interface' + +export interface IPushService extends IService { + send(item: Push): Promise + + confirmPushRead(id: string): Promise + + getAllByUser(userId: string, query: IQuery): Promise> +} diff --git a/src/application/port/push.token.repository.interface.ts b/src/application/port/push.token.repository.interface.ts new file mode 100644 index 0000000..2590b57 --- /dev/null +++ b/src/application/port/push.token.repository.interface.ts @@ -0,0 +1,12 @@ +import { IRepository } from './repository.interface' +import { PushToken } from '../domain/model/push.token' + +export interface IPushTokenRepository extends IRepository { + getUserTokens(userId: string): Promise> + + findFromUserAndType(userId: string, clientType: string): Promise + + createOrUpdate(item: PushToken): Promise + + deleteFromUser(userId: string, clientType: string): Promise +} diff --git a/src/application/port/push.token.service.interface.ts b/src/application/port/push.token.service.interface.ts new file mode 100644 index 0000000..6116db5 --- /dev/null +++ b/src/application/port/push.token.service.interface.ts @@ -0,0 +1,9 @@ +import { PushToken } from '../domain/model/push.token' + +export interface IPushTokenService { + findFromUserAndType(userId: string, clientType: string): Promise + + createOrUpdate(item: PushToken): Promise + + deleteFromUser(userId: string, clientType: string): Promise +} diff --git a/src/application/service/email.service.ts b/src/application/service/email.service.ts index 79cb3c2..d19cf95 100644 --- a/src/application/service/email.service.ts +++ b/src/application/service/email.service.ts @@ -7,6 +7,9 @@ import { IQuery } from '../port/query.interface' import { EmailSendValidator } from '../domain/validator/email.send.validator' import { ObjectIdValidator } from '../domain/validator/object.id.validator' import { Strings } from '../../utils/strings' +import { EmailTemplate, EmailTemplateResources, EmailTemplateTypes } from '../domain/model/email.template' +import { EmailTemplateValidator } from '../domain/validator/email.template.validator' +import { EnumValuesValidator } from '../domain/validator/enum.values.validator' /** * Implementing email Service. @@ -42,7 +45,7 @@ export class EmailService implements IEmailService { public async getByIdAndFromUser(emailId: string, userId: string, query: IQuery): Promise { try { - ObjectIdValidator.validate(emailId, Strings.ERROR_MESSAGE.UUID_NOT_VALID_FORMAT) + ObjectIdValidator.validate(emailId, Strings.ERROR_MESSAGE.VALIDATE.UUID_NOT_VALID_FORMAT) ObjectIdValidator.validate(userId, Strings.USER.PARAM_ID_NOT_VALID_FORMAT) query.addFilter({ _id: emailId, user_id: userId }) @@ -75,4 +78,25 @@ export class EmailService implements IEmailService { public count(query: IQuery): Promise { return this._emailRepository.count(query) } + + public async findTemplateByTypeAndResource(type: string, resource: string): Promise { + try { + EnumValuesValidator.validate(type, 'type', EmailTemplateTypes) + EnumValuesValidator.validate(resource, 'resource', EmailTemplateResources) + const result: Buffer = await this._emailRepository.findTemplateByTypeAndResource(type, resource) + return Promise.resolve(result) + } catch (err) { + return Promise.reject(err) + } + } + + public async updateTemplate(item: EmailTemplate): Promise { + try { + EmailTemplateValidator.validate(item) + const result: EmailTemplate = await this._emailRepository.updateTemplate(item) + return Promise.resolve(result) + } catch (err) { + return Promise.reject(err) + } + } } diff --git a/src/application/service/push.service.ts b/src/application/service/push.service.ts new file mode 100644 index 0000000..8ca79b1 --- /dev/null +++ b/src/application/service/push.service.ts @@ -0,0 +1,103 @@ +import { IPushService } from '../port/push.service.interface' +import { inject, injectable } from 'inversify' +import { PushTypes, Push } from '../domain/model/push' +import { IQuery } from '../port/query.interface' +import { PushValidator } from '../domain/validator/push.validator' +import { Identifier } from '../../di/identifiers' +import { IPushRepository } from '../port/push.repository.interface' +import { ChoiceTypes } from '../domain/utils/choice.types' +import { ObjectIdValidator } from '../domain/validator/object.id.validator' +import { IPushTokenRepository } from '../port/push.token.repository.interface' +import { ValidationException } from '../domain/exception/validation.exception' +import { PushToken } from '../domain/model/push.token' +import { Strings } from '../../utils/strings' + +@injectable() +export class PushService implements IPushService { + constructor( + @inject(Identifier.PUSH_REPOSITORY) private readonly _pushRepo: IPushRepository, + @inject(Identifier.PUSH_TOKEN_REPOSITORY) private readonly _pushTokenRepo: IPushTokenRepository + ) { + } + + public async send(item: Push): Promise { + try { + PushValidator.validate(item) + if (item.type === PushTypes.DIRECT) { + // List to store all users ids that does not have saved push tokens + const users_not_exists: Array = [] + for await (const id of item.to!) { + const push_tokens: Array = await this._pushTokenRepo.getUserTokens(id) + if (!push_tokens?.length) users_not_exists.push(id) + } + if (users_not_exists.length > 0) { + throw new ValidationException( + Strings.ERROR_MESSAGE.VALIDATE.USER_HAS_NO_PUSH_TOKEN.replace('{0}', users_not_exists.join(', ')), + Strings.ERROR_MESSAGE.VALIDATE.USER_HAS_NO_PUSH_TOKEN_DESC) + } + } + // Await mount the payload and send the push + await this._pushRepo.send(item) + + // If the push is of the direct type and is to be maintained, save it in the database + if (item.type === PushTypes.DIRECT && item.keep_it === ChoiceTypes.YES) { + const result: Push = await this._pushRepo.create(item) + item.id = result.id + } + return Promise.resolve(item) + } catch (err) { + return Promise.reject(err) + } + } + + public async add(item: Push): Promise { + throw new Error('Not implemented') + } + + public getAll(query: IQuery): Promise> { + throw new Error('Not implemented') + } + + public getAllByUser(userId: string, query: IQuery): Promise> { + try { + ObjectIdValidator.validate(userId, Strings.USER.PARAM_ID_NOT_VALID_FORMAT) + + query.addFilter({ keep_it: ChoiceTypes.YES }) + + return this._pushRepo.find(query) + } catch (err) { + return Promise.reject(err) + } + } + + public getById(id: string, query: IQuery): Promise { + throw new Error('Not implemented') + } + + public remove(id: string): Promise { + try { + ObjectIdValidator.validate(id, Strings.PUSH.PARAM_ID_NOT_VALID_FORMAT) + return this._pushRepo.delete(id) + } catch (err) { + return Promise.reject(err) + } + } + + public update(item: Push): Promise { + throw new Error('Not implemented') + } + + public count(query: IQuery): Promise { + query.addFilter({ keep_it: ChoiceTypes.YES }) + return this._pushRepo.count(query) + } + + public confirmPushRead(id: string): Promise { + try { + ObjectIdValidator.validate(id, Strings.PUSH.PARAM_ID_NOT_VALID_FORMAT) + return this._pushRepo.updateTokenReadStatus(id, ChoiceTypes.YES) + } catch (err) { + return Promise.reject(err) + } + } +} diff --git a/src/application/service/push.token.service.ts b/src/application/service/push.token.service.ts new file mode 100644 index 0000000..1398829 --- /dev/null +++ b/src/application/service/push.token.service.ts @@ -0,0 +1,50 @@ +import { IPushTokenService } from '../port/push.token.service.interface' +import { inject, injectable } from 'inversify' +import { Identifier } from '../../di/identifiers' +import { IPushTokenRepository } from '../port/push.token.repository.interface' +import { PushToken, PushTokenClientTypes } from '../domain/model/push.token' +import { PushTokenValidator } from '../domain/validator/push.token.validator' +import { ObjectIdValidator } from '../domain/validator/object.id.validator' +import { EnumValuesValidator } from '../domain/validator/enum.values.validator' +import { Strings } from '../../utils/strings' + +@injectable() +export class PushTokenService implements IPushTokenService { + constructor( + @inject(Identifier.PUSH_TOKEN_REPOSITORY) private readonly _pushTokenRepo: IPushTokenRepository + ) { + } + + public async findFromUserAndType(userId: string, clientType: string): Promise { + try { + ObjectIdValidator.validate(userId, Strings.USER.PARAM_ID_NOT_VALID_FORMAT) + EnumValuesValidator.validate(clientType, 'client_type', PushTokenClientTypes) + const result: PushToken = await this._pushTokenRepo.findFromUserAndType(userId, clientType) + return Promise.resolve(result) + } catch (err) { + return Promise.reject(err) + } + } + + public async createOrUpdate(item: PushToken): Promise { + try { + PushTokenValidator.validate(item) + const result: PushToken = await this._pushTokenRepo.createOrUpdate(item) + return Promise.resolve(result) + } catch (err) { + return Promise.reject(err) + } + } + + public async deleteFromUser(userId: string, clientType: string): Promise { + try { + ObjectIdValidator.validate(userId, Strings.USER.PARAM_ID_NOT_VALID_FORMAT) + EnumValuesValidator.validate(clientType, 'client_type', PushTokenClientTypes) + const result: boolean = await this._pushTokenRepo.deleteFromUser(userId, clientType) + return Promise.resolve(result) + } catch (err) { + return Promise.reject(err) + } + } + +} diff --git a/src/background/background.service.ts b/src/background/background.service.ts index 35a549d..bf4cd3c 100644 --- a/src/background/background.service.ts +++ b/src/background/background.service.ts @@ -5,6 +5,7 @@ import { ILogger } from '../utils/custom.logger' import { IConnectionDB } from '../infrastructure/port/connection.db.interface' import { IBackgroundTask } from '../application/port/background.task.interface' import { IEventBus } from '../infrastructure/port/event.bus.interface' +import { IConnectionFirebase } from '../infrastructure/port/connection.firebase.interface' @injectable() export class BackgroundService { @@ -12,6 +13,7 @@ export class BackgroundService { constructor( @inject(Identifier.RABBITMQ_EVENT_BUS) private readonly _eventBus: IEventBus, @inject(Identifier.MONGODB_CONNECTION) private readonly _mongodb: IConnectionDB, + @inject(Identifier.FIREBASE_CONNECTION) private readonly _firebase: IConnectionFirebase, @inject(Identifier.SUBSCRIBE_EVENT_BUS_TASK) private readonly _subscribeTask: IBackgroundTask, @inject(Identifier.LOGGER) private readonly _logger: ILogger ) { @@ -25,7 +27,10 @@ export class BackgroundService { const dbConfigs = Config.getMongoConfig() await this._mongodb.tryConnect(dbConfigs.uri, dbConfigs.options) - // Open RabbitMQ connection and perform tasks + // Initializes the Firebase SDK. + this._initFirebase() + + // Opens RabbitMQ connection to perform tasks this._startTasks() } catch (err) { return Promise.reject(new Error(`Error initializing services in background! ${err.message}`)) @@ -42,11 +47,30 @@ export class BackgroundService { } } + /** + * Initializes Firebase connection + */ + private _initFirebase(): void { + const firebaseConfigs = Config.getFirebaseConfig() + + if (firebaseConfigs.options.is_enable) { + this._firebase + .open(firebaseConfigs.options) + .then(() => { + this._logger.info('Connection to Google Firebase successful!') + }) + .catch(err => { + this._logger.error(`Could not initialize the Firebase SDK: ${err.message}`) + }) + } + } + /** * Open RabbitMQ connection and perform tasks */ private _startTasks(): void { const rabbitConfigs = Config.getRabbitConfig() + this._eventBus .connectionSub .open(rabbitConfigs.uri, rabbitConfigs.options) diff --git a/src/background/task/subscribe.event.bus.task.ts b/src/background/task/subscribe.event.bus.task.ts index d91fc97..58b1c5e 100644 --- a/src/background/task/subscribe.event.bus.task.ts +++ b/src/background/task/subscribe.event.bus.task.ts @@ -12,6 +12,8 @@ import { EmailUpdatePasswordEventHandler } from '../../application/integration-e import { EmailPilotStudyDataEventHandler } from '../../application/integration-event/handler/email.pilot.study.data.event.handler' import { UserDeleteEvent } from '../../application/integration-event/event/user.delete.event' import { UserDeleteEventHandler } from '../../application/integration-event/handler/user.delete.event.handler' +import { PushSendEvent } from '../../application/integration-event/event/push.send.event' +import { PushSendEventHandler } from '../../application/integration-event/handler/push.send.event.handler' @injectable() export class SubscribeEventBusTask implements IBackgroundTask { @@ -38,7 +40,7 @@ export class SubscribeEventBusTask implements IBackgroundTask { */ private initializeSubscribe(): void { /** - * Subscribe in EmailEvent + * Subscribe in EmailSendEvent */ this._eventBus .subscribe(new EmailEvent('EmailSendEvent'), @@ -52,7 +54,7 @@ export class SubscribeEventBusTask implements IBackgroundTask { }) /** - * Subscribe in EmailEvent + * Subscribe in EmailWelcomeEvent */ this._eventBus .subscribe(new EmailEvent('EmailWelcomeEvent'), @@ -120,5 +122,18 @@ export class SubscribeEventBusTask implements IBackgroundTask { .catch(err => { this._logger.error(`Error in Subscribe UserDeleteEvent! ${err.message}`) }) + /** + * Subscribe in UserDeleteEvent + */ + this._eventBus + .subscribe(new PushSendEvent(), + new PushSendEventHandler(DIContainer.get(Identifier.PUSH_SERVICE), this._logger), + PushSendEvent.ROUTING_KEY) + .then((result: boolean) => { + if (result) this._logger.info('Subscribe in PushSendEvent successful!') + }) + .catch(err => { + this._logger.error(`Error in Subscribe PushSendEvent! ${err.message}`) + }) } } diff --git a/src/di/di.ts b/src/di/di.ts index eed2602..42e0ad6 100644 --- a/src/di/di.ts +++ b/src/di/di.ts @@ -5,7 +5,7 @@ import { Identifier } from './identifiers' import { EmailEntity } from '../infrastructure/entity/email.entity' import { IEntityMapper } from '../infrastructure/port/entity.mapper.interface' import { IConnectionDB } from '../infrastructure/port/connection.db.interface' -import { IConnectionFactory } from '../infrastructure/port/connection.factory.interface' +import { IConnectionFactory, IConnectionFirebaseFactory } from '../infrastructure/port/connection.factory.interface' import { BackgroundService } from '../background/background.service' import { App } from '../app' import { CustomLogger, ILogger } from '../utils/custom.logger' @@ -26,6 +26,29 @@ import { IBackgroundTask } from '../application/port/background.task.interface' import { SubscribeEventBusTask } from '../background/task/subscribe.event.bus.task' import { ConnectionFactoryMongodb } from '../infrastructure/database/connection.factory.mongodb' import { ConnectionMongodb } from '../infrastructure/database/connection.mongodb' +import { EmailTemplateController } from '../ui/controller/email.template.controller' +import { PushToken } from '../application/domain/model/push.token' +import { PushTokenEntity } from '../infrastructure/entity/push.token.entity' +import { PushTokenEntityMapper } from '../infrastructure/entity/mapper/push.token.entity.mapper' +import { PushTokenRepoModel } from '../infrastructure/database/schema/push.token.schema' +import { IPushTokenRepository } from '../application/port/push.token.repository.interface' +import { PushTokenRepository } from '../infrastructure/repository/push.token.repository' +import { IPushTokenService } from '../application/port/push.token.service.interface' +import { PushTokenService } from '../application/service/push.token.service' +import { UsersPushTokensController } from '../ui/controller/users.push.tokens.controller' +import { PushController } from '../ui/controller/push.controller' +import { UsersPushController } from '../ui/controller/users.push.controller' +import { IPushService } from '../application/port/push.service.interface' +import { PushService } from '../application/service/push.service' +import { IPushRepository } from '../application/port/push.repository.interface' +import { PushRepository } from '../infrastructure/repository/push.repository' +import { PushRepoModel } from '../infrastructure/database/schema/push.schema' +import { Push } from '../application/domain/model/push' +import { PushEntity } from '../infrastructure/entity/push.entity' +import { PushEntityMapper } from '../infrastructure/entity/mapper/push.entity.mapper' +import { IConnectionFirebase } from '../infrastructure/port/connection.firebase.interface' +import { ConnectionFirebase } from '../infrastructure/firebase/connection.firebase' +import { ConnectionFactoryFirebase } from '../infrastructure/firebase/connection.factory.firebase' class IoC { private readonly _container: Container @@ -58,22 +81,46 @@ class IoC { .to(HomeController).inSingletonScope() this._container.bind(Identifier.EMAIL_CONTROLLER) .to(EmailController).inSingletonScope() + this._container.bind(Identifier.EMAIL_TEMPLATE_CONTROLLER) + .to(EmailTemplateController).inSingletonScope() + this._container.bind(Identifier.USERS_PUSH_TOKENS_CONTROLLER) + .to(UsersPushTokensController).inSingletonScope() + this._container.bind(Identifier.PUSH_CONTROLLER) + .to(PushController).inSingletonScope() + this._container.bind(Identifier.USERS_PUSH_CONTROLLER) + .to(UsersPushController).inSingletonScope() // Services this._container.bind(Identifier.EMAIL_SERVICE) .to(EmailService).inSingletonScope() + this._container.bind(Identifier.PUSH_TOKEN_SERVICE) + .to(PushTokenService).inSingletonScope() + this._container.bind(Identifier.PUSH_SERVICE) + .to(PushService).inSingletonScope() // Repositories this._container.bind(Identifier.EMAIL_REPOSITORY) .to(EmailRepository).inSingletonScope() + this._container.bind(Identifier.PUSH_TOKEN_REPOSITORY) + .to(PushTokenRepository).inSingletonScope() + this._container.bind(Identifier.PUSH_REPOSITORY) + .to(PushRepository).inSingletonScope() // Mongoose Schema this._container.bind(Identifier.EMAIL_REPO_MODEL).toConstantValue(EmailRepoModel) + this._container.bind(Identifier.PUSH_TOKEN_REPO_MODEL).toConstantValue(PushTokenRepoModel) + this._container.bind(Identifier.PUSH_REPO_MODEL).toConstantValue(PushRepoModel) // Mappers this._container .bind>(Identifier.EMAIL_ENTITY_MAPPER) .to(EmailEntityMapper).inSingletonScope() + this._container + .bind>(Identifier.PUSH_TOKEN_ENTITY_MAPPER) + .to(PushTokenEntityMapper).inSingletonScope() + this._container + .bind>(Identifier.PUSH_ENTITY_MAPPER) + .to(PushEntityMapper).inSingletonScope() // Background Services this._container @@ -82,6 +129,12 @@ class IoC { this._container .bind(Identifier.MONGODB_CONNECTION) .to(ConnectionMongodb).inSingletonScope() + this._container + .bind(Identifier.FIREBASE_CONNECTION_FACTORY) + .to(ConnectionFactoryFirebase).inSingletonScope() + this._container + .bind(Identifier.FIREBASE_CONNECTION) + .to(ConnectionFirebase).inSingletonScope() this._container .bind(Identifier.RABBITMQ_CONNECTION_FACTORY) .to(ConnectionFactoryRabbitMQ).inSingletonScope() @@ -94,6 +147,8 @@ class IoC { this._container .bind(Identifier.BACKGROUND_SERVICE) .to(BackgroundService).inSingletonScope() + + // Tasks this._container .bind(Identifier.SUBSCRIBE_EVENT_BUS_TASK) .to(SubscribeEventBusTask).inSingletonScope() diff --git a/src/di/identifiers.ts b/src/di/identifiers.ts index 5c41914..de66ca9 100644 --- a/src/di/identifiers.ts +++ b/src/di/identifiers.ts @@ -9,30 +9,43 @@ export abstract class Identifier { // Controllers public static readonly HOME_CONTROLLER: any = Symbol.for('HomeController') public static readonly EMAIL_CONTROLLER: any = Symbol.for('EmailController') + public static readonly EMAIL_TEMPLATE_CONTROLLER: any = Symbol.for('EmailTemplateController') + public static readonly USERS_PUSH_TOKENS_CONTROLLER: any = Symbol.for('UsersPushTokensController') + public static readonly PUSH_CONTROLLER: any = Symbol.for('PushController') + public static readonly USERS_PUSH_CONTROLLER: any = Symbol.for('UsersPushController') // Services public static readonly EMAIL_SERVICE: any = Symbol.for('EmailService') + public static readonly PUSH_TOKEN_SERVICE: any = Symbol.for('PushTokenService') + public static readonly PUSH_SERVICE: any = Symbol.for('PushService') // Repositories public static readonly EMAIL_REPOSITORY: any = Symbol.for('EmailRepository') + public static readonly PUSH_TOKEN_REPOSITORY: any = Symbol.for('PushTokenRepository') + public static readonly PUSH_REPOSITORY: any = Symbol.for('PushRepository') // Models public static readonly EMAIL_REPO_MODEL: any = Symbol.for('EmailRepoModel') + public static readonly PUSH_TOKEN_REPO_MODEL: any = Symbol.for('PushTokenRepoModel') + public static readonly PUSH_REPO_MODEL: any = Symbol.for('PushRepoModel') // Mappers public static readonly EMAIL_ENTITY_MAPPER: any = Symbol.for('EmailEntityMapper') + public static readonly PUSH_TOKEN_ENTITY_MAPPER: any = Symbol.for('PushTokenEntityMapper') + public static readonly PUSH_ENTITY_MAPPER: any = Symbol.for('PushEntityMapper') // Background Services public static readonly MONGODB_CONNECTION_FACTORY: any = Symbol.for('ConnectionFactoryMongodb') public static readonly MONGODB_CONNECTION: any = Symbol.for('ConnectionMongodb') + public static readonly FIREBASE_CONNECTION_FACTORY: any = Symbol.for('ConnectionFactoryFirebase') + public static readonly FIREBASE_CONNECTION: any = Symbol.for('ConnectionFirebase') public static readonly RABBITMQ_CONNECTION_FACTORY: any = Symbol.for('ConnectionFactoryRabbitMQ') public static readonly RABBITMQ_CONNECTION: any = Symbol.for('ConnectionRabbitMQ') public static readonly RABBITMQ_EVENT_BUS: any = Symbol.for('EventBusRabbitMQ') public static readonly BACKGROUND_SERVICE: any = Symbol.for('BackgroundService') - public static readonly SUBSCRIBE_EVENT_BUS_TASK: any = Symbol.for('SubscribeEventBusTask') // Tasks - public static readonly EVENT_BUS_TASK: any = Symbol.for('SubscribeEventBusTask') + public static readonly SUBSCRIBE_EVENT_BUS_TASK: any = Symbol.for('SubscribeEventBusTask') // Log public static readonly LOGGER: any = Symbol.for('CustomLogger') diff --git a/src/infrastructure/database/schema/push.schema.ts b/src/infrastructure/database/schema/push.schema.ts new file mode 100644 index 0000000..b969cd7 --- /dev/null +++ b/src/infrastructure/database/schema/push.schema.ts @@ -0,0 +1,34 @@ +import Mongoose from 'mongoose' +import { ChoiceTypes } from '../../../application/domain/utils/choice.types' + +interface IPushModel extends Mongoose.Document { +} + +const pushSchema: any = { + type: String, + keep_it: String, + to: [String], + message: { + type: { type: String }, + pt: Object, + eng: Object + }, + is_read: { + type: String, + default: ChoiceTypes.NO + } +} + +const options: any = { + timestamps: { createdAt: 'created_at', updatedAt: false }, + toJSON: { + transform: (doc, ret) => { + ret.id = ret._id + delete ret._id + delete ret.__v + return ret + } + } +} + +export const PushRepoModel = Mongoose.model('Push', new Mongoose.Schema(pushSchema, options)) diff --git a/src/infrastructure/database/schema/push.token.schema.ts b/src/infrastructure/database/schema/push.token.schema.ts new file mode 100644 index 0000000..798d7be --- /dev/null +++ b/src/infrastructure/database/schema/push.token.schema.ts @@ -0,0 +1,24 @@ +import Mongoose from 'mongoose' + +interface IPushTokenModel extends Mongoose.Document { +} + +const pushTokenSchema: any = { + user_id: String, + client_type: String, + token: String +} + +const options: any = { + timestamps: { createdAt: 'created_at', updatedAt: false }, + toJSON: { + transform: (doc, ret) => { + ret.id = ret._id + delete ret._id + delete ret.__v + return ret + } + } +} + +export const PushTokenRepoModel = Mongoose.model('PushToken', new Mongoose.Schema(pushTokenSchema, options)) diff --git a/src/infrastructure/entity/email.entity.ts b/src/infrastructure/entity/email.entity.ts index 7c4c428..d257bc5 100644 --- a/src/infrastructure/entity/email.entity.ts +++ b/src/infrastructure/entity/email.entity.ts @@ -1,5 +1,6 @@ -export class EmailEntity { - public id?: string +import { Entity } from './entity' + +export class EmailEntity extends Entity { public from!: any public reply?: any public to!: Array @@ -9,6 +10,5 @@ export class EmailEntity { public text?: string public html?: string public attachments?: Array - public created_at?: string public user_id?: string } diff --git a/src/infrastructure/entity/entity.ts b/src/infrastructure/entity/entity.ts new file mode 100644 index 0000000..d67c50a --- /dev/null +++ b/src/infrastructure/entity/entity.ts @@ -0,0 +1,4 @@ +export class Entity { + public id?: string + public created_at?: string +} diff --git a/src/infrastructure/entity/mapper/push.entity.mapper.ts b/src/infrastructure/entity/mapper/push.entity.mapper.ts new file mode 100644 index 0000000..3155bbc --- /dev/null +++ b/src/infrastructure/entity/mapper/push.entity.mapper.ts @@ -0,0 +1,40 @@ +import { injectable } from 'inversify' +import { IEntityMapper } from '../../port/entity.mapper.interface' +import { Push } from '../../../application/domain/model/push' +import { PushEntity } from '../push.entity' +import { PushMessage } from '../../../application/domain/model/push.message' + +@injectable() +export class PushEntityMapper implements IEntityMapper { + public transform(item: any): any { + if (item instanceof Push) return this.modelToModelEntity(item) + return this.jsonToModel(item) // json + } + + public modelToModelEntity(item: Push): PushEntity { + const result: PushEntity = new PushEntity() + + if (item.id) result.id = item.id + if (item.type) result.type = item.type + if (item.keep_it) result.keep_it = item.keep_it + if (item.is_read) result.is_read = item.is_read + if (item.to) result.to = item.to + if (item.message) result.message = item.message.toJSON() + + return result + } + + public jsonToModel(json: any): Push { + const result: Push = new Push() + if (!json) return result + + if (json.id) result.id = json.id + if (json.type) result.type = json.type + if (json.keep_it) result.keep_it = json.keep_it + if (json.is_read) result.is_read = json.is_read + if (json.to) result.to = json.to + if (json.message) result.message = new PushMessage().fromJSON(json.message) + + return result + } +} diff --git a/src/infrastructure/entity/mapper/push.token.entity.mapper.ts b/src/infrastructure/entity/mapper/push.token.entity.mapper.ts new file mode 100644 index 0000000..46a0f6c --- /dev/null +++ b/src/infrastructure/entity/mapper/push.token.entity.mapper.ts @@ -0,0 +1,35 @@ +import { injectable } from 'inversify' +import { IEntityMapper } from '../../port/entity.mapper.interface' +import { PushToken } from '../../../application/domain/model/push.token' +import { PushTokenEntity } from '../push.token.entity' + +@injectable() +export class PushTokenEntityMapper implements IEntityMapper { + public transform(item: any): any { + if (item instanceof PushToken) return this.modelToModelEntity(item) + return this.jsonToModel(item) // json + } + + public modelToModelEntity(item: PushToken): PushTokenEntity { + const result: PushTokenEntity = new PushTokenEntity() + + if (item.id) result.id = item.id + if (item.user_id) result.user_id = item.user_id + if (item.client_type) result.client_type = item.client_type + if (item.token) result.token = item.token + + return result + } + + public jsonToModel(json: any): PushToken { + const result: PushToken = new PushToken() + if (!json) return result + + if (json.id !== undefined) result.id = json.id + if (json.user_id !== undefined) result.user_id = json.user_id + if (json.client_type !== undefined) result.client_type = json.client_type + if (json.token !== undefined) result.token = json.token + + return result + } +} diff --git a/src/infrastructure/entity/push.entity.ts b/src/infrastructure/entity/push.entity.ts new file mode 100644 index 0000000..f0f3c72 --- /dev/null +++ b/src/infrastructure/entity/push.entity.ts @@ -0,0 +1,9 @@ +import { Entity } from './entity' + +export class PushEntity extends Entity { + public type?: string + public keep_it?: string + public is_read?: string + public to?: Array + public message?: any +} diff --git a/src/infrastructure/entity/push.token.entity.ts b/src/infrastructure/entity/push.token.entity.ts new file mode 100644 index 0000000..ebcaf57 --- /dev/null +++ b/src/infrastructure/entity/push.token.entity.ts @@ -0,0 +1,7 @@ +import { Entity } from './entity' + +export class PushTokenEntity extends Entity{ + public user_id?: string + public client_type?: string + public token?: string +} diff --git a/src/infrastructure/eventbus/rabbitmq/eventbus.rabbitmq.ts b/src/infrastructure/eventbus/rabbitmq/eventbus.rabbitmq.ts index 2c4d053..865b011 100644 --- a/src/infrastructure/eventbus/rabbitmq/eventbus.rabbitmq.ts +++ b/src/infrastructure/eventbus/rabbitmq/eventbus.rabbitmq.ts @@ -41,7 +41,9 @@ export class EventBusRabbitMQ implements IEventBus { */ public async publish(event: IntegrationEvent, routingKey: string): Promise { return new Promise(async (resolve, reject) => { - if (!this.connectionPub.isOpen) return resolve(false) + if (!this.connectionPub.isOpen) { + return reject(new EventBusException('No connection open!')) + } const message = { content: event.toJSON() } this.connectionPub @@ -67,9 +69,12 @@ export class EventBusRabbitMQ implements IEventBus { public async subscribe(event: IntegrationEvent, handler: IIntegrationEventHandler>, routingKey: string): Promise { return new Promise(async (resolve, reject) => { - if (!this.connectionSub.isOpen) return resolve(false) + if (!this.connectionSub.isOpen) { + return reject(new EventBusException('No connection open!')) + } if (this._event_handlers.has(event.event_name)) return resolve(true) + this._event_handlers.set(event.event_name, handler) this.connectionSub .subscribe(this.RABBITMQ_QUEUE_NAME, event.type, routingKey, (message) => { message.ack() @@ -93,7 +98,6 @@ export class EventBusRabbitMQ implements IEventBus { receiveFromYourself: this._receiveFromYourself }) .then(() => { - this._event_handlers.set(event.event_name, handler) resolve(true) }) .catch(reject) diff --git a/src/infrastructure/firebase/connection.factory.firebase.ts b/src/infrastructure/firebase/connection.factory.firebase.ts new file mode 100644 index 0000000..46cf9ef --- /dev/null +++ b/src/infrastructure/firebase/connection.factory.firebase.ts @@ -0,0 +1,39 @@ +import { injectable } from 'inversify' +import { IConnectionFirebaseFactory, IFirebaseOptions } from '../port/connection.factory.interface' +import admin from 'firebase-admin' +import fs from 'fs' + +@injectable() +export class ConnectionFactoryFirebase implements IConnectionFirebaseFactory { + /** + * Creates instance of Firebase. + * + * @param options {IFirebaseOptions} Firebase connection setup options. + * @return Promise + */ + public createInstance(options: IFirebaseOptions): Promise { + try { + const credentials_file: any = this._readJSONFile(options.credentialsFilePath) + return Promise.resolve(admin.initializeApp({ + credential: admin.credential.cert(credentials_file) + })) + } catch (err) { + return Promise.reject(err) + } + } + + /** + * Reads the content of a path and converts its to JSON. + * + * @param path Content path to convert to JSON. + * @return {Promise} + */ + private _readJSONFile(path: string): any { + try { + const file: any = fs.readFileSync(path) + return JSON.parse(file) + } catch (err) { + throw err + } + } +} diff --git a/src/infrastructure/firebase/connection.firebase.ts b/src/infrastructure/firebase/connection.firebase.ts new file mode 100644 index 0000000..a79c741 --- /dev/null +++ b/src/infrastructure/firebase/connection.firebase.ts @@ -0,0 +1,45 @@ +import { inject, injectable } from 'inversify' +import * as admin from 'firebase-admin' +import { IConnectionFirebase } from '../port/connection.firebase.interface' +import { Identifier } from '../../di/identifiers' +import { IConnectionFirebaseFactory, IFirebaseOptions } from '../port/connection.factory.interface' + +/** + * Implementation of the interface that provides connection with Firebase. + * To implement the Firebase abstraction the firebase-admin library was used. + * + * @see {@link https://firebase.google.com/docs/reference/admin/} for more details. + * @implements {IConnectionFirebase} + */ +@injectable() +export class ConnectionFirebase implements IConnectionFirebase { + private _connection!: admin.app.App + + constructor( + @inject(Identifier.FIREBASE_CONNECTION_FACTORY) private readonly _connectionFactory: IConnectionFirebaseFactory + ) { + } + + get connection(): admin.app.App { + return this._connection + } + + set connection(value: admin.app.App ) { + this._connection = value + } + + /** + * Reads the Google application credentials and use them to launch the Firebase Admin SDK. + * + * @param options {IFirebaseOptions} Firebase connection setup options. + * @return {Promise} + */ + public async open(options: IFirebaseOptions): Promise { + try { + this.connection = await this._connectionFactory.createInstance(options) + return Promise.resolve() + } catch (err) { + return Promise.reject(err) + } + } +} diff --git a/src/infrastructure/port/connection.factory.interface.ts b/src/infrastructure/port/connection.factory.interface.ts index a1b3882..8b28837 100644 --- a/src/infrastructure/port/connection.factory.interface.ts +++ b/src/infrastructure/port/connection.factory.interface.ts @@ -1,3 +1,5 @@ +import admin from 'firebase-admin' + export interface IConnectionFactory { createConnection(uri: string, options?: IDBOptions | IEventBusOptions): Promise } @@ -5,6 +7,15 @@ export interface IConnectionFactory { export interface IDBOptions { } +export interface IConnectionFirebaseFactory { + createInstance(options: IFirebaseOptions): Promise +} + +export interface IFirebaseOptions { + is_enable: boolean + credentialsFilePath: string +} + export interface IEventBusOptions { retries?: number interval?: number diff --git a/src/infrastructure/port/connection.firebase.interface.ts b/src/infrastructure/port/connection.firebase.interface.ts new file mode 100644 index 0000000..e7c2559 --- /dev/null +++ b/src/infrastructure/port/connection.firebase.interface.ts @@ -0,0 +1,8 @@ +import { IFirebaseOptions } from './connection.factory.interface' +import * as admin from 'firebase-admin' + +export interface IConnectionFirebase { + connection: admin.app.App + + open(options: IFirebaseOptions): Promise +} diff --git a/src/infrastructure/repository/email.repository.ts b/src/infrastructure/repository/email.repository.ts index 8e338fa..10736b3 100644 --- a/src/infrastructure/repository/email.repository.ts +++ b/src/infrastructure/repository/email.repository.ts @@ -9,8 +9,10 @@ import { ILogger } from '../../utils/custom.logger' import { IEntityMapper } from '../port/entity.mapper.interface' import { Address } from '../../application/domain/model/address' import { ValidationException } from '../../application/domain/exception/validation.exception' -import EmailTemplate from 'email-templates' +import Template from 'email-templates' import path from 'path' +import fs from 'fs' +import { EmailTemplate } from '../../application/domain/model/email.template' /** * Implementation of the email repository. @@ -20,6 +22,7 @@ import path from 'path' @injectable() export class EmailRepository extends BaseRepository implements IEmailRepository { private readonly smtpTransport: any + private readonly _path: string constructor( @inject(Identifier.EMAIL_REPO_MODEL) readonly emailModel: any, @@ -35,6 +38,7 @@ export class EmailRepository extends BaseRepository implemen } this.logger.info('SMTP credentials successfully verified!') }) + this._path = this.getEmailTemplateInstance().config.views.root } /** @@ -81,8 +85,7 @@ export class EmailRepository extends BaseRepository implemen }) } - public sendTemplateAndAttachment(name: string, to: any, attachments: Array, - data: any, lang?: string): Promise { + public sendTemplateAndAttachment(name: string, to: any, attachments: Array, data: any, lang?: string): Promise { return new Promise((resolve, reject) => { this.getEmailTemplateInstance() .send({ @@ -190,7 +193,7 @@ export class EmailRepository extends BaseRepository implemen } private getEmailTemplateInstance(): any { - return new EmailTemplate({ + return new Template({ transport: this.smtpTransport, send: true, preview: false, @@ -206,4 +209,38 @@ export class EmailRepository extends BaseRepository implemen }) } + public async findTemplateByTypeAndResource(type: string, resource: string): Promise { + try { + let filePath: string = `${this._path}/${type}/${resource}.pug` + // Transforms the path to the Windows use case. + if (process.platform === 'win32') filePath = this.replaceForwardSlashes(filePath) + const result: Buffer = await fs.readFileSync(filePath) + return Promise.resolve(Buffer.from(result)) + } catch (err) { + return Promise.reject(err) + } + } + + public async updateTemplate(item: EmailTemplate): Promise { + try { + let htmlFilePath: string = `${this._path}/${item.type}/html.pug` + let subjectFilePath: string = `${this._path}/${item.type}/subject.pug` + let textFilePath: string = `${this._path}/${item.type}/text.pug` + if (process.platform === 'win32') { + htmlFilePath = this.replaceForwardSlashes(htmlFilePath) + subjectFilePath = this.replaceForwardSlashes(subjectFilePath) + textFilePath = this.replaceForwardSlashes(textFilePath) + } + await fs.writeFileSync(htmlFilePath, Buffer.from(item.html?.buffer!)) + await fs.writeFileSync(subjectFilePath, Buffer.from(item.subject?.buffer!)) + await fs.writeFileSync(textFilePath, Buffer.from(item.text?.buffer!)) + return Promise.resolve(item) + } catch (err) { + return Promise.reject(err) + } + } + + private replaceForwardSlashes(input: string): string { + return input.replace(/\//g, '\\') + } } diff --git a/src/infrastructure/repository/push.repository.ts b/src/infrastructure/repository/push.repository.ts new file mode 100644 index 0000000..badac98 --- /dev/null +++ b/src/infrastructure/repository/push.repository.ts @@ -0,0 +1,174 @@ +import { BaseRepository } from './base/base.repository' +import { Push, PushTypes } from '../../application/domain/model/push' +import { PushEntity } from '../entity/push.entity' +import { IPushRepository } from '../../application/port/push.repository.interface' +import { inject, injectable } from 'inversify' +import { Identifier } from '../../di/identifiers' +import { ILogger } from '../../utils/custom.logger' +import { IConnectionFirebase } from '../port/connection.firebase.interface' +import { FirebaseClientException } from '../../application/domain/exception/firebase.client.exception' +import HttpStatus from 'http-status-codes' +import { Strings } from '../../utils/strings' +import { PushToken } from '../../application/domain/model/push.token' +import { IPushTokenRepository } from '../../application/port/push.token.repository.interface' + +@injectable() +export class PushRepository extends BaseRepository implements IPushRepository { + + constructor( + @inject(Identifier.PUSH_REPO_MODEL) readonly _model: any, + @inject(Identifier.PUSH_ENTITY_MAPPER) readonly _mapper: any, + @inject(Identifier.LOGGER) readonly _logger: ILogger, + @inject(Identifier.PUSH_TOKEN_REPOSITORY) readonly _pushTokenRepo: IPushTokenRepository, + @inject(Identifier.FIREBASE_CONNECTION) readonly _firebase: IConnectionFirebase + ) { + super(_model, _mapper, _logger) + } + + public updateTokenReadStatus(id: string, is_read: string): Promise { + return new Promise((resolve, reject) => { + this._model.findOneAndUpdate({ _id: id }, { $set: { is_read } }) + .then(res => resolve(!!res)) + .catch(err => reject(super.mongoDBErrorListener(err))) + }) + } + + public async send(push: Push): Promise { + try { + const payloads: Array = await this.mountPayloads(push) + for await(const payload of payloads) await this.sendPayload(payload) + return Promise.resolve() + } catch (err) { + return Promise.reject(err) + } + } + + private sendPayload(payload: any): Promise { + return new Promise((resolve, reject) => { + this._firebase.connection.messaging().send(payload) + .then(res => resolve(!!res)) + .catch(err => reject(this.firebaseAdminErrorListener(err))) + }) + } + + private async mountPayloads(item: Push): Promise> { + try { + const result: Array = [] + // Transform the message from object to json + const message: any = item.message?.toJSON() + // If the type of push is direct, the 'to' parameter should be an array of ids + if (item.type === PushTypes.DIRECT) { + for await(const owner_id of item.to!) { + // Get all push tokens from user, for any type of client + const push_tokens: Array = await this._pushTokenRepo.getUserTokens(owner_id) + // Create a push payload for each push token + push_tokens.forEach(push_token => { + result.push({ + token: push_token.token, + data: { type: message.type, body: this.serializePayloadBody(message) } + }) + }) + } + return Promise.resolve(result) + } + + // If the type of push is topic, the 'to' parameters should be an array of topic names + item.to!.forEach(topic => result.push({ + topic, + data: { type: message.type, body: this.serializePayloadBody(message) } + })) + return Promise.resolve(result) + } catch (err) { + return Promise.reject(err) + } + } + + private serializePayloadBody(payload: any): any { + return JSON.stringify({ + pt: payload.pt, + eng: payload.eng + }) + } + + /* + * Firebase Admin Sdk errors. For more information, consult the reference: + * https://firebase.google.com/docs/cloud-messaging/send-message?hl=pt-br#admin + */ + private firebaseAdminErrorListener(err: any, recipient?: string) { + if (err.errorInfo) { + const info: any = err.errorInfo + const errorLiterals = { + 'messaging/invalid-payload': () => { + return new FirebaseClientException( + HttpStatus.BAD_REQUEST, + Strings.FIREBASE_ADMIN_ERROR.INVALID_PAYLOAD, err.message) + }, + 'messaging/payload-size-limit-exceeded': () => { + return new FirebaseClientException( + HttpStatus.REQUEST_TOO_LONG, + Strings.FIREBASE_ADMIN_ERROR.PAYLOAD_LIMIT_EXCEEDED) + }, + 'messaging/invalid-registration-token': () => { + return new FirebaseClientException( + HttpStatus.UNAUTHORIZED, + Strings.FIREBASE_ADMIN_ERROR.INVALID_TOKEN.replace(': {0}', recipient ? `: ${recipient}` : '.'), + Strings.FIREBASE_ADMIN_ERROR.INVALID_TOKEN_DESC) + }, + 'messaging/registration-token-not-registered': () => { + return new FirebaseClientException( + HttpStatus.UNAUTHORIZED, + Strings.FIREBASE_ADMIN_ERROR.TOKEN_NOT_REGISTERED.replace(': {0}', recipient ? `: ${recipient}` : '.'), + Strings.FIREBASE_ADMIN_ERROR.TOKEN_NOT_REGISTERED_DESC) + }, + 'messaging/message-rate-exceeded': () => { + return new FirebaseClientException( + HttpStatus.TOO_MANY_REQUESTS, + Strings.FIREBASE_ADMIN_ERROR.MESSAGE_RATE_EXCEEDED.replace(': {0}', recipient ? `: ${recipient}` : '.'), + Strings.FIREBASE_ADMIN_ERROR.MESSAGE_RATE_EXCEEDED_DESC) + }, + 'messaging/topics-message-rate-exceeded': () => { + return new FirebaseClientException( + HttpStatus.TOO_MANY_REQUESTS, + Strings.FIREBASE_ADMIN_ERROR.TOPICS_MESSAGE_RATE_EXCEEDED.replace(': {0}', recipient ? `: ${recipient}` : '.'), + Strings.FIREBASE_ADMIN_ERROR.TOPICS_MESSAGE_RATE_EXCEEDED_DESC) + }, + 'messaging/device-message-rate-exceeded': () => { + return new FirebaseClientException( + HttpStatus.TOO_MANY_REQUESTS, + Strings.FIREBASE_ADMIN_ERROR.DIRECT_MESSAGE_RATE_EXCEEDED.replace(': {0}', recipient ? `: ${recipient}` : '.'), + Strings.FIREBASE_ADMIN_ERROR.DIRECT_MESSAGE_RATE_EXCEEDED_DESC) + }, + 'messaging/mismatched-credential': () => { + return new FirebaseClientException( + HttpStatus.BAD_REQUEST, + Strings.FIREBASE_ADMIN_ERROR.MISMATCHED_CREDENTIAL.replace(': {0}', recipient ? `: ${recipient}` : '.'), + Strings.FIREBASE_ADMIN_ERROR.MISMATCHED_CREDENTIAL_DESC) + }, + 'messaging/authentication-error': () => { + return new FirebaseClientException( + HttpStatus.UNAUTHORIZED, + Strings.FIREBASE_ADMIN_ERROR.AUTHENTICATION_ERROR, + Strings.FIREBASE_ADMIN_ERROR.AUTHENTICATION_ERROR_DESC) + }, + 'messaging/server-unavailable': () => { + return new FirebaseClientException( + HttpStatus.SERVICE_UNAVAILABLE, + Strings.FIREBASE_ADMIN_ERROR.SERVER_UNAVAILABLE) + }, + 'messaging/internal-error': () => { + return new FirebaseClientException( + HttpStatus.INTERNAL_SERVER_ERROR, + Strings.FIREBASE_ADMIN_ERROR.INTERNAL_ERROR) + }, + 'messaging/unknown-error': () => { + return new FirebaseClientException( + HttpStatus.INTERNAL_SERVER_ERROR, + Strings.FIREBASE_ADMIN_ERROR.UNKNOWN_ERROR) + } + } + + return (errorLiterals[info.code] || errorLiterals['messaging/unknown-error'])() + } + return new FirebaseClientException(HttpStatus.INTERNAL_SERVER_ERROR, Strings.FIREBASE_ADMIN_ERROR.UNKNOWN_ERROR) + } +} diff --git a/src/infrastructure/repository/push.token.repository.ts b/src/infrastructure/repository/push.token.repository.ts new file mode 100644 index 0000000..a52bbbb --- /dev/null +++ b/src/infrastructure/repository/push.token.repository.ts @@ -0,0 +1,56 @@ +import { BaseRepository } from './base/base.repository' +import { PushTokenEntity } from '../entity/push.token.entity' +import { PushToken } from '../../application/domain/model/push.token' +import { IPushTokenRepository } from '../../application/port/push.token.repository.interface' +import { inject, injectable } from 'inversify' +import { Identifier } from '../../di/identifiers' +import { IEntityMapper } from '../port/entity.mapper.interface' +import { ILogger } from '../../utils/custom.logger' +import { Query } from './query/query' +import { IQuery } from '../../application/port/query.interface' + +@injectable() +export class PushTokenRepository extends BaseRepository implements IPushTokenRepository { + constructor( + @inject(Identifier.PUSH_TOKEN_REPO_MODEL) readonly _model: any, + @inject(Identifier.PUSH_TOKEN_ENTITY_MAPPER) readonly _mapper: IEntityMapper, + @inject(Identifier.LOGGER) readonly _logger: ILogger + ) { + super(_model, _mapper, _logger) + } + + public createOrUpdate(item: PushToken): Promise { + const itemNew: any = this.mapper.transform(item) + return new Promise((resolve, reject) => { + this.Model.findOneAndUpdate( + { user_id: item.user_id, client_type: item.client_type }, + itemNew, + { new: true, upsert: true, setDefaultsOnInsert: true }) + .then(result => { + if (!result) return resolve(undefined) + return resolve(this.mapper.transform(result)) + }) + }) + } + + public deleteFromUser(userId: string, clientType: string): Promise { + return new Promise((resolve, reject) => { + this.Model.findOneAndDelete({ user_id: userId, client_type: clientType }) + .then(result => resolve(!!result)) + .catch(err => reject(this.mongoDBErrorListener(err))) + }) + } + + public findFromUserAndType(userId: string, clientType: string): Promise { + return super.findOne(new Query().fromJSON({ filters: { user_id: userId, client_type: clientType } })) + } + + public getUserTokens(userId: string): Promise> { + const query: IQuery = new Query().fromJSON({ filters: { user_id: userId } }) + return new Promise>((resolve, reject) => { + this.find(query) + .then(res => resolve(res.map(item => this._mapper.transform(item)))) + .catch(err => reject(err)) + }) + } +} diff --git a/src/ui/controller/email.template.controller.ts b/src/ui/controller/email.template.controller.ts new file mode 100644 index 0000000..66efc35 --- /dev/null +++ b/src/ui/controller/email.template.controller.ts @@ -0,0 +1,82 @@ +import HttpStatus from 'http-status-codes' +import { inject } from 'inversify' +import { controller, httpGet, httpPut, request, response } from 'inversify-express-utils' +import { Request, Response } from 'express' +import { Identifier } from '../../di/identifiers' +import { ApiExceptionManager } from '../exception/api.exception.manager' +import { ApiException } from '../exception/api.exception' +import { Strings } from '../../utils/strings' +import { EmailTemplate } from '../../application/domain/model/email.template' +import multer from 'multer' +import { FileFormatType } from '../../application/domain/model/file' +import { IEmailService } from '../../application/port/email.service.interface' + +@controller('/v1/emails/templates') +export class EmailTemplateController { + + constructor( + @inject(Identifier.EMAIL_SERVICE) private readonly _emailService: IEmailService + ) { + } + + @httpPut( + '/:type', + multer().fields([{ name: 'html', maxCount: 1 }, { name: 'subject', maxCount: 1 }, { name: 'text', maxCount: 1 }]) + ) + public async updateEmailTemplate(@request() req: Request | any, @response() res: Response): Promise { + try { + const html: any = req.files?.html + const subject: any = req.files?.subject + const text: any = req.files?.text + + const template: EmailTemplate = new EmailTemplate().fromJSON({ + type: req.params.type, + html: html && html.length ? { + filename: html[0].originalname, + buffer: html[0].buffer, + mimetype: html[0].mimetype + } : undefined, + subject: subject && subject.length ? { + filename: subject[0].originalname, + buffer: subject[0].buffer, + mimetype: subject[0].mimetype + } : undefined, + text: text ? { + filename: text[0].originalname, + buffer: text[0].buffer, + mimetype: text[0].mimetype + } : undefined + }) + const result: EmailTemplate = await this._emailService.updateTemplate(template) + if (!result) return res.status(HttpStatus.NOT_FOUND).send(this.getMessageNotFound()) + return res.status(HttpStatus.NO_CONTENT).send() + } catch (err) { + const handlerError = ApiExceptionManager.build(err) + return res.status(handlerError.code).send(handlerError.toJSON()) + } + } + + @httpGet('/:type/:resource') + public async downloadEmailTemplate(@request() req: Request, @response() res: Response): Promise { + try { + const type: string = req.params.type + const resource: string = req.params.resource + const result: Buffer = await this._emailService.findTemplateByTypeAndResource(type, resource) + if (!result) return res.status(HttpStatus.NOT_FOUND).send(this.getMessageNotFound()) + res.set('Content-Type', FileFormatType.OCTET_STREAM) + res.set('Content-Disposition', `filename=${resource}.pug`) + res.status(HttpStatus.OK).end(result) + } catch (err) { + const handlerError = ApiExceptionManager.build(err) + return res.status(handlerError.code).send(handlerError.toJSON()) + } + } + + private getMessageNotFound(): object { + return new ApiException( + HttpStatus.NOT_FOUND, + Strings.EMAIL_TEMPLATE.NOT_FOUND, + Strings.EMAIL_TEMPLATE.NOT_FOUND_DESCRIPTION + ).toJSON() + } +} diff --git a/src/ui/controller/push.controller.ts b/src/ui/controller/push.controller.ts new file mode 100644 index 0000000..a7dceda --- /dev/null +++ b/src/ui/controller/push.controller.ts @@ -0,0 +1,58 @@ +import HttpStatus from 'http-status-codes' +import { inject } from 'inversify' +import { controller, httpDelete, httpPost, request, response } from 'inversify-express-utils' +import { Request, Response } from 'express' +import { Identifier } from '../../di/identifiers' +import { ApiExceptionManager } from '../exception/api.exception.manager' +import { IPushService } from '../../application/port/push.service.interface' +import { Push } from '../../application/domain/model/push' + +@controller('/v1/push') +export class PushController { + + constructor( + @inject(Identifier.PUSH_SERVICE) private readonly _pushService: IPushService + ) { + } + + @httpPost('/') + public async sendPush(@request() req: Request, @response() res: Response): Promise { + try { + const push: Push = new Push().fromJSON(req.body) + const result: Push = await this._pushService.send(push) + return res.status(HttpStatus.CREATED).send(this.toJSONView(result)) + } catch (err) { + const handlerError = ApiExceptionManager.build(err) + return res.status(handlerError.code).send(handlerError.toJSON()) + } + } + + @httpDelete('/:push_id') + public async deletePush(@request() req: Request, @response() res: Response): Promise { + try { + await this._pushService.remove(req.params.push_id) + return res.status(HttpStatus.NO_CONTENT).send() + } catch (err) { + const handlerError = ApiExceptionManager.build(err) + return res.status(handlerError.code).send(handlerError.toJSON()) + } + } + + @httpPost('/:push_id/read') + public async confirmPushRead(@request() req: Request, @response() res: Response): Promise { + try { + await this._pushService.confirmPushRead(req.params.push_id) + return res.status(HttpStatus.NO_CONTENT).send() + } catch (err) { + const handlerError = ApiExceptionManager.build(err) + return res.status(handlerError.code).send(handlerError.toJSON()) + } + } + + private toJSONView(push: Push | Array): object { + if (push instanceof Array) return push.map(item => this.toJSONView(item)) + const result: any = push.toJSON() + delete result.keep_it + return result + } +} diff --git a/src/ui/controller/users.push.controller.ts b/src/ui/controller/users.push.controller.ts new file mode 100644 index 0000000..cd8e2f9 --- /dev/null +++ b/src/ui/controller/users.push.controller.ts @@ -0,0 +1,42 @@ +import HttpStatus from 'http-status-codes' +import { inject } from 'inversify' +import { controller, httpGet, request, response } from 'inversify-express-utils' +import { Request, Response } from 'express' +import { Identifier } from '../../di/identifiers' +import { ApiExceptionManager } from '../exception/api.exception.manager' +import { IPushService } from '../../application/port/push.service.interface' +import { Push } from '../../application/domain/model/push' +import { Query } from '../../infrastructure/repository/query/query' +import { IQuery } from '../../application/port/query.interface' + +@controller('/v1/users/:user_id/push') +export class UsersPushController { + constructor( + @inject(Identifier.PUSH_SERVICE) private readonly _pushService: IPushService + ) { + } + + @httpGet('/') + public async getAllPushFromUser(@request() req: Request, @response() res: Response): Promise { + try { + const query: IQuery = new Query().fromJSON(req.query) + query.addFilter({ to: req.params.user_id }) + const result: Array = await this._pushService.getAllByUser(req.params.user_id, query) + const count: number = await this._pushService.count(query) + res.setHeader('X-Total-Count', count) + return res.status(HttpStatus.OK).send(this.toJSONView(result)) + } catch (err) { + const handlerError = ApiExceptionManager.build(err) + return res.status(handlerError.code).send(handlerError.toJSON()) + } finally { + req.query = {} + } + } + + private toJSONView(push: Push | Array): object { + if (push instanceof Array) return push.map(item => this.toJSONView(item)) + const result: any = push.toJSON() + delete result.keep_it + return result + } +} diff --git a/src/ui/controller/users.push.tokens.controller.ts b/src/ui/controller/users.push.tokens.controller.ts new file mode 100644 index 0000000..d1e34df --- /dev/null +++ b/src/ui/controller/users.push.tokens.controller.ts @@ -0,0 +1,61 @@ +import HttpStatus from 'http-status-codes' +import { inject } from 'inversify' +import { controller, httpDelete, httpGet, httpPut, request, response } from 'inversify-express-utils' +import { Request, Response } from 'express' +import { Identifier } from '../../di/identifiers' +import { ApiExceptionManager } from '../exception/api.exception.manager' +import { IPushTokenService } from '../../application/port/push.token.service.interface' +import { PushToken, PushTokenClientTypes } from '../../application/domain/model/push.token' + +@controller('/v1/users/:user_id/push') +export class UsersPushTokensController { + + constructor( + @inject(Identifier.PUSH_TOKEN_SERVICE) private readonly _pushTokenService: IPushTokenService + ) { + } + + @httpGet('/tokens') + public async getPushTokensFromUser(@request() req: Request, @response() res: Response): Promise { + try { + const resultWebToken: PushToken = + await this._pushTokenService.findFromUserAndType(req.params.user_id, PushTokenClientTypes.WEB) + const resultMobileToken: PushToken = + await this._pushTokenService.findFromUserAndType(req.params.user_id, PushTokenClientTypes.MOBILE) + + const result = { + web_token: resultWebToken?.token ? resultWebToken.token : '', + mobile_token: resultMobileToken?.token ? resultMobileToken.token : '' + } + return res.status(HttpStatus.OK).send(result) + } catch (err) { + const handlerError = ApiExceptionManager.build(err) + return res.status(handlerError.code).send(handlerError.toJSON()) + } + } + + @httpPut('/:client_type/tokens') + public async createOrUpdatePushTokenForUser(@request() req: Request, @response() res: Response): Promise { + try { + const push_token: PushToken = new PushToken().fromJSON(req.body) + push_token.user_id = req.params.user_id + push_token.client_type = req.params.client_type + await this._pushTokenService.createOrUpdate(push_token) + return res.status(HttpStatus.NO_CONTENT).send() + } catch (err) { + const handlerError = ApiExceptionManager.build(err) + return res.status(handlerError.code).send(handlerError.toJSON()) + } + } + + @httpDelete('/:client_type/tokens') + public async deletePushTokenFromUser(@request() req: Request, @response() res: Response): Promise { + try { + await this._pushTokenService.deleteFromUser(req.params.user_id, req.params.client_type) + return res.status(HttpStatus.NO_CONTENT).send() + } catch (err) { + const handlerError = ApiExceptionManager.build(err) + return res.status(handlerError.code).send(handlerError.toJSON()) + } + } +} diff --git a/src/ui/exception/api.exception.manager.ts b/src/ui/exception/api.exception.manager.ts index 241283d..5ed1487 100644 --- a/src/ui/exception/api.exception.manager.ts +++ b/src/ui/exception/api.exception.manager.ts @@ -4,6 +4,7 @@ import { ValidationException } from '../../application/domain/exception/validati import { ApiException } from './api.exception' import { ConflictException } from '../../application/domain/exception/conflict.exception' import { RepositoryException } from '../../application/domain/exception/repository.exception' +import { FirebaseClientException } from '../../application/domain/exception/firebase.client.exception' /** * Treats the exception types of the application and converts @@ -25,6 +26,8 @@ export abstract class ApiExceptionManager { return new ApiException(HttpStatus.CONFLICT, err.message, err.description) } else if (err instanceof RepositoryException) { return new ApiException(HttpStatus.INTERNAL_SERVER_ERROR, err.message, err.description) + } else if (err instanceof FirebaseClientException) { + return new ApiException(err.code || HttpStatus.INTERNAL_SERVER_ERROR, err.message, err.description) } return new ApiException(HttpStatus.INTERNAL_SERVER_ERROR, err.message, err.description) diff --git a/src/utils/config.ts b/src/utils/config.ts index 57ad9fb..c3298c9 100644 --- a/src/utils/config.ts +++ b/src/utils/config.ts @@ -1,6 +1,11 @@ import fs from 'fs' import { Default } from './default' -import { IDBOptions, IEventBusOptions, ISSL } from '../infrastructure/port/connection.factory.interface' +import { + IDBOptions, + IEventBusOptions, + IFirebaseOptions, + ISSL +} from '../infrastructure/port/connection.factory.interface' export abstract class Config { @@ -28,6 +33,24 @@ export abstract class Config { } as IMongoConfig } + /** + * Retrieve the options for connection to Firebase. + * + * @return { + * options: { + * credential: admin.credential.Credential + * } + * } + */ + public static getFirebaseConfig(): IFirebaseConfig { + return { + options: { + is_enable: process.env.FIREBASE_ENABLE === 'true', + credentialsFilePath: process.env.FIREBASE_CREDENTIALS_PATH || Default.FIREBASE_CREDENTIALS_PATH + } as IFirebaseOptions + } as IFirebaseConfig + } + /** * Retrieve the URI and options for connection to RabbitMQ. * @@ -66,6 +89,10 @@ interface IMongoConfig { options: IDBOptions } +interface IFirebaseConfig { + options: IFirebaseOptions +} + interface IRabbitConfig { uri: string options: IEventBusOptions diff --git a/src/utils/default.ts b/src/utils/default.ts index c4d847d..56fbc3e 100644 --- a/src/utils/default.ts +++ b/src/utils/default.ts @@ -30,4 +30,7 @@ export abstract class Default { // To generate self-signed certificates, see: https://devcenter.heroku.com/articles/ssl-certificate-self public static readonly SSL_KEY_PATH: string = '.certs/server.key' public static readonly SSL_CERT_PATH: string = '.certs/server.crt' + + // The Google Firebase Config JSON file path for use firebase admin features. + public static readonly FIREBASE_CREDENTIALS_PATH: string = '/path/to/firebase_credentials_file.json' } diff --git a/src/utils/strings.ts b/src/utils/strings.ts index 35e68f0..4e3f2d7 100644 --- a/src/utils/strings.ts +++ b/src/utils/strings.ts @@ -22,12 +22,57 @@ export abstract class Strings { NOT_FOUND_DESCRIPTION: 'Email not found or already removed. A new operation for the same resource is not required.' } + public static readonly EMAIL_TEMPLATE: any = { + NOT_FOUND: 'Email template not found!', + NOT_FOUND_DESCRIPTION: 'Email template not found or already removed. A new operation for the same resource is not required.' + } + + public static readonly PUSH: any = { + PARAM_ID_NOT_VALID_FORMAT: 'Parameter {push_id} is not in valid format!' + } + public static readonly ERROR_MESSAGE: any = { REQUEST_BODY_INVALID: 'Unable to process request body!', REQUEST_BODY_INVALID_DESC: 'Please verify that the JSON provided in the request body has a valid format and try again.', ENDPOINT_NOT_FOUND: 'Endpoint {0} does not found!', - UNEXPECTED: 'An unexpected error has occurred. Please try again later...', - UUID_NOT_VALID_FORMAT: 'Some ID provided does not have a valid format!', - UUID_NOT_VALID_FORMAT_DESC: 'A 24-byte hex ID similar to this: 507f191e810c19729de860ea is expected.' + VALIDATE: { + REQUIRED_FIELDS: 'Required fields were not provided...', + REQUIRED_FIELDS_DESC: '{0} are required!', + UUID_NOT_VALID_FORMAT: 'Some ID provided does not have a valid format!', + UUID_NOT_VALID_FORMAT_DESC: 'A 24-byte hex ID similar to this: 507f191e810c19729de860ea is expected.', + INVALID_FIELDS: 'One or more request fields are invalid...', + INVALID_STRING: '{0} must be a string!', + EMPTY_STRING: '{0} must have at least one character!', + NOT_MAPPED: 'Value not mapped for {0}:', + NOT_MAPPED_DESC: 'The mapped values are:', + AT_LEAST_ONE_RECIPIENT: 'At least one recipient is required.', + AT_LEAST_ONE_RECIPIENT_DESC: 'Please enter at least one user id for direct notifications or at least one topic name for topic notifications.', + USER_HAS_NO_PUSH_TOKEN: 'Some user ids do not have saved push tokens for any type of client: {0}.', + USER_HAS_NO_PUSH_TOKEN_DESC: 'Please submit a valid user id and try again.' + } + } + + public static readonly FIREBASE_ADMIN_ERROR: any = { + INVALID_PAYLOAD: 'Some invalid argument was provided into the message parameters.', + PAYLOAD_LIMIT_EXCEEDED: 'The maximum message size has been reached.', + INVALID_TOKEN: 'The recipient token is invalid: {0}', + INVALID_TOKEN_DESC: 'Please provide a new token to recipient and try again later.', + TOKEN_NOT_REGISTERED: 'The recipient is not registered: {0}', + TOKEN_NOT_REGISTERED_DESC: 'Probably the token has been expired or deleted. Please provide a new token and try again later.', + MESSAGE_RATE_EXCEEDED: 'The maximum message rate has been reached for the destination: {0}.', + MESSAGE_RATE_EXCEEDED_DESC: 'Please wait for a while before resending messages to this destination again.', + DIRECT_MESSAGE_RATE_EXCEEDED: 'The maximum message rate has been reached for token: {0}.', + DIRECT_MESSAGE_RATE_EXCEEDED_DESC: 'Please wait for a while before resending messages to this token again.', + TOPICS_MESSAGE_RATE_EXCEEDED: 'The maximum message rate has been reached for topic: {0}.', + TOPICS_MESSAGE_RATE_EXCEEDED_DESC: 'Please wait for a while before resending messages to this topic again.', + MISMATCHED_CREDENTIAL: 'The credential used into Firebase Admin SDK is not allowed to send messages to the push token: {0}', + MISMATCHED_CREDENTIAL_DESC: 'Please verify if the push token and the SDK credential belonging to the same project.', + AUTHENTICATION_ERROR: 'The Firebase Admin SDK could not connect with the FCM Server.', + AUTHENTICATION_ERROR_DESC: 'Please verify your SDK credentials and try again.', + SERVER_UNAVAILABLE: 'The FCM Server is unavailable now. PLease try again later.', + INTERNAL_ERROR: 'The FCM server encountered an error while trying to process the request. Please try again later.', + INVALID_CREDENTIALS: 'Failed to determine the project ID. ' + + 'Please verify that Google Application Credentials has been configured correctly.', + UNKNOWN_ERROR: 'An unknown error has occurred. Please try again later.' } } diff --git a/test/integration/eventbus/eventbus.rabbitmq.spec.ts b/test/integration/eventbus/eventbus.rabbitmq.spec.ts index 2048f25..e54b171 100644 --- a/test/integration/eventbus/eventbus.rabbitmq.spec.ts +++ b/test/integration/eventbus/eventbus.rabbitmq.spec.ts @@ -5,43 +5,64 @@ import { EventBusRabbitMQ } from '../../../src/infrastructure/eventbus/rabbitmq/ import { IntegrationEvent } from '../../../src/application/integration-event/event/integration.event' import { IIntegrationEventHandler } from '../../../src/application/integration-event/handler/integration.event.handler.interface' import { Default } from '../../../src/utils/default' +import { GenericPushSendEventMock } from '../../mocks/integration-event/generic.push.send.event.mock' +import { Push } from '../../../src/application/domain/model/push' +import { GenericPushSendEventHandlerMock } from '../../mocks/integration-event/generic.push.send.event.handler.mock' const eventBus: EventBusRabbitMQ = DIContainer.get(Identifier.RABBITMQ_EVENT_BUS) -describe('EVENT BUS', () => { +describe('EVENTBUS: EventBusRabbitMQ', () => { before(() => { eventBus.receiveFromYourself = true + eventBus.enableLogger('info') }) + // Stops RabbitMQ connections. after(async () => { try { - await eventBus.connectionPub.dispose() - await eventBus.connectionSub.dispose() - await eventBus.connectionRpcServer.dispose() - await eventBus.connectionRpcClient.dispose() + await eventBus.dispose() } catch (err) { - throw new Error('Failure on EventBus test: ' + err.message) + throw new Error('Failure on EventBusRabbitMQ test: ' + err.message) } }) describe('CONNECTION', () => { - it('should return false when trying to publish up without connection.', () => { + it('should throw an error when trying to publish without connection.', async () => { return eventBus - .publish({} as IntegrationEvent, '') - .then((result: boolean) => { - expect(result).to.eql(false) + .publish(new GenericPushSendEventMock(new Date(), new Push()), GenericPushSendEventMock.ROUTING_KEY) + .catch(err => { + expect(err).to.have.property('message', 'No connection open!') }) }) - it('should return false when trying to subscribe up without connection.', () => { + it('should throw an error when trying to subscribe up without connection.', () => { return eventBus .subscribe( - {} as IntegrationEvent, - {} as IIntegrationEventHandler, - '' + new GenericPushSendEventMock(), + new GenericPushSendEventHandlerMock(), + GenericPushSendEventMock.ROUTING_KEY ) - .then((result: boolean) => { - expect(result).to.eql(false) + .catch(err => { + expect(err).to.have.property('message', 'No connection open!') + }) + }) + + it('should throw an error when trying to provide resource without connection.', async () => { + return eventBus + .provideResource('resource.test.get', (query: string) => { + return { content: '123', original_query: query } + }) + .catch(err => { + expect(err).to.have.property('message', 'No connection open!') + }) + }) + + it('should throw an error when trying to request resource without connection.', async () => { + return eventBus.executeResource('notification.rpc', + 'resource.test.get', + '?test=321') + .catch(err => { + expect(err).to.have.property('message', 'No connection open!') }) }) @@ -51,9 +72,8 @@ describe('EVENT BUS', () => { { interval: 100, sslOptions: { ca: [] } }) expect(eventBus.connectionPub.isOpen).to.eql(true) } catch (err) { - throw new Error('Failure on EventBus test: ' + err.message) + throw new Error('Failure on EventBusRabbitMQ test: ' + err.message) } - }) it('should connect successfully to subscribe.', async () => { @@ -62,7 +82,27 @@ describe('EVENT BUS', () => { { interval: 100, sslOptions: { ca: [] } }) expect(eventBus.connectionSub.isOpen).to.eql(true) } catch (err) { - throw new Error('Failure on EventBus test: ' + err.message) + throw new Error('Failure on EventBusRabbitMQ test: ' + err.message) + } + }) + + it('should connect successfully to provider.', async () => { + try { + await eventBus.connectionRpcServer.open(process.env.RABBITMQ_URI || Default.RABBITMQ_URI, + { interval: 100, sslOptions: { ca: [] } }) + expect(eventBus.connectionRpcServer.isOpen).to.eql(true) + } catch (err) { + throw new Error('Failure on EventBusRabbitMQ test: ' + err.message) + } + }) + + it('should connect successfully to client.', async () => { + try { + await eventBus.connectionRpcClient.open(process.env.RABBITMQ_URI || Default.RABBITMQ_URI, + { interval: 100, sslOptions: { ca: [] } }) + expect(eventBus.connectionRpcClient.isOpen).to.eql(true) + } catch (err) { + throw new Error('Failure on EventBusRabbitMQ test: ' + err.message) } }) }) @@ -80,7 +120,7 @@ describe('EVENT BUS', () => { expect(result).to.equal(true) }) } catch (err) { - throw new Error('Failure on EventBus test: ' + err.message) + throw new Error('Failure on EventBusRabbitMQ test: ' + err.message) } }) @@ -96,7 +136,7 @@ describe('EVENT BUS', () => { expect(result).to.equal(true) }) } catch (err) { - throw new Error('Failure on EventBus test: ' + err.message) + throw new Error('Failure on EventBusRabbitMQ test: ' + err.message) } }) }) @@ -109,28 +149,30 @@ describe('EVENT BUS', () => { return eventBus.publish( createEventFake('TestOneEvent'), - 'test.save') + 'testone.save') .then((result: boolean) => { expect(result).to.equal(true) }) } catch (err) { - throw new Error('Failure on EventBus test: ' + err.message) + throw new Error('Failure on EventBusRabbitMQ test: ' + err.message) } }) it('should return true for published TestTwoEvent.', async () => { try { + eventBus.enableLogger() + await eventBus.connectionPub.open(process.env.RABBITMQ_URI || Default.RABBITMQ_URI, { interval: 100, sslOptions: { ca: [] } }) return eventBus.publish( createEventFake('TestTwoEvent'), - 'test.two') + 'testtwo.save') .then((result: boolean) => { expect(result).to.equal(true) }) } catch (err) { - throw new Error('Failure on EventBus test: ' + err.message) + throw new Error('Failure on EventBusRabbitMQ test: ' + err.message) } }) }) @@ -142,25 +184,29 @@ describe('EVENT BUS', () => { { interval: 100, sslOptions: { ca: [] } }) return eventBus - .provideResource('resource.get', (query: string) => { + .provideResource('resource.test.get', (query: string) => { return { content: '123', original_query: query } }) .then((result: boolean) => { expect(result).to.equal(true) }) } catch (err) { - throw new Error('Failure on EventBus test: ' + err.message) + throw new Error('Failure on EventBusRabbitMQ test: ' + err.message) } }) it('should return a requested resource.', async () => { try { - await eventBus.connectionRpcClient.open(process.env.RABBITMQ_URI || Default.RABBITMQ_URI, + await eventBus.connectionRpcServer.open(process.env.RABBITMQ_URI || Default.RABBITMQ_URI, { interval: 100, sslOptions: { ca: [] } }) + await eventBus.provideResource('resource.test.get', (query: string) => { return { content: '123', original_query: query } }) + await eventBus.connectionRpcClient.open(process.env.RABBITMQ_URI || Default.RABBITMQ_URI, + { interval: 100, sslOptions: { ca: [] } }) + return eventBus.executeResource('notification.rpc', 'resource.test.get', '?test=321') @@ -169,7 +215,7 @@ describe('EVENT BUS', () => { expect(res).to.have.property('original_query', '?test=321') }) } catch (err) { - throw new Error('Failure on EventBus test: ' + err.message) + throw new Error('Failure on EventBusRabbitMQ test: ' + err.message) } }) @@ -178,13 +224,13 @@ describe('EVENT BUS', () => { await eventBus.connectionRpcClient.open(process.env.RABBITMQ_URI || Default.RABBITMQ_URI, { interval: 100, sslOptions: { ca: [] } }) - return eventBus.executeResource('notification.service', + return eventBus.executeResource('notification.rpc', 'resource.find', '') .catch(err => { expect(err).to.be.an('error') }) } catch (err) { - throw new Error('Failure on EventBus test: ' + err.message) + throw new Error('Failure on EventBusRabbitMQ test: ' + err.message) } }) }) diff --git a/test/integration/routes/emails.templates.spec.ts b/test/integration/routes/emails.templates.spec.ts new file mode 100644 index 0000000..0fa146a --- /dev/null +++ b/test/integration/routes/emails.templates.spec.ts @@ -0,0 +1,172 @@ +import { DIContainer } from '../../../src/di/di' +import { Identifier } from '../../../src/di/identifiers' +import { App } from '../../../src/app' +import { expect } from 'chai' +import { Strings } from '../../../src/utils/strings' +import { EmailTemplateResources, EmailTemplateTypes } from '../../../src/application/domain/model/email.template' +import { FileFormatType } from '../../../src/application/domain/model/file' + +const app: App = DIContainer.get(Identifier.APP) +const request = require('supertest')(app.getExpress()) + +describe('Routes: emails.templates', () => { + describe('GET /v1/emails/templates/:type/:resource', () => { + context('when get an email template successfully', () => { + it('should return status code 200 and the html file template of the welcome email', () => { + return request + .get(`/v1/emails/templates/${EmailTemplateTypes.WELCOME}/${EmailTemplateResources.HTML}`) + .set('Content-Type', FileFormatType.OCTET_STREAM) + .expect(200) + .then(res => { + expect(res.body).to.be.an.instanceof(Buffer) + }) + }) + + it('should return status code 200 and the subject file template of the welcome email', () => { + return request + .get(`/v1/emails/templates/${EmailTemplateTypes.WELCOME}/${EmailTemplateResources.SUBJECT}`) + .set('Content-Type', FileFormatType.OCTET_STREAM) + .expect(200) + .then(res => { + expect(res.body).to.be.an.instanceof(Buffer) + }) + }) + + it('should return status code 200 and the text file template of the welcome email', () => { + return request + .get(`/v1/emails/templates/${EmailTemplateTypes.WELCOME}/${EmailTemplateResources.TEXT}`) + .set('Content-Type', FileFormatType.OCTET_STREAM) + .expect(200) + .then(res => { + expect(res.body).to.be.an.instanceof(Buffer) + }) + }) + + it('should return status code 200 and the html file template of the password reset email', () => { + return request + .get(`/v1/emails/templates/${EmailTemplateTypes.RESET_PASSWORD}/${EmailTemplateResources.HTML}`) + .set('Content-Type', FileFormatType.OCTET_STREAM) + .expect(200) + .then(res => { + expect(res.body).to.be.an.instanceof(Buffer) + }) + }) + + it('should return status code 200 and the subject file template of the password reset email', () => { + return request + .get(`/v1/emails/templates/${EmailTemplateTypes.RESET_PASSWORD}/${EmailTemplateResources.SUBJECT}`) + .set('Content-Type', FileFormatType.OCTET_STREAM) + .expect(200) + .then(res => { + expect(res.body).to.be.an.instanceof(Buffer) + }) + }) + + it('should return status code 200 and the text file template of the password reset email', () => { + return request + .get(`/v1/emails/templates/${EmailTemplateTypes.RESET_PASSWORD}/${EmailTemplateResources.TEXT}`) + .set('Content-Type', FileFormatType.OCTET_STREAM) + .expect(200) + .then(res => { + expect(res.body).to.be.an.instanceof(Buffer) + }) + }) + + it('should return status code 200 and the html file template of the password update email', () => { + return request + .get(`/v1/emails/templates/${EmailTemplateTypes.UPDATED_PASSWORD}/${EmailTemplateResources.HTML}`) + .set('Content-Type', FileFormatType.OCTET_STREAM) + .expect(200) + .then(res => { + expect(res.body).to.be.an.instanceof(Buffer) + }) + }) + + it('should return status code 200 and the subject file template of the password update email', () => { + return request + .get(`/v1/emails/templates/${EmailTemplateTypes.UPDATED_PASSWORD}/${EmailTemplateResources.SUBJECT}`) + .set('Content-Type', FileFormatType.OCTET_STREAM) + .expect(200) + .then(res => { + expect(res.body).to.be.an.instanceof(Buffer) + }) + }) + + it('should return status code 200 and the text file template of the password update email', () => { + return request + .get(`/v1/emails/templates/${EmailTemplateTypes.UPDATED_PASSWORD}/${EmailTemplateResources.TEXT}`) + .set('Content-Type', FileFormatType.OCTET_STREAM) + .expect(200) + .then(res => { + expect(res.body).to.be.an.instanceof(Buffer) + }) + }) + + it('should return status code 200 and the html file template of the pilot study email', () => { + return request + .get(`/v1/emails/templates/${EmailTemplateTypes.PILOT_STUDY_DATA}/${EmailTemplateResources.HTML}`) + .set('Content-Type', FileFormatType.OCTET_STREAM) + .expect(200) + .then(res => { + expect(res.body).to.be.an.instanceof(Buffer) + }) + }) + + it('should return status code 200 and the subject file template of the pilot study email', () => { + return request + .get(`/v1/emails/templates/${EmailTemplateTypes.PILOT_STUDY_DATA}/${EmailTemplateResources.SUBJECT}`) + .set('Content-Type', FileFormatType.OCTET_STREAM) + .expect(200) + .then(res => { + expect(res.body).to.be.an.instanceof(Buffer) + }) + }) + + it('should return status code 200 and the text file template of the pilot study email', () => { + return request + .get(`/v1/emails/templates/${EmailTemplateTypes.PILOT_STUDY_DATA}/${EmailTemplateResources.TEXT}`) + .set('Content-Type', FileFormatType.OCTET_STREAM) + .expect(200) + .then(res => { + expect(res.body).to.be.an.instanceof(Buffer) + }) + }) + }) + + context('when there are validation errors', () => { + context('when the email template type is invalid', () => { + const emailTemplateTypes: Array = Object.values(EmailTemplateTypes) + + it('should return status code 400 and info message about invalid email template type', () => { + return request + .get(`/v1/emails/templates/invalidEmailTemplateType/${EmailTemplateResources.HTML}`) + .set('Content-Type', FileFormatType.OCTET_STREAM) + .expect(400) + .then(err => { + expect(err.body.message).to.eql(`${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED + .replace('{0}', 'type')} invalidEmailTemplateType`) + expect(err.body.description) + .to.eql(`${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED_DESC} ${emailTemplateTypes.join(', ')}.`) + }) + }) + }) + + context('when the email template resource type is invalid', () => { + const templateResourceTypes: Array = Object.values(EmailTemplateResources) + + it('should return status code 400 and info message about invalid email template type', () => { + return request + .get(`/v1/emails/templates/${EmailTemplateTypes.WELCOME}/invalidResource`) + .set('Content-Type', FileFormatType.OCTET_STREAM) + .expect(400) + .then(err => { + expect(err.body.message).to.eql(`${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED + .replace('{0}', 'resource')} invalidResource`) + expect(err.body.description) + .to.eql(`${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED_DESC} ${templateResourceTypes.join(', ')}.`) + }) + }) + }) + }) + }) +}) diff --git a/test/integration/routes/push.spec.ts b/test/integration/routes/push.spec.ts new file mode 100644 index 0000000..48f8ce9 --- /dev/null +++ b/test/integration/routes/push.spec.ts @@ -0,0 +1,384 @@ +import { IConnectionDB } from '../../../src/infrastructure/port/connection.db.interface' +import { DIContainer } from '../../../src/di/di' +import { Identifier } from '../../../src/di/identifiers' +import { App } from '../../../src/app' +import { Config } from '../../../src/utils/config' +import { DatabaseUtils } from '../../utils/database.utils' +import { PushTokenRepoModel } from '../../../src/infrastructure/database/schema/push.token.schema' +import { Push, PushTypes } from '../../../src/application/domain/model/push' +import { PushMock } from '../../mocks/models/push.mock' +import { PushToken, PushTokenClientTypes } from '../../../src/application/domain/model/push.token' +import { PushTokenMock } from '../../mocks/models/push.token.mock' +import { PushRepoModel } from '../../../src/infrastructure/database/schema/push.schema' +import { expect } from 'chai' +import { GeneratorMock } from '../../mocks/generator.mock' +import { Strings } from '../../../src/utils/strings' +import { ChoiceTypes } from '../../../src/application/domain/utils/choice.types' +import { IPushRepository } from '../../../src/application/port/push.repository.interface' +import { Query } from '../../../src/infrastructure/repository/query/query' + +const dbConnection: IConnectionDB = DIContainer.get(Identifier.MONGODB_CONNECTION) +const app: App = DIContainer.get(Identifier.APP) +const request = require('supertest')(app.getExpress()) +const pushRepository: IPushRepository = DIContainer.get(Identifier.PUSH_REPOSITORY) + +describe('Routes: push', () => { + const direct_push: Push = new PushMock().generate(PushTypes.DIRECT) + const push_token: PushToken = new PushTokenMock().generate(PushTokenClientTypes.MOBILE) + + before(async () => { + try { + const mongoConfigs = Config.getMongoConfig() + await dbConnection.tryConnect(mongoConfigs.uri, mongoConfigs.options) + await DatabaseUtils.deleteMany(PushTokenRepoModel) + await DatabaseUtils.deleteMany(PushRepoModel) + } catch (err) { + throw new Error('Failure on push test: ' + err.message) + } + } + ) + + after(async () => { + try { + await DatabaseUtils.deleteMany(PushTokenRepoModel) + await DatabaseUtils.deleteMany(PushRepoModel) + await dbConnection.dispose() + } catch (err) { + throw new Error('Failure on push test: ' + err.message) + } + }) + + describe('POST /v1/push', () => { + context('when there are validation errors', () => { + before(async () => { + try { + await DatabaseUtils.deleteMany(PushTokenRepoModel) + await DatabaseUtils.deleteMany(PushRepoModel) + const token: any = await DatabaseUtils.create(PushTokenRepoModel, push_token.toJSON()) + direct_push.to = [token.user_id] + } catch (err) { + throw new Error('Failure on push test: ' + err.message) + } + }) + + context('when the Push is incomplete', () => { + it('should return status code 400 and info message about missing parameters (message.type, message.pt, message.eng)', + () => { + const body = { + type: direct_push.type, + keep_it: direct_push.keep_it, + to: direct_push.to, + message: {} + } + + return request + .post('/v1/push') + .send(body) + .set('Content-Type', 'application/json') + .expect(400) + .then(err => { + expect(err.body.message).to.eql(Strings.ERROR_MESSAGE.VALIDATE.REQUIRED_FIELDS) + expect(err.body.description).to.eql(Strings.ERROR_MESSAGE.VALIDATE.REQUIRED_FIELDS_DESC + .replace('{0}', 'message.type, message.pt, message.eng')) + }) + }) + + it('should return status code 400 and info message about missing parameters (message.pt.title)', () => { + const body = { + type: direct_push.type, + keep_it: direct_push.keep_it, + to: direct_push.to, + message: { + type: direct_push.message?.type, + pt: { + text: direct_push.message?.pt.text + }, + eng: direct_push.message?.eng + } + } + + return request + .post('/v1/push') + .send(body) + .set('Content-Type', 'application/json') + .expect(400) + .then(err => { + expect(err.body.message).to.eql(Strings.ERROR_MESSAGE.VALIDATE.REQUIRED_FIELDS) + expect(err.body.description).to.eql(Strings.ERROR_MESSAGE.VALIDATE.REQUIRED_FIELDS_DESC + .replace('{0}', 'message.pt.title')) + }) + }) + + it('should return status code 400 and info message about missing parameters (message.pt.text)', () => { + const body = { + type: direct_push.type, + keep_it: direct_push.keep_it, + to: direct_push.to, + message: { + type: direct_push.message?.type, + pt: { + title: direct_push.message?.pt.title + }, + eng: direct_push.message?.eng + } + } + + return request + .post('/v1/push') + .send(body) + .set('Content-Type', 'application/json') + .expect(400) + .then(err => { + expect(err.body.message).to.eql(Strings.ERROR_MESSAGE.VALIDATE.REQUIRED_FIELDS) + expect(err.body.description).to.eql(Strings.ERROR_MESSAGE.VALIDATE.REQUIRED_FIELDS_DESC + .replace('{0}', 'message.pt.text')) + }) + }) + + it('should return status code 400 and info message about missing all parameters', () => { + return request + .post('/v1/push') + .send({}) + .set('Content-Type', 'application/json') + .expect(400) + .then(err => { + expect(err.body.message).to.eql(Strings.ERROR_MESSAGE.VALIDATE.REQUIRED_FIELDS) + expect(err.body.description).to.eql(Strings.ERROR_MESSAGE.VALIDATE.REQUIRED_FIELDS_DESC + .replace('{0}', 'type, keep_it, to, message')) + }) + }) + }) + + context('when the Push type is invalid', () => { + const pushTypes: Array = Object.values(PushTypes) + + it('should return status code 400 and info message about unmapped type', () => { + const body = { + type: 'invalidPushType', + keep_it: direct_push.keep_it, + to: direct_push.to, + message: direct_push.message + } + + return request + .post('/v1/push') + .send(body) + .set('Content-Type', 'application/json') + .expect(400) + .then(err => { + expect(err.body.message).to.eql(`${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED + .replace('{0}', 'type')} invalidPushType`) + expect(err.body.description) + .to.eql(`${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED_DESC} ${pushTypes.join(', ')}.`) + }) + }) + }) + + context('when the Push keep_it is invalid', () => { + const choiceTypes: Array = Object.values(ChoiceTypes) + + it('should return status code 400 and info message about unmapped keep_it', () => { + const body = { + type: direct_push.type, + keep_it: 'invalidChoiceType', + to: direct_push.to, + message: direct_push.message + } + + return request + .post('/v1/push') + .send(body) + .set('Content-Type', 'application/json') + .expect(400) + .then(err => { + expect(err.body.message).to.eql(`${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED + .replace('{0}', 'keep_it')} invalidChoiceType`) + expect(err.body.description) + .to.eql(`${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED_DESC} ${choiceTypes.join(', ')}.`) + }) + }) + }) + + context('when the Push to is invalid', () => { + it('should return status code 400 and info message about empty to', () => { + const body = { + type: direct_push.type, + keep_it: direct_push.keep_it, + to: [], + message: direct_push.message + } + + return request + .post('/v1/push') + .send(body) + .set('Content-Type', 'application/json') + .expect(400) + .then(err => { + expect(err.body.message).to.eql(Strings.ERROR_MESSAGE.VALIDATE.AT_LEAST_ONE_RECIPIENT) + expect(err.body.description).to.eql(Strings.ERROR_MESSAGE.VALIDATE.AT_LEAST_ONE_RECIPIENT_DESC) + }) + }) + + it('should return status code 400 and info message about an invalid id in \'to\' array', () => { + const body = { + type: direct_push.type, + keep_it: direct_push.keep_it, + to: ['5f5a3c5accefbde6e36d1b31', '123', '4e3b3c5accefbde6e45d2c23'], + message: direct_push.message + } + + return request + .post('/v1/push') + .send(body) + .set('Content-Type', 'application/json') + .expect(400) + .then(err => { + expect(err.body.message).to.eql(Strings.ERROR_MESSAGE.VALIDATE.UUID_NOT_VALID_FORMAT) + expect(err.body.description).to.eql(Strings.ERROR_MESSAGE.VALIDATE.UUID_NOT_VALID_FORMAT_DESC) + }) + }) + }) + + context('when some user do not have saved push tokens', () => { + it('should return status code 400 and info message about the user not having saved push tokens', () => { + const random_id: string = GeneratorMock.generateObjectId() + return request + .post('/v1/push') + .send({ ...direct_push.toJSON(), to: [random_id] }) + .set('Content-Type', 'application/json') + .expect(400) + .then(err => { + expect(err.body.message).to.eql(Strings.ERROR_MESSAGE.VALIDATE.USER_HAS_NO_PUSH_TOKEN + .replace('{0}', random_id)) + expect(err.body.description).to.eql(Strings.ERROR_MESSAGE.VALIDATE.USER_HAS_NO_PUSH_TOKEN_DESC) + }) + }) + }) + }) + }) + + describe('POST /v1/push/:push_id/read', () => { + context('when want signalize that the push has been read', () => { + let result + + before(async () => { + try { + await DatabaseUtils.deleteMany(PushTokenRepoModel) + await DatabaseUtils.deleteMany(PushRepoModel) + const token: any = await DatabaseUtils.create(PushTokenRepoModel, push_token.toJSON()) + direct_push.to = [token.user_id] + result = await DatabaseUtils.create(PushRepoModel, direct_push.toJSON()) + } catch (err) { + throw new Error('Failure on push test: ' + err.message) + } + }) + it('should return status code 204 and no content', () => { + return request + .post(`/v1/push/${result.id}/read`) + .set('Content-Type', 'application/json') + .expect(204) + .then(async res => { + expect(res.body).to.be.empty + const pushRepo: Push = await pushRepository.findOne(new Query().fromJSON({ filters: { _id: result.id } })) + expect(pushRepo.is_read).to.eql(ChoiceTypes.YES) + }) + }) + }) + context('when the push does not exists', () => { + before(async () => { + try { + await DatabaseUtils.deleteMany(PushTokenRepoModel) + await DatabaseUtils.deleteMany(PushRepoModel) + } catch (err) { + throw new Error('Failure on push test: ' + err.message) + } + }) + it('should return status code 204 and no content', () => { + return request + .post(`/v1/push/${direct_push.id}/read`) + .set('Content-Type', 'application/json') + .expect(204) + .then(res => { + expect(res.body).to.be.empty + }) + }) + }) + + context('when there are validation errors', () => { + it('should return status code 400 and info message about invalid push id', () => { + return request + .post('/v1/push/123/read') + .set('Content-Type', 'application/json') + .expect(400) + .then(err => { + expect(err.body.message).to.eql(Strings.PUSH.PARAM_ID_NOT_VALID_FORMAT) + expect(err.body.description).to.eql(Strings.ERROR_MESSAGE.VALIDATE.UUID_NOT_VALID_FORMAT_DESC) + }) + }) + }) + }) + + describe('DELETE /v1/push/:push_id', () => { + context('when delete a saved push', () => { + let result + + before(async () => { + try { + await DatabaseUtils.deleteMany(PushTokenRepoModel) + await DatabaseUtils.deleteMany(PushRepoModel) + const token: any = await DatabaseUtils.create(PushTokenRepoModel, push_token.toJSON()) + direct_push.to = [token.user_id] + result = await DatabaseUtils.create(PushRepoModel, direct_push.toJSON()) + } catch (err) { + throw new Error('Failure on push test: ' + err.message) + } + }) + + it('should return status code 204 and no content', () => { + return request + .delete(`/v1/push/${result.id}`) + .set('Content-Type', 'application/json') + .expect(204) + .then(async res => { + expect(res.body).to.be.empty + const countPush = await pushRepository.count(new Query()) + expect(countPush).to.eql(0) + }) + }) + }) + + context('when the push is already removed', () => { + before(async () => { + try { + await DatabaseUtils.deleteMany(PushTokenRepoModel) + await DatabaseUtils.deleteMany(PushRepoModel) + } catch (err) { + throw new Error('Failure on push test: ' + err.message) + } + }) + it('should return status code 204 and no content', () => { + return request + .delete(`/v1/push/${direct_push.id}`) + .set('Content-Type', 'application/json') + .expect(204) + .then(res => { + expect(res.body).to.be.empty + }) + }) + }) + + + context('when there are validation errors', () => { + it('should return status code 400 and info message about invalid push id', () => { + return request + .delete('/v1/push/123') + .set('Content-Type', 'application/json') + .expect(400) + .then(err => { + expect(err.body.message).to.eql(Strings.PUSH.PARAM_ID_NOT_VALID_FORMAT) + expect(err.body.description).to.eql(Strings.ERROR_MESSAGE.VALIDATE.UUID_NOT_VALID_FORMAT_DESC) + }) + }) + }) + }) +}) diff --git a/test/integration/routes/users.push.spec.ts b/test/integration/routes/users.push.spec.ts new file mode 100644 index 0000000..9457446 --- /dev/null +++ b/test/integration/routes/users.push.spec.ts @@ -0,0 +1,101 @@ +import { IConnectionDB } from '../../../src/infrastructure/port/connection.db.interface' +import { DIContainer } from '../../../src/di/di' +import { Identifier } from '../../../src/di/identifiers' +import { App } from '../../../src/app' +import { Config } from '../../../src/utils/config' +import { DatabaseUtils } from '../../utils/database.utils' +import { PushTypes, Push } from '../../../src/application/domain/model/push' +import { PushMock } from '../../mocks/models/push.mock' +import { PushRepoModel } from '../../../src/infrastructure/database/schema/push.schema' +import { expect } from 'chai' +import { GeneratorMock } from '../../mocks/generator.mock' +import { Strings } from '../../../src/utils/strings' + +const dbConnection: IConnectionDB = DIContainer.get(Identifier.MONGODB_CONNECTION) +const app: App = DIContainer.get(Identifier.APP) +const request = require('supertest')(app.getExpress()) + +describe('Routes: users.push', () => { + const direct_push: Push = new PushMock().generate(PushTypes.DIRECT, [GeneratorMock.generateObjectId()]) + + before(async () => { + try { + const mongoConfigs = Config.getMongoConfig() + await dbConnection.tryConnect(mongoConfigs.uri, mongoConfigs.options) + await DatabaseUtils.deleteMany(PushRepoModel) + } catch (err) { + throw new Error('Failure on users.push test: ' + err.message) + } + } + ) + + after(async () => { + try { + await DatabaseUtils.deleteMany(PushRepoModel) + await dbConnection.dispose() + } catch (err) { + throw new Error('Failure on users.push test: ' + err.message) + } + }) + + describe('GET /v1/users/:user_id/push', () => { + context('when get the saved push notifications from user', () => { + before(async () => { + try { + await DatabaseUtils.deleteMany(PushRepoModel, {}) + await DatabaseUtils.create(PushRepoModel, direct_push.toJSON()) + } catch (err) { + throw new Error('Failure on users.push test: ' + err.message) + } + }) + it('should return status code 200 and a list of push notifications', () => { + return request + .get(`/v1/users/${direct_push.to![0]}/push`) + .set('Content-Type', 'application/json') + .expect(200) + .then(res => { + expect(res.body.length).to.be.eql(1) + expect(res.body[0]).to.have.property('id') + expect(res.body[0].type).to.be.eql(direct_push.type) + expect(res.body[0].is_read).to.be.eql(direct_push.is_read) + expect(res.body[0].to).to.be.eql(direct_push.to) + expect(res.body[0].message.type).to.be.eql(direct_push.message?.type) + expect(res.body[0].message.pt).to.be.eql(direct_push.message?.pt) + expect(res.body[0].message.eng).to.be.eql(direct_push.message?.eng) + }) + }) + }) + + context('when the user does not have push notifications', () => { + before(async () => { + try { + await DatabaseUtils.deleteMany(PushRepoModel, {}) + } catch (err) { + throw new Error('Failure on users.push test: ' + err.message) + } + }) + it('should return status code 200 and an empty list', () => { + return request + .get(`/v1/users/${direct_push.to![0]}/push`) + .set('Content-Type', 'application/json') + .expect(200) + .then(res => { + expect(res.body.length).to.be.eql(0) + }) + }) + }) + + context('when there are validation errors', () => { + it('should return status code 400 and info message about invalid user id', () => { + return request + .get(`/v1/users/123/push`) + .set('Content-Type', 'application/json') + .expect(400) + .then(err => { + expect(err.body.message).to.eql(Strings.USER.PARAM_ID_NOT_VALID_FORMAT) + expect(err.body.description).to.eql(Strings.ERROR_MESSAGE.VALIDATE.UUID_NOT_VALID_FORMAT_DESC) + }) + }) + }) + }) +}) diff --git a/test/integration/routes/users.push.tokens.spec.ts b/test/integration/routes/users.push.tokens.spec.ts new file mode 100644 index 0000000..a7808b5 --- /dev/null +++ b/test/integration/routes/users.push.tokens.spec.ts @@ -0,0 +1,473 @@ +import { IConnectionDB } from '../../../src/infrastructure/port/connection.db.interface' +import { DIContainer } from '../../../src/di/di' +import { Identifier } from '../../../src/di/identifiers' +import { App } from '../../../src/app' +import { Config } from '../../../src/utils/config' +import { DatabaseUtils } from '../../utils/database.utils' +import { PushTokenRepoModel } from '../../../src/infrastructure/database/schema/push.token.schema' +import { PushTokenMock } from '../../mocks/models/push.token.mock' +import { PushToken, PushTokenClientTypes } from '../../../src/application/domain/model/push.token' +import { expect } from 'chai' +import { Strings } from '../../../src/utils/strings' +import { IPushTokenRepository } from '../../../src/application/port/push.token.repository.interface' +import { Query } from '../../../src/infrastructure/repository/query/query' + +const dbConnection: IConnectionDB = DIContainer.get(Identifier.MONGODB_CONNECTION) +const app: App = DIContainer.get(Identifier.APP) +const request = require('supertest')(app.getExpress()) +const pushTokenRepository: IPushTokenRepository = DIContainer.get(Identifier.PUSH_TOKEN_REPOSITORY) + +describe('Routes: users.push.tokens', () => { + const web_push_token: PushToken = new PushTokenMock().generate(PushTokenClientTypes.WEB) + const mobile_push_token: PushToken = new PushTokenMock().generate(PushTokenClientTypes.MOBILE) + mobile_push_token.user_id = web_push_token.user_id + + before(async () => { + try { + const mongoConfigs = Config.getMongoConfig() + await dbConnection.tryConnect(mongoConfigs.uri, mongoConfigs.options) + await DatabaseUtils.deleteMany(PushTokenRepoModel) + } catch (err) { + throw new Error('Failure on users.push.tokens test: ' + err.message) + } + } + ) + + after(async () => { + try { + await DatabaseUtils.deleteMany(PushTokenRepoModel) + await dbConnection.dispose() + } catch (err) { + throw new Error('Failure on users.push.tokens test: ' + err.message) + } + }) + + describe('PUT /v1/users/:user_id/push/:client_type/tokens', () => { + context('when save a web client token successfully', () => { + before(async () => { + try { + await DatabaseUtils.deleteMany(PushTokenRepoModel) + } catch (err) { + throw new Error('Failure on users.push.tokens test: ' + err.message) + } + }) + it('should return status code 204 and no content for web client token', () => { + return request + .put(`/v1/users/${web_push_token.user_id}/push/${PushTokenClientTypes.WEB}/tokens`) + .send({ token: web_push_token.token }) + .set('Content-Type', 'application/json') + .expect(204) + .then(async res => { + expect(res.body).to.be.empty + const pushTokens = await pushTokenRepository.find(new Query().fromJSON( + { filters: { user_id: web_push_token.user_id } } + )) + expect(pushTokens.length).to.eql(1) + expect(pushTokens[0]).to.have.property('id') + expect(pushTokens[0].user_id).to.eql(web_push_token.user_id) + expect(pushTokens[0].client_type).to.eql(web_push_token.client_type) + expect(pushTokens[0].token).to.eql(web_push_token.token) + }) + }) + }) + + context('when save a mobile client token successfully', () => { + before(async () => { + try { + await DatabaseUtils.deleteMany(PushTokenRepoModel) + } catch (err) { + throw new Error('Failure on users.push.tokens test: ' + err.message) + } + }) + it('should return status code 204 and no content for mobile client token', () => { + return request + .put(`/v1/users/${mobile_push_token.user_id}/push/${PushTokenClientTypes.MOBILE}/tokens`) + .send({ token: mobile_push_token.token }) + .set('Content-Type', 'application/json') + .expect(204) + .then(async res => { + expect(res.body).to.be.empty + const pushTokens = await pushTokenRepository.find(new Query().fromJSON( + { filters: { user_id: mobile_push_token.user_id } } + )) + expect(pushTokens.length).to.eql(1) + expect(pushTokens[0]).to.have.property('id') + expect(pushTokens[0].user_id).to.eql(mobile_push_token.user_id) + expect(pushTokens[0].client_type).to.eql(mobile_push_token.client_type) + expect(pushTokens[0].token).to.eql(mobile_push_token.token) + }) + }) + }) + + context('when save a mobile client token for an user who already has a web client token', () => { + before(async () => { + try { + await DatabaseUtils.deleteMany(PushTokenRepoModel) + await DatabaseUtils.create(PushTokenRepoModel, web_push_token.toJSON()) + } catch (err) { + throw new Error('Failure on users.push.tokens test: ' + err.message) + } + }) + it('should return status code 204 and no content', () => { + return request + .put(`/v1/users/${web_push_token.user_id}/push/${PushTokenClientTypes.MOBILE}/tokens`) + .send({ token: mobile_push_token.token }) + .set('Content-Type', 'application/json') + .expect(204) + .then(async res => { + expect(res.body).to.be.empty + const pushTokens = await pushTokenRepository.find(new Query().fromJSON( + { filters: { user_id: mobile_push_token.user_id } } + )) + expect(pushTokens.length).to.eql(2) + expect(pushTokens[0]).to.have.property('id') + expect(pushTokens[0].user_id).to.eql(mobile_push_token.user_id) + expect(pushTokens[0].client_type).to.eql(mobile_push_token.client_type) + expect(pushTokens[0].token).to.eql(mobile_push_token.token) + expect(pushTokens[1]).to.have.property('id') + expect(pushTokens[1].user_id).to.eql(web_push_token.user_id) + expect(pushTokens[1].client_type).to.eql(web_push_token.client_type) + expect(pushTokens[1].token).to.eql(web_push_token.token) + }) + }) + }) + + context('when save a web client token for an user who already has a mobile client token', () => { + before(async () => { + try { + await DatabaseUtils.deleteMany(PushTokenRepoModel) + await DatabaseUtils.create(PushTokenRepoModel, mobile_push_token.toJSON()) + } catch (err) { + throw new Error('Failure on users.push.tokens test: ' + err.message) + } + }) + it('should return status code 204 and no content', () => { + return request + .put(`/v1/users/${mobile_push_token.user_id}/push/${PushTokenClientTypes.WEB}/tokens`) + .send({ token: web_push_token.token }) + .set('Content-Type', 'application/json') + .expect(204) + .then(async res => { + expect(res.body).to.be.empty + const pushTokens = await pushTokenRepository.find(new Query().fromJSON( + { filters: { user_id: mobile_push_token.user_id } } + )) + expect(pushTokens.length).to.eql(2) + expect(pushTokens[0]).to.have.property('id') + expect(pushTokens[0].user_id).to.eql(web_push_token.user_id) + expect(pushTokens[0].client_type).to.eql(web_push_token.client_type) + expect(pushTokens[0].token).to.eql(web_push_token.token) + expect(pushTokens[1]).to.have.property('id') + expect(pushTokens[1].user_id).to.eql(mobile_push_token.user_id) + expect(pushTokens[1].client_type).to.eql(mobile_push_token.client_type) + expect(pushTokens[1].token).to.eql(mobile_push_token.token) + }) + }) + }) + + context('when save a web client token for an user that already have a web client token', () => { + before(async () => { + try { + await DatabaseUtils.deleteMany(PushTokenRepoModel) + await DatabaseUtils.create(PushTokenRepoModel, web_push_token.toJSON()) + } catch (err) { + throw new Error('Failure on users.push.tokens test: ' + err.message) + } + }) + it('should return status code 204 and no content', () => { + return request + .put(`/v1/users/${web_push_token.user_id}/push/${PushTokenClientTypes.WEB}/tokens`) + .send(web_push_token.toJSON()) + .set('Content-Type', 'application/json') + .expect(204) + .then(async res => { + expect(res.body).to.be.empty + const pushTokens = await pushTokenRepository.find(new Query().fromJSON( + { filters: { user_id: mobile_push_token.user_id } } + )) + expect(pushTokens.length).to.eql(1) + expect(pushTokens[0]).to.have.property('id') + expect(pushTokens[0].user_id).to.eql(web_push_token.user_id) + expect(pushTokens[0].client_type).to.eql(web_push_token.client_type) + expect(pushTokens[0].token).to.eql(web_push_token.token) + }) + }) + }) + + context('when save a mobile client token for an user that already have a mobile client token', () => { + before(async () => { + try { + await DatabaseUtils.deleteMany(PushTokenRepoModel) + await DatabaseUtils.create(PushTokenRepoModel, mobile_push_token.toJSON()) + } catch (err) { + throw new Error('Failure on users.push.tokens test: ' + err.message) + } + }) + it('should return status code 204 and no content', () => { + return request + .put(`/v1/users/${mobile_push_token.user_id}/push/${PushTokenClientTypes.MOBILE}/tokens`) + .send(mobile_push_token.toJSON()) + .set('Content-Type', 'application/json') + .expect(204) + .then(async res => { + expect(res.body).to.be.empty + const pushTokens = await pushTokenRepository.find(new Query().fromJSON( + { filters: { user_id: mobile_push_token.user_id } } + )) + expect(pushTokens.length).to.eql(1) + expect(pushTokens[0]).to.have.property('id') + expect(pushTokens[0].user_id).to.eql(mobile_push_token.user_id) + expect(pushTokens[0].client_type).to.eql(mobile_push_token.client_type) + expect(pushTokens[0].token).to.eql(mobile_push_token.token) + }) + }) + }) + + context('when there are validation errors', () => { + it('should return status code 400 and message from missing parameters', () => { + return request + .put(`/v1/users/${web_push_token.user_id}/push/${web_push_token.client_type}/tokens`) + .send({}) + .set('Content-Type', 'application/json') + .expect(400) + .then(err => { + expect(err.body.message).to.eql(Strings.ERROR_MESSAGE.VALIDATE.REQUIRED_FIELDS) + expect(err.body.description).to.eql(Strings.ERROR_MESSAGE.VALIDATE.REQUIRED_FIELDS_DESC + .replace('{0}', 'token')) + }) + }) + + it('should return status code 400 and message from invalid user id', () => { + return request + .put(`/v1/users/123/push/${PushTokenClientTypes.MOBILE}/tokens`) + .send(web_push_token.toJSON()) + .set('Content-Type', 'application/json') + .expect(400) + .then(err => { + expect(err.body.message).to.eql(Strings.USER.PARAM_ID_NOT_VALID_FORMAT) + expect(err.body.description).to.eql(Strings.ERROR_MESSAGE.VALIDATE.UUID_NOT_VALID_FORMAT_DESC) + }) + }) + + it('should return status code 400 and message from invalid client type', () => { + const pushTokenClientTypes: Array = Object.values(PushTokenClientTypes) + return request + .put(`/v1/users/${web_push_token.user_id}/push/tablet/tokens`) + .send(web_push_token.toJSON()) + .set('Content-Type', 'application/json') + .expect(400) + .then(err => { + expect(err.body.message).to.eql(`${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED + .replace('{0}', 'client_type')} tablet`) + expect(err.body.description) + .to.eql(`${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED_DESC} ${pushTokenClientTypes.join(', ')}.`) + }) + }) + }) + }) + + describe('GET /v1/users/:user_id/push/tokens', () => { + context('when get the web and mobile tokens from user', () => { + before(async () => { + try { + await DatabaseUtils.deleteMany(PushTokenRepoModel) + await DatabaseUtils.create(PushTokenRepoModel, web_push_token.toJSON()) + await DatabaseUtils.create(PushTokenRepoModel, mobile_push_token.toJSON()) + } catch (err) { + throw new Error('Failure on users.push.tokens test: ' + err.message) + } + }) + it('should return status code 200 and both web and mobile token', () => { + return request + .get(`/v1/users/${web_push_token.user_id}/push/tokens`) + .set('Content-Type', 'application/json') + .expect(200) + .then(res => { + expect(res.body.web_token).to.eql(web_push_token.token) + expect(res.body.mobile_token).to.eql(mobile_push_token.token) + }) + }) + }) + + context('when the user has only a web client token', () => { + before(async () => { + try { + await DatabaseUtils.deleteMany(PushTokenRepoModel) + await DatabaseUtils.create(PushTokenRepoModel, web_push_token.toJSON()) + } catch (err) { + throw new Error('Failure on users.push.tokens test: ' + err.message) + } + }) + it('should return status code 200 and the web client token', () => { + return request + .get(`/v1/users/${web_push_token.user_id}/push/tokens`) + .set('Content-Type', 'application/json') + .expect(200) + .then(res => { + expect(res.body.web_token).to.eql(web_push_token.token) + expect(res.body.mobile_token).to.eql('') + }) + }) + }) + + context('when the user has only a mobile client token', () => { + before(async () => { + try { + await DatabaseUtils.deleteMany(PushTokenRepoModel) + await DatabaseUtils.create(PushTokenRepoModel, mobile_push_token.toJSON()) + } catch (err) { + throw new Error('Failure on users.push.tokens test: ' + err.message) + } + }) + it('should return status code 200 and the mobile client token', () => { + return request + .get(`/v1/users/${mobile_push_token.user_id}/push/tokens`) + .set('Content-Type', 'application/json') + .expect(200) + .then(res => { + expect(res.body.web_token).to.eql('') + expect(res.body.mobile_token).to.eql(mobile_push_token.token) + }) + }) + }) + + context('when the user does not have any type of token', () => { + before(async () => { + try { + await DatabaseUtils.deleteMany(PushTokenRepoModel) + } catch (err) { + throw new Error('Failure on users.push.tokens test: ' + err.message) + } + }) + it('should return status code 200 and an object with empty web_token and mobile_token.', () => { + return request + .get(`/v1/users/${web_push_token.user_id}/push/tokens`) + .set('Content-Type', 'application/json') + .expect(200) + .then(res => { + expect(res.body.web_token).to.eql('') + expect(res.body.mobile_token).to.eql('') + }) + }) + }) + + context('when there are validation errors', () => { + it('should return status code 400 and message from invalid user id', () => { + return request + .get('/v1/users/123/push/tokens') + .set('Content-Type', 'application/json') + .expect(400) + .then(err => { + expect(err.body.message).to.eql(Strings.USER.PARAM_ID_NOT_VALID_FORMAT) + expect(err.body.description).to.eql(Strings.ERROR_MESSAGE.VALIDATE.UUID_NOT_VALID_FORMAT_DESC) + }) + }) + }) + }) + + describe('DELETE /v1/users/:user_id/push/:client_type/tokens', () => { + context('when remove a web client token successfully', () => { + before(async () => { + try { + await DatabaseUtils.deleteMany(PushTokenRepoModel) + await DatabaseUtils.create(PushTokenRepoModel, web_push_token.toJSON()) + await DatabaseUtils.create(PushTokenRepoModel, mobile_push_token.toJSON()) + } catch (err) { + throw new Error('Failure on users.push.tokens test: ' + err.message) + } + }) + it('should return status code 204 and no content for web client token', () => { + return request + .delete(`/v1/users/${web_push_token.user_id}/push/${PushTokenClientTypes.WEB}/tokens`) + .set('Content-Type', 'application/json') + .expect(204) + .then(async res => { + expect(res.body).to.be.empty + const countWebPushToken = await pushTokenRepository.count(new Query().fromJSON( + { filters: { user_id: web_push_token.user_id, client_type: web_push_token.client_type } } + )) + expect(countWebPushToken).to.eql(0) + const countMobilePushToken = await pushTokenRepository.count(new Query().fromJSON( + { filters: { user_id: mobile_push_token.user_id, client_type: mobile_push_token.client_type } } + )) + expect(countMobilePushToken).to.eql(1) + }) + }) + }) + + context('when remove a mobile client token successfully', () => { + before(async () => { + try { + await DatabaseUtils.deleteMany(PushTokenRepoModel) + await DatabaseUtils.create(PushTokenRepoModel, web_push_token.toJSON()) + await DatabaseUtils.create(PushTokenRepoModel, mobile_push_token.toJSON()) + } catch (err) { + throw new Error('Failure on users.push.tokens test: ' + err.message) + } + }) + it('should return status code 204 and no content for mobile client token', () => { + return request + .delete(`/v1/users/${mobile_push_token.user_id}/push/${PushTokenClientTypes.MOBILE}/tokens`) + .set('Content-Type', 'application/json') + .expect(204) + .then(async res => { + expect(res.body).to.be.empty + const countWebPushToken = await pushTokenRepository.count(new Query().fromJSON( + { filters: { user_id: web_push_token.user_id, client_type: web_push_token.client_type } } + )) + expect(countWebPushToken).to.eql(1) + const countMobilePushToken = await pushTokenRepository.count(new Query().fromJSON( + { filters: { user_id: mobile_push_token.user_id, client_type: mobile_push_token.client_type } } + )) + expect(countMobilePushToken).to.eql(0) + }) + }) + }) + + context('when remove a push token from non-existent user', () => { + before(async () => { + try { + await DatabaseUtils.deleteMany(PushTokenRepoModel) + } catch (err) { + throw new Error('Failure on users.push.tokens test: ' + err.message) + } + }) + it('should return status code 204 and no content', () => { + return request + .delete(`/v1/users/${web_push_token.user_id}/push/${PushTokenClientTypes.WEB}/tokens`) + .set('Content-Type', 'application/json') + .expect(204) + .then(res => { + expect(res.body).to.be.empty + }) + }) + }) + + context('when there are validation errors', () => { + it('should return status code 400 and message from invalid user id', () => { + return request + .delete(`/v1/users/123/push/${PushTokenClientTypes.MOBILE}/tokens`) + .set('Content-Type', 'application/json') + .expect(400) + .then(err => { + expect(err.body.message).to.eql(Strings.USER.PARAM_ID_NOT_VALID_FORMAT) + expect(err.body.description).to.eql(Strings.ERROR_MESSAGE.VALIDATE.UUID_NOT_VALID_FORMAT_DESC) + }) + }) + + it('should return status code 400 and message from invalid client type', () => { + const pushTokenClientTypes: Array = Object.values(PushTokenClientTypes) + return request + .delete(`/v1/users/${web_push_token.user_id}/push/tablet/tokens`) + .set('Content-Type', 'application/json') + .expect(400) + .then(err => { + expect(err.body.message).to.eql(`${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED + .replace('{0}', 'client_type')} tablet`) + expect(err.body.description) + .to.eql(`${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED_DESC} ${pushTokenClientTypes.join(', ')}.`) + }) + }) + }) + }) +}) diff --git a/test/mocks/connection.factory.rabbitmq.mock.ts b/test/mocks/connection.factory.rabbitmq.mock.ts deleted file mode 100644 index b2f2fdc..0000000 --- a/test/mocks/connection.factory.rabbitmq.mock.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { IConnectionFactory, IEventBusOptions } from '../../src/infrastructure/port/connection.factory.interface' -import { Connection } from './connection.mock' - -export class ConnectionFactoryRabbitmqMock implements IConnectionFactory { - public createConnection(uri: string, options?: IEventBusOptions): Promise { - return Promise.resolve(new Connection()) - } -} diff --git a/test/mocks/connection.mock.ts b/test/mocks/connection.mock.ts deleted file mode 100644 index d841295..0000000 --- a/test/mocks/connection.mock.ts +++ /dev/null @@ -1,7 +0,0 @@ -export class Connection { - public isConnected: boolean - - constructor() { - this.isConnected = true - } -} diff --git a/test/mocks/connection.rabbitmq.mock.ts b/test/mocks/connection.rabbitmq.mock.ts deleted file mode 100644 index 906d729..0000000 --- a/test/mocks/connection.rabbitmq.mock.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { IConnectionEventBus } from '../../src/infrastructure/port/connection.event.bus.interface' -import { Connection } from './connection.mock' -import { IConnectionFactory, IEventBusOptions } from '../../src/infrastructure/port/connection.factory.interface' - -export class ConnectionRabbitmqMock implements IConnectionEventBus { - private _connection?: Connection - private _isOpen: boolean - - constructor(private readonly connectionFactory: IConnectionFactory) { - this._isOpen = true - } - - get isConnected(): boolean { - return this._connection ? this._connection.isConnected : false - } - - set isConnected(value: boolean) { - if (this._connection) this._connection.isConnected = value - } - - get conn(): any { - return new Connection() - } - - get isOpen(): boolean { - return this._isOpen - } - - set isOpen(value: boolean) { - this._isOpen = value - } - - public open(uri: string, options?: IEventBusOptions): Promise { - return new Promise((resolve, reject) => { - this.connectionFactory.createConnection(uri, options) - .then(conn => { - this._connection = conn - return resolve() - }) - .catch(err => { - this._connection = undefined - return reject(err) - }) - }) - } - - public close(): Promise { - return Promise.resolve(true) - } - - public dispose(): Promise { - return Promise.resolve(true) - } - - public on(event: string, listener: (...args: any[]) => void): void { - // not implemented! - } - - public publish(exchangeName: string, routingKey: string, message: any, options?: any): Promise { - return Promise.resolve() - } - - public subscribe(queueName: string, exchangeName: string, routingKey: string, - callback: (err: any, message: any) => void, options?: any): Promise { - return Promise.resolve() - } - - public createRpcServer(queueName: string, exchangeName: string, routingKeys: string[], options?: any): Promise { - return Promise.resolve() - } - - public rpcClient(exchangeName: string, resourceName: string, parameters: any[], - optOrCall?: any | ((err: any, message: any) => void), options?: any): any { - // not implemented! - } -} diff --git a/test/mocks/custom.logger.mock.ts b/test/mocks/custom.logger.mock.ts deleted file mode 100644 index 0b88f69..0000000 --- a/test/mocks/custom.logger.mock.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { ILogger } from '../../src/utils/custom.logger' - -export class CustomLoggerMock implements ILogger { - private _logger: any - - constructor() { - this._logger = { - add: {}, - debug: (message: string) => message, - error: (message: string) => message, - info: (message: string) => message, - silly: (message: string) => message, - verbose: (message: string) => message, - warn: (message: string) => message - } - } - - public addTransport(transport: any): any { - return this._logger.add(transport) - } - - get logger(): any { - return this._logger - } - - public debug(message: string): void { - this._logger.debug(message) - } - - public error(message: string): void { - this._logger.error(message) - } - - public info(message: string): void { - this._logger.info(message) - } - - public silly(message: string): void { - this._logger.silly(message) - } - - public verbose(message: string): void { - this._logger.verbose(message) - } - - public warn(message: string): void { - this._logger.warn(message) - } -} diff --git a/test/mocks/email.mock.ts b/test/mocks/email.mock.ts deleted file mode 100644 index ae63128..0000000 --- a/test/mocks/email.mock.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Email } from '../../src/application/domain/model/email' -import { Address } from '../../src/application/domain/model/address' -import { GeneratorMock } from './generator.mock' - -export class EmailMock extends Email { - - constructor() { - super() - this.generateUser() - } - - private generateUser(): void { - super.id = GeneratorMock.generateObjectId() - super.from = new Address('NOTIFICATION/HANIoT', process.env.SMTP_USER) - super.reply = new Address('NOTIFICATION/HANIoT', process.env.SMTP_USER) - super.to = new Array(new Address(`Test ${super.id}`, `${super.id}@mail.com`)) - super.subject = GeneratorMock.generateLoremIpsum(Math.floor(Math.random() * 100)) - super.text = GeneratorMock.generateLoremIpsum(Math.floor(Math.random() * 500)) - super.html = GeneratorMock.generateLoremIpsum(Math.floor(Math.random() * 2000)) - super.createdAt = new Date().toISOString() - super.userId = GeneratorMock.generateObjectId() - } -} diff --git a/test/mocks/generator.mock.ts b/test/mocks/generator.mock.ts index 6aa06a4..d821059 100644 --- a/test/mocks/generator.mock.ts +++ b/test/mocks/generator.mock.ts @@ -11,6 +11,15 @@ export class GeneratorMock { return randS } + public static generateRandomString(max_length: number): string { + const chars: string = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' + let randS = '' + for (let i = 0; i < max_length; i++) { + randS += chars.charAt(Math.floor(Math.random() * chars.length)) + } + return randS + } + public static async generateEmailTemp(email): Promise { return new Promise((resolve, reject) => { const options = { diff --git a/test/mocks/integration-event/generic.push.send.event.handler.mock.ts b/test/mocks/integration-event/generic.push.send.event.handler.mock.ts new file mode 100644 index 0000000..d7467a7 --- /dev/null +++ b/test/mocks/integration-event/generic.push.send.event.handler.mock.ts @@ -0,0 +1,8 @@ +import { GenericPushSendEventMock } from './generic.push.send.event.mock' +import { IIntegrationEventHandler } from '../../../src/application/integration-event/handler/integration.event.handler.interface' + +export class GenericPushSendEventHandlerMock implements IIntegrationEventHandler { + public handle(event: GenericPushSendEventMock): void { + return + } +} diff --git a/test/mocks/integration-event/generic.push.send.event.mock.ts b/test/mocks/integration-event/generic.push.send.event.mock.ts new file mode 100644 index 0000000..89aa94b --- /dev/null +++ b/test/mocks/integration-event/generic.push.send.event.mock.ts @@ -0,0 +1,19 @@ +import { IntegrationEvent } from '../../../src/application/integration-event/event/integration.event' +import { Push } from '../../../src/application/domain/model/push' + +export class GenericPushSendEventMock extends IntegrationEvent { + public static readonly ROUTING_KEY: string = 'generic.event' + public static readonly NAME: string = 'GenericPushSendEvent' + + constructor(public timestamp?: Date, public push?: Push) { + super(GenericPushSendEventMock.NAME, 'generic', timestamp) + } + + public toJSON(): any { + if (!this.push) return {} + return { + ...super.toJSON(), + push: this.push.toJSON() + } + } +} diff --git a/test/mocks/models/default.entity.mock.ts b/test/mocks/models/default.entity.mock.ts new file mode 100644 index 0000000..d81ea88 --- /dev/null +++ b/test/mocks/models/default.entity.mock.ts @@ -0,0 +1,35 @@ +import { GeneratorMock } from '../generator.mock' +import { PushTokenClientTypes } from '../../../src/application/domain/model/push.token' +import { PushTypes } from '../../../src/application/domain/model/push' +import { ChoiceTypes } from '../../../src/application/domain/utils/choice.types' + +export abstract class DefaultEntityMock { + // MODELS + public static readonly PUSH_MESSAGE: any = { + type: 'notification-type', + pt: { + title: 'Isto é um título.', + text: 'Isto é um texto.' + }, + eng: { + title: 'This is a title.', + text: 'This is a text.' + } + } + + public static readonly PUSH_TOKEN: any = { + id: GeneratorMock.generateObjectId(), + user_id: GeneratorMock.generateObjectId(), + client_type: PushTokenClientTypes.MOBILE, + token: GeneratorMock.generateRandomString(40) + } + + public static readonly PUSH: any = { + id: GeneratorMock.generateObjectId(), + type: PushTypes.DIRECT, + keep_it: ChoiceTypes.YES, + is_read: ChoiceTypes.NO, + to: [GeneratorMock.generateObjectId()], + message: DefaultEntityMock.PUSH_MESSAGE + } +} diff --git a/test/mocks/models/email.mock.ts b/test/mocks/models/email.mock.ts new file mode 100644 index 0000000..dfce6a4 --- /dev/null +++ b/test/mocks/models/email.mock.ts @@ -0,0 +1,21 @@ +import { Email } from '../../../src/application/domain/model/email' +import { Address } from '../../../src/application/domain/model/address' +import { GeneratorMock } from '../generator.mock' + +export class EmailMock { + + public generate(): Email { + const email: Email = new Email() + email.id = GeneratorMock.generateObjectId() + email.from = new Address('NOTIFICATION/HANIoT', process.env.SMTP_USER) + email.reply = new Address('NOTIFICATION/HANIoT', process.env.SMTP_USER) + email.to = new Array(new Address(`Test ${email.id}`, `${email.id}@mail.com`)) + email.subject = GeneratorMock.generateLoremIpsum(Math.floor(Math.random() * 100)) + email.text = GeneratorMock.generateLoremIpsum(Math.floor(Math.random() * 500)) + email.html = GeneratorMock.generateLoremIpsum(Math.floor(Math.random() * 2000)) + email.createdAt = new Date().toISOString() + email.userId = GeneratorMock.generateObjectId() + + return email + } +} diff --git a/test/mocks/models/push.message.mock.ts b/test/mocks/models/push.message.mock.ts new file mode 100644 index 0000000..f85f28d --- /dev/null +++ b/test/mocks/models/push.message.mock.ts @@ -0,0 +1,19 @@ +import { PushMessage } from '../../../src/application/domain/model/push.message' + +export class PushMessageMock { + + public generate(): PushMessage { + const pushMessage: PushMessage = new PushMessage() + pushMessage.type = 'notification-type' + pushMessage.pt = { + title: 'Isto é um título.', + text: 'Isto é um texto.' + } + pushMessage.eng = { + title: 'This is a title.', + text: 'This is a text.' + } + + return pushMessage + } +} diff --git a/test/mocks/models/push.mock.ts b/test/mocks/models/push.mock.ts new file mode 100644 index 0000000..dd33fd0 --- /dev/null +++ b/test/mocks/models/push.mock.ts @@ -0,0 +1,28 @@ +import { PushTypes, Push } from '../../../src/application/domain/model/push' +import { GeneratorMock } from '../generator.mock' +import { ChoiceTypes } from '../../../src/application/domain/utils/choice.types' +import { PushMessageMock } from './push.message.mock' + +export class PushMock { + + public generate(type?: string, to?: Array): Push { + const push: Push = new Push() + push.id = GeneratorMock.generateObjectId() + push.type = type ? type : this.generateType() + push.keep_it = push.type === PushTypes.DIRECT ? ChoiceTypes.YES : ChoiceTypes.NO + if (push.keep_it === ChoiceTypes.YES) push.is_read = ChoiceTypes.NO + push.to = to ? to : [GeneratorMock.generateObjectId()] + push.message = new PushMessageMock().generate() + + return push + } + + private generateType(): PushTypes { + const pushTypes = { + 0: PushTypes.DIRECT, + 1: PushTypes.TOPIC + } + + return pushTypes[Math.floor((Math.random() * 2))] // 0-1 + } +} diff --git a/test/mocks/models/push.token.mock.ts b/test/mocks/models/push.token.mock.ts new file mode 100644 index 0000000..15f1c9e --- /dev/null +++ b/test/mocks/models/push.token.mock.ts @@ -0,0 +1,24 @@ +import { PushToken, PushTokenClientTypes } from '../../../src/application/domain/model/push.token' +import { GeneratorMock } from '../generator.mock' + +export class PushTokenMock { + + public generate(clientType?: string): PushToken { + const pushToken: PushToken = new PushToken() + pushToken.id = GeneratorMock.generateObjectId() + pushToken.user_id = GeneratorMock.generateObjectId() + pushToken.client_type = clientType ? clientType : this.generateClientType() + pushToken.token = GeneratorMock.generateRandomString(40) + + return pushToken + } + + private generateClientType(): PushTokenClientTypes { + const clientTypes = { + 0: PushTokenClientTypes.WEB, + 1: PushTokenClientTypes.MOBILE + } + + return clientTypes[Math.floor((Math.random() * 2))] // 0-1 + } +} diff --git a/test/mocks/mongodb.model.mock.ts b/test/mocks/mongodb.model.mock.ts deleted file mode 100644 index 31c6c92..0000000 --- a/test/mocks/mongodb.model.mock.ts +++ /dev/null @@ -1,42 +0,0 @@ -import sinon from 'sinon' - -export class MongoDBModelMock { - public mock = { - find: {}, - select: this.select(null), - findOne: this.findOne(null) - } - - public find(conditions: any) { - return this - } - - public findOne(conditions: any) { - return this - } - - public select(arg: string | any) { - return this - } - - public sort(arg: string | any) { - return this - } - - public skip(arg: number) { - return this - } - - public limit(arg: number) { - return this - } - - public exec() { - return this - } - - constructor() { - sinon.stub(this.mock, `find`) - sinon.stub(this.mock, `select`) - } -} diff --git a/test/unit/events/push.send.event.spec.ts b/test/unit/events/push.send.event.spec.ts new file mode 100644 index 0000000..3eac06f --- /dev/null +++ b/test/unit/events/push.send.event.spec.ts @@ -0,0 +1,48 @@ +import { assert } from 'chai' +import { EventType } from '../../../src/application/integration-event/event/integration.event' +import { Push, PushTypes } from '../../../src/application/domain/model/push' +import { PushMock } from '../../mocks/models/push.mock' +import { PushSendEvent } from '../../../src/application/integration-event/event/push.send.event' + +describe('EVENTS: PushSendEvent', () => { + describe('toJSON()', () => { + context('when toJSON() is executed', () => { + it('should return a JSON from a complete PushSendEvent', () => { + const push: Push = new PushMock().generate(PushTypes.DIRECT) + const timestamp: Date = new Date() + const pushSendEvent: PushSendEvent = new PushSendEvent(timestamp, push) + const result: any = pushSendEvent.toJSON() + + assert.propertyVal(result, 'event_name', PushSendEvent.NAME) + assert.propertyVal(result, 'timestamp', timestamp) + assert.propertyVal(result, 'type', EventType.PUSH) + assert.deepPropertyVal(result, 'push', push.toJSON()) + }) + + it('should return a JSON with some attributes equal to undefined from a PushSendEvent ' + + 'with an incomplete Push', () => { + const timestamp: Date = new Date() + const pushSendEvent: PushSendEvent = new PushSendEvent(timestamp, new Push()) + const result: any = pushSendEvent.toJSON() + + assert.propertyVal(result, 'event_name', PushSendEvent.NAME) + assert.propertyVal(result, 'timestamp', timestamp) + assert.propertyVal(result, 'type', EventType.PUSH) + assert.isUndefined(result.push.id) + assert.isUndefined(result.push.type) + assert.isUndefined(result.push.keep_it) + assert.isUndefined(result.push.is_read) + assert.isUndefined(result.push.to) + assert.isUndefined(result.push.message) + }) + + it('should return an empty JSON from a PushSendEvent without Push', () => { + const timestamp: Date = new Date() + const pushSendEvent: PushSendEvent = new PushSendEvent(timestamp) + const result: any = pushSendEvent.toJSON() + + assert.isEmpty(result) + }) + }) + }) +}) diff --git a/test/unit/exceptions/api.exception.manager.spec.ts b/test/unit/exceptions/api.exception.manager.spec.ts new file mode 100644 index 0000000..efd5398 --- /dev/null +++ b/test/unit/exceptions/api.exception.manager.spec.ts @@ -0,0 +1,109 @@ +import { assert } from 'chai' +import { ApiExceptionManager } from '../../../src/ui/exception/api.exception.manager' +import { ValidationException } from '../../../src/application/domain/exception/validation.exception' +import { ApiException } from '../../../src/ui/exception/api.exception' +import { RepositoryException } from '../../../src/application/domain/exception/repository.exception' +import { ConflictException } from '../../../src/application/domain/exception/conflict.exception' +import { EventBusException } from '../../../src/application/domain/exception/eventbus.exception' +import { FirebaseClientException } from '../../../src/application/domain/exception/firebase.client.exception' + +describe('EXCEPTIONS: ApiExceptionManager', () => { + it('should return the ValidationException object transformed to ApiException with code 400, message and description.', () => { + const exception: ApiException = ApiExceptionManager.build( + new ValidationException('ValidationException message', 'ValidationException description') + ) + + assert.propertyVal(exception, 'code', 400) + assert.propertyVal(exception, 'message', 'ValidationException message') + assert.propertyVal(exception, 'description', 'ValidationException description') + }) + + it('should return the ValidationException object transformed to ApiException with code 400 and message.', () => { + const exception: ApiException = ApiExceptionManager.build(new ValidationException('ValidationException message')) + + assert.propertyVal(exception, 'code', 400) + assert.propertyVal(exception, 'message', 'ValidationException message') + assert.propertyVal(exception, 'description', undefined) + }) + + it('should return the ConflictException object transformed to ApiException with code 409, message and description.', () => { + const exception: ApiException = ApiExceptionManager.build( + new ConflictException('ConflictException message', 'ConflictException description') + ) + + assert.propertyVal(exception, 'code', 409) + assert.propertyVal(exception, 'message', 'ConflictException message') + assert.propertyVal(exception, 'description', 'ConflictException description') + }) + + it('should return the ConflictException object transformed to ApiException with code 409 and message.', () => { + const exception: ApiException = ApiExceptionManager.build(new ConflictException('ConflictException message')) + + assert.propertyVal(exception, 'code', 409) + assert.propertyVal(exception, 'message', 'ConflictException message') + assert.propertyVal(exception, 'description', undefined) + }) + + it('should return the RepositoryException object transformed to ApiException with code 500, message and description.', () => { + const exception: ApiException = ApiExceptionManager.build( + new RepositoryException('RepositoryException message', 'RepositoryException description') + ) + + assert.propertyVal(exception, 'code', 500) + assert.propertyVal(exception, 'message', 'RepositoryException message') + assert.propertyVal(exception, 'description', 'RepositoryException description') + }) + + it('should return the RepositoryException object transformed to ApiException with code 500 and message.', () => { + const exception: ApiException = ApiExceptionManager.build(new RepositoryException('RepositoryException message')) + + assert.propertyVal(exception, 'code', 500) + assert.propertyVal(exception, 'message', 'RepositoryException message') + assert.propertyVal(exception, 'description', undefined) + }) + + it('should return the FirebaseClientException object transformed to ApiException with code 500, message and description.', + () => { + const exception: ApiException = ApiExceptionManager.build( + new FirebaseClientException(500, 'FirebaseClientException message', 'FirebaseClientException description') + ) + + assert.propertyVal(exception, 'code', 500) + assert.propertyVal(exception, 'message', 'FirebaseClientException message') + assert.propertyVal(exception, 'description', 'FirebaseClientException description') + }) + + it('should return the FirebaseClientException object transformed to ApiException with code 500 and message.', () => { + const exception: ApiException = ApiExceptionManager.build( + new FirebaseClientException(500, 'FirebaseClientException message')) + + assert.propertyVal(exception, 'code', 500) + assert.propertyVal(exception, 'message', 'FirebaseClientException message') + assert.propertyVal(exception, 'description', undefined) + }) + + it('should return the EventBusException object transformed to ApiException with code 500, message and description.', () => { + const exception: ApiException = ApiExceptionManager.build( + new EventBusException('Event Bus error message', 'Event Bus error description')) + + assert.propertyVal(exception, 'code', 500) + assert.propertyVal(exception, 'message', 'Event Bus error message') + assert.propertyVal(exception, 'description', 'Event Bus error description') + }) + + it('should return the EventBusException object transformed to ApiException with code 500 and message.', () => { + const exception: ApiException = ApiExceptionManager.build(new EventBusException('Event Bus error message')) + + assert.propertyVal(exception, 'code', 500) + assert.propertyVal(exception, 'message', 'Event Bus error message') + assert.propertyVal(exception, 'description', undefined) + }) + + it('should return the Error object transformed to ApiException with code 500 and message.', () => { + const exception: ApiException = ApiExceptionManager.build(new Error('Error message')) + + assert.propertyVal(exception, 'code', 500) + assert.propertyVal(exception, 'message', 'Error message') + assert.propertyVal(exception, 'description', undefined) + }) +}) diff --git a/test/unit/mappers/push.entity.mapper.spec.ts b/test/unit/mappers/push.entity.mapper.spec.ts new file mode 100644 index 0000000..08c0a73 --- /dev/null +++ b/test/unit/mappers/push.entity.mapper.spec.ts @@ -0,0 +1,68 @@ +import { assert } from 'chai' +import { Push, PushTypes } from '../../../src/application/domain/model/push' +import { PushEntityMapper } from '../../../src/infrastructure/entity/mapper/push.entity.mapper' +import { PushEntity } from '../../../src/infrastructure/entity/push.entity' +import { PushMock } from '../../mocks/models/push.mock' +import { DefaultEntityMock } from '../../mocks/models/default.entity.mock' + +describe('MAPPERS: PushEntityMapper', () => { + const pushTokenEntityMapper: PushEntityMapper = new PushEntityMapper() + + // Create Push model. + const push: Push = new PushMock().generate(PushTypes.DIRECT) + + // Create Push JSON. + const pushJSON: any = DefaultEntityMock.PUSH + + describe('transform(item: any)', () => { + context('when the parameter is of type Push', () => { + it('should return a PushEntity from a complete Push', () => { + const result: PushEntity = pushTokenEntityMapper.transform(push) + + assert.propertyVal(result, 'id', push.id) + assert.propertyVal(result, 'type', push.type) + assert.propertyVal(result, 'keep_it', push.keep_it) + assert.propertyVal(result, 'is_read', push.is_read) + assert.propertyVal(result, 'to', push.to) + assert.propertyVal(result.message, 'type', push.message?.type) + assert.propertyVal(result.message, 'pt', push.message?.pt) + assert.propertyVal(result.message, 'eng', push.message?.eng) + }) + + it('should return an empty PushEntity from empty Push', () => { + const result: PushEntity = pushTokenEntityMapper.transform(new Push()) + + assert.isEmpty(result) + }) + }) + + context('when the parameter is a JSON', () => { + it('should return a Push from a complete JSON', () => { + const result: Push = pushTokenEntityMapper.transform(pushJSON) + + assert.propertyVal(result, 'id', pushJSON.id) + assert.propertyVal(result, 'type', pushJSON.type) + assert.propertyVal(result, 'keep_it', pushJSON.keep_it) + assert.propertyVal(result, 'is_read', pushJSON.is_read) + assert.propertyVal(result, 'to', pushJSON.to) + assert.propertyVal(result.message, 'type', pushJSON.message.type) + assert.propertyVal(result.message, 'pt', pushJSON.message.pt) + assert.propertyVal(result.message, 'eng', pushJSON.message.eng) + }) + + it('should return a Push with some attributes equal to undefined from an empty JSON', () => { + const result: Push = pushTokenEntityMapper.transform({}) + + assert.isUndefined(result.id) + }) + }) + + context('when the parameter is undefined', () => { + it('should return a Push with some attributes equal to undefined from undefined json', () => { + const result: Push = pushTokenEntityMapper.transform(undefined) + + assert.isUndefined(result.id) + }) + }) + }) +}) diff --git a/test/unit/mappers/push.token.entity.mapper.spec.ts b/test/unit/mappers/push.token.entity.mapper.spec.ts new file mode 100644 index 0000000..cb421dc --- /dev/null +++ b/test/unit/mappers/push.token.entity.mapper.spec.ts @@ -0,0 +1,60 @@ +import { assert } from 'chai' +import { PushToken } from '../../../src/application/domain/model/push.token' +import { PushTokenEntityMapper } from '../../../src/infrastructure/entity/mapper/push.token.entity.mapper' +import { PushTokenEntity } from '../../../src/infrastructure/entity/push.token.entity' +import { PushTokenMock } from '../../mocks/models/push.token.mock' +import { DefaultEntityMock } from '../../mocks/models/default.entity.mock' + +describe('MAPPERS: PushTokenEntityMapper', () => { + const pushTokenEntityMapper: PushTokenEntityMapper = new PushTokenEntityMapper() + + // Create PushToken model. + const pushToken: PushToken = new PushTokenMock().generate() + + // Create PushToken JSON. + const pushTokenJSON: any = DefaultEntityMock.PUSH_TOKEN + + describe('transform(item: any)', () => { + context('when the parameter is of type PushToken', () => { + it('should return a PushTokenEntity from a complete PushToken', () => { + const result: PushTokenEntity = pushTokenEntityMapper.transform(pushToken) + + assert.propertyVal(result, 'id', pushToken.id) + assert.propertyVal(result, 'user_id', pushToken.user_id) + assert.propertyVal(result, 'client_type', pushToken.client_type) + assert.propertyVal(result, 'token', pushToken.token) + }) + + it('should return an empty PushTokenEntity from empty PushToken', () => { + const result: PushTokenEntity = pushTokenEntityMapper.transform(new PushToken()) + + assert.isEmpty(result) + }) + }) + + context('when the parameter is a JSON', () => { + it('should return a PushToken from a complete JSON', () => { + const result: PushToken = pushTokenEntityMapper.transform(pushTokenJSON) + + assert.propertyVal(result, 'id', pushTokenJSON.id) + assert.propertyVal(result, 'user_id', pushTokenJSON.user_id) + assert.propertyVal(result, 'client_type', pushTokenJSON.client_type) + assert.propertyVal(result, 'token', pushTokenJSON.token) + }) + + it('should return a PushToken with some attributes equal to undefined from an empty JSON', () => { + const result: PushToken = pushTokenEntityMapper.transform({}) + + assert.isUndefined(result.id) + }) + }) + + context('when the parameter is undefined', () => { + it('should return a PushToken with some attributes equal to undefined from undefined json', () => { + const result: PushToken = pushTokenEntityMapper.transform(undefined) + + assert.isUndefined(result.id) + }) + }) + }) +}) diff --git a/test/unit/models/push.message.model.spec.ts b/test/unit/models/push.message.model.spec.ts new file mode 100644 index 0000000..eb401ce --- /dev/null +++ b/test/unit/models/push.message.model.spec.ts @@ -0,0 +1,76 @@ +import { assert } from 'chai' +import { PushMessage } from '../../../src/application/domain/model/push.message' +import { DefaultEntityMock } from '../../mocks/models/default.entity.mock' + +describe('MODELS: PushMessage', () => { + const pushMessageJSON: any = JSON.parse(JSON.stringify(DefaultEntityMock.PUSH_MESSAGE)) + + describe('fromJSON()', () => { + context('when a json is passed', () => { + it('should return a PushMessage from a complete json', () => { + const result: PushMessage = new PushMessage().fromJSON(pushMessageJSON) + + assert.propertyVal(result, 'type', pushMessageJSON.type) + assert.propertyVal(result, 'pt', pushMessageJSON.pt) + assert.propertyVal(result, 'eng', pushMessageJSON.eng) + }) + + it('should return an empty PushMessage from an empty json', () => { + const result: PushMessage = new PushMessage().fromJSON({}) + + assert.isEmpty(result) + }) + }) + + context('when the parameter is undefined', () => { + it('should return an empty PushMessage from an undefined json', () => { + const result: PushMessage = new PushMessage().fromJSON(undefined) + + assert.isEmpty(result) + }) + }) + + context('when the json is a string', () => { + it('should return a PushMessage from a complete json', () => { + const result: PushMessage = new PushMessage().fromJSON(JSON.stringify(pushMessageJSON)) + + assert.propertyVal(result, 'type', pushMessageJSON.type) + assert.deepPropertyVal(result, 'pt', pushMessageJSON.pt) + assert.deepPropertyVal(result, 'eng', pushMessageJSON.eng) + }) + + it('should return an empty PushMessage from an empty string', () => { + const result: PushMessage = new PushMessage().fromJSON(JSON.stringify('')) + + assert.isEmpty(result) + }) + + it('should return an empty PushMessage from an invalid string', () => { + const result: PushMessage = new PushMessage().fromJSON('d52215d412') + + assert.isEmpty(result) + }) + }) + }) + + describe('toJSON()', () => { + context('when toJSON() is executed', () => { + it('should return a JSON from a complete PushMessage', () => { + const pushMessage: PushMessage = new PushMessage().fromJSON(pushMessageJSON) + const result: any = pushMessage.toJSON() + + assert.propertyVal(result, 'type', pushMessageJSON.type) + assert.propertyVal(result, 'pt', pushMessageJSON.pt) + assert.propertyVal(result, 'eng', pushMessageJSON.eng) + }) + + it('should return a JSON with all attributes equal to undefined from an incomplete PushMessage', () => { + const result: any = new PushMessage().toJSON() + + assert.isUndefined(result.type) + assert.isUndefined(result.pt) + assert.isUndefined(result.eng) + }) + }) + }) +}) diff --git a/test/unit/models/push.model.spec.ts b/test/unit/models/push.model.spec.ts new file mode 100644 index 0000000..900319d --- /dev/null +++ b/test/unit/models/push.model.spec.ts @@ -0,0 +1,92 @@ +import { assert } from 'chai' +import { Push } from '../../../src/application/domain/model/push' +import { DefaultEntityMock } from '../../mocks/models/default.entity.mock' + +describe('MODELS: Push', () => { + const pushJSON: any = JSON.parse(JSON.stringify(DefaultEntityMock.PUSH)) + + describe('fromJSON()', () => { + context('when a json is passed', () => { + it('should return a Push from a complete json', () => { + const result: Push = new Push().fromJSON(pushJSON) + + assert.propertyVal(result, 'id', pushJSON.id) + assert.propertyVal(result, 'type', pushJSON.type) + assert.propertyVal(result, 'keep_it', pushJSON.keep_it) + assert.propertyVal(result, 'is_read', pushJSON.is_read) + assert.propertyVal(result, 'to', pushJSON.to) + assert.propertyVal(result.message, 'type', pushJSON.message.type) + assert.propertyVal(result.message, 'pt', pushJSON.message.pt) + assert.propertyVal(result.message, 'eng', pushJSON.message.eng) + }) + + it('should return a Push with some attributes equal to undefined from an empty json', () => { + const result: Push = new Push().fromJSON({}) + + assert.isUndefined(result.id) + }) + }) + + context('when the parameter is undefined', () => { + it('should return a Push with some attributes equal to undefined from an undefined json', () => { + const result: Push = new Push().fromJSON(undefined) + + assert.isUndefined(result.id) + }) + }) + + context('when the json is a string', () => { + it('should return a Push from a complete json', () => { + const result: Push = new Push().fromJSON(JSON.stringify(pushJSON)) + + assert.propertyVal(result, 'id', pushJSON.id) + assert.propertyVal(result, 'type', pushJSON.type) + assert.propertyVal(result, 'keep_it', pushJSON.keep_it) + assert.propertyVal(result, 'is_read', pushJSON.is_read) + assert.deepPropertyVal(result, 'to', pushJSON.to) + assert.propertyVal(result.message, 'type', pushJSON.message.type) + assert.deepPropertyVal(result.message, 'pt', pushJSON.message.pt) + assert.deepPropertyVal(result.message, 'eng', pushJSON.message.eng) + }) + + it('should return a Push with some attributes equal to undefined from an empty string', () => { + const result: Push = new Push().fromJSON(JSON.stringify('')) + + assert.isUndefined(result.id) + }) + + it('should return a Push with some attributes equal to undefined from an invalid string', () => { + const result: Push = new Push().fromJSON('d52215d412') + + assert.isUndefined(result.id) + }) + }) + }) + + describe('toJSON()', () => { + context('when toJSON() is executed', () => { + it('should return a JSON from a complete Push', () => { + const push: Push = new Push().fromJSON(pushJSON) + const result: any = push.toJSON() + + assert.propertyVal(result, 'id', pushJSON.id) + assert.propertyVal(result, 'type', pushJSON.type) + assert.propertyVal(result, 'keep_it', pushJSON.keep_it) + assert.propertyVal(result, 'is_read', pushJSON.is_read) + assert.propertyVal(result, 'to', pushJSON.to) + assert.deepPropertyVal(result, 'message', pushJSON.message) + }) + + it('should return a JSON with all attributes equal to undefined from an incomplete Push', () => { + const result: any = new Push().toJSON() + + assert.isUndefined(result.id) + assert.isUndefined(result.type) + assert.isUndefined(result.keep_it) + assert.isUndefined(result.is_read) + assert.isUndefined(result.to) + assert.isUndefined(result.message) + }) + }) + }) +}) diff --git a/test/unit/models/push.token.model.spec.ts b/test/unit/models/push.token.model.spec.ts new file mode 100644 index 0000000..2f7d3f9 --- /dev/null +++ b/test/unit/models/push.token.model.spec.ts @@ -0,0 +1,80 @@ +import { assert } from 'chai' +import { PushToken } from '../../../src/application/domain/model/push.token' +import { DefaultEntityMock } from '../../mocks/models/default.entity.mock' + +describe('MODELS: PushToken', () => { + const pushTokenJSON: any = JSON.parse(JSON.stringify(DefaultEntityMock.PUSH_TOKEN)) + + describe('fromJSON()', () => { + context('when a json is passed', () => { + it('should return a PushToken from a complete json', () => { + const result: PushToken = new PushToken().fromJSON(pushTokenJSON) + + assert.propertyVal(result, 'id', pushTokenJSON.id) + assert.propertyVal(result, 'user_id', pushTokenJSON.user_id) + assert.propertyVal(result, 'client_type', pushTokenJSON.client_type) + assert.propertyVal(result, 'token', pushTokenJSON.token) + }) + + it('should return a PushToken with some attributes equal to undefined from an empty json', () => { + const result: PushToken = new PushToken().fromJSON({}) + + assert.isUndefined(result.id) + }) + }) + + context('when the parameter is undefined', () => { + it('should return a PushToken with some attributes equal to undefined from an undefined json', () => { + const result: PushToken = new PushToken().fromJSON(undefined) + + assert.isUndefined(result.id) + }) + }) + + context('when the json is a string', () => { + it('should return a PushToken from a complete json', () => { + const result: PushToken = new PushToken().fromJSON(JSON.stringify(pushTokenJSON)) + + assert.propertyVal(result, 'id', pushTokenJSON.id) + assert.propertyVal(result, 'user_id', pushTokenJSON.user_id) + assert.propertyVal(result, 'client_type', pushTokenJSON.client_type) + assert.propertyVal(result, 'token', pushTokenJSON.token) + }) + + it('should return a PushToken with some attributes equal to undefined from an empty string', () => { + const result: PushToken = new PushToken().fromJSON(JSON.stringify('')) + + assert.isUndefined(result.id) + }) + + it('should return a PushToken with some attributes equal to undefined from an invalid string', () => { + const result: PushToken = new PushToken().fromJSON('d52215d412') + + assert.isUndefined(result.id) + }) + }) + }) + + describe('toJSON()', () => { + context('when toJSON() is executed', () => { + it('should return a JSON from a complete PushToken', () => { + const pushToken: PushToken = new PushToken().fromJSON(pushTokenJSON) + const result: any = pushToken.toJSON() + + assert.propertyVal(result, 'id', pushTokenJSON.id) + assert.propertyVal(result, 'user_id', pushTokenJSON.user_id) + assert.propertyVal(result, 'client_type', pushTokenJSON.client_type) + assert.propertyVal(result, 'token', pushTokenJSON.token) + }) + + it('should return a JSON with all attributes equal to undefined from an incomplete PushToken', () => { + const result: any = new PushToken().toJSON() + + assert.isUndefined(result.id) + assert.isUndefined(result.user_id) + assert.isUndefined(result.client_type) + assert.isUndefined(result.token) + }) + }) + }) +}) diff --git a/test/unit/utils/custom.logger.spec.ts b/test/unit/utils/custom.logger.spec.ts index 4fa5ade..5812848 100644 --- a/test/unit/utils/custom.logger.spec.ts +++ b/test/unit/utils/custom.logger.spec.ts @@ -22,12 +22,12 @@ describe('UTILS: CustomLogger', () => { it('should be possible to change the module name.', () => { const logger = new CustomLogger() - assert.equal(logger.moduleName, Default.APP_ID) + assert.propertyVal(logger, 'moduleName', Default.APP_ID) // change module name logger.moduleName = 'test.app' - assert.equal(logger.moduleName, 'test.app') + assert.propertyVal(logger, 'moduleName', 'test.app') }) }) @@ -40,8 +40,8 @@ describe('UTILS: CustomLogger', () => { const expectedMessage = 'testing logger.error("str")' const transport = new transports.Console({ log: (info) => { - assert.equal(info.level, 'error') - assert.equal(info.message, expectedMessage) + assert.propertyVal(info, 'level', 'error') + assert.propertyVal(info, 'message', expectedMessage) done() } }) @@ -53,8 +53,8 @@ describe('UTILS: CustomLogger', () => { const expectedMessage = 'testing logger.warn("str")' const transport = new transports.Console({ log: (info) => { - assert.equal(info.level, 'warn') - assert.equal(info.message, expectedMessage) + assert.propertyVal(info, 'level', 'warn') + assert.propertyVal(info, 'message', expectedMessage) done() } }) @@ -66,8 +66,8 @@ describe('UTILS: CustomLogger', () => { const expectedMessage = 'testing logger.info("str")' const transport = new transports.Console({ log: (info) => { - assert.equal(info.level, 'info') - assert.equal(info.message, expectedMessage) + assert.propertyVal(info, 'level', 'info') + assert.propertyVal(info, 'message', expectedMessage) done() } }) @@ -79,8 +79,8 @@ describe('UTILS: CustomLogger', () => { const expectedMessage = 'testing logger.debug("str")' const transport = new transports.Console({ log: (info) => { - assert.equal(info.level, 'debug') - assert.equal(info.message, expectedMessage) + assert.propertyVal(info, 'level', 'debug') + assert.propertyVal(info, 'message', expectedMessage) done() } }) @@ -96,12 +96,12 @@ describe('UTILS: CustomLogger', () => { const expectedMessage = new CustomException('testing logger.error(Error)', 'description...') const transport = new transports.Console({ log: (info) => { - assert.equal(info.level, 'error') - assert.equal( - info.message, expectedMessage.message + assert.propertyVal(info, 'level', 'error') + assert.propertyVal( + info, 'message', expectedMessage.message .concat(expectedMessage.description ? ` | ${expectedMessage.description}` : '') ) - assert.deepEqual(info.stack, expectedMessage.stack) + assert.propertyVal(info, 'stack', expectedMessage.stack) done() } }) @@ -113,12 +113,12 @@ describe('UTILS: CustomLogger', () => { const expectedMessage = new CustomException('testing logger.warn(Error)', 'description...') const transport = new transports.Console({ log: (info) => { - assert.equal(info.level, 'warn') - assert.equal( - info.message, expectedMessage.message + assert.propertyVal(info, 'level', 'warn') + assert.propertyVal( + info, 'message', expectedMessage.message .concat(expectedMessage.description ? ` | ${expectedMessage.description}` : '') ) - assert.deepEqual(info.stack, expectedMessage.stack) + assert.propertyVal(info, 'stack', expectedMessage.stack) done() } }) @@ -130,9 +130,9 @@ describe('UTILS: CustomLogger', () => { const expectedMessage = new Error('testing logger.info(Error)') const transport = new transports.Console({ log: (info) => { - assert.equal(info.level, 'info') - assert.equal(info.message, expectedMessage.message) - assert.deepEqual(info.stack, expectedMessage.stack) + assert.propertyVal(info, 'level', 'info') + assert.propertyVal(info, 'message', expectedMessage.message) + assert.propertyVal(info, 'stack', expectedMessage.stack) done() } }) @@ -144,12 +144,12 @@ describe('UTILS: CustomLogger', () => { const expectedMessage = new CustomException('testing logger.debug(Error)', 'description...') const transport = new transports.Console({ log: (info) => { - assert.equal(info.level, 'debug') - assert.equal( - info.message, expectedMessage.message + assert.propertyVal(info, 'level', 'debug') + assert.propertyVal( + info, 'message', expectedMessage.message .concat(expectedMessage.description ? ` | ${expectedMessage.description}` : '') ) - assert.deepEqual(info.stack, expectedMessage.stack) + assert.propertyVal(info, 'stack', expectedMessage.stack) done() } }) diff --git a/test/unit/utils/json.utils.spec.ts b/test/unit/utils/json.utils.spec.ts index e0f29db..ebaa392 100644 --- a/test/unit/utils/json.utils.spec.ts +++ b/test/unit/utils/json.utils.spec.ts @@ -1,7 +1,7 @@ import { JsonUtils } from '../../../src/application/domain/utils/json.utils' import { assert } from 'chai' -describe('Utils: JsonUtils', () => { +describe('UTILS: JsonUtils', () => { describe('isJsonString()', () => { context('when validate if a string is a json', () => { it('should return true', () => { diff --git a/test/unit/validators/enum.values.validator.spec.ts b/test/unit/validators/enum.values.validator.spec.ts new file mode 100644 index 0000000..d74f4f2 --- /dev/null +++ b/test/unit/validators/enum.values.validator.spec.ts @@ -0,0 +1,337 @@ +import { assert } from 'chai' +import { ValidationException } from '../../../src/application/domain/exception/validation.exception' +import { Strings } from '../../../src/utils/strings' +import { EnumValuesValidator } from '../../../src/application/domain/validator/enum.values.validator' +import { PushTokenClientTypes } from '../../../src/application/domain/model/push.token' +import { PushTypes } from '../../../src/application/domain/model/push' +import { ChoiceTypes } from '../../../src/application/domain/utils/choice.types' +import { EmailTemplateResources, EmailTemplateTypes } from '../../../src/application/domain/model/email.template' + +describe('VALIDATORS: EnumValuesValidator', () => { + context('when the enum value is valid', () => { + it('should return undefined when the validation was successful for push token client type', () => { + try { + const result = EnumValuesValidator.validate(PushTokenClientTypes.WEB, 'client_type', PushTokenClientTypes) + assert.isUndefined(result) + } catch (err) { + assert.fail(err) + } + }) + + it('should return undefined when the validation was successful for push type', () => { + try { + const result = EnumValuesValidator.validate(PushTypes.DIRECT, 'type', PushTypes) + assert.isUndefined(result) + } catch (err) { + assert.fail(err) + } + }) + + it('should return undefined when the validation was successful for choice type', () => { + try { + const result = EnumValuesValidator.validate(ChoiceTypes.YES, 'keep_it', ChoiceTypes) + assert.isUndefined(result) + } catch (err) { + assert.fail(err) + } + }) + + it('should return undefined when the validation was successful for template type', () => { + try { + const result = EnumValuesValidator.validate(EmailTemplateTypes.WELCOME, 'type', EmailTemplateTypes) + assert.isUndefined(result) + } catch (err) { + assert.fail(err) + } + }) + + it('should return undefined when the validation was successful for template resource type', () => { + try { + const result = EnumValuesValidator.validate(EmailTemplateResources.HTML, 'resource', EmailTemplateResources) + assert.isUndefined(result) + } catch (err) { + assert.fail(err) + } + }) + }) + + context('when the push token client type is invalid', () => { + const pushTokenClientTypes: Array = Object.values(PushTokenClientTypes) + + it('should throw a ValidationException for an unmapped type', () => { + try { + EnumValuesValidator.validate('invalidClientType', 'client_type', PushTokenClientTypes) + assert.fail() + } catch (err) { + assert.instanceOf(err, ValidationException) + assert.propertyVal(err, 'message', + `${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED.replace('{0}', 'client_type')} invalidClientType`) + assert.propertyVal(err, 'description', + `${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED_DESC} ${pushTokenClientTypes.join(', ')}.`) + } + }) + + it('should throw a ValidationException for an undefined type', () => { + try { + EnumValuesValidator.validate(undefined!, 'client_type', PushTokenClientTypes) + assert.fail() + } catch (err) { + assert.instanceOf(err, ValidationException) + assert.propertyVal(err, 'message', + `${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED.replace('{0}', 'client_type')} undefined`) + assert.propertyVal(err, 'description', + `${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED_DESC} ${pushTokenClientTypes.join(', ')}.`) + } + }) + + it('should throw a ValidationException for a null type', () => { + try { + EnumValuesValidator.validate(null!, 'client_type', PushTokenClientTypes) + assert.fail() + } catch (err) { + assert.instanceOf(err, ValidationException) + assert.propertyVal(err, 'message', + `${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED.replace('{0}', 'client_type')} null`) + assert.propertyVal(err, 'description', + `${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED_DESC} ${pushTokenClientTypes.join(', ')}.`) + } + }) + + it('should throw a ValidationException for an empty type', () => { + try { + EnumValuesValidator.validate('', 'client_type', PushTokenClientTypes) + assert.fail() + } catch (err) { + assert.instanceOf(err, ValidationException) + assert.propertyVal(err, 'message', + `${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED.replace('{0}', 'client_type')} `) + assert.propertyVal(err, 'description', + `${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED_DESC} ${pushTokenClientTypes.join(', ')}.`) + } + }) + }) + + context('when the push type is invalid', () => { + const pushTypes: Array = Object.values(PushTypes) + + it('should throw a ValidationException for an unmapped type', () => { + try { + EnumValuesValidator.validate('invalidPushType', 'type', PushTypes) + assert.fail() + } catch (err) { + assert.instanceOf(err, ValidationException) + assert.propertyVal(err, 'message', + `${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED.replace('{0}', 'type')} invalidPushType`) + assert.propertyVal(err, 'description', + `${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED_DESC} ${pushTypes.join(', ')}.`) + } + }) + + it('should throw a ValidationException for an undefined type', () => { + try { + EnumValuesValidator.validate(undefined!, 'type', PushTypes) + assert.fail() + } catch (err) { + assert.instanceOf(err, ValidationException) + assert.propertyVal(err, 'message', + `${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED.replace('{0}', 'type')} undefined`) + assert.propertyVal(err, 'description', + `${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED_DESC} ${pushTypes.join(', ')}.`) + } + }) + + it('should throw a ValidationException for a null type', () => { + try { + EnumValuesValidator.validate(null!, 'type', PushTypes) + assert.fail() + } catch (err) { + assert.instanceOf(err, ValidationException) + assert.propertyVal(err, 'message', + `${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED.replace('{0}', 'type')} null`) + assert.propertyVal(err, 'description', + `${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED_DESC} ${pushTypes.join(', ')}.`) + } + }) + + it('should throw a ValidationException for an empty type', () => { + try { + EnumValuesValidator.validate('', 'type', PushTypes) + assert.fail() + } catch (err) { + assert.instanceOf(err, ValidationException) + assert.propertyVal(err, 'message', + `${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED.replace('{0}', 'type')} `) + assert.propertyVal(err, 'description', + `${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED_DESC} ${pushTypes.join(', ')}.`) + } + }) + }) + + context('when the choice type is invalid', () => { + const choiceTypes: Array = Object.values(ChoiceTypes) + + it('should throw a ValidationException for an unmapped type', () => { + try { + EnumValuesValidator.validate('invalidChoiceType', 'keep_it', ChoiceTypes) + assert.fail() + } catch (err) { + assert.instanceOf(err, ValidationException) + assert.propertyVal(err, 'message', + `${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED.replace('{0}', 'keep_it')} invalidChoiceType`) + assert.propertyVal(err, 'description', + `${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED_DESC} ${choiceTypes.join(', ')}.`) + } + }) + + it('should throw a ValidationException for an undefined type', () => { + try { + EnumValuesValidator.validate(undefined!, 'keep_it', ChoiceTypes) + assert.fail() + } catch (err) { + assert.instanceOf(err, ValidationException) + assert.propertyVal(err, 'message', + `${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED.replace('{0}', 'keep_it')} undefined`) + assert.propertyVal(err, 'description', + `${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED_DESC} ${choiceTypes.join(', ')}.`) + } + }) + + it('should throw a ValidationException for a null type', () => { + try { + EnumValuesValidator.validate(null!, 'keep_it', ChoiceTypes) + assert.fail() + } catch (err) { + assert.instanceOf(err, ValidationException) + assert.propertyVal(err, 'message', + `${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED.replace('{0}', 'keep_it')} null`) + assert.propertyVal(err, 'description', + `${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED_DESC} ${choiceTypes.join(', ')}.`) + } + }) + + it('should throw a ValidationException for an empty type', () => { + try { + EnumValuesValidator.validate('', 'keep_it', ChoiceTypes) + assert.fail() + } catch (err) { + assert.instanceOf(err, ValidationException) + assert.propertyVal(err, 'message', + `${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED.replace('{0}', 'keep_it')} `) + assert.propertyVal(err, 'description', + `${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED_DESC} ${choiceTypes.join(', ')}.`) + } + }) + }) + + context('when the email template type is invalid', () => { + const emailTemplateTypes: Array = Object.values(EmailTemplateTypes) + + it('should throw a ValidationException for an unmapped type', () => { + try { + EnumValuesValidator.validate('invalidEmailTemplateType', 'type', EmailTemplateTypes) + assert.fail() + } catch (err) { + assert.instanceOf(err, ValidationException) + assert.propertyVal(err, 'message', + `${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED.replace('{0}', 'type')} invalidEmailTemplateType`) + assert.propertyVal(err, 'description', + `${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED_DESC} ${emailTemplateTypes.join(', ')}.`) + } + }) + + it('should throw a ValidationException for an undefined type', () => { + try { + EnumValuesValidator.validate(undefined!, 'type', EmailTemplateTypes) + assert.fail() + } catch (err) { + assert.instanceOf(err, ValidationException) + assert.propertyVal(err, 'message', + `${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED.replace('{0}', 'type')} undefined`) + assert.propertyVal(err, 'description', + `${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED_DESC} ${emailTemplateTypes.join(', ')}.`) + } + }) + + it('should throw a ValidationException for a null type', () => { + try { + EnumValuesValidator.validate(null!, 'type', EmailTemplateTypes) + assert.fail() + } catch (err) { + assert.instanceOf(err, ValidationException) + assert.propertyVal(err, 'message', + `${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED.replace('{0}', 'type')} null`) + assert.propertyVal(err, 'description', + `${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED_DESC} ${emailTemplateTypes.join(', ')}.`) + } + }) + + it('should throw a ValidationException for an empty type', () => { + try { + EnumValuesValidator.validate('', 'type', EmailTemplateTypes) + assert.fail() + } catch (err) { + assert.instanceOf(err, ValidationException) + assert.propertyVal(err, 'message', + `${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED.replace('{0}', 'type')} `) + assert.propertyVal(err, 'description', + `${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED_DESC} ${emailTemplateTypes.join(', ')}.`) + } + }) + }) + + context('when the email template resource type is invalid', () => { + const emailTemplateResourceTypes: Array = Object.values(EmailTemplateResources) + + it('should throw a ValidationException for an unmapped type', () => { + try { + EnumValuesValidator.validate('invalidResource', 'resource', EmailTemplateResources) + assert.fail() + } catch (err) { + assert.instanceOf(err, ValidationException) + assert.propertyVal(err, 'message', + `${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED.replace('{0}', 'resource')} invalidResource`) + assert.propertyVal(err, 'description', + `${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED_DESC} ${emailTemplateResourceTypes.join(', ')}.`) + } + }) + + it('should throw a ValidationException for an undefined type', () => { + try { + EnumValuesValidator.validate(undefined!, 'resource', EmailTemplateResources) + assert.fail() + } catch (err) { + assert.instanceOf(err, ValidationException) + assert.propertyVal(err, 'message', + `${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED.replace('{0}', 'resource')} undefined`) + assert.propertyVal(err, 'description', + `${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED_DESC} ${emailTemplateResourceTypes.join(', ')}.`) + } + }) + + it('should throw a ValidationException for a null type', () => { + try { + EnumValuesValidator.validate(null!, 'resource', EmailTemplateResources) + assert.fail() + } catch (err) { + assert.instanceOf(err, ValidationException) + assert.propertyVal(err, 'message', + `${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED.replace('{0}', 'resource')} null`) + assert.propertyVal(err, 'description', + `${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED_DESC} ${emailTemplateResourceTypes.join(', ')}.`) + } + }) + + it('should throw a ValidationException for an empty type', () => { + try { + EnumValuesValidator.validate('', 'resource', EmailTemplateResources) + assert.fail() + } catch (err) { + assert.instanceOf(err, ValidationException) + assert.propertyVal(err, 'message', + `${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED.replace('{0}', 'resource')} `) + assert.propertyVal(err, 'description', + `${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED_DESC} ${emailTemplateResourceTypes.join(', ')}.`) + } + }) + }) +}) diff --git a/test/unit/validators/object.id.validator.spec.ts b/test/unit/validators/object.id.validator.spec.ts new file mode 100644 index 0000000..8cd37e1 --- /dev/null +++ b/test/unit/validators/object.id.validator.spec.ts @@ -0,0 +1,42 @@ +import { assert } from 'chai' +import { ObjectIdValidator } from '../../../src/application/domain/validator/object.id.validator' +import { ObjectID } from 'bson' +import { Strings } from '../../../src/utils/strings' +import { ValidationException } from '../../../src/application/domain/exception/validation.exception' + +describe('VALIDATORS: ObjectIdValidator', () => { + context('when the ObjectId is valid', () => { + it('should return undefined when the validation was successful', () => { + try { + const result = ObjectIdValidator.validate(`${new ObjectID()}`) + assert.isUndefined(result) + } catch (err) { + assert.fail(err) + } + }) + }) + + context('when the ObjectId is invalid', () => { + it('should throw ValidationException for invalid ObjectId: 123', () => { + try { + ObjectIdValidator.validate('123') + assert.fail() + } catch (err) { + assert.instanceOf(err, ValidationException) + assert.propertyVal(err, 'message', Strings.ERROR_MESSAGE.VALIDATE.UUID_NOT_VALID_FORMAT) + assert.propertyVal(err, 'description', Strings.ERROR_MESSAGE.VALIDATE.UUID_NOT_VALID_FORMAT_DESC) + } + }) + + it('should throw an error for invalid ObjectId 123 received along with an error message', () => { + try { + ObjectIdValidator.validate('123', 'any message') + assert.fail() + } catch (err) { + assert.instanceOf(err, ValidationException) + assert.propertyVal(err, 'message', 'any message') + assert.propertyVal(err, 'description', Strings.ERROR_MESSAGE.VALIDATE.UUID_NOT_VALID_FORMAT_DESC) + } + }) + }) +}) diff --git a/test/unit/validators/push.token.validator.spec.ts b/test/unit/validators/push.token.validator.spec.ts new file mode 100644 index 0000000..f6c073e --- /dev/null +++ b/test/unit/validators/push.token.validator.spec.ts @@ -0,0 +1,83 @@ +import { assert } from 'chai' +import { ValidationException } from '../../../src/application/domain/exception/validation.exception' +import { Strings } from '../../../src/utils/strings' +import { PushToken, PushTokenClientTypes } from '../../../src/application/domain/model/push.token' +import { PushTokenMock } from '../../mocks/models/push.token.mock' +import { PushTokenValidator } from '../../../src/application/domain/validator/push.token.validator' + +describe('VALIDATORS: PushTokenValidator', () => { + let pushToken: PushToken = new PushTokenMock().generate() + + afterEach(() => { + pushToken = new PushTokenMock().generate() + }) + + context('when the PushToken is valid', () => { + it('should return undefined when the validation was successful', () => { + try { + const result = PushTokenValidator.validate(pushToken) + assert.isUndefined(result) + } catch (err) { + assert.fail(err) + } + }) + }) + + context('when the PushToken is incomplete', () => { + it('should throw ValidationException for an incomplete PushToken (missing user_id)', () => { + try { + pushToken.user_id = undefined + PushTokenValidator.validate(pushToken) + assert.fail() + } catch (err) { + assert.instanceOf(err, ValidationException) + assert.propertyVal(err, 'message', Strings.ERROR_MESSAGE.VALIDATE.REQUIRED_FIELDS) + assert.propertyVal(err, 'description', Strings.ERROR_MESSAGE.VALIDATE.REQUIRED_FIELDS_DESC + .replace('{0}', 'user_id')) + } + }) + + it('should throw ValidationException for an incomplete PushToken (missing all fields)', () => { + try { + PushTokenValidator.validate(new PushToken()) + assert.fail() + } catch (err) { + assert.instanceOf(err, ValidationException) + assert.propertyVal(err, 'message', Strings.ERROR_MESSAGE.VALIDATE.REQUIRED_FIELDS) + assert.propertyVal(err, 'description', Strings.ERROR_MESSAGE.VALIDATE.REQUIRED_FIELDS_DESC + .replace('{0}', 'user_id, client_type, token')) + } + }) + }) + + context('when the PushToken user_id is invalid', () => { + it('should throw ValidationException for invalid ObjectId: 123', () => { + try { + pushToken.user_id = '123' + PushTokenValidator.validate(pushToken) + assert.fail() + } catch (err) { + assert.instanceOf(err, ValidationException) + assert.propertyVal(err, 'message', Strings.USER.PARAM_ID_NOT_VALID_FORMAT) + assert.propertyVal(err, 'description', Strings.ERROR_MESSAGE.VALIDATE.UUID_NOT_VALID_FORMAT_DESC) + } + }) + }) + + context('when the PushToken client_type is invalid', () => { + const pushTokenClientTypes: Array = Object.values(PushTokenClientTypes) + + it('should throw a ValidationException for an unmapped type', () => { + try { + pushToken.client_type = 'invalidClientType' + PushTokenValidator.validate(pushToken) + assert.fail() + } catch (err) { + assert.instanceOf(err, ValidationException) + assert.propertyVal(err, 'message', + `${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED.replace('{0}', 'client_type')} invalidClientType`) + assert.propertyVal(err, 'description', `${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED_DESC} ${pushTokenClientTypes.join(', ')}.`) + } + }) + }) +}) diff --git a/test/unit/validators/push.validator.spec.ts b/test/unit/validators/push.validator.spec.ts new file mode 100644 index 0000000..085a6d7 --- /dev/null +++ b/test/unit/validators/push.validator.spec.ts @@ -0,0 +1,195 @@ +import { assert } from 'chai' +import { ValidationException } from '../../../src/application/domain/exception/validation.exception' +import { Strings } from '../../../src/utils/strings' +import { Push, PushTypes } from '../../../src/application/domain/model/push' +import { PushMock } from '../../mocks/models/push.mock' +import { PushValidator } from '../../../src/application/domain/validator/push.validator' +import { ChoiceTypes } from '../../../src/application/domain/utils/choice.types' + +describe('VALIDATORS: PushValidator', () => { + let push: Push = new PushMock().generate(PushTypes.TOPIC) + + afterEach(() => { + push = new PushMock().generate(PushTypes.DIRECT) + }) + + context('when the Push is valid', () => { + it('should return undefined when the validation was successful', () => { + try { + const result = PushValidator.validate(push) + assert.isUndefined(result) + } catch (err) { + assert.fail(err) + } + }) + }) + + context('when the Push is incomplete', () => { + it('should throw ValidationException for an incomplete Push (missing message.type)', () => { + try { + push.message!.type = undefined + PushValidator.validate(push) + assert.fail() + } catch (err) { + assert.instanceOf(err, ValidationException) + assert.propertyVal(err, 'message', Strings.ERROR_MESSAGE.VALIDATE.REQUIRED_FIELDS) + assert.propertyVal(err, 'description', Strings.ERROR_MESSAGE.VALIDATE.REQUIRED_FIELDS_DESC + .replace('{0}', 'message.type')) + } + }) + + it('should throw ValidationException for an incomplete Push (missing message.pt)', () => { + try { + push.message!.pt = undefined + PushValidator.validate(push) + assert.fail() + } catch (err) { + assert.instanceOf(err, ValidationException) + assert.propertyVal(err, 'message', Strings.ERROR_MESSAGE.VALIDATE.REQUIRED_FIELDS) + assert.propertyVal(err, 'description', Strings.ERROR_MESSAGE.VALIDATE.REQUIRED_FIELDS_DESC + .replace('{0}', 'message.pt')) + } + }) + + it('should throw ValidationException for an incomplete Push (missing message.pt.title)', () => { + try { + push.message!.pt.title = undefined + PushValidator.validate(push) + assert.fail() + } catch (err) { + assert.instanceOf(err, ValidationException) + assert.propertyVal(err, 'message', Strings.ERROR_MESSAGE.VALIDATE.REQUIRED_FIELDS) + assert.propertyVal(err, 'description', Strings.ERROR_MESSAGE.VALIDATE.REQUIRED_FIELDS_DESC + .replace('{0}', 'message.pt.title')) + } + }) + + it('should throw ValidationException for an incomplete Push (missing message.pt.text)', () => { + try { + push.message!.pt.text = undefined + PushValidator.validate(push) + assert.fail() + } catch (err) { + assert.instanceOf(err, ValidationException) + assert.propertyVal(err, 'message', Strings.ERROR_MESSAGE.VALIDATE.REQUIRED_FIELDS) + assert.propertyVal(err, 'description', Strings.ERROR_MESSAGE.VALIDATE.REQUIRED_FIELDS_DESC + .replace('{0}', 'message.pt.text')) + } + }) + + it('should throw ValidationException for an incomplete Push (missing message.eng)', () => { + try { + push.message!.eng = undefined + PushValidator.validate(push) + assert.fail() + } catch (err) { + assert.instanceOf(err, ValidationException) + assert.propertyVal(err, 'message', Strings.ERROR_MESSAGE.VALIDATE.REQUIRED_FIELDS) + assert.propertyVal(err, 'description', Strings.ERROR_MESSAGE.VALIDATE.REQUIRED_FIELDS_DESC + .replace('{0}', 'message.eng')) + } + }) + + it('should throw ValidationException for an incomplete Push (missing message.eng.title)', () => { + try { + push.message!.eng.title = undefined + PushValidator.validate(push) + assert.fail() + } catch (err) { + assert.instanceOf(err, ValidationException) + assert.propertyVal(err, 'message', Strings.ERROR_MESSAGE.VALIDATE.REQUIRED_FIELDS) + assert.propertyVal(err, 'description', Strings.ERROR_MESSAGE.VALIDATE.REQUIRED_FIELDS_DESC + .replace('{0}', 'message.eng.title')) + } + }) + + it('should throw ValidationException for an incomplete Push (missing message.eng.text)', () => { + try { + push.message!.eng.text = undefined + PushValidator.validate(push) + assert.fail() + } catch (err) { + assert.instanceOf(err, ValidationException) + assert.propertyVal(err, 'message', Strings.ERROR_MESSAGE.VALIDATE.REQUIRED_FIELDS) + assert.propertyVal(err, 'description', Strings.ERROR_MESSAGE.VALIDATE.REQUIRED_FIELDS_DESC + .replace('{0}', 'message.eng.text')) + } + }) + + it('should throw ValidationException for an incomplete Push (missing all fields)', () => { + try { + PushValidator.validate(new Push()) + assert.fail() + } catch (err) { + assert.instanceOf(err, ValidationException) + assert.propertyVal(err, 'message', Strings.ERROR_MESSAGE.VALIDATE.REQUIRED_FIELDS) + assert.propertyVal(err, 'description', Strings.ERROR_MESSAGE.VALIDATE.REQUIRED_FIELDS_DESC + .replace('{0}', 'type, keep_it, to, message')) + } + }) + }) + + context('when the Push type is invalid', () => { + const pushTypes: Array = Object.values(PushTypes) + + it('should throw a ValidationException for an unmapped type', () => { + try { + push.type = 'invalidPushType' + PushValidator.validate(push) + assert.fail() + } catch (err) { + assert.instanceOf(err, ValidationException) + assert.propertyVal(err, 'message', + `${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED.replace('{0}', 'type')} invalidPushType`) + assert.propertyVal(err, 'description', `${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED_DESC} ${pushTypes.join(', ')}.`) + } + }) + }) + + context('when the Push keep_it is invalid', () => { + const choiceTypes: Array = Object.values(ChoiceTypes) + + it('should throw a ValidationException for an unmapped keep_it', () => { + try { + push.keep_it = 'invalidChoiceType' + PushValidator.validate(push) + assert.fail() + } catch (err) { + assert.instanceOf(err, ValidationException) + assert.propertyVal(err, 'message', + `${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED.replace('{0}', 'keep_it')} invalidChoiceType`) + assert.propertyVal(err, 'description', `${Strings.ERROR_MESSAGE.VALIDATE.NOT_MAPPED_DESC} ${choiceTypes.join(', ')}.`) + } + }) + }) + + context('when the Push to is invalid', () => { + before(() => { + push = new PushMock().generate(PushTypes.DIRECT) + }) + + it('should throw a ValidationException for an empty to', () => { + try { + push.to = [] + PushValidator.validate(push) + assert.fail() + } catch (err) { + assert.instanceOf(err, ValidationException) + assert.propertyVal(err, 'message', Strings.ERROR_MESSAGE.VALIDATE.AT_LEAST_ONE_RECIPIENT) + assert.propertyVal(err, 'description', Strings.ERROR_MESSAGE.VALIDATE.AT_LEAST_ONE_RECIPIENT_DESC) + } + }) + + it('should throw a ValidationException for an invalid id in \'to\' array', () => { + try { + push.to = ['5f5a3c5accefbde6e36d1b31', '123', '4e3b3c5accefbde6e45d2c23'] + PushValidator.validate(push) + assert.fail() + } catch (err) { + assert.instanceOf(err, ValidationException) + assert.propertyVal(err, 'message', Strings.ERROR_MESSAGE.VALIDATE.UUID_NOT_VALID_FORMAT) + assert.propertyVal(err, 'description', Strings.ERROR_MESSAGE.VALIDATE.UUID_NOT_VALID_FORMAT_DESC) + } + }) + }) +}) diff --git a/test/utils/database.utils.ts b/test/utils/database.utils.ts new file mode 100644 index 0000000..c7d7ac7 --- /dev/null +++ b/test/utils/database.utils.ts @@ -0,0 +1,9 @@ +export class DatabaseUtils { + public static async deleteMany(model: any, query?: any) { + return await model.deleteMany(query || {}) + } + + public static async create(model: any, doc: any) { + return await model.create(doc) + } +}