diff --git a/.funcignore b/.funcignore deleted file mode 100644 index c12a758..0000000 --- a/.funcignore +++ /dev/null @@ -1,46 +0,0 @@ -*.ts -*.js.map -*.development.js -*.es{,m}.js -*.mjs - -test*/ -coverage/ -*.{test,spec}.js - -.git/ -.gitignore - -.github/ -.travis.yml - -.vscode/ - -.eslintrc* -.prettierrc* -jest.config.* -tsconfig.* -.ncurc.* - -.funcignore -local.settings.json - -package-lock.json -node_modules/.bin/ -node_modules/.package-lock.json -yarn.lock -node_modules/.cache/ -node_modules/.yarn-integrity - -README.* -Readme.* -CHANGELOG.* -HISTORY.* -LICENSE* -License* -Makefile -.DS_Store - -*.mp4 -bin/ffmpeg -*.txt \ No newline at end of file diff --git a/.gitignore b/.gitignore index fb9ce64..10a6c96 100644 --- a/.gitignore +++ b/.gitignore @@ -60,7 +60,7 @@ typings/ # dotenv environment variables file .env -.env.test +.env.* # parcel-bundler cache (https://parceljs.org/) .cache @@ -88,4 +88,7 @@ dist out # Runtime artifacts -*.mp4 \ No newline at end of file +*.mp4 + +# AWS build artifacts +.aws-sam/ diff --git a/.vscode/extensions.json b/.vscode/extensions.json index e910edc..3b6afc3 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,3 +1,7 @@ { - "recommendations": ["ms-azuretools.vscode-azurefunctions"] + "recommendations": [ + "dbaeumer.vscode-eslint", + "github.vscode-pull-request-github", + "esbenp.prettier-vscode" + ] } diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..f661e6d --- /dev/null +++ b/Dockerfile @@ -0,0 +1,24 @@ +# Start with the most recent AWS Lambda Nodejs version +FROM public.ecr.aws/lambda/nodejs:20 + +# Add yarn +RUN npm i -g yarn && npm cache clean --force + +# Add Python 3 & other dependencies +RUN dnf install python3 openssl -y && dnf clean all && rm -rf /var/cache/yum + +# Add ffmpeg +COPY ffmpeg/bin/ffmpeg ${LAMBDA_TASK_ROOT}/ffmpeg/bin/ffmpeg + +# Install dependencies with yarn +COPY package.json yarn.lock ${LAMBDA_TASK_ROOT}/ +RUN yarn install --frozen-lockfile --production && yarn cache clean +# For some reason the lambda function doesn't have access to the files otherwise: +RUN chmod -R 755 ${LAMBDA_TASK_ROOT}/* + +# Copy function code +COPY src/ ${LAMBDA_TASK_ROOT}/src +RUN chmod -R 755 ${LAMBDA_TASK_ROOT}/src/* + +# Workaround for https://github.com/aws/aws-lambda-base-images/issues/137 +ENV LD_LIBRARY_PATH="" \ No newline at end of file diff --git a/README.md b/README.md index 76ec630..0ee0f71 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,14 @@ # VReddit Telegram Bot -A telegram bot which listens for v.redd.it URLs and replies with the video file (including audio) +A telegram bot which listens for video URLs and replies with the video file (including audio) + +Supports v.redd.it and some other video platforms. ## Usage -Simply add [@vreddit_bot](https://t.me/vreddit_bot) to any group, or send it a private message containing a v.redd.it link. +To use the live bot, add [@vreddit_bot](https://t.me/vreddit_bot) to any group, or send it a private message containing a video link. + +To run locally, see the **Development** section of this readme. ## TO DO @@ -18,16 +22,16 @@ Simply add [@vreddit_bot](https://t.me/vreddit_bot) to any group, or send it a p - [x] Support reddit links - [x] Add reddit post title as video caption - [x] Include link to source as [inline keyboard](https://core.telegram.org/bots/2-0-intro#new-inline-keyboards) +- [x] Use youtube-dl to download videos so that more sites are supported +- [ ] Improve youtube-dl output message +- [ ] If v.redd.it video is > 50 mb, try to use lower quality stream - [ ] If forwarding to the bot from a group chat, give a button to send the video back to that chat (e.g. via inline) -- [ ] If video is > 50 mb, try to use lower quality stream - [ ] Reply to /help with a short text about what the bot can do - [ ] Stop the bot sometimes asking for location info when using inline mode -- [ ] Use youtube-dl to add support for youtube & many other sites - - Check output for "ERROR: Unsupported URL: https://example.com" - - Probably need to add FFmpeg to PATH - - Format opts (in config file?): `-f 'bestvideo[ext=mp4][filesize was much faster & cheaper than Azure! +- [ ] Swap CI from Azure to AWS - [ ] CI: Optimise so that we don't run checks twice on releases? -- [ ] Set up [git-lfs](https://git-lfs.github.com/) to work with husky. See also: [1], [2], [3] -- [ ] Tune Azure max workers param -- [ ] Try other hosting options to see if it's faster and/or cheaper: - - [ ] Azure x64 Windows host - - [ ] Azure Linux host - - [ ] AWS Lambda - - [ ] Google Cloud Functions - [ ] Collect stats on inline option chosen? +- [ ] Some kind of solution for when more than 20 seconds are needed to download a file? [1]: https://dev.to/mbelsky/pair-husky-with-git-lfs-in-your-javascript-project-2kh0 [2]: https://github.com/typicode/husky/issues/108 @@ -60,7 +61,8 @@ Simply add [@vreddit_bot](https://t.me/vreddit_bot) to any group, or send it a p - Git - Node.js v14 (it must be 14 to match the version in Azure/AWS). _Tip: You can install & manage multiple Node versions using tools like [nodist](https://github.com/nullivex/nodist) (Windows) or [n](https://github.com/tj/n) (Linux/MacOS/WSL)_ - - [Yarn](https://yarnpkg.com/) + - [Yarn](https://yarnpkg.com/) (`npm install -g yarn`) + - Python (any version, but it must be on your `$PATH` as `python`. If it's called `python3` it won't be found by youtube-dl.) 1. `git clone` the repo and `cd` into it @@ -68,20 +70,18 @@ Simply add [@vreddit_bot](https://t.me/vreddit_bot) to any group, or send it a p 1. Create a `.env` file in the root of the project with the following params: - ```properties - BOT_ERROR_CHAT_ID= + ```bash BOT_API_TOKEN= + BOT_ERROR_CHAT_ID= # optional + DOWNLOAD_TIMEOUT=300 # optional (default = no timeout) ``` - - If you don't konw your telegram chat ID, don't worry. Just set it to 0 and update it later once you've found out your ID from the bot logs. - - If you don't have a spare bot you can use for local development, make a new bot using the botfather. Don't try use a bot that you are already using for something else, it won't work. - 1. Run `yarn dev` to start local dev server. You can now message your dev bot in telegram to test your code. -### Common Commands +### Useful Commands ```sh -# Run a local bot server connected to the bot configured in the `.env` file: +# Run a local bot server (configured in .env), restarts any time you save a source code file: yarn dev # Run all tests in watch mode: @@ -89,13 +89,55 @@ yarn test:watch # Auto fix lint/formatting issues (where possible): yarn lint:fix +``` + +### Testing the DynamoDB video info caching + +For normal development purposes video caching is disabled, since that reduces the amount of setup needed, plus it's helpful for testing to _not_ have a cache. + +However, sometimes you might want to specifically test the caching logic locally. + +In that case you will need: + +- an AWS account +- AWS credentials saved in your `~/.aws/credentials` file (see [Loading credentials in Node.js from the shared credentials file](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/loading-node-credentials-shared.html)) +- a DynamoDB table with a primary key called `url` of type `String`. + +Then, add these properties to your `.env` file: + +```properties +AWS_PROFILE=<(optional) credentials profile e.g. personal> +AWS_REGION= +CACHE_TABLE_NAME= +``` + +Now run `yarn dev` as normal, and the cache table you specified will be used. + +### Manual deployment to staging + +1. Create a new file `.env.staging`, with the following content: + +```properties +AWS_PROFILE=<(optional) credentials profile e.g. personal-account> +AWS_REGION=eu-central-1 +CACHE_TABLE_NAME=video-info-cache-staging + +BOT_API_TOKEN= +BOT_ERROR_CHAT_ID= -# Run the Azure function locally in watch mode: -yarn start +SAM_ENV=staging ``` +2. Install the AWS SAM CLI +3. Run `yarn deploy` +4. If needed, update the webook URL by running `npx env-cmd -f .env.staging set-webhook ` + ### CI/CD 1. Open a **pull request** to run linting & tests 1. Push to the **master branch** (e.g. merge a PR) to deploy to **staging** ([@staging_vreddit_bot](https://t.me/staging_vreddit_bot)) 1. Create a tag by running **`yarn release`** to deploy to **prod** ([@vreddit_bot](https://t.me/vreddit_bot)) + +``` + +``` diff --git a/events/message.json b/events/message.json new file mode 100644 index 0000000..a895379 --- /dev/null +++ b/events/message.json @@ -0,0 +1,3 @@ +{ + "body": "{\"update_id\":1,\"message\":{\"text\":\"https://v.redd.it/hf352syjjka61\",\"entities\":[{\"offset\":0,\"length\":31,\"type\":\"url\"}],\"chat\":{\"id\":60764253,\"first_name\":\"Dave\",\"last_name\":\"Rolle\",\"username\":\"DavidRolle\",\"is_bot\":false,\"language_code\":\"en\",\"type\":\"private\"},\"message_id\":42}}" +} diff --git a/ffmpeg/bin/ffmpeg b/ffmpeg/bin/ffmpeg index 9e6df0f..aa22e7e 100755 Binary files a/ffmpeg/bin/ffmpeg and b/ffmpeg/bin/ffmpeg differ diff --git a/ffmpeg/bin/ffmpeg.exe b/ffmpeg/bin/ffmpeg.exe deleted file mode 100755 index 247664b..0000000 Binary files a/ffmpeg/bin/ffmpeg.exe and /dev/null differ diff --git a/host.json b/host.json deleted file mode 100644 index 6ab6643..0000000 --- a/host.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "version": "2.0", - "logging": { - "applicationInsights": { - "samplingSettings": { - "isEnabled": true, - "excludedTypes": "Request" - } - } - }, - "extensionBundle": { - "id": "Microsoft.Azure.Functions.ExtensionBundle", - "version": "[1.*, 2.0.0)" - } -} diff --git a/local.settings.json b/local.settings.json deleted file mode 100644 index 9f24a6b..0000000 --- a/local.settings.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "IsEncrypted": false, - "Values": { - "AzureWebJobsStorage": "", - "FUNCTIONS_WORKER_RUNTIME": "node", - "AzureFunctionsJobHost__logging__applicationInsights__samplingSettings__isEnabled": "false", - "AzureFunctionsJobHost__logging__logLevel__Function": "trace" - } -} diff --git a/package.json b/package.json index 6acf058..9c4e5f3 100644 --- a/package.json +++ b/package.json @@ -6,10 +6,17 @@ "name": "David Drem Rolle", "email": "dave@rolle.family" }, + "engines": { + "node": "^16 || ^18 || ^20" + }, "license": "UNLICENSED", "private": "true", + "files": [ + "src", + "ffmpeg" + ], "scripts": { - "dev": "nodemon -x env-cmd start-dev-server", + "dev": "nodemon -x env-cmd start-dev-server src/webhook.webhook", "start": "env-cmd func start", "lint": "eslint \"**/*.js\" && prettier -c \"**/*.{js,css,md,json}\" --ignore-path .gitignore", "lint:fix": "eslint --fix \"**/*.js\" && prettier -w \"**/*.{js,css,md,json}\" --ignore-path .gitignore", @@ -17,26 +24,35 @@ "test:watch": "jest --watchAll --runInBand --no-cache", "test:update": "env-cmd jest -u", "update-deps": "ncu -u -x husky --doctor && yarn lint:fix && ncu -u -t minor --doctor && yarn lint", - "release": "ncu -e2 -t minor && npx np --no-publish" + "release": "ncu -e2 -t minor && npx np --no-publish", + "deploy": "sam build && env-cmd -f .env.staging npm run sam-deploy", + "deploy-prod": "sam build && env-cmd -f .env.prod npm run sam-deploy", + "sam-deploy": "sam deploy --config-env $SAM_ENV --parameter-overrides BotApiToken=$BOT_API_TOKEN BotErrorChatId=$BOT_ERROR_CHAT_ID CacheTableName=$CACHE_TABLE_NAME", + "logs": "env-cmd -f .env.staging npm run sam-logs", + "logs-prod": "env-cmd -f .env.prod npm run sam-logs", + "sam-logs": "sam logs --config-env $SAM_ENV -t" }, "dependencies": { + "@aws-sdk/client-dynamodb": "^3.34.0", + "filenamify": "^4.3.0", "form-data": "^4.0.0", - "node-fetch": "^2.6.2", - "serverless-telegram": "^0.6.0" + "he": "^1.2.0", + "node-fetch": "^2.6.5", + "serverless-telegram": "^0.8.3", + "youtube-dl-exec": "^2.4.15" }, "devDependencies": { - "@types/jest": "^27.0.1", + "@types/jest": "^27.0.2", "env-cmd": "^10.1.0", "eslint": "^7.32.0", - "eslint-plugin-jest": "^24.4.0", - "filenamify": "^4.3.0", + "eslint-plugin-jest": "^24.4.2", "husky": "^3.1.0", - "jest": "^27.1.1", + "jest": "^27.2.2", "lint-staged": "^11.1.2", "nock": "^13.1.3", - "nodemon": "^2.0.12", + "nodemon": "^2.0.13", "npm-check-updates": "^11.8.5", - "prettier": "^2.4.0" + "prettier": "^2.4.1" }, "prettier": { "singleQuote": true, diff --git a/samconfig.toml b/samconfig.toml new file mode 100644 index 0000000..44895aa --- /dev/null +++ b/samconfig.toml @@ -0,0 +1,41 @@ +version = 0.1 +[default] +[default.global.parameters] +stack_name = "vreddit-bot-dev" +[default.deploy] +[default.deploy.parameters] +s3_bucket = "aws-sam-cli-managed-default-samclisourcebucket-o69yjxke7qem" +s3_prefix = "vreddit-bot-dev" +region = "eu-central-1" +capabilities = "CAPABILITY_IAM" +image_repositories = ["WebhookFunction=192461062174.dkr.ecr.eu-central-1.amazonaws.com/vredditbotfe383b7b/webhookfunction2cdba7a2repo"] +[default.logs.parameters] +region = "eu-central-1" + +[staging] +[staging.global.parameters] +stack_name = "vreddit-bot-staging" +[staging.deploy] +[staging.deploy.parameters] +stack_name = "vreddit-bot-staging" +s3_bucket = "aws-sam-cli-managed-default-samclisourcebucket-o69yjxke7qem" +s3_prefix = "vreddit-bot-staging" +region = "eu-central-1" +capabilities = "CAPABILITY_IAM" +image_repositories = ["WebhookFunction=192461062174.dkr.ecr.eu-central-1.amazonaws.com/vredditbotfe383b7b/webhookfunction2cdba7a2repo"] +[staging.logs.parameters] +region = "eu-central-1" + +[prod] +[prod.global.parameters] +stack_name = "vreddit-bot" +[prod.deploy] +[prod.deploy.parameters] +stack_name = "vreddit-bot" +s3_bucket = "aws-sam-cli-managed-default-samclisourcebucket-o69yjxke7qem" +s3_prefix = "vreddit-bot" +region = "eu-central-1" +capabilities = "CAPABILITY_IAM" +image_repositories = ["WebhookFunction=192461062174.dkr.ecr.eu-central-1.amazonaws.com/vredditbotfe383b7b/webhookfunction2cdba7a2repo"] +[prod.logs.parameters] +region = "eu-central-1" \ No newline at end of file diff --git a/src/handler.js b/src/handler.js index 67c4fdd..ff0c915 100644 --- a/src/handler.js +++ b/src/handler.js @@ -2,54 +2,88 @@ const { CACHE_CHAT, OS_INFO, setLogMethods } = require('./io/environment'); const VideoPost = require('./video-post'); /** @type import('serverless-telegram').MessageHandler */ -exports.message = async ({ text, chat, message_id }, env) => { +exports.message = async ({ text, chat, message_id, entities }, env) => { + if (!text) return; setLogMethods(env); - env.debug('Running', process.title, process.version, 'on', OS_INFO); - - // Check message for a v.redd.it link or reddit comments link - const post = await VideoPost.findInText(env, text); - if (!post) return; - - // Check if we can re-use an existing file - if (post.fileId) { - await post.getMissingInfo(); - return { - video: post.fileId, - caption: post.title, - reply_to_message_id: message_id, - ...post.sourceButton(), - }; - } - // Inform the users that the work is in progress since it might take a while - // NOTE: we don't wait for this to complete, just fire it and let it run - env.send({ action: 'upload_video' }); + env.debug('Running Node.js', process.version, 'on', OS_INFO); + + // Find all URLs in the message + const urls = + entities + ?.filter((e) => e.type === 'url') + .map((e) => text.substr(e.offset, e.length)) || []; + env.debug('urls:', urls); + if (!urls.length) return; + + const verbose = text.startsWith('/verbose'); + env.debug({ verbose }); + + // try to download (or load from cache) each one + const results = await Promise.all( + urls.map(async (url) => { + // Load the video info + const post = await VideoPost.loadCachedInfo(env, url); + + // Check if we can re-use an existing file, otherwise download and send it + return post.fileId + ? { + video: post.fileId, + caption: post.title, + reply_to_message_id: message_id, + ...post.sourceButton(), + } + : post.downloadAndSend(chat, message_id, verbose); + }), + ); + + // filter any responses to be sent to the TG api + const responses = results.filter((r) => r); - // Download and send the file - return post.downloadAndSend(chat, message_id); + // if there's exactly one API response we can just return it + // otherwise we need to send them to the API individually + if (responses.length === 1) { + return responses[0]; + } else { + await Promise.all(responses.map((r) => env.send(r))); + } }; +const urlRegex = + /(http(s)?:\/\/.)?(www\.)?[-a-zA-Z0-9@:%._+~#=]{2,256}\.[a-z]{2,63}\b([-a-zA-Z0-9@:%_+.~#?&/=]*)/g; + +/** @param {string} text */ +const findUrls = (text) => [...text.matchAll(urlRegex)].map((m) => m[0]); + /** @type import('serverless-telegram').InlineHandler */ exports.inline = async ({ query }, env) => { + if (!query) return; setLogMethods(env); - // Check message for a v.redd.it link - const post = await VideoPost.findInText(env, query); - if (!post) return; - - // Check if we can re-use an existing file, otherwise upload it to CACHE_CHAT - if (!post.fileId) await post.downloadAndSend(CACHE_CHAT); - - // Send the results list - if (post.fileId) { - const video_file_id = post.fileId; - const caption = post.title; - const src = post.sourceButton(); - return [ - { title: `Send video "${post.title}"`, video_file_id, caption, ...src }, - { title: `Send without caption`, video_file_id, ...src }, - { title: `Send without source`, video_file_id, caption }, - { title: `Send without caption or source (no context)`, video_file_id }, - ]; + // Check message for URLs + const urls = findUrls(query); + env.debug('urls:', urls); + if (!urls.length) return; + + // Try each one until one works + for (const url of urls) { + // Load the video info + const post = await VideoPost.loadCachedInfo(env, url); + + // Check if we can re-use an existing file, otherwise upload it to CACHE_CHAT + if (!post.fileId) await post.downloadAndSend(CACHE_CHAT); + + // If we have an existing file ID or download succeeded, return the inline query result + if (post.fileId) { + const video_file_id = post.fileId; + const caption = post.title; + const src = post.sourceButton(); + return [ + { title: `Send video "${post.title}"`, video_file_id, caption, ...src }, + { title: `Send without caption`, video_file_id, ...src }, + { title: `Send without source`, video_file_id, caption }, + { title: `Send without caption or source (no context)`, video_file_id }, + ]; + } } }; diff --git a/src/io/ddb-cache.js b/src/io/ddb-cache.js new file mode 100644 index 0000000..06dc99b --- /dev/null +++ b/src/io/ddb-cache.js @@ -0,0 +1,50 @@ +const { + DynamoDBClient, + PutItemCommand, + GetItemCommand, +} = require('@aws-sdk/client-dynamodb'); +const { log, CACHE_TABLE_NAME } = require('./environment'); + +// disable caching & don't connect to DDB if CACHE_TABLE_NAME is not set. +const ddbClient = CACHE_TABLE_NAME && new DynamoDBClient({}); + +const fromAttrVals = (obj) => + Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, v.S])); + +const toAttrVals = (obj) => + Object.fromEntries( + Object.entries(obj) + .filter(([, v]) => v !== undefined) + .map(([k, v]) => [k, { S: v }]), + ); + +/** @param {string} url video URL */ +exports.read = async (url) => { + if (!url || !ddbClient) return; + const data = await ddbClient.send( + new GetItemCommand({ + TableName: CACHE_TABLE_NAME, + Key: toAttrVals({ url }), + ProjectionExpression: 'sourceUrl,title,fileId', + }), + ); + const info = data?.Item && fromAttrVals(data.Item); + if (info) log.debug('Loaded cached video info:', { url, ...info }); + return info; +}; + +/** @param {import('../video-post')} post */ +exports.write = async ({ url, sourceUrl, title, fileId }) => { + if (!fileId || !ddbClient) return; + try { + await ddbClient.send( + new PutItemCommand({ + TableName: CACHE_TABLE_NAME, + Item: toAttrVals({ url, sourceUrl, title, fileId }), + }), + ); + log.debug('Saved cached video info:', { url, sourceUrl, title, fileId }); + } catch (err) { + log.error(err); + } +}; diff --git a/src/io/download-video.js b/src/io/download-video.js index 33daebb..2fb4695 100644 --- a/src/io/download-video.js +++ b/src/io/download-video.js @@ -1,71 +1,197 @@ -const { log, FFMPEG } = require('./environment'); -const { exec } = require('child_process'); -const { existsSync, unlinkSync, statSync } = require('fs'); +require('./environment'); +const { stat, access, rm, rename } = require('fs/promises'); const { tmpdir } = require('os'); const { resolve } = require('path'); +const youtubedl = require('youtube-dl-exec'); +const { constants } = require('fs'); +const filenamify = require('filenamify'); +const he = require('he'); -/** @param {import('fs').PathLike} videoFile */ -const deleteIfExisting = (videoFile) => { - if (existsSync(videoFile)) { - log.warn(`${videoFile} already exists, attempting to delete`); - unlinkSync(videoFile); +const UPDATE_INTERVAL_MS = 1000 * 60 * 60 * 24; // 1 day +/** @type {Date | undefined} */ +let lastUpdated; + +const shouldUpdate = () => { + // @ts-ignore + if (!lastUpdated || lastUpdated < new Date() - UPDATE_INTERVAL_MS) { + lastUpdated = new Date(); + return true; + } +}; + +const exists = async (/** @type {import("fs").PathLike} */ path) => { + try { + await access(path, constants.R_OK); + return true; + } catch { + return false; } }; +const DOWNLOAD_TIMEOUT = parseInt(process.env.DOWNLOAD_TIMEOUT || '0'); + +/** @returns {string} */ +const getErrorMessage = (url, { stderr, originalMessage, message }) => { + if (originalMessage === 'Timed out') { + return `Timed out after ${DOWNLOAD_TIMEOUT} seconds`; + } + if (stderr?.includes('requested format not available')) { + return `Video too large (> 50 MB) or no supported formats available: ${url}`; + } else if (stderr?.includes('Unable to extract video url')) { + return `Unable to extract video url from ${url}.`; + } else { + return stderr?.match(/ERROR: (.*)/)?.[1] || originalMessage || message; + } +}; + +// Sequence to ensure unique paths during parallel execution +let seq = 0; +// Return an output path in the temp directory based on the current timestamp +const uniqueTempPath = (extension) => + resolve(tmpdir(), `${Date.now()}${seq++}.${extension}`); + +const vOpts = '[ext=mp4][vcodec!^=?av01]'; +const gif = '[ext=gif][filesize + `bestvideo${vOpts}[filesize} */ -const execFFmpeg = (id, outputFile, httpProxy) => { - const scheme = httpProxy ? 'http' : 'https'; - const dashUrl = `${scheme}://v.redd.it/${id}/DASHPlaylist.mpd`; - httpProxy = httpProxy ? `-http_proxy ${httpProxy}` : ''; - log.info('Saving:', dashUrl, 'to:', outputFile, 'using:', FFMPEG, httpProxy); - return new Promise((resolve, reject) => - exec( - `${FFMPEG} ${httpProxy} -i "${dashUrl}" -c copy ${outputFile}`, - (err, stdout, stderr) => { - if (err) reject(err); - log.debug(`stdout: ${stdout}`); - log.debug(`stderr: ${stderr}`); - resolve(stderr); +const execYtdl = async (post, proxy, verbose = false) => { + const output = uniqueTempPath('tmp'); + post.env.debug({ output }); + + const url = post.url.toLowerCase().startsWith('http') + ? post.url + : `https://${post.url}`; + + try { + const subprocess = youtubedl.exec( + url, + { + format, + proxy, + output, + writeInfoJson: true, + noProgress: true, + mergeOutputFormat: 'mp4', + recodeVideo: 'mp4', + verbose: verbose || undefined, + update: shouldUpdate(), }, - ), - ); -}; + { timeout: DOWNLOAD_TIMEOUT * 1000 }, + ); -/** @param {number} n */ -const nanToUndef = (n) => (isNaN(n) ? undefined : n); + subprocess.stdout?.setEncoding('utf-8'); + subprocess.stderr?.setEncoding('utf-8'); + subprocess.stdout?.on('data', (s) => + verbose ? post.statusLog(s.trim()) : post.env.debug(s.trim()), + ); + subprocess.stderr?.on('data', async (s) => + verbose ? post.statusLog(s.trim()) : post.env.warn(s.trim()), + ); -/** @param {string} ffmpegStderr */ -const getOutputDimensions = (ffmpegStderr) => { - const output = ffmpegStderr?.split('Output #0')?.[1]; - const [, w, h] = output?.match(/Video:\s.*\s(\d+)x(\d+)\s/) || []; - return { width: nanToUndef(parseInt(w)), height: nanToUndef(parseInt(h)) }; + await subprocess; + } catch (/** @type {any} */ e) { + post.env.error(e); + await rm(output.replace('.tmp', '*')).catch(() => {}); + return { error: getErrorMessage(url, e) }; + } + + let path; + for (const p of [output.replace('.tmp', '.mp4'), output, `${output}.mp4`]) { + if (await exists(p)) { + path = p; + break; + } + } + if (!path) return { error: 'ERROR: youtube-dl output file not found' }; + + return { path, infoJson: `${output}.info.json` }; }; /** - * Downloads a video using ffmpeg, returns the output path and some statistics - * @param {string} id v.redd.it video ID + * Downloads a video using youtube-dl, returns output file path and other params + * ready to be passed directly to telegram sendVideo + * @param {import('../video-post')} post Any URL to attempt to download with youtube-dl * @param {string} [httpProxy] optional proxy URL e.g. http://127.0.0.1:8080 + * @returns {Promise<{video: string, size: number, [key: string]: any} | {error: string}>} */ -const downloadVideo = async (id, httpProxy) => { - const path = resolve(tmpdir(), `${id}.mp4`); - // Delete the video if it exists in case it is from a failed previous execution - deleteIfExisting(path); - // Use ffmpeg to save the video to a temp file at source quality - const ffmpegStderr = await execFFmpeg(id, path, httpProxy); - // Get video dimensions from ffmpeg output - const { width, height } = getOutputDimensions(ffmpegStderr); - const { size } = statSync(path); - log.debug({ path, width, height, size }); - return { path, width, height, size }; +const downloadVideo = async (post, httpProxy, verbose = false) => { + // Use youtube-dl to download the video + const res = await execYtdl(post, httpProxy, verbose); + post.env.info('res', res); + if ('error' in res) return { error: res.error }; + const { path, infoJson } = res; + + // Load info from json + const info = require(infoJson); + // post.env.debug('youtube-dl info JSON:', info); + + info.resolution = info.resolution || `${info.width}x${info.height}`; + if (info.title === info.extractor && info.playlist_title) { + info.title = info.playlist_title; + } + if (info.title === info.id) info.title = undefined; + + // Clean up temp file in background (intentionally do not await) + rm(infoJson); + + // rename the file to something more sensible before upload + const video = resolve( + tmpdir(), + filenamify(info.title || info.id, { replacement: '_' }) + '.mp4', + ).replace(/#/g, '_'); + await rename(path, video); + + // get file size from fs + const { size } = await stat(video); + info.size = size; + + // log all formats for debugging purposes + // console.table( + // info.formats?.map(({ format, ext, vcodec, acodec, filesize }) => ({ + // format, + // ext, + // vcodec, + // acodec, + // mb: filesize / 1024 / 1024, + // })), + // ); + + // post.statusLog(`\nvideo id: ${info.extractor}/${info.id}`); + + post.statusLog('\n✅ Video ready:\n', undefined, true); + const logInfo = (key, xform = (x) => x) => + info[key] && + post.statusLog(`${key}: ${xform(info[key])}`, undefined, true); + + logInfo('title', he.encode); + logInfo('duration', (d) => `${Math.round(d)} sec`); + logInfo('size', (s) => `${(s / 1024 / 1024).toFixed(2)} MB`); + logInfo('resolution'); + logInfo('vcodec', (v) => `${v} ${info.vbr ? `@ ${info.vbr} kbps` : ''}`); + logInfo('acodec', (a) => `${a} ${info.abr ? `@ ${info.abr} kbps` : ''}`); + + return { + video, + size, + title: info.title, + width: info.width, + height: info.height, + duration: info.duration, + }; }; module.exports = { - deleteIfExisting, - execFFmpeg, downloadVideo, - getOutputDimensions, }; diff --git a/src/io/environment.js b/src/io/environment.js index 8f11fde..f76f867 100644 --- a/src/io/environment.js +++ b/src/io/environment.js @@ -1,6 +1,4 @@ -const { existsSync, mkdirSync } = require('fs'); const os = require('os'); -const { resolve } = require('path'); // limit imposed by telegram API const MAX_FILE_SIZE_BYTES = 50 * 1024 * 1024; @@ -8,27 +6,45 @@ const MAX_FILE_SIZE_BYTES = 50 * 1024 * 1024; // string describing the runtime OS const OS_INFO = `${os.platform()} ${os.arch()} (${os.type()} ${os.version()})`; -// path to ffmpeg executable -const fileExtension = os.platform().startsWith('win') ? '.exe' : ''; -const FFMPEG = - process.env.FFMPEG || - resolve(__dirname, `../../ffmpeg/bin/ffmpeg${fileExtension}`); +// Add path to ffmpeg executable +process.env.PATH += ':./ffmpeg/bin'; +// const fileExtension = os.platform().startsWith('win') ? '.exe' : ''; +// const FFMPEG = +// process.env.FFMPEG || +// resolve(__dirname, `../../ffmpeg/bin/ffmpeg${fileExtension}`); // Load constants from environment variables, throw an error if they are missing -/** @param {string} key */ -const loadEnvOrThrow = (key) => { +/** + * @template {boolean} T + * @param {string} key + * @param {T} [toInteger] + * @returns {T extends true ? number : string} + */ +const loadEnvOrThrow = (key, toInteger) => { const value = process.env[key]; if (!value) throw new Error(`${key} environment variable not set!`); + if (toInteger) { + const intValue = parseInt(value); + if (isNaN(intValue)) { + throw new Error(`${key} env var is not a valid integer: ${value}`); + } + // @ts-ignore + return intValue; + } + // @ts-ignore return value; }; + const BOT_API_TOKEN = loadEnvOrThrow('BOT_API_TOKEN'); -const BOT_ERROR_CHAT_ID = parseInt(loadEnvOrThrow('BOT_ERROR_CHAT_ID')); + // make sure chat ID is a number -if (isNaN(BOT_ERROR_CHAT_ID)) { - throw new Error(`BOT_ERROR_CHAT_ID env var is not a valid integer`); -} -const CACHE_DIR = resolve(loadEnvOrThrow('HOME'), '.vreddit-bot-cache'); -if (!existsSync(CACHE_DIR)) mkdirSync(CACHE_DIR); +const BOT_ERROR_CHAT_ID = loadEnvOrThrow('BOT_ERROR_CHAT_ID', true); + +// cache table is mandatory in production but optional when running locally. +const CACHE_TABLE_NAME = + process.env.NODE_ENV === 'production' + ? loadEnvOrThrow('CACHE_TABLE_NAME', false) + : process.env.CACHE_TABLE_NAME; /** @type {import('serverless-telegram').Chat} */ const CACHE_CHAT = { @@ -52,8 +68,7 @@ module.exports = { BOT_API_TOKEN, BOT_ERROR_CHAT_ID, CACHE_CHAT, - CACHE_DIR, - FFMPEG, + CACHE_TABLE_NAME, MAX_FILE_SIZE_BYTES, OS_INFO, log, diff --git a/src/io/file-cache.js b/src/io/file-cache.js deleted file mode 100644 index 256527a..0000000 --- a/src/io/file-cache.js +++ /dev/null @@ -1,21 +0,0 @@ -const { existsSync, writeFileSync, readFileSync } = require('fs'); -const { resolve } = require('path'); -const { log, CACHE_DIR } = require('./environment'); - -/** @param {string} id video ID */ -const getPath = (id) => resolve(CACHE_DIR, `${id}.json`); - -/** @param {string} id video ID */ -exports.read = (id) => { - const path = getPath(id); - const data = existsSync(path) && JSON.parse(readFileSync(path, 'utf8')); - if (data) log.info('Found existing video info for', id, data); - return data; -}; - -/** @param {import('../video-post')} post */ -exports.write = ({ id, url, title, fileId }) => { - if (!fileId) return; - const path = getPath(id); - writeFileSync(path, JSON.stringify({ url, title, fileId })); -}; diff --git a/src/io/reddit-api.js b/src/io/reddit-api.js index b48450d..532fae3 100644 --- a/src/io/reddit-api.js +++ b/src/io/reddit-api.js @@ -4,13 +4,13 @@ const { log } = require('./environment'); /** * Get reddit comments URL from v.redd.it URL * @param {string} id - v.redd.it id (the part after the slash) - * @returns {Promise} Link to reddit comments page + * @returns {Promise} Link to reddit comments page */ exports.getCommentsUrl = (id) => { log.info('Getting comments URL for:', id); return fetch(`https://www.reddit.com/video/${id}`, { redirect: 'manual', - }).then((r) => r.headers.get('location')); + }).then((r) => r.headers.get('location') || undefined); }; /** diff --git a/src/video-post.js b/src/video-post.js index 6e3333b..1544eaa 100644 --- a/src/video-post.js +++ b/src/video-post.js @@ -1,108 +1,207 @@ const { MAX_FILE_SIZE_BYTES } = require('./io/environment'); -const cache = require('./io/file-cache'); +const cache = require('./io/ddb-cache'); const reddit = require('./io/reddit-api'); const { downloadVideo } = require('./io/download-video'); +const { unlink } = require('fs/promises'); +const he = require('he'); -const idRegex = /https?:\/\/v\.redd\.it\/(\w+)/; -const urlRegex = - /https?:\/\/(www\.)?reddit\.com\/r\/\w+\/comments\/\w+\/\w+\/?/; -/** @param {string} [text] */ -const findId = (text) => text?.match(idRegex)?.[1]; -/** @param {string} [text] */ -const findUrl = (text) => text?.match(urlRegex)?.[0]; +const DEBOUNCE_MS = parseInt(process.env.DEBOUNCE_MS || '150'); +const MAX_LENGTH = 4096; +const TRUNCATED_MSG = '\n--- message too long ---'; + +const vredditIdRegex = /https?:\/\/v\.redd\.it\/(\w+)/; class VideoPost { /** + * Create a new VideoPost, including any cached info (if present) * @param {import('serverless-telegram').Env} env - * @param {string} [text] + * @param {string} url video URL */ - static async findInText(env, text) { - if (!text) return; - const id = findId(text); - return id ? new VideoPost(env, id) : VideoPost.fromUrl(env, findUrl(text)); + static async loadCachedInfo(env, url) { + const { sourceUrl, title, fileId } = (await cache.read(url)) || {}; + return new VideoPost(env, url, sourceUrl, title, fileId); } /** * @param {import('serverless-telegram').Env} env - * @param {string} [url] + * @param {string} url video URL + * @param {string} [sourceUrl] reddit comments URL for v.redd.it links + * @param {string} [title] video title + * @param {string} [fileId] telegram file ID for previously uploaded videos */ - static async fromUrl(env, url) { - if (!url) return; - const { videoUrl, title } = await reddit.getPostData(url); - const id = findId(videoUrl); - if (id) return new VideoPost(env, id, url, title); + constructor(env, url, sourceUrl, title, fileId) { + this.env = env; + this.url = url; + this.sourceUrl = sourceUrl; + this.title = title; + this.fileId = fileId; + this.status = ''; + this.statusMsg = undefined; + this.timer = undefined; + this.sendStatus = 'message' in this.env; } - /** - * @param {import('serverless-telegram').Env} env - * @param {string} id - * @param {string} [url] - * @param {string} [title] - * @param {string} [fileId] - */ - constructor(env, id, url, title, fileId) { - this.env = env; - this.id = id; - const cachedInfo = cache.read(this.id); - this.url = url || cachedInfo.url; - this.title = title || cachedInfo.title; - this.fileId = fileId || cachedInfo.fileId; + async statusLog( + /** @type {string} */ message = '', + debounceMs = DEBOUNCE_MS, + allowHtml = false, + ) { + if (this.timer) clearTimeout(this.timer); + this.env.info(message); + this.status += (allowHtml ? message : he.encode(message)) + '\n'; + if (debounceMs > 0) { + this.timer = setTimeout( + () => this._updateStatus(this.status), + debounceMs, + ); + } else { + await this._updateStatus(this.status); + } + } + + async setStatus(/** @type {string} */ text, chat, replyTo) { + if (this.timer) clearTimeout(this.timer); + this.status = text + '\n'; + await this._updateStatus(this.status, chat, replyTo); + } + + async _updateStatus(/** @type {string} */ text, chat, replyTo) { + if (!this.sendStatus) return; + if (text.length > MAX_LENGTH) { + text = text.slice(0, MAX_LENGTH - TRUNCATED_MSG.length) + TRUNCATED_MSG; + this.sendStatus = false; // prevent further updates as they are pointless + } + const content = { + text, + parse_mode: 'HTML', + disable_web_page_preview: true, + disable_notification: true, + }; + if (!this.statusMsg) { + if (!chat) return; + this.statusMsg = await this.env.send({ + chat_id: chat.id, + reply_to_message_id: replyTo, + ...content, + }); + } else { + await this.env.send({ + method: 'editMessageText', + message_id: this.statusMsg.message_id, + ...content, + }); + } } /** * @param {import('serverless-telegram').Chat} chat * @param {number} [replyTo] ID of the original message to reply to + * @returns {Promise} */ - async downloadAndSend(chat, replyTo) { - const [{ path, width, height, size }] = await Promise.all([ - downloadVideo(this.id), - this.getMissingInfo(), - ]); - if (size > MAX_FILE_SIZE_BYTES) { - const text = `Video too large (${(size / 1024 / 1024).toFixed(2)} MB)`; - this.env.error(text); - return chat.type === 'private' && { text, reply_to_message_id: replyTo }; + async downloadAndSend(chat, replyTo, verbose = false) { + // Don't spam group chats + this.sendStatus = this.sendStatus && chat.type === 'private'; + // Inform the users that the work is in progress since it might take a while + // NOTE: we don't wait for this to complete, just fire it and let it run + if (this.sendStatus) this.env.send({ action: 'upload_video' }); + + this.setStatus(`⬇️ Downloading ${this.url}`, chat, replyTo); + + let title, video; + try { + // @ts-ignore + [{ title, ...video }] = await Promise.all([ + downloadVideo(this, undefined, verbose), + this.getVredditInfo(), + ]); + if (video.error) { + await this.statusLog( + `\n💥 Download failed: ${he.encode(video.error)}`, + 0, + true, + ); + return; + } + if (video.size > MAX_FILE_SIZE_BYTES) { + const sizeMb = (video.size / 1024 / 1024).toFixed(2); + await this.statusLog( + `\n😞 Video too large (${sizeMb} MB): ${this.url}`, + 0, + ); + return; + } + + if (this.sourceUrl) this.statusLog(`source: ${this.sourceUrl}`); + if (this.title) { + this.statusLog(`title: ${this.title}`, 1000, true); + } else { + this.title = title; + } + + // Send the video to telegram + await this.sendVideo(chat, video, replyTo); + } catch (e) { + this.statusLog( + '\n🚨 An unexpected error occurred, please try again later', + 0, + ); + throw e; + } finally { + if (video?.video) await unlink(video.video).catch(() => {}); } - // Send the video to telegram - return this.sendVideo(chat, path, width, height, replyTo); } - async getMissingInfo() { - if (!this.url) { - this.url = await reddit.getCommentsUrl(this.id); + /** + * @param {string} text + * @param {import('serverless-telegram').Chat} chat + * @param {number} [replyTo] + * @returns {import('serverless-telegram').MessageResponse} + */ + errorResponse(text, chat, replyTo) { + this.env.error(text); + return chat.type === 'private' && { text, reply_to_message_id: replyTo }; + } + + /** For v.redd.it urls, gets the comments url and title from reddit */ + async getVredditInfo() { + if (!this.sourceUrl) { + const vredditId = this.url.match(vredditIdRegex)?.[1]; + if (vredditId) this.sourceUrl = await reddit.getCommentsUrl(vredditId); } - if (this.url && !this.title) { - this.title = (await reddit.getPostData(this.url)).title; + if (this.sourceUrl && !this.title) { + this.title = (await reddit.getPostData(this.sourceUrl)).title; cache.write(this); } } /** * @param {import('serverless-telegram').Chat} chat - * @param {import('fs').PathLike} path - * @param {number} [width] - * @param {number} [height] + * @param {{video: import('fs').PathLike, width?: number, height?: number, + * duration?: number }} video * @param {number} [replyTo] */ - async sendVideo(chat, path, width, height, replyTo) { + async sendVideo(chat, video, replyTo) { + this.statusLog('\n🚀 Uploading', 0, true); const result = await this.env.send({ - chat_id: chat.id, - video: path, - width, - height, + method: 'sendVideo', // necessary for inline queries + ...video, caption: this.title, + chat_id: chat.id, reply_to_message_id: replyTo, + allow_sending_without_reply: replyTo && 'true', ...this.sourceButton(), }); this.fileId = result?.video?.file_id; this.env.debug('fileId:', this.fileId); - cache.write(this); + await cache.write(this); } sourceButton() { return { reply_markup: { - inline_keyboard: [[{ text: 'Source', url: this.url }]], + inline_keyboard: [ + [{ text: 'Source', url: this.sourceUrl || this.url }], + ], }, }; } diff --git a/src/webhook.js b/src/webhook.js index c8d7563..7761a20 100644 --- a/src/webhook.js +++ b/src/webhook.js @@ -1,6 +1,6 @@ -const { createAzureTelegramWebhook } = require('serverless-telegram'); +const { createAwsTelegramWebhook } = require('serverless-telegram'); const handler = require('./handler'); const { BOT_ERROR_CHAT_ID } = require('./io/environment'); -// Entry point for the Azure function -module.exports = createAzureTelegramWebhook(handler, BOT_ERROR_CHAT_ID); +// Entry point for the AWS lambda +exports.webhook = createAwsTelegramWebhook(handler, BOT_ERROR_CHAT_ID); diff --git a/template.yml b/template.yml new file mode 100644 index 0000000..9f9c258 --- /dev/null +++ b/template.yml @@ -0,0 +1,58 @@ +AWSTemplateFormatVersion: 2010-09-09 +Transform: AWS::Serverless-2016-10-31 + +Description: vreddit-bot + +Parameters: + BotApiToken: + Description: Telegram Bot API Token + Type: String + BotErrorChatId: + Description: ID of the telegram chat (or user) to send error reports to + Type: Number + CacheTableName: + Description: DynamoDB table name for caching previously sent video info + Type: String + +Resources: + WebhookFunction: + Type: AWS::Serverless::Function + Properties: + PackageType: Image + ImageConfig: + Command: ['src/webhook.webhook'] + # At 1,769 MB, a function has the equivalent of one vCPU + MemorySize: 1769 + Timeout: 29 + Environment: + Variables: + BOT_API_TOKEN: !Ref BotApiToken + BOT_ERROR_CHAT_ID: !Ref BotErrorChatId + CACHE_TABLE_NAME: !Ref CacheTableName + DOWNLOAD_TIMEOUT: 20 + NODE_ENV: production + Events: + Webhook: + Type: HttpApi + Properties: + Path: /webhook + Method: post + Policies: + - DynamoDBCrudPolicy: + TableName: !Ref CacheTableName + Metadata: + Dockerfile: Dockerfile + DockerContext: . + + CacheTable: + Type: AWS::Serverless::SimpleTable + Properties: + TableName: !Ref CacheTableName + PrimaryKey: + Name: url + Type: String + +Outputs: + WebhookApi: + Description: 'HTTP API endpoint URL for Telegram webhook' + Value: !Sub 'https://${ServerlessHttpApi}.execute-api.${AWS::Region}.amazonaws.com/webhook' diff --git a/test/handler.test.js b/test/handler.test.js index 56d07e1..137da83 100644 --- a/test/handler.test.js +++ b/test/handler.test.js @@ -1,4 +1,11 @@ -const { CHAT, FROM, mocked, withFnMocks, env } = require('./helpers'); +const { + CHAT, + FROM, + mocked, + withFnMocks, + env, + setDefaultImpl, +} = require('./helpers'); const { message, inline } = require('../src/handler'); const { CACHE_CHAT } = require('../src/io/environment'); @@ -7,29 +14,45 @@ const VideoPost = mocked(require('../src/video-post')); const id = 'hf352syjjka61'; const text = `https://v.redd.it/${id}`; +const entities = [{ offset: 0, length: 31, type: 'url' }]; const url = 'url'; const title = 'title'; let post; beforeEach(() => { + jest.resetAllMocks(); // VideoPost.mockClear(); post = new VideoPost(env, id); Object.assign(post, { id, url, title }); + setDefaultImpl('env.send', jest.spyOn(env, 'send')); }); describe('handler.message', () => { const message_id = 42; - // @ts-ignore - const msgReply = (text) => message({ text, chat: CHAT, message_id }, env); + const msgReply = (text, entities) => + // @ts-ignore + message({ text, entities, chat: CHAT, message_id }, env); it('ignores messages without v.redd.it links', () => { return withFnMocks( - () => expect(msgReply('abcd')).resolves.toBeUndefined(), - [VideoPost.findInText, [env, 'abcd'], undefined], + () => + expect( + msgReply('http://www.example.com', entities), + ).resolves.toBeUndefined(), + [ + VideoPost.fromUrls, + [env, ['http://www.example.com']], + [new VideoPost(env, 'http://www.example.com')], + ], + [jest.spyOn(env, 'send'), [{ action: 'upload_video' }]], ); }); + it('ignores messages without URLs', () => { + return withFnMocks(() => expect(msgReply('abcd')).resolves.toBeUndefined()); + }); + it('re-uses an existing file ID', async () => { post.fileId = `cached file ID for ${id}`; const expected = { @@ -40,8 +63,8 @@ describe('handler.message', () => { }; return withFnMocks( - () => expect(msgReply(text)).resolves.toEqual(expected), - [VideoPost.findInText, [env, text], post], + () => expect(msgReply(text, entities)).resolves.toEqual(expected), + [VideoPost.fromUrls, [env, [text]], [post]], [post.getMissingInfo, []], [post.sourceButton, [], { url }], ); @@ -49,8 +72,8 @@ describe('handler.message', () => { it('downloads and sends a new video', async () => { return withFnMocks( - () => expect(msgReply(text)).resolves.toBeUndefined(), - [VideoPost.findInText, [env, text], post], + () => expect(msgReply(text, entities)).resolves.toBeUndefined(), + [VideoPost.fromUrls, [env, [text]], [post]], [ jest.spyOn(env, 'send').mockName('env.send'), [{ action: 'upload_video' }], @@ -78,7 +101,7 @@ describe('handler.message', () => { it('ignores messages without v.redd.it links', () => { return withFnMocks( () => expect(inlineReply(text)).resolves.toBeUndefined(), - [VideoPost.findInText, [env, text]], + [VideoPost.findAllInText, [env, text], []], ); }); @@ -86,7 +109,7 @@ describe('handler.message', () => { post.fileId = video_file_id; await withFnMocks( () => expect(inlineReply(text)).resolves.toEqual(results), - [VideoPost.findInText, [env, text], post], + [VideoPost.findAllInText, [env, text], [post]], [post.sourceButton, [], { url }], ); expect(post.downloadAndSend).toHaveBeenCalledTimes(0); @@ -95,7 +118,7 @@ describe('handler.message', () => { it('downloads a new video, sends it to cache, and uses the file ID', async () => { return withFnMocks( () => expect(inlineReply(text)).resolves.toEqual(results), - [VideoPost.findInText, [env, text], post], + [VideoPost.findAllInText, [env, text], [post]], [ post.downloadAndSend, [CACHE_CHAT], @@ -110,7 +133,7 @@ describe('handler.message', () => { it('returns nothing if the video is too large (-> no fileId)', async () => { return withFnMocks( () => expect(inlineReply(text)).resolves.toBeUndefined(), - [VideoPost.findInText, [env, text], post], + [VideoPost.findAllInText, [env, text], [post]], [post.downloadAndSend, [CACHE_CHAT]], ); }); diff --git a/test/helpers.js b/test/helpers.js index 47777fa..cbe462d 100644 --- a/test/helpers.js +++ b/test/helpers.js @@ -33,7 +33,10 @@ const log = Object.assign(jest.fn(), { // @ts-ignore const ctx = { log }; +// @ts-ignore +// const env = new MessageEnv(ctx, { chat: CHAT }); const env = new Env(ctx); + require('../src/io/environment').setLogMethods(env); // note this does not start the server, to do so call .listen(port). @@ -141,6 +144,7 @@ module.exports = { FROM, log, mocked, + setDefaultImpl, setDefaultImpls, withFnMocks, }; diff --git a/test/io/__fixtures__/downloadVideo returns undefined if a URL can't be downloaded as a video.json b/test/io/__fixtures__/downloadVideo returns undefined if a URL can't be downloaded as a video.json new file mode 100644 index 0000000..fe51488 --- /dev/null +++ b/test/io/__fixtures__/downloadVideo returns undefined if a URL can't be downloaded as a video.json @@ -0,0 +1 @@ +[] diff --git a/test/io/download-video.test.js b/test/io/download-video.test.js index 3963760..6ae95a1 100644 --- a/test/io/download-video.test.js +++ b/test/io/download-video.test.js @@ -1,4 +1,4 @@ -const { createProxyServer, log } = require('../helpers'); +const { createProxyServer, log, env } = require('../helpers'); const { downloadVideo, deleteIfExisting, @@ -10,6 +10,7 @@ const filenamify = require('filenamify'); const { tmpdir } = require('os'); const { existsSync, copyFileSync, unlinkSync } = require('fs'); const { resolve } = require('path'); +const VideoPost = require('../../src/video-post'); nock.back.fixtures = __dirname + '/__fixtures__/'; nock.back.setMode(process.env.CI ? 'lockdown' : 'record'); @@ -29,8 +30,9 @@ afterAll(() => { // TEST DATA const videoId = 'hf352syjjka61'; +const url = `https://v.redd.it/${videoId}`; -const tempVideoFile = resolve(tmpdir(), `${videoId}.mp4`); +const tempVideoFile = resolve(tmpdir(), `${filenamify(url)}.mp4`); const width = 406; const height = 720; @@ -59,15 +61,18 @@ describe('downloadVideo', () => { it('saves video file from v.redd.it URL', async () => { _deleteIfExisting(tempVideoFile); await expect( - withNockback(downloadVideo)(videoId, `http://127.0.0.1:${PROXY_PORT}`), + withNockback(downloadVideo)( + new VideoPost(env, url), + `http://127.0.0.1:${PROXY_PORT}`, + ), ).resolves.toEqual({ path: tempVideoFile, size, width, height }); expect(existsSync(tempVideoFile)).toBe(true); _deleteIfExisting(tempVideoFile); }); - it('throws an error in case of failure', async () => { + it(`returns undefined if a URL can't be downloaded as a video`, async () => { return expect( - withNockback(downloadVideo)('does not exist!'), - ).rejects.toThrow(/Command failed/); + withNockback(downloadVideo)(new VideoPost(env, 'does not exist!')), + ).resolves.toBeUndefined(); }); }); diff --git a/test/io/file-cache.test.js b/test/io/file-cache.test.js index b0de0ab..6c16949 100644 --- a/test/io/file-cache.test.js +++ b/test/io/file-cache.test.js @@ -1,6 +1,6 @@ /* eslint-disable jest/expect-expect */ const { withFnMocks, setDefaultImpls, mocked, env } = require('../helpers'); -const cache = require('../../src/io/file-cache'); +const cache = require('../../src/io/ddb-cache'); const VideoPost = require('../../src/video-post'); const { resolve } = require('path'); const { CACHE_DIR } = require('../../src/io/environment'); diff --git a/test/test-urls.txt b/test/test-urls.txt new file mode 100644 index 0000000..5d6d096 --- /dev/null +++ b/test/test-urls.txt @@ -0,0 +1,21 @@ +https://v.redd.it/hf352syjjka61 +https://gfycat.com/slipperytastyannelida +https://youtube.com/shorts/rs2IlSYVt24?feature=share +https://www.youtube.com/watch?v=UuEMaRgAtZM +https://www.youtube.com/watch?v=d99_h30swtM&feature=youtu.be +https://www.reddit.com/r/aww/comments/exng3g/bunnies_flop_over_when_they_feel_completely_safe/?utm_source=share&utm_medium=web2x&context=3 +https://youtu.be/TcTSqhpm80Y +https://youtu.be/fXs6YAZL6jg +https://youtu.be/S5NY1fqisSY +https://youtu.be/KW0eUrUiyxo +youtu.be/gMlf1ELvRzc +example.com +https://www.youtube.com/watch?v=iy47RTZRy9w +https://www.youtube.com/watch?v=f62W8c63scM +https://imgur.com/gallery/a1FSiQe +https://www.reddit.com/r/oddlysatisfying/comments/p6rj00/i_polished_a_penny_into_a_mirror/?utm_medium=android_app&utm_source=share +https://v.redd.it/rk2repuuvc261 +https://v.redd.it/s090h1f828b61 +https://v.redd.it/1dotd45nabb61 +https://www.instagram.com/p/CULuX1aBBJ2 +https://youtu.be/yiw6_JakZFc \ No newline at end of file diff --git a/test/video-post.test.js b/test/video-post.test.js index 743c948..e70eaff 100644 --- a/test/video-post.test.js +++ b/test/video-post.test.js @@ -4,6 +4,7 @@ const { CHAT, setDefaultImpls, env, + setDefaultImpl, } = require('./helpers'); const VideoPost = require('../src/video-post'); const { MAX_FILE_SIZE_BYTES } = require('../src/io/environment'); @@ -11,13 +12,13 @@ const { MAX_FILE_SIZE_BYTES } = require('../src/io/environment'); jest.mock('../src/io/file-cache'); jest.mock('../src/io/download-video'); jest.mock('../src/io/reddit-api'); -const cache = mocked(require('../src/io/file-cache')); +const cache = mocked(require('../src/io/ddb-cache')); const { downloadVideo } = mocked(require('../src/io/download-video')); const reddit = mocked(require('../src/io/reddit-api')); const id = 's090h1f828b61'; -const videoUrl = `https://v.redd.it/${id}`; -const url = +const url = `https://v.redd.it/${id}`; +const redditUrl = 'https://www.reddit.com/r/AnimalsBeingDerps/comments/kwxvu7/blah_blah_blah_blah/'; const title = 'Blah blah blah blah...'; const fileId = 'file ID'; @@ -25,76 +26,109 @@ const fileId = 'file ID'; beforeEach(() => { jest.resetAllMocks(); setDefaultImpls(cache, { downloadVideo }, reddit); + setDefaultImpl('env.send', jest.spyOn(env, 'send')); }); -describe('VideoPost.findInText / fromUrl', () => { +describe('VideoPost.findAllInText / fromUrl', () => { it('extracts v.redd.it URLs and videoId', () => { - const text = `...${videoUrl}/blah`; + const text = `...${url}/blah`; return withFnMocks( () => - expect(VideoPost.findInText(env, text)).resolves.toEqual({ env, id }), - [cache.read, [id], false], + expect( + VideoPost.findAllInText(env, text).then((posts) => + posts[0]?.getVredditId(), + ), + ).resolves.toEqual(id), + [cache.read, [text], false], ); }); - it('extracts reddit comments urls and converts to ID', () => { - const text = `${title}\n${url}gj722jl?utm_source=share&utm_medium=web2x&context=3`; + it('extracts reddit comments urls and gets the video info', () => { + const text = `${title}\n${redditUrl}gj722jl?utm_source=share&utm_medium=web2x&context=3`; return withFnMocks( () => - expect(VideoPost.findInText(env, text)).resolves.toEqual({ - env, - fileId: undefined, - id: 's090h1f828b61', - title: 'Blah blah blah blah...', - url: 'https://www.reddit.com/r/AnimalsBeingDerps/comments/kwxvu7/blah_blah_blah_blah/', - }), - [reddit.getPostData, [url], { title, videoUrl }], - [cache.read, [id], false], + expect(VideoPost.findAllInText(env, text)).resolves.toEqual([ + { + env, + url: 'https://v.redd.it/s090h1f828b61', + redditUrl: + 'https://www.reddit.com/r/AnimalsBeingDerps/comments/kwxvu7/blah_blah_blah_blah/', + title: 'Blah blah blah blah...', + fileId: undefined, + }, + ]), + [reddit.getPostData, [redditUrl], { title, videoUrl: url }], + [cache.read, [url], false], ); }); - it('ignores messages with other URLs', async () => { - expect(await VideoPost.findInText(env, 'foo')).toBeUndefined(); - expect( - await VideoPost.findInText(env, 'https://example.com/foo'), - ).toBeUndefined(); - expect( - await VideoPost.findInText(env, 'https://v.redd.it'), - ).toBeUndefined(); + it('finds all URLs', () => { + return withFnMocks( + async () => { + expect(await VideoPost.findAllInText(env, 'foo')).toEqual([]); + expect( + await VideoPost.findAllInText( + env, + 'https://example.com/foo https://example.com?bar', + ), + ).toEqual([ + { env, url: 'https://example.com/foo' }, + { env, url: 'https://example.com?bar' }, + ]); + }, + [cache.read, ['https://example.com/foo'], false], + [cache.read, ['https://example.com?bar'], false], + ); }); - it('ignores reddit posts without v.redd.it videos', async () => { + it('treats reddit posts without v.redd.it videos as unknown URLs', async () => { + const url = 'http://www.example.com'; return withFnMocks( - () => expect(VideoPost.fromUrl(env, url)).resolves.toBeUndefined(), + () => + expect(VideoPost.loadCachedInfo(env, redditUrl)).resolves.toEqual({ + env, + url, + redditUrl, + title, + }), [ reddit.getPostData, - [url], - Promise.resolve({ title, videoUrl: 'http://www.example.com' }), + [redditUrl], + Promise.resolve({ title, videoUrl: url }), ], + [cache.read, [url], false], ); }); - it('ignores undefined', async () => { - await expect(VideoPost.findInText(env, undefined)).resolves.toBeUndefined(); - await expect(VideoPost.fromUrl(env, undefined)).resolves.toBeUndefined(); + it('returns empty array when given no input', async () => { + await expect(VideoPost.findAllInText(env, undefined)).resolves.toEqual([]); + await expect(VideoPost.findAllInText(env, '')).resolves.toEqual([]); }); }); describe('new VideoPost', () => { it('prefers new data over cache', () => { - const expected = { env, id, url, title, fileId }; + const expected = { env, url, redditUrl, title, fileId }; const cached = { - url: 'cached url', + redditUrl: 'cached url', title: 'cached title', fileId: 'cached fileId', }; return withFnMocks( () => - expect(new VideoPost(env, id, url, title, fileId)).toEqual(expected), - [cache.read, [id], cached], + expect(new VideoPost(env, url, redditUrl, title, fileId)).toEqual( + expected, + ), + [cache.read, [url], cached], ); }); it('falls back to cached data if available', () => { return withFnMocks( () => - expect(new VideoPost(env, id)).toEqual({ env, id, url, title, fileId }), - [cache.read, [id], { url, title, fileId }], + expect(new VideoPost(env, url)).toEqual({ + env, + url, + redditUrl, + title, + fileId, + }), + [cache.read, [url], { redditUrl, title, fileId }], ); }); }); @@ -111,52 +145,56 @@ describe('post.downloadAndSend', () => { caption: title, reply_to_message_id, reply_markup: { - inline_keyboard: [[{ text: 'Source', url }]], + inline_keyboard: [[{ text: 'Source', url: redditUrl }]], }, }; it('downloads and sends the video with post.sendVideo()', () => { return withFnMocks( () => { - const post = new VideoPost(env, id, url, title); + const post = new VideoPost(env, url, redditUrl, title); return expect( post.downloadAndSend(CHAT, reply_to_message_id), ).resolves.toBeUndefined(); }, - [cache.read, [id], false], - [downloadVideo, [id], Promise.resolve(stats)], + [cache.read, [url], false], + [downloadVideo, [{ env, url, redditUrl, title }], Promise.resolve(stats)], [ jest.spyOn(env, 'send').mockName('env.send'), [sendVideoParams], { video: { file_id: fileId } }, ], - [cache.write, [{ env, id, title, url, fileId }]], + [cache.write, [{ env, url, redditUrl, title, fileId }]], ); }); it('fetches url/title if not provided with post.getMissingInfo()', () => { return withFnMocks( () => expect( - new VideoPost(env, id).downloadAndSend(CHAT, reply_to_message_id), + new VideoPost(env, url).downloadAndSend(CHAT, reply_to_message_id), ).resolves.toBeUndefined(), - [cache.read, [id], false], - [downloadVideo, [id], Promise.resolve(stats)], - [reddit.getCommentsUrl, [id], Promise.resolve(url)], - [reddit.getPostData, [url], Promise.resolve({ title, videoUrl })], - [cache.write, [{ env, id, title, url }]], + [cache.read, [url], false], + [downloadVideo, [{ env, url }], Promise.resolve(stats)], + [reddit.getCommentsUrl, [id], Promise.resolve(redditUrl)], + [ + reddit.getPostData, + [redditUrl], + Promise.resolve({ title, videoUrl: url }), + ], + [cache.write, [{ env, url, redditUrl, title }]], [ jest.spyOn(env, 'send').mockName('env.send'), [sendVideoParams], { video: { file_id: fileId } }, ], - [cache.write, [{ env, id, title, url, fileId }]], + [cache.write, [{ env, url, redditUrl, title, fileId }]], ); }); it('skips files > max size', () => { - withFnMocks( + return withFnMocks( () => expect( - new VideoPost(env, id, url, title).downloadAndSend( + new VideoPost(env, url, redditUrl, title).downloadAndSend( CHAT, reply_to_message_id, ), @@ -164,20 +202,29 @@ describe('post.downloadAndSend', () => { text: `Video too large (100.00 MB)`, reply_to_message_id: reply_to_message_id, }), - [cache.read, [id], false], - [downloadVideo, [id], Promise.resolve(bigFileStats)], + [cache.read, [url], false], + [ + downloadVideo, + [{ env, url, redditUrl, title }], + Promise.resolve(bigFileStats), + ], ); }); - it("doesn't send file too large error to group chats", () => - withFnMocks( + it("doesn't send file too large error to group chats", () => { + return withFnMocks( () => expect( - new VideoPost(env, id).downloadAndSend( + new VideoPost(env, url).downloadAndSend( { ...CHAT, type: 'group' }, reply_to_message_id, ), ).resolves.toBeFalsy(), - [cache.read, [id], { url, title }], - [downloadVideo, [id], Promise.resolve(bigFileStats)], - )); + [cache.read, [url], { url, title, redditUrl }], + [ + downloadVideo, + [{ env, url, title, redditUrl }], + Promise.resolve(bigFileStats), + ], + ); + }); }); diff --git a/webhook/function.json b/webhook/function.json deleted file mode 100644 index 1369df8..0000000 --- a/webhook/function.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "scriptFile": "../src/webhook.js", - "bindings": [ - { - "authLevel": "function", - "type": "httpTrigger", - "direction": "in", - "name": "req", - "methods": ["post"] - }, - { - "type": "http", - "direction": "out", - "name": "res" - } - ] -} diff --git a/yarn.lock b/yarn.lock index af600a2..234735f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,623 @@ # yarn lockfile v1 +"@aws-crypto/ie11-detection@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/ie11-detection/-/ie11-detection-1.0.0.tgz#d3a6af29ba7f15458f79c41d1cd8cac3925e726a" + integrity sha512-kCKVhCF1oDxFYgQrxXmIrS5oaWulkvRcPz+QBDMsUr2crbF4VGgGT6+uQhSwJFdUAQ2A//Vq+uT83eJrkzFgXA== + dependencies: + tslib "^1.11.1" + +"@aws-crypto/sha256-browser@^1.0.0": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@aws-crypto/sha256-browser/-/sha256-browser-1.2.1.tgz#b75b89a63039f97667e61ac92c9c4ee04f35fec0" + integrity sha512-WX/Wp6sXPhcBWx/w1aSJv3bDJL0ut5Ik6hl7yfqA1pn3cfsahl4rgHzRRXqYfJ+hnhnCqdgadS17wyBbVPsK+w== + dependencies: + "@aws-crypto/ie11-detection" "^1.0.0" + "@aws-crypto/sha256-js" "^1.2.1" + "@aws-crypto/supports-web-crypto" "^1.0.0" + "@aws-crypto/util" "^1.2.1" + "@aws-sdk/types" "^3.1.0" + "@aws-sdk/util-locate-window" "^3.0.0" + tslib "^1.11.1" + +"@aws-crypto/sha256-js@^1.0.0", "@aws-crypto/sha256-js@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@aws-crypto/sha256-js/-/sha256-js-1.2.1.tgz#88c6c0cfff7f269b21740c71157987837da502df" + integrity sha512-KtZ4qFDWZy6pKcky6RvwSytR/I8vPX9Z47pXh9sOuTcxjjtcegzS2uupg9vo0vbFcAWkSHVOEmNPh6ygiC3VFQ== + dependencies: + "@aws-crypto/util" "^1.2.1" + "@aws-sdk/types" "^3.1.0" + tslib "^1.11.1" + +"@aws-crypto/supports-web-crypto@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/supports-web-crypto/-/supports-web-crypto-1.0.0.tgz#c40901bc17ac1e875e248df16a2b47ad8bfd9a93" + integrity sha512-IHLfv+WmVH89EW4n6a5eE8/hUlz6qkWGMn/v4r5ZgzcXdTC5nolii2z3k46y01hWRiC2PPhOdeSLzMUCUMco7g== + dependencies: + tslib "^1.11.1" + +"@aws-crypto/util@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@aws-crypto/util/-/util-1.2.1.tgz#9bd31a37843aca63829a8fcae6b897283bf6ff12" + integrity sha512-H6Qrl28lzGGXZgLkdP7DQpJ3D3jJagQJugziThcqZCJVUT0HABHJt9EQMiiuf93KcUV/MMoisl56UfCxCFfmWQ== + dependencies: + "@aws-sdk/types" "^3.1.0" + "@aws-sdk/util-utf8-browser" "^3.0.0" + tslib "^1.11.1" + +"@aws-sdk/abort-controller@3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/abort-controller/-/abort-controller-3.34.0.tgz#35c4daee7c8343f2040249c90c4f6bd22ecbf7f2" + integrity sha512-+Qz8/e5sieViXW2qKbfGsXt73Mo87duI2n8HwfDmshjPRXBjpaN478bFHmAjvq9L4MbvW3hSL0mEGrpPErP9Tw== + dependencies: + "@aws-sdk/types" "3.34.0" + tslib "^2.3.0" + +"@aws-sdk/client-dynamodb@^3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-dynamodb/-/client-dynamodb-3.34.0.tgz#c2ee5ab8e07a455d984c3e0d265894c0ea064ec2" + integrity sha512-d6hy7oThQ2Y4yEWUERmCMBRgPci1PFS7J+fN/yvHZ9GF2X8c2ja24hY0L2D+k87u6IFRNiy/Gi6u2iZm/eGb1w== + dependencies: + "@aws-crypto/sha256-browser" "^1.0.0" + "@aws-crypto/sha256-js" "^1.0.0" + "@aws-sdk/client-sts" "3.34.0" + "@aws-sdk/config-resolver" "3.34.0" + "@aws-sdk/credential-provider-node" "3.34.0" + "@aws-sdk/fetch-http-handler" "3.34.0" + "@aws-sdk/hash-node" "3.34.0" + "@aws-sdk/invalid-dependency" "3.34.0" + "@aws-sdk/middleware-content-length" "3.34.0" + "@aws-sdk/middleware-endpoint-discovery" "3.34.0" + "@aws-sdk/middleware-host-header" "3.34.0" + "@aws-sdk/middleware-logger" "3.34.0" + "@aws-sdk/middleware-retry" "3.34.0" + "@aws-sdk/middleware-serde" "3.34.0" + "@aws-sdk/middleware-signing" "3.34.0" + "@aws-sdk/middleware-stack" "3.34.0" + "@aws-sdk/middleware-user-agent" "3.34.0" + "@aws-sdk/node-config-provider" "3.34.0" + "@aws-sdk/node-http-handler" "3.34.0" + "@aws-sdk/protocol-http" "3.34.0" + "@aws-sdk/smithy-client" "3.34.0" + "@aws-sdk/types" "3.34.0" + "@aws-sdk/url-parser" "3.34.0" + "@aws-sdk/util-base64-browser" "3.34.0" + "@aws-sdk/util-base64-node" "3.34.0" + "@aws-sdk/util-body-length-browser" "3.34.0" + "@aws-sdk/util-body-length-node" "3.34.0" + "@aws-sdk/util-user-agent-browser" "3.34.0" + "@aws-sdk/util-user-agent-node" "3.34.0" + "@aws-sdk/util-utf8-browser" "3.34.0" + "@aws-sdk/util-utf8-node" "3.34.0" + "@aws-sdk/util-waiter" "3.34.0" + tslib "^2.3.0" + uuid "^8.3.2" + +"@aws-sdk/client-sso@3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso/-/client-sso-3.34.0.tgz#4b663b604b57d231a2256f36e0224597f0141ec4" + integrity sha512-2eBbOF1AssSSYCCelTYdJabyY46dvc5AjHWzAhCPI4mpvKp5UtIxjmV8MmlKhS3WmPHZEpUNYnjou9uc86MCqA== + dependencies: + "@aws-crypto/sha256-browser" "^1.0.0" + "@aws-crypto/sha256-js" "^1.0.0" + "@aws-sdk/config-resolver" "3.34.0" + "@aws-sdk/fetch-http-handler" "3.34.0" + "@aws-sdk/hash-node" "3.34.0" + "@aws-sdk/invalid-dependency" "3.34.0" + "@aws-sdk/middleware-content-length" "3.34.0" + "@aws-sdk/middleware-host-header" "3.34.0" + "@aws-sdk/middleware-logger" "3.34.0" + "@aws-sdk/middleware-retry" "3.34.0" + "@aws-sdk/middleware-serde" "3.34.0" + "@aws-sdk/middleware-stack" "3.34.0" + "@aws-sdk/middleware-user-agent" "3.34.0" + "@aws-sdk/node-config-provider" "3.34.0" + "@aws-sdk/node-http-handler" "3.34.0" + "@aws-sdk/protocol-http" "3.34.0" + "@aws-sdk/smithy-client" "3.34.0" + "@aws-sdk/types" "3.34.0" + "@aws-sdk/url-parser" "3.34.0" + "@aws-sdk/util-base64-browser" "3.34.0" + "@aws-sdk/util-base64-node" "3.34.0" + "@aws-sdk/util-body-length-browser" "3.34.0" + "@aws-sdk/util-body-length-node" "3.34.0" + "@aws-sdk/util-user-agent-browser" "3.34.0" + "@aws-sdk/util-user-agent-node" "3.34.0" + "@aws-sdk/util-utf8-browser" "3.34.0" + "@aws-sdk/util-utf8-node" "3.34.0" + tslib "^2.3.0" + +"@aws-sdk/client-sts@3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-sts/-/client-sts-3.34.0.tgz#716fa7d161cf79950f7e8e369fdb0ecd847c2031" + integrity sha512-jC+Z779fOyU8kk+Xlbu0Mox2mku91X40jZ0TAxNNEhe0i5mOxv8/L2SkE2daKTQxXBem/gTctQmrZ9g4fRm7dw== + dependencies: + "@aws-crypto/sha256-browser" "^1.0.0" + "@aws-crypto/sha256-js" "^1.0.0" + "@aws-sdk/config-resolver" "3.34.0" + "@aws-sdk/credential-provider-node" "3.34.0" + "@aws-sdk/fetch-http-handler" "3.34.0" + "@aws-sdk/hash-node" "3.34.0" + "@aws-sdk/invalid-dependency" "3.34.0" + "@aws-sdk/middleware-content-length" "3.34.0" + "@aws-sdk/middleware-host-header" "3.34.0" + "@aws-sdk/middleware-logger" "3.34.0" + "@aws-sdk/middleware-retry" "3.34.0" + "@aws-sdk/middleware-sdk-sts" "3.34.0" + "@aws-sdk/middleware-serde" "3.34.0" + "@aws-sdk/middleware-signing" "3.34.0" + "@aws-sdk/middleware-stack" "3.34.0" + "@aws-sdk/middleware-user-agent" "3.34.0" + "@aws-sdk/node-config-provider" "3.34.0" + "@aws-sdk/node-http-handler" "3.34.0" + "@aws-sdk/protocol-http" "3.34.0" + "@aws-sdk/smithy-client" "3.34.0" + "@aws-sdk/types" "3.34.0" + "@aws-sdk/url-parser" "3.34.0" + "@aws-sdk/util-base64-browser" "3.34.0" + "@aws-sdk/util-base64-node" "3.34.0" + "@aws-sdk/util-body-length-browser" "3.34.0" + "@aws-sdk/util-body-length-node" "3.34.0" + "@aws-sdk/util-user-agent-browser" "3.34.0" + "@aws-sdk/util-user-agent-node" "3.34.0" + "@aws-sdk/util-utf8-browser" "3.34.0" + "@aws-sdk/util-utf8-node" "3.34.0" + entities "2.2.0" + fast-xml-parser "3.19.0" + tslib "^2.3.0" + +"@aws-sdk/config-resolver@3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/config-resolver/-/config-resolver-3.34.0.tgz#b1bc09de26773401277cf6035e23993a20b4e449" + integrity sha512-goAv4oFpDhsnCJehf0Cr0s4bCxOmfj5fz/SZXsLJbW3rGh8CE+s9hxkFqa1JBR/AN+OBDZ8NRRFIYwnftq9+xw== + dependencies: + "@aws-sdk/signature-v4" "3.34.0" + "@aws-sdk/types" "3.34.0" + tslib "^2.3.0" + +"@aws-sdk/credential-provider-env@3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-env/-/credential-provider-env-3.34.0.tgz#ec1f3278af425ae9764dd33e446a93f71e13132f" + integrity sha512-vbsLvcZ1gPnjcToSNYMmc0YR1xz4WzkubpFGVXfwcG3iJcQnu+gKXWeCVEfVrjprlNHuqWXnCCi0rNIncKVavQ== + dependencies: + "@aws-sdk/property-provider" "3.34.0" + "@aws-sdk/types" "3.34.0" + tslib "^2.3.0" + +"@aws-sdk/credential-provider-imds@3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-imds/-/credential-provider-imds-3.34.0.tgz#6ddffd72ba654d140b6f83425ceceadd4cba2ef4" + integrity sha512-f56md7Xpd7nzkvRFoAaGf3VCXLuNN0jHh9HQFx4VnrQtp9dcVJK1G/2hYl1dlHFIvHBupWKOnzuUplyxyV55pQ== + dependencies: + "@aws-sdk/node-config-provider" "3.34.0" + "@aws-sdk/property-provider" "3.34.0" + "@aws-sdk/types" "3.34.0" + "@aws-sdk/url-parser" "3.34.0" + tslib "^2.3.0" + +"@aws-sdk/credential-provider-ini@3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.34.0.tgz#879da3464b1e7f326222f995fcfe896231467013" + integrity sha512-8SPeje+0tnGPbQ21+TESNE7B8PaIuEGxbkvbMSCm+cKij8Oo6N+StSGZh+0FJhlh+QptWjR/yaH0MKYpIUFUGw== + dependencies: + "@aws-sdk/credential-provider-env" "3.34.0" + "@aws-sdk/credential-provider-imds" "3.34.0" + "@aws-sdk/credential-provider-sso" "3.34.0" + "@aws-sdk/credential-provider-web-identity" "3.34.0" + "@aws-sdk/property-provider" "3.34.0" + "@aws-sdk/shared-ini-file-loader" "3.34.0" + "@aws-sdk/types" "3.34.0" + "@aws-sdk/util-credentials" "3.34.0" + tslib "^2.3.0" + +"@aws-sdk/credential-provider-node@3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.34.0.tgz#059d548dece894e1b7b356e50065ca7ac2881a99" + integrity sha512-aZkBWHe9bVNraQlhHageKPaxrZGGIFBTKJ1WT19cHZVnCj8ufCoogvct28eW1SEucn1jPBWPFhaX3/0y16Bc6Q== + dependencies: + "@aws-sdk/credential-provider-env" "3.34.0" + "@aws-sdk/credential-provider-imds" "3.34.0" + "@aws-sdk/credential-provider-ini" "3.34.0" + "@aws-sdk/credential-provider-process" "3.34.0" + "@aws-sdk/credential-provider-sso" "3.34.0" + "@aws-sdk/credential-provider-web-identity" "3.34.0" + "@aws-sdk/property-provider" "3.34.0" + "@aws-sdk/shared-ini-file-loader" "3.34.0" + "@aws-sdk/types" "3.34.0" + "@aws-sdk/util-credentials" "3.34.0" + tslib "^2.3.0" + +"@aws-sdk/credential-provider-process@3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-process/-/credential-provider-process-3.34.0.tgz#0b46db054662976e76c721e69a4ad896ffdb4bd2" + integrity sha512-4n07qYfLLzjTLyrQTT7iuZTlKZLYyKT4yFjnKyJT3dOoFyW4jWFWjZyicY9TNg4D4f2ya6P3w+H+03DGof0Opw== + dependencies: + "@aws-sdk/property-provider" "3.34.0" + "@aws-sdk/shared-ini-file-loader" "3.34.0" + "@aws-sdk/types" "3.34.0" + "@aws-sdk/util-credentials" "3.34.0" + tslib "^2.3.0" + +"@aws-sdk/credential-provider-sso@3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.34.0.tgz#ac241429a04a67ff8f0403e74ed664e53a150ab1" + integrity sha512-/xyUHA8o5dIaMoKVigjeIgGI1ofGBsVmb1cK16m3w0CDDUgyoEeQD+pust68rYiyuuhgaRqnRtVvjysPPJPMaA== + dependencies: + "@aws-sdk/client-sso" "3.34.0" + "@aws-sdk/property-provider" "3.34.0" + "@aws-sdk/shared-ini-file-loader" "3.34.0" + "@aws-sdk/types" "3.34.0" + "@aws-sdk/util-credentials" "3.34.0" + tslib "^2.3.0" + +"@aws-sdk/credential-provider-web-identity@3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.34.0.tgz#54c4ef1d54e7007bde9751769b4b1d15833353f3" + integrity sha512-4YvuKL2TOyEuktU7v1objIc9PoDMSm2Fxp90KJckitcGnLMsdcaoIV9GgF5nIZrZ7tOUg2ultamCJUkUFZYLMA== + dependencies: + "@aws-sdk/property-provider" "3.34.0" + "@aws-sdk/types" "3.34.0" + tslib "^2.3.0" + +"@aws-sdk/endpoint-cache@3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/endpoint-cache/-/endpoint-cache-3.34.0.tgz#254b344774cebcf99140a743a31e30e3e795c33f" + integrity sha512-9OGmxYxtWNZeyImdrZ3/j0RyTnrLRvVS+eLGMhLORkPE/AyT+vJsz7GgPVGjO+Rzbkqb7u98ikTh/AqO/7uLpQ== + dependencies: + mnemonist "0.38.3" + tslib "^2.3.0" + +"@aws-sdk/fetch-http-handler@3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/fetch-http-handler/-/fetch-http-handler-3.34.0.tgz#36b3689fcc228eaab986261a842198a629fdee8a" + integrity sha512-evcyWkI0xsQYjSZOzYy+1ke8CwzOaUpg0+tCLPQwG4GKC+GmRWd0ABrxs8OmPBo4SHPQvDpUakX0CRiZu/gm/g== + dependencies: + "@aws-sdk/protocol-http" "3.34.0" + "@aws-sdk/querystring-builder" "3.34.0" + "@aws-sdk/types" "3.34.0" + "@aws-sdk/util-base64-browser" "3.34.0" + tslib "^2.3.0" + +"@aws-sdk/hash-node@3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/hash-node/-/hash-node-3.34.0.tgz#3734fab2dcbd55592592fc2143f6b243c093825b" + integrity sha512-gAPr7p+MgLJ75YRQSELidq0bU/RduPI+kjyY516Od79CNwwe9DEN2JeQakXN3lUfExgp38P9onsRr1d6wKP4EQ== + dependencies: + "@aws-sdk/types" "3.34.0" + "@aws-sdk/util-buffer-from" "3.34.0" + tslib "^2.3.0" + +"@aws-sdk/invalid-dependency@3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/invalid-dependency/-/invalid-dependency-3.34.0.tgz#818637b8631b6b90ee853114afc5c5ec84e27ccd" + integrity sha512-Seaiu1fT9k7F03KVi/rVf+buRAyAEAzg66ryg14GQW/cei/b/uD4+Gohyk+mRnMqDh4nNluCaktzhr7wSZGeVA== + dependencies: + "@aws-sdk/types" "3.34.0" + tslib "^2.3.0" + +"@aws-sdk/is-array-buffer@3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/is-array-buffer/-/is-array-buffer-3.34.0.tgz#99c72146882ec3e01ddb265d87316f424623c8fb" + integrity sha512-YOblEaT5Fq7qHys8mSrNdJS3ZUsLviYyOk6q3ixmHPFcFpO3etGr5AnlDSFqlSm0jnX/0irmp3fbF96kMKLtUg== + dependencies: + tslib "^2.3.0" + +"@aws-sdk/middleware-content-length@3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-content-length/-/middleware-content-length-3.34.0.tgz#6df02122a7dd6108a6c9cd81328545f0ba07c33a" + integrity sha512-+5NnSDEDxcAWXDQSXe1P+Dywrb6sYKSaoiu+kNcYB21mTwG3i9St1A2Q0wNMsdRMRVwSg/lJV5MbY5Mbk0+D5w== + dependencies: + "@aws-sdk/protocol-http" "3.34.0" + "@aws-sdk/types" "3.34.0" + tslib "^2.3.0" + +"@aws-sdk/middleware-endpoint-discovery@3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-endpoint-discovery/-/middleware-endpoint-discovery-3.34.0.tgz#282c677ddcda41d2a7a2f67443f3fab010d2c909" + integrity sha512-AV+X6rBXdI8akLQgnqwvvFzGd6BjF0fy7TEob+uDp9iE5zX+YN7Jy2b3ORly93s9Z2GxxHdFeS13A3MZbvzQWQ== + dependencies: + "@aws-sdk/config-resolver" "3.34.0" + "@aws-sdk/endpoint-cache" "3.34.0" + "@aws-sdk/protocol-http" "3.34.0" + "@aws-sdk/types" "3.34.0" + tslib "^2.3.0" + +"@aws-sdk/middleware-host-header@3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-host-header/-/middleware-host-header-3.34.0.tgz#bb6c085591c7ac872e27b55482334d63cb0b14b6" + integrity sha512-3ZcBTicauGp0reKQ8Pw8ardk9kRTO7/lWAKGV7f0PnfOoGn+zUqVP0qvaHL3VNVdTJEz6fkkU7pJ0duCVCnpmA== + dependencies: + "@aws-sdk/protocol-http" "3.34.0" + "@aws-sdk/types" "3.34.0" + tslib "^2.3.0" + +"@aws-sdk/middleware-logger@3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-logger/-/middleware-logger-3.34.0.tgz#2ba8556c355d286d1186e2ef8ae9f7314b3bfbd6" + integrity sha512-EBVp3tkEbMSW9ikMzejucYLOf+G98+vGqWSkvGbR0N8lBHs2za+fufBqknEY8lD7a8RLFQ3enevl8DMJ9d75pA== + dependencies: + "@aws-sdk/types" "3.34.0" + tslib "^2.3.0" + +"@aws-sdk/middleware-retry@3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-retry/-/middleware-retry-3.34.0.tgz#b760ac22dceaa573d89bdb31ab348ac146744510" + integrity sha512-PKWlJWhr7b3jEsTDRBXf7mIUkjnGMQiYvNs3G1kdwrySbii2yEOlwy7KXAAo/TCwuL10W+HZG7EZwkwXNg7LTQ== + dependencies: + "@aws-sdk/protocol-http" "3.34.0" + "@aws-sdk/service-error-classification" "3.34.0" + "@aws-sdk/types" "3.34.0" + tslib "^2.3.0" + uuid "^8.3.2" + +"@aws-sdk/middleware-sdk-sts@3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.34.0.tgz#aabd30de0e50a01afa821e8f4ecc8435c3639cc5" + integrity sha512-tJPkij4H0z4AbjOnRlcUP99dJud4SlH9fYfnxQ1CeG1PO0Zz1zTEc+2Sc93068bS4K4nrD4QaVsoOokvjW/4MQ== + dependencies: + "@aws-sdk/middleware-signing" "3.34.0" + "@aws-sdk/property-provider" "3.34.0" + "@aws-sdk/protocol-http" "3.34.0" + "@aws-sdk/signature-v4" "3.34.0" + "@aws-sdk/types" "3.34.0" + tslib "^2.3.0" + +"@aws-sdk/middleware-serde@3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-serde/-/middleware-serde-3.34.0.tgz#4c329f6dd6ef60a2be88bcb2becaf64152425639" + integrity sha512-oU2oa6hy1+DrFROD+K00NkRNdrsNgryay6rCA/jSUfwGEFmvN3iANTgXkEEvEKCSYqyh/ai998jk+6v60ChFBQ== + dependencies: + "@aws-sdk/types" "3.34.0" + tslib "^2.3.0" + +"@aws-sdk/middleware-signing@3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-signing/-/middleware-signing-3.34.0.tgz#a33a51b22be0bc9a464f962f4e48a8d1282344fc" + integrity sha512-dlxyxIg6Mrjx6GzUVQoQV9M/n3kEvs6aMEzR3DTF0i8CGqWtKXE5OZhsjLVRflVOejONM7NEXwNeFHr+lQ4Z9w== + dependencies: + "@aws-sdk/property-provider" "3.34.0" + "@aws-sdk/protocol-http" "3.34.0" + "@aws-sdk/signature-v4" "3.34.0" + "@aws-sdk/types" "3.34.0" + tslib "^2.3.0" + +"@aws-sdk/middleware-stack@3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-stack/-/middleware-stack-3.34.0.tgz#2e76f8c03557d784bfafa7f517cbbeb27c38f580" + integrity sha512-7WI+spzWcTYWIP0MwTDlE+LsWGlpZq44mvRGnNHDjnTjqfr5C3kWSc86fedujvbaW5ZGTes5NGmKQf/PqAmUqQ== + dependencies: + tslib "^2.3.0" + +"@aws-sdk/middleware-user-agent@3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.34.0.tgz#ad8e37578a0e2baba2cd2f9a652daea5e71c9f64" + integrity sha512-EZBy0fNhuenzxlVAx2GcgBFOGCTyHs1Gg1GB/qUIhqZ2MHY66Yogj4BqPkiOg+zo7nFwCjWQ9LRypYZYXCWMLw== + dependencies: + "@aws-sdk/protocol-http" "3.34.0" + "@aws-sdk/types" "3.34.0" + tslib "^2.3.0" + +"@aws-sdk/node-config-provider@3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/node-config-provider/-/node-config-provider-3.34.0.tgz#5280260700316929ca28347da4d4773a47a733d8" + integrity sha512-VPDYLNkwvhAlhSWecMbmiXpa/n20i9S4z0/+pSB4kXT73MvH3us77ecYXD5g8IIRrAR+Oaq8hrQLDP88bbte9g== + dependencies: + "@aws-sdk/property-provider" "3.34.0" + "@aws-sdk/shared-ini-file-loader" "3.34.0" + "@aws-sdk/types" "3.34.0" + tslib "^2.3.0" + +"@aws-sdk/node-http-handler@3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/node-http-handler/-/node-http-handler-3.34.0.tgz#17e76f6feeb4f5c62c6e3ebb8c981ec146e4fbda" + integrity sha512-Fyf0VsgatDr3puqIA4CHTqVKUPBveq0skQhXyPDXxXIllxExpqcrWu8fFqE2DalCqiDiDC7zR8Yh56syiBxmvA== + dependencies: + "@aws-sdk/abort-controller" "3.34.0" + "@aws-sdk/protocol-http" "3.34.0" + "@aws-sdk/querystring-builder" "3.34.0" + "@aws-sdk/types" "3.34.0" + tslib "^2.3.0" + +"@aws-sdk/property-provider@3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/property-provider/-/property-provider-3.34.0.tgz#64f84be6d486e986852bc36d17559e923c64bc99" + integrity sha512-Xkj6l14bYhKnzUXenBn8TANvR5p4pzLZF8Uk/pCVYZFgotOd5gVTC89SAQa4Lw2PzwoTqOsWKglS7CWi4KaBDg== + dependencies: + "@aws-sdk/types" "3.34.0" + tslib "^2.3.0" + +"@aws-sdk/protocol-http@3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/protocol-http/-/protocol-http-3.34.0.tgz#7eb22d1f772f92818d62bb6204d76d1926f81931" + integrity sha512-VPzI/VcDXqoWcyJNc0p/ee1pjGXFC8PmaZwK7PO3FkNEa8BE/9IbfVg3AGIekEDIXwpdZAjQeLCmOsMx866S2Q== + dependencies: + "@aws-sdk/types" "3.34.0" + tslib "^2.3.0" + +"@aws-sdk/querystring-builder@3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/querystring-builder/-/querystring-builder-3.34.0.tgz#38bb4c6d1bd0e12d8064553c0245b75bfbd44108" + integrity sha512-lDkJPvDsR+Ou0NIWUJlVCpi6vDNaPVgxtP2r/4kktoSYzwFz4UCkmKcDRdL4cxEZ6LYA0QkjtkyfQotoZMuIXA== + dependencies: + "@aws-sdk/types" "3.34.0" + "@aws-sdk/util-uri-escape" "3.34.0" + tslib "^2.3.0" + +"@aws-sdk/querystring-parser@3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/querystring-parser/-/querystring-parser-3.34.0.tgz#213d21fb5312d804ddb741d3796d8a6744232572" + integrity sha512-IMpHNQOSPVk7qaDDaFL6xmTW4VuMbTxyhRdrVmxZXWWgRvMRs2chjXdq3iyTtpnKq5GNnEdPY7udjfWqYoaN8A== + dependencies: + "@aws-sdk/types" "3.34.0" + tslib "^2.3.0" + +"@aws-sdk/service-error-classification@3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/service-error-classification/-/service-error-classification-3.34.0.tgz#9302562c7862c1f1d47e82d8e7d6eafed50a8327" + integrity sha512-RYmgfrGSfULduJf753rMlK0E+NoK6PUaACb5eiVKoGyo8NpzDFcDWVL2yAb40tAqI7ahhWz1uqGzb1sRPWSAIg== + +"@aws-sdk/shared-ini-file-loader@3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/shared-ini-file-loader/-/shared-ini-file-loader-3.34.0.tgz#cbe5dfcb35aa656d2800e728853f8a7d954272ab" + integrity sha512-zh238/JYpP9+7FkCODH+UYqL1StbBtidFOt8Vmo4MNdh10R31XImnL0npdhRLiZSkOoatpHc+ViCOeH+I67QiQ== + dependencies: + tslib "^2.3.0" + +"@aws-sdk/signature-v4@3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/signature-v4/-/signature-v4-3.34.0.tgz#57a378fafe2c4e4395db9b16479d5397ef8fc048" + integrity sha512-oKW5Frsu4TMc/8eLcRE4kdgKj1XqsVCry9VdjDgNpx37aeH7oggg75YDU+UZuhdF++hDr6uCa+DYxtluQ8g9sg== + dependencies: + "@aws-sdk/is-array-buffer" "3.34.0" + "@aws-sdk/types" "3.34.0" + "@aws-sdk/util-hex-encoding" "3.34.0" + "@aws-sdk/util-uri-escape" "3.34.0" + tslib "^2.3.0" + +"@aws-sdk/smithy-client@3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/smithy-client/-/smithy-client-3.34.0.tgz#4b982823c804beff373723461a6057d26442ac5e" + integrity sha512-nhLNpqehOYuBVr2f2LodMbO1j8J97WBTjfiyosUhVh36WDmSCPu4LjAJuzOLPF6LXp4fBK0BA04rJQA6nyFC0g== + dependencies: + "@aws-sdk/middleware-stack" "3.34.0" + "@aws-sdk/types" "3.34.0" + tslib "^2.3.0" + +"@aws-sdk/types@3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.34.0.tgz#832a802838d0f0ae568db8e3ce1ee550f05bb4b4" + integrity sha512-rx9mJp+yKEgb6HVyMtytG+45xwiX3eaHy1VrPC0RV/Uxym1iGyFmpHYo+0/UgL1BTRrJXLA9gTfj15H5kyZ6/Q== + +"@aws-sdk/types@^3.1.0": + version "3.32.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.32.0.tgz#67f5324e70272c17b67e19fb5c9647b08d9e40ed" + integrity sha512-ZkB+jFk6FZ9wA9wvQTqv6ao2sPSVeMlUF349NecPGLtpy2c/+RPxO10NmJ8yG9jFfmB0OXLnEPrDR7VinxTHNw== + +"@aws-sdk/url-parser@3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/url-parser/-/url-parser-3.34.0.tgz#c39ea8b40ec1ae93cf4e13748664e95f2ee840da" + integrity sha512-RqF5vQ3+kTRhNmsT7V9KqPnfrDsHZg8XjgEFdr29iKXEupOockQlJBVGTesy73y9nvnMueNKNZkk4VLeHEW6cg== + dependencies: + "@aws-sdk/querystring-parser" "3.34.0" + "@aws-sdk/types" "3.34.0" + tslib "^2.3.0" + +"@aws-sdk/util-base64-browser@3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-base64-browser/-/util-base64-browser-3.34.0.tgz#9036eb043e1e73c124d3a001e2722fea78ec85f7" + integrity sha512-3ScDOSlrX4EnwWzYnDfKa3GjiLYnHfk2YEV5G+f/NOEKhsuTsj++PGvdLqbXc4m03rLiDBoMgN204tz78rSgsA== + dependencies: + tslib "^2.3.0" + +"@aws-sdk/util-base64-node@3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-base64-node/-/util-base64-node-3.34.0.tgz#888de451a17d79664660007b7f3e1792bc808f40" + integrity sha512-r7tPqydtuzjMH9BBPw34l8zlN84O8EuJPl2mx1JXFrDE+ADUx1VBU+KK1pnIFoFVb7/FWMVPeaKz1u0EjsSKRQ== + dependencies: + "@aws-sdk/util-buffer-from" "3.34.0" + tslib "^2.3.0" + +"@aws-sdk/util-body-length-browser@3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-body-length-browser/-/util-body-length-browser-3.34.0.tgz#da6b06476fcdd4466ae423734d4c77c85f5af184" + integrity sha512-Av7x9dKoygju9QBuTIGa7JITxGlIj7NhN22CxX3FThIIJofp5qbnTTiLURivM6jw6Wvf2sZ4mpP34xEeDvXG/A== + dependencies: + tslib "^2.3.0" + +"@aws-sdk/util-body-length-node@3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-body-length-node/-/util-body-length-node-3.34.0.tgz#88ab279384584b2b7dcb399345eab82ef5732d46" + integrity sha512-slrlSnPNQd/6pvTpvK0Z8z7hbrND0dEA+0/4Y3l47hBftznxhiKbLKiCg5a3iBjTxUB0qSQ03YNY/fK3DtPJTA== + dependencies: + tslib "^2.3.0" + +"@aws-sdk/util-buffer-from@3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-buffer-from/-/util-buffer-from-3.34.0.tgz#c4f721e892186b304077b875e5a87caa0c148ce9" + integrity sha512-G1XXPD3IfHYXEGwknC2ogn2CylxrYcdwwWbS1vGji9zP6YX8C/gf+moLtJz3bDlMwSHTLdzdH/RWBIL75djGKg== + dependencies: + "@aws-sdk/is-array-buffer" "3.34.0" + tslib "^2.3.0" + +"@aws-sdk/util-credentials@3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-credentials/-/util-credentials-3.34.0.tgz#74a9b84295093b065e4bffa10cde0e31bccf27fb" + integrity sha512-Spb7MtRMmAeHekaju85m5WOO8/C277+6pWvBQlh3vADwmxsBdyvDCIZtXLhlqW/zeUUKvr7atkuhW5h2Ck+gfw== + dependencies: + "@aws-sdk/shared-ini-file-loader" "3.34.0" + tslib "^2.3.0" + +"@aws-sdk/util-hex-encoding@3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-hex-encoding/-/util-hex-encoding-3.34.0.tgz#e8f618800485adbd5696f11cba9607b9f80ff5a9" + integrity sha512-eM1jKmD1SOCluLMqOxmbJRqPbuSyDGp1HELEAYt1zG2xy1pAugiJK7zyCcNZ8Wt/1RQQA1QnPnSTtQFK1GVzbA== + dependencies: + tslib "^2.3.0" + +"@aws-sdk/util-locate-window@^3.0.0": + version "3.32.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-locate-window/-/util-locate-window-3.32.0.tgz#655fe4fbd7e3cbc05b630984c5813c6d9c4cee01" + integrity sha512-P5sOlu7AhzxhyUtKj4aYK35MrXFrt64XivwgAUo7h+ZUx6iWUEflpurMqm2dExUYNVnpGaeTKkNX8qdvbnDGZw== + dependencies: + tslib "^2.3.0" + +"@aws-sdk/util-uri-escape@3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-uri-escape/-/util-uri-escape-3.34.0.tgz#9a22877f6366fe0f30416c035a66474dde8a7200" + integrity sha512-UxB5LfwB4BWdQyLJkkHz/U/gigQBkkfVhQHJx0Sg/yMH/PnTuUqdj+MWhezca/9VQShx24uBewXWV4ToybYULA== + dependencies: + tslib "^2.3.0" + +"@aws-sdk/util-user-agent-browser@3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.34.0.tgz#d3ca8f4c77e1cd1b9cce99acdfbbf078dd504ac1" + integrity sha512-7bSk/d0LPzGnYLkKI0Z91dsoPudoayRywrYjdEbTbD85d+2VDKuxszhACVV4hbcQSc7x0zCumRJE7rx3c2weIQ== + dependencies: + "@aws-sdk/types" "3.34.0" + bowser "^2.11.0" + tslib "^2.3.0" + +"@aws-sdk/util-user-agent-node@3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.34.0.tgz#328d0d986c3614601279d9b9d2a1744efc07610d" + integrity sha512-EupJGySosLRk9NWYajE4x6gEEW+ZiW4cdNlDUkDWng+qruxNJDxLIO2xRrPBFiA+s/DMy6HRXWROBWpGnqLB5w== + dependencies: + "@aws-sdk/node-config-provider" "3.34.0" + "@aws-sdk/types" "3.34.0" + tslib "^2.3.0" + +"@aws-sdk/util-utf8-browser@3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.34.0.tgz#6fd3b06cbed53a83f1f438f5ef8f29aa045f5666" + integrity sha512-auB09BoFk5mPA444WmBpF5dZN+59ojrmpAcJf4zc0S/UfsWzAQDj7Lsj2aSu9O0xWoDmtzUWxfiwQqNIEqcNIA== + dependencies: + tslib "^2.3.0" + +"@aws-sdk/util-utf8-browser@^3.0.0": + version "3.32.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.32.0.tgz#049c26cfacf1db19e3b0c1841ece7d9b900a59bf" + integrity sha512-KjyGj1TFmR5siw5960hKaicdtyQJJDXgiXm0CM7PMXKLgrT8C2/PmVrpF2qYDGpGgfXVRgZNHNMv3XNMAw9vlw== + dependencies: + tslib "^2.3.0" + +"@aws-sdk/util-utf8-node@3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-utf8-node/-/util-utf8-node-3.34.0.tgz#f5f7e5a5a8a076ee990eb14a25d8ffa509e52ddd" + integrity sha512-Okarq9OUG99UOcen3dYUQCw62Fm9DQk3zyt/7MMgbt9G6Sz6kQ6s1vtC4Ip+HxhALBQPFj8THcf2ijh55L6unQ== + dependencies: + "@aws-sdk/util-buffer-from" "3.34.0" + tslib "^2.3.0" + +"@aws-sdk/util-waiter@3.34.0": + version "3.34.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-waiter/-/util-waiter-3.34.0.tgz#8ce558595bbb6592f0c44360fcb80fe4dae6be48" + integrity sha512-UlrNCXnYJwB0ERX8CVDHD6jDqTU89CRMZsKxYJlfSnmQ5utQs4b2bxVhqDSE7aHFGW23SXEqQr5XlcSAixbYMA== + dependencies: + "@aws-sdk/abort-controller" "3.34.0" + "@aws-sdk/types" "3.34.0" + tslib "^2.3.0" + "@azure/functions@^1.2.3": version "1.2.3" resolved "https://registry.yarnpkg.com/@azure/functions/-/functions-1.2.3.tgz#65765837e7319eedffbf8a971cb2f78d4e043d54" @@ -536,27 +1153,27 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd" integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw== -"@jest/console@^27.1.1": - version "27.1.1" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-27.1.1.tgz#e1eb8ef8a410e75e80bb17429047ed5d43411d20" - integrity sha512-VpQJRsWSeAem0zpBjeRtDbcD6DlbNoK11dNYt+PSQ+DDORh9q2/xyEpErfwgnLjWX0EKkSZmTGx/iH9Inzs6vQ== +"@jest/console@^27.2.2": + version "27.2.2" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-27.2.2.tgz#a977245155c519ac2ef713ec0e722d13eda893c9" + integrity sha512-m7tbzPWyvSFfoanTknJoDnaeruDARsUe555tkVjG/qeaRDKwyPqqbgs4yFx583gmoETiAts1deeYozR5sVRhNA== dependencies: "@jest/types" "^27.1.1" "@types/node" "*" chalk "^4.0.0" - jest-message-util "^27.1.1" - jest-util "^27.1.1" + jest-message-util "^27.2.2" + jest-util "^27.2.0" slash "^3.0.0" -"@jest/core@^27.1.1": - version "27.1.1" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-27.1.1.tgz#d9d42214920cb96c2a6cc48517cf62d4351da3aa" - integrity sha512-oCkKeTgI0emznKcLoq5OCD0PhxCijA4l7ejDnWW3d5bgSi+zfVaLybVqa+EQOxpNejQWtTna7tmsAXjMN9N43Q== +"@jest/core@^27.2.2": + version "27.2.2" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-27.2.2.tgz#9eea16101b2f04bf799dcdbdf1792d4ef7553ecf" + integrity sha512-4b9km/h9pAGdCkwWYtbfoeiOtajOlGmr5rL1Eq6JCAVbOevOqxWHxJ6daWxRJW9eF6keXJoJ1H+uVAVcdZu8Bg== dependencies: - "@jest/console" "^27.1.1" - "@jest/reporters" "^27.1.1" - "@jest/test-result" "^27.1.1" - "@jest/transform" "^27.1.1" + "@jest/console" "^27.2.2" + "@jest/reporters" "^27.2.2" + "@jest/test-result" "^27.2.2" + "@jest/transform" "^27.2.2" "@jest/types" "^27.1.1" "@types/node" "*" ansi-escapes "^4.2.1" @@ -565,64 +1182,64 @@ exit "^0.1.2" graceful-fs "^4.2.4" jest-changed-files "^27.1.1" - jest-config "^27.1.1" - jest-haste-map "^27.1.1" - jest-message-util "^27.1.1" + jest-config "^27.2.2" + jest-haste-map "^27.2.2" + jest-message-util "^27.2.2" jest-regex-util "^27.0.6" - jest-resolve "^27.1.1" - jest-resolve-dependencies "^27.1.1" - jest-runner "^27.1.1" - jest-runtime "^27.1.1" - jest-snapshot "^27.1.1" - jest-util "^27.1.1" - jest-validate "^27.1.1" - jest-watcher "^27.1.1" + jest-resolve "^27.2.2" + jest-resolve-dependencies "^27.2.2" + jest-runner "^27.2.2" + jest-runtime "^27.2.2" + jest-snapshot "^27.2.2" + jest-util "^27.2.0" + jest-validate "^27.2.2" + jest-watcher "^27.2.2" micromatch "^4.0.4" p-each-series "^2.1.0" rimraf "^3.0.0" slash "^3.0.0" strip-ansi "^6.0.0" -"@jest/environment@^27.1.1": - version "27.1.1" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-27.1.1.tgz#a1f7a552f7008f773988b9c0e445ede35f77bbb7" - integrity sha512-+y882/ZdxhyqF5RzxIrNIANjHj991WH7jifdcplzMDosDUOyCACFYUyVTBGbSTocbU+s1cesroRzkwi8hZ9SHg== +"@jest/environment@^27.2.2": + version "27.2.2" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-27.2.2.tgz#2e57b9d2cc01028b0e35fae5833c1c63df4c5e41" + integrity sha512-gO9gVnZfn5ldeOJ5q+35Kru9QWGHEqZCB7W/M+8mD6uCwOGC9HR6mzpLSNRuDsxY/KhaGBYHpgFqtpe4Rl1gDQ== dependencies: - "@jest/fake-timers" "^27.1.1" + "@jest/fake-timers" "^27.2.2" "@jest/types" "^27.1.1" "@types/node" "*" jest-mock "^27.1.1" -"@jest/fake-timers@^27.1.1": - version "27.1.1" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-27.1.1.tgz#557a1c0d067d33bcda4dfae9a7d8f96a15a954b5" - integrity sha512-u8TJ5VlsVYTsGFatoyIae2l25pku4Bu15QCPTx2Gs5z+R//Ee3tHN85462Vc9yGVcdDvgADbqNkhOLxbEwPjMQ== +"@jest/fake-timers@^27.2.2": + version "27.2.2" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-27.2.2.tgz#43e6f191c95ae74e95d0ddba2ecb8470b4a288b7" + integrity sha512-gDIIqs0yxyjyxEI9HlJ8SEJ4uCc8qr8BupG1Hcx7tvyk/SLocyXE63rFxL+HQ0ZLMvSyEcJUmYnvvHH1osWiGA== dependencies: "@jest/types" "^27.1.1" "@sinonjs/fake-timers" "^7.0.2" "@types/node" "*" - jest-message-util "^27.1.1" + jest-message-util "^27.2.2" jest-mock "^27.1.1" - jest-util "^27.1.1" + jest-util "^27.2.0" -"@jest/globals@^27.1.1": - version "27.1.1" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-27.1.1.tgz#cfe5f4d5b37483cef62b79612128ccc7e3c951d8" - integrity sha512-Q3JcTPmY+DAEHnr4MpnBV3mwy50EGrTC6oSDTNnW7FNGGacTJAfpWNk02D7xv422T1OzK2A2BKx+26xJOvHkyw== +"@jest/globals@^27.2.2": + version "27.2.2" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-27.2.2.tgz#df66aaafda5c69b2bb0dae548e3cfb345f549c31" + integrity sha512-fWa/Luwod1hyehnuep+zCnOTqTVvyc4HLUU/1VpFNOEu0tCWNSODyvKSSOjtb1bGOpCNjgaDcyjzo5f7rl6a7g== dependencies: - "@jest/environment" "^27.1.1" + "@jest/environment" "^27.2.2" "@jest/types" "^27.1.1" - expect "^27.1.1" + expect "^27.2.2" -"@jest/reporters@^27.1.1": - version "27.1.1" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-27.1.1.tgz#ee5724092f197bb78c60affb9c6f34b6777990c2" - integrity sha512-cEERs62n1P4Pqox9HWyNOEkP57G95aK2mBjB6D8Ruz1Yc98fKH53b58rlVEnsY5nLmkLNZk65fxNi9C0Yds/8w== +"@jest/reporters@^27.2.2": + version "27.2.2" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-27.2.2.tgz#e2d41cd9f8088676b81b9a9908cb1ba67bdbee78" + integrity sha512-ufwZ8XoLChEfPffDeVGroYbhbcYPom3zKDiv4Flhe97rr/o2IfUXoWkDUDoyJ3/V36RFIMjokSu0IJ/pbFtbHg== dependencies: "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^27.1.1" - "@jest/test-result" "^27.1.1" - "@jest/transform" "^27.1.1" + "@jest/console" "^27.2.2" + "@jest/test-result" "^27.2.2" + "@jest/transform" "^27.2.2" "@jest/types" "^27.1.1" chalk "^4.0.0" collect-v8-coverage "^1.0.0" @@ -634,10 +1251,10 @@ istanbul-lib-report "^3.0.0" istanbul-lib-source-maps "^4.0.0" istanbul-reports "^3.0.2" - jest-haste-map "^27.1.1" - jest-resolve "^27.1.1" - jest-util "^27.1.1" - jest-worker "^27.1.1" + jest-haste-map "^27.2.2" + jest-resolve "^27.2.2" + jest-util "^27.2.0" + jest-worker "^27.2.2" slash "^3.0.0" source-map "^0.6.0" string-length "^4.0.1" @@ -653,30 +1270,30 @@ graceful-fs "^4.2.4" source-map "^0.6.0" -"@jest/test-result@^27.1.1": - version "27.1.1" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-27.1.1.tgz#1086b39af5040b932a55e7f1fa1bc4671bed4781" - integrity sha512-8vy75A0Jtfz9DqXFUkjC5Co/wRla+D7qRFdShUY8SbPqBS3GBx3tpba7sGKFos8mQrdbe39n+c1zgVKtarfy6A== +"@jest/test-result@^27.2.2": + version "27.2.2" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-27.2.2.tgz#cd4ba1ca9b0521e463bd4b32349ba1842277563b" + integrity sha512-yENoDEoWlEFI7l5z7UYyJb/y5Q8RqbPd4neAVhKr6l+vVaQOPKf8V/IseSMJI9+urDUIxgssA7RGNyCRhGjZvw== dependencies: - "@jest/console" "^27.1.1" + "@jest/console" "^27.2.2" "@jest/types" "^27.1.1" "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" -"@jest/test-sequencer@^27.1.1": - version "27.1.1" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-27.1.1.tgz#cea3722ec6f6330000240fd999ad3123adaf5992" - integrity sha512-l8zD3EdeixvwmLNlJoMX3hhj8iIze95okj4sqmBzOq/zW8gZLElUveH4bpKEMuR+Nweazjlwc7L6g4C26M/y6Q== +"@jest/test-sequencer@^27.2.2": + version "27.2.2" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-27.2.2.tgz#9a6d735317f525741a5913ee3cdefeffc9b0aba6" + integrity sha512-YnJqwNQP2Zeu0S4TMqkxg6NN7Y1EFq715n/nThNKrvIS9wmRZjDt2XYqsHbuvhAFjshi0iKDQ813NewFITBH+Q== dependencies: - "@jest/test-result" "^27.1.1" + "@jest/test-result" "^27.2.2" graceful-fs "^4.2.4" - jest-haste-map "^27.1.1" - jest-runtime "^27.1.1" + jest-haste-map "^27.2.2" + jest-runtime "^27.2.2" -"@jest/transform@^27.1.1": - version "27.1.1" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-27.1.1.tgz#51a22f5a48d55d796c02757117c02fcfe4da13d7" - integrity sha512-qM19Eu75U6Jc5zosXXVnq900Nl9JDpoGaZ4Mg6wZs7oqbu3heYSMOZS19DlwjlhWdfNRjF4UeAgkrCJCK3fEXg== +"@jest/transform@^27.2.2": + version "27.2.2" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-27.2.2.tgz#89b16b4de84354fb48d15712b3ea34cadc1cb600" + integrity sha512-l4Z/7PpajrOjCiXLWLfMY7fgljY0H8EwW7qdzPXXuv2aQF8kY2+Uyj3O+9Popnaw1V7JCw32L8EeI/thqFDkPA== dependencies: "@babel/core" "^7.1.0" "@jest/types" "^27.1.1" @@ -685,9 +1302,9 @@ convert-source-map "^1.4.0" fast-json-stable-stringify "^2.0.0" graceful-fs "^4.2.4" - jest-haste-map "^27.1.1" + jest-haste-map "^27.2.2" jest-regex-util "^27.0.6" - jest-util "^27.1.1" + jest-util "^27.2.0" micromatch "^4.0.4" pirates "^4.0.1" slash "^3.0.0" @@ -861,6 +1478,11 @@ dependencies: "@babel/types" "^7.3.0" +"@types/caseless@*": + version "0.12.5" + resolved "https://registry.yarnpkg.com/@types/caseless/-/caseless-0.12.5.tgz#db9468cb1b1b5a925b8f34822f1669df0c5472f5" + integrity sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg== + "@types/graceful-fs@^4.1.2": version "4.1.4" resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.4.tgz#4ff9f641a7c6d1a3508ff88bc3141b152772e753" @@ -887,10 +1509,10 @@ dependencies: "@types/istanbul-lib-report" "*" -"@types/jest@^27.0.1": - version "27.0.1" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-27.0.1.tgz#fafcc997da0135865311bb1215ba16dba6bdf4ca" - integrity sha512-HTLpVXHrY69556ozYkcq47TtQJXpcWAWfkoqz+ZGz2JnmZhzlRjprCIyFnetSy8gpDWwTTGBcRVv1J1I1vBrHw== +"@types/jest@^27.0.2": + version "27.0.2" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-27.0.2.tgz#ac383c4d4aaddd29bbf2b916d8d105c304a5fcd7" + integrity sha512-4dRxkS/AFX0c5XW6IPMNOydLn2tEhNhJV7DnYK+0bjoJZ+QTmfucBlihX7aoEsh/ocYtkLC73UbnBXBXIxsULA== dependencies: jest-diff "^27.0.0" pretty-format "^27.0.0" @@ -900,6 +1522,14 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.6.tgz#f4c7ec43e81b319a9815115031709f26987891f0" integrity sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw== +"@types/node-telegram-bot-api@^0.64.6": + version "0.64.7" + resolved "https://registry.yarnpkg.com/@types/node-telegram-bot-api/-/node-telegram-bot-api-0.64.7.tgz#d24158dcb0754eff0b2d49141ab630fbff18a230" + integrity sha512-nuvFFXnvU2sItucyEJ03I+m34z5st386isfEuF6BJTL7p3RjG7naMhvvXjY7oeKahTm1Jf0Gu4PrDa6jDt78/Q== + dependencies: + "@types/node" "*" + "@types/request" "*" + "@types/node@*": version "14.14.20" resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.20.tgz#f7974863edd21d1f8a494a73e8e2b3658615c340" @@ -920,11 +1550,26 @@ resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.3.2.tgz#fc8c2825e4ed2142473b4a81064e6e081463d1b3" integrity sha512-eI5Yrz3Qv4KPUa/nSIAi0h+qX0XyewOliug5F2QAtuRg6Kjg6jfmxe1GIwoIRhZspD1A0RP8ANrPwvEXXtRFog== +"@types/request@*": + version "2.48.12" + resolved "https://registry.yarnpkg.com/@types/request/-/request-2.48.12.tgz#0f590f615a10f87da18e9790ac94c29ec4c5ef30" + integrity sha512-G3sY+NpsA9jnwm0ixhAFQSJ3Q9JkpLZpJbI3GMv0mIAT0y3mRabYeINzal5WOChIiaTEGQYlHOKgkaM9EisWHw== + dependencies: + "@types/caseless" "*" + "@types/node" "*" + "@types/tough-cookie" "*" + form-data "^2.5.0" + "@types/stack-utils@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.0.tgz#7036640b4e21cc2f259ae826ce843d277dad8cff" integrity sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw== +"@types/tough-cookie@*": + version "4.0.5" + resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.5.tgz#cb6e2a691b70cb177c6e3ae9c1d2e8b2ea8cd304" + integrity sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA== + "@types/yargs-parser@*": version "20.2.0" resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.0.tgz#dd3e6699ba3237f0348cd085e4698780204842f9" @@ -1105,6 +1750,11 @@ ansi-regex@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" @@ -1202,16 +1852,16 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== -babel-jest@^27.1.1: - version "27.1.1" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-27.1.1.tgz#9359c45995d0940b84d2176ab83423f9eed07617" - integrity sha512-JA+dzJl4n2RBvWQEnph6HJaTHrsIPiXGQYatt/D8nR4UpX9UG4GaDzykVVPQBbrdTebZREkRb6SOxyIXJRab6Q== +babel-jest@^27.2.2: + version "27.2.2" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-27.2.2.tgz#d7e96f3f6f56be692de948092697e1bfea7f1184" + integrity sha512-XNFNNfGKnZXzhej7TleVP4s9ktH5JjRW8Rmcbb223JJwKB/gmTyeWN0JfiPtSgnjIjDXtKNoixiy0QUHtv3vFA== dependencies: - "@jest/transform" "^27.1.1" + "@jest/transform" "^27.2.2" "@jest/types" "^27.1.1" "@types/babel__core" "^7.1.14" babel-plugin-istanbul "^6.0.0" - babel-preset-jest "^27.0.6" + babel-preset-jest "^27.2.0" chalk "^4.0.0" graceful-fs "^4.2.4" slash "^3.0.0" @@ -1227,10 +1877,10 @@ babel-plugin-istanbul@^6.0.0: istanbul-lib-instrument "^4.0.0" test-exclude "^6.0.0" -babel-plugin-jest-hoist@^27.0.6: - version "27.0.6" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.0.6.tgz#f7c6b3d764af21cb4a2a1ab6870117dbde15b456" - integrity sha512-CewFeM9Vv2gM7Yr9n5eyyLVPRSiBnk6lKZRjgwYnGKSl9M14TMn2vkN02wTF04OGuSDLEzlWiMzvjXuW9mB6Gw== +babel-plugin-jest-hoist@^27.2.0: + version "27.2.0" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.2.0.tgz#79f37d43f7e5c4fdc4b2ca3e10cc6cf545626277" + integrity sha512-TOux9khNKdi64mW+0OIhcmbAn75tTlzKhxmiNXevQaPbrBYK7YKjP1jl6NHTJ6XR5UgUrJbCnWlKVnJn29dfjw== dependencies: "@babel/template" "^7.3.3" "@babel/types" "^7.3.3" @@ -1255,12 +1905,12 @@ babel-preset-current-node-syntax@^1.0.0: "@babel/plugin-syntax-optional-chaining" "^7.8.3" "@babel/plugin-syntax-top-level-await" "^7.8.3" -babel-preset-jest@^27.0.6: - version "27.0.6" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-27.0.6.tgz#909ef08e9f24a4679768be2f60a3df0856843f9d" - integrity sha512-WObA0/Biw2LrVVwZkF/2GqbOdzhKD6Fkdwhoy9ASIrOWr/zodcSpQh72JOkEn6NWyjmnPDjNSqaGN4KnpKzhXw== +babel-preset-jest@^27.2.0: + version "27.2.0" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-27.2.0.tgz#556bbbf340608fed5670ab0ea0c8ef2449fba885" + integrity sha512-z7MgQ3peBwN5L5aCqBKnF6iqdlvZvFUQynEhu0J+X9nHLU72jO3iY331lcYrg+AssJ8q7xsv5/3AICzVmJ/wvg== dependencies: - babel-plugin-jest-hoist "^27.0.6" + babel-plugin-jest-hoist "^27.2.0" babel-preset-current-node-syntax "^1.0.0" balanced-match@^1.0.0: @@ -1275,24 +1925,32 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" +bin-version-check@~5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/bin-version-check/-/bin-version-check-5.1.0.tgz#788e80e036a87313f8be7908bc20e5abe43f0837" + integrity sha512-bYsvMqJ8yNGILLz1KP9zKLzQ6YpljV3ln1gqhuLkUtyfGi3qXKGuK2p+U4NAvjVFzDFiBBtOpCOSFNuYYEGZ5g== + dependencies: + bin-version "^6.0.0" + semver "^7.5.3" + semver-truncate "^3.0.0" + +bin-version@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/bin-version/-/bin-version-6.0.0.tgz#08ecbe5fc87898b441425e145f9e105064d00315" + integrity sha512-nk5wEsP4RiKjG+vF+uG8lFsEn4d7Y6FVDamzzftSunXOoOcOOkzcWdKVlGgFFwlUQCj63SgnUkLLGF8v7lufhw== + dependencies: + execa "^5.0.0" + find-versions "^5.0.0" + binary-extensions@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== -boxen@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/boxen/-/boxen-4.2.0.tgz#e411b62357d6d6d36587c8ac3d5d974daa070e64" - integrity sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ== - dependencies: - ansi-align "^3.0.0" - camelcase "^5.3.1" - chalk "^3.0.0" - cli-boxes "^2.2.0" - string-width "^4.1.0" - term-size "^2.1.0" - type-fest "^0.8.1" - widest-line "^3.1.0" +bowser@^2.11.0: + version "2.11.0" + resolved "https://registry.yarnpkg.com/bowser/-/bowser-2.11.0.tgz#5ca3c35757a7aa5771500c70a73a9f91ef420a8f" + integrity sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA== boxen@^5.0.0: version "5.0.1" @@ -1446,14 +2104,6 @@ chalk@^2.0.0, chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" - integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - chalk@^4.0.0, chalk@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" @@ -1520,7 +2170,7 @@ clean-stack@^2.0.0: resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== -cli-boxes@^2.2.0, cli-boxes@^2.2.1: +cli-boxes@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== @@ -1736,6 +2386,11 @@ cssstyle@^2.3.0: dependencies: cssom "~0.3.6" +dargs@~7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/dargs/-/dargs-7.0.0.tgz#04015c41de0bcb69ec84050f3d9be0caf8d6d5cc" + integrity sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg== + dashdash@^1.12.0: version "1.14.1" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" @@ -1792,6 +2447,13 @@ decompress-response@^3.3.0: dependencies: mimic-response "^1.0.0" +decompress-response@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" + integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== + dependencies: + mimic-response "^3.1.0" + dedent@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" @@ -1924,6 +2586,11 @@ enquirer@^2.3.5, enquirer@^2.3.6: dependencies: ansi-colors "^4.1.1" +entities@2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" + integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== + env-cmd@^10.1.0: version "10.1.0" resolved "https://registry.yarnpkg.com/env-cmd/-/env-cmd-10.1.0.tgz#c7f5d3b550c9519f137fdac4dd8fb6866a8c8c4b" @@ -1986,10 +2653,10 @@ escodegen@^2.0.0: optionalDependencies: source-map "~0.6.1" -eslint-plugin-jest@^24.4.0: - version "24.4.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-24.4.0.tgz#fa4b614dbd46a98b652d830377971f097bda9262" - integrity sha512-8qnt/hgtZ94E9dA6viqfViKBfkJwFHXgJmTWlMGDgunw1XJEGqm3eiPjDsTanM3/u/3Az82nyQM9GX7PM/QGmg== +eslint-plugin-jest@^24.4.2: + version "24.4.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-24.4.2.tgz#9e8cf05ee6a0e3025e6149df2f36950abfa8d5bf" + integrity sha512-jNMnqwX75z0RXRMXkxwb/+9ylKJYJLJ8nT8nBT0XFM5qx4IQGxP4edMawa0qGkSbHae0BDPBmi8I2QF0/F04XQ== dependencies: "@typescript-eslint/experimental-utils" "^4.0.1" @@ -2120,7 +2787,7 @@ execa@^1.0.0: signal-exit "^3.0.0" strip-eof "^1.0.0" -execa@^5.0.0: +execa@^5.0.0, execa@~5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== @@ -2140,16 +2807,16 @@ exit@^0.1.2: resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" integrity sha1-BjJjj42HfMghB9MKD/8aF8uhzQw= -expect@^27.1.1: - version "27.1.1" - resolved "https://registry.yarnpkg.com/expect/-/expect-27.1.1.tgz#020215da67d41cd6ad805fa00bd030985ca7c093" - integrity sha512-JQAzp0CJoFFHF1RnOtrMUNMdsfx/Tl0+FhRzVl8q0fa23N+JyWdPXwb3T5rkHCvyo9uttnK7lVdKCBl1b/9EDw== +expect@^27.2.2: + version "27.2.2" + resolved "https://registry.yarnpkg.com/expect/-/expect-27.2.2.tgz#65c414697415c0867ef588813e9c729ebab8a9a9" + integrity sha512-sjHBeEk47/eshN9oLbvPJZMgHQihOXXQzSMPCJ4MqKShbU9HOVFSNHEEU4dp4ujzxFSiNvPFzB2AMOFmkizhvA== dependencies: "@jest/types" "^27.1.1" ansi-styles "^5.0.0" jest-get-type "^27.0.6" - jest-matcher-utils "^27.1.1" - jest-message-util "^27.1.1" + jest-matcher-utils "^27.2.2" + jest-message-util "^27.2.2" jest-regex-util "^27.0.6" extend@~3.0.2: @@ -2199,6 +2866,11 @@ fast-memoize@^2.5.2: resolved "https://registry.yarnpkg.com/fast-memoize/-/fast-memoize-2.5.2.tgz#79e3bb6a4ec867ea40ba0e7146816f6cdce9b57e" integrity sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw== +fast-xml-parser@3.19.0: + version "3.19.0" + resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-3.19.0.tgz#cb637ec3f3999f51406dd8ff0e6fc4d83e520d01" + integrity sha512-4pXwmBplsCPv8FOY1WRakF970TjNGnGnfbOnLqjlYvMiF1SR3yOHyxMR/YCXpPTOspNF5gwudqktIP4VsWkvBg== + fastq@^1.6.0: version "1.10.0" resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.10.0.tgz#74dbefccade964932cdf500473ef302719c652bb" @@ -2269,6 +2941,13 @@ find-up@^4.0.0, find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" +find-versions@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/find-versions/-/find-versions-5.1.0.tgz#973f6739ce20f5e439a27eba8542a4b236c8e685" + integrity sha512-+iwzCJ7C5v5KgcBuueqVoNiHVoQpwiUK5XFLjf0affFTep+Wcw93tPvmb8tqujDNmzhBDPddnWV/qgWSXgq+Hg== + dependencies: + semver-regex "^4.0.5" + flat-cache@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" @@ -2287,6 +2966,15 @@ forever-agent@~0.6.1: resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= +form-data@^2.5.0: + version "2.5.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4" + integrity sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + form-data@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" @@ -2454,13 +3142,6 @@ glob@^7.1.6: once "^1.3.0" path-is-absolute "^1.0.0" -global-dirs@^2.0.1: - version "2.1.0" - resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-2.1.0.tgz#e9046a49c806ff04d6c1825e196c8f0091e8df4d" - integrity sha512-MG6kdOUh/xBnyo9cJFeIKkLEc1AyFq42QTU4XiX51i2NEdxLxLWXIjEjmqKeSuKR7pAZjTqUVoT2b2huxVLgYQ== - dependencies: - ini "1.3.7" - global-dirs@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-3.0.0.tgz#70a76fe84ea315ab37b1f5576cbde7d48ef72686" @@ -2571,6 +3252,11 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" +he@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + hosted-git-info@^2.1.4: version "2.8.8" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" @@ -2748,11 +3434,6 @@ inherits@2, inherits@~2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -ini@1.3.7: - version "1.3.7" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.7.tgz#a09363e1911972ea16d7a8851005d84cf09a9a84" - integrity sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ== - ini@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5" @@ -2847,14 +3528,6 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" -is-installed-globally@^0.3.1: - version "0.3.2" - resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.3.2.tgz#fd3efa79ee670d1187233182d5b0a1dd00313141" - integrity sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g== - dependencies: - global-dirs "^2.0.1" - is-path-inside "^3.0.1" - is-installed-globally@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.4.0.tgz#9a0fd407949c30f86eb6959ef1b7994ed0b7b520" @@ -2868,11 +3541,6 @@ is-lambda@^1.0.1: resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5" integrity sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU= -is-npm@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-4.0.0.tgz#c90dd8380696df87a7a6d823c20d0b12bbe3c84d" - integrity sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig== - is-npm@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-5.0.0.tgz#43e8d65cc56e1b67f8d47262cf667099193f45a8" @@ -2893,7 +3561,7 @@ is-obj@^2.0.0: resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== -is-path-inside@^3.0.1, is-path-inside@^3.0.2: +is-path-inside@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== @@ -2928,6 +3596,11 @@ is-unicode-supported@^0.1.0: resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== +is-unix@~2.0.1: + version "2.0.9" + resolved "https://registry.yarnpkg.com/is-unix/-/is-unix-2.0.9.tgz#fdf1bf490cdc416384ed0d382969ebe174ece407" + integrity sha512-PetX6i7selThHQPxo6RgfA8nAwqE3W/VcPZU1ducfhNTJXAhn+MLse+eNQDeYl2ZNL9WOzq8vqOD2E6f5uhpcw== + is-yarn-global@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/is-yarn-global/-/is-yarn-global-0.3.0.tgz#d502d3382590ea3004893746754c89139973e232" @@ -2998,77 +3671,77 @@ jest-changed-files@^27.1.1: execa "^5.0.0" throat "^6.0.1" -jest-circus@^27.1.1: - version "27.1.1" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-27.1.1.tgz#08dd3ec5cbaadce68ce6388ebccbe051d1b34bc6" - integrity sha512-Xed1ApiMFu/yzqGMBToHr8sp2gkX/ARZf4nXoGrHJrXrTUdVIWiVYheayfcOaPdQvQEE/uyBLgW7I7YBLIrAXQ== +jest-circus@^27.2.2: + version "27.2.2" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-27.2.2.tgz#a3647082f3eba1226f7664a36a2b7ebf45ceaf7b" + integrity sha512-8txlqs0EDrvPasCgwfLMkG0l3F4FxqQa6lxOsvYfOl04eSJjRw3F4gk9shakuC00nMD+VT+SMtFYXxe64f0VZw== dependencies: - "@jest/environment" "^27.1.1" - "@jest/test-result" "^27.1.1" + "@jest/environment" "^27.2.2" + "@jest/test-result" "^27.2.2" "@jest/types" "^27.1.1" "@types/node" "*" chalk "^4.0.0" co "^4.6.0" dedent "^0.7.0" - expect "^27.1.1" + expect "^27.2.2" is-generator-fn "^2.0.0" - jest-each "^27.1.1" - jest-matcher-utils "^27.1.1" - jest-message-util "^27.1.1" - jest-runtime "^27.1.1" - jest-snapshot "^27.1.1" - jest-util "^27.1.1" - pretty-format "^27.1.1" + jest-each "^27.2.2" + jest-matcher-utils "^27.2.2" + jest-message-util "^27.2.2" + jest-runtime "^27.2.2" + jest-snapshot "^27.2.2" + jest-util "^27.2.0" + pretty-format "^27.2.2" slash "^3.0.0" stack-utils "^2.0.3" throat "^6.0.1" -jest-cli@^27.1.1: - version "27.1.1" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-27.1.1.tgz#6491a0278231ffee61083ad468809328e96a8eb2" - integrity sha512-LCjfEYp9D3bcOeVUUpEol9Y1ijZYMWVqflSmtw/wX+6Fb7zP4IlO14/6s9v1pxsoM4Pn46+M2zABgKuQjyDpTw== +jest-cli@^27.2.2: + version "27.2.2" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-27.2.2.tgz#0973a717c109f23de642b63486f3cb71c5a971be" + integrity sha512-jbEythw22LR/IHYgNrjWdO74wO9wyujCxTMjbky0GLav4rC4y6qDQr4TqQ2JPP51eDYJ2awVn83advEVSs5Brg== dependencies: - "@jest/core" "^27.1.1" - "@jest/test-result" "^27.1.1" + "@jest/core" "^27.2.2" + "@jest/test-result" "^27.2.2" "@jest/types" "^27.1.1" chalk "^4.0.0" exit "^0.1.2" graceful-fs "^4.2.4" import-local "^3.0.2" - jest-config "^27.1.1" - jest-util "^27.1.1" - jest-validate "^27.1.1" + jest-config "^27.2.2" + jest-util "^27.2.0" + jest-validate "^27.2.2" prompts "^2.0.1" yargs "^16.0.3" -jest-config@^27.1.1: - version "27.1.1" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-27.1.1.tgz#cde823ad27f7ec0b9440035eabc75d4ac1ea024c" - integrity sha512-2iSd5zoJV4MsWPcLCGwUVUY/j6pZXm4Qd3rnbCtrd9EHNTg458iHw8PZztPQXfxKBKJxLfBk7tbZqYF8MGtxJA== +jest-config@^27.2.2: + version "27.2.2" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-27.2.2.tgz#970d8466c60ac106ac9d7d0b8dcf3ff150fa713a" + integrity sha512-2nhms3lp52ZpU0636bB6zIFHjDVtYxzFQIOHZjBFUeXcb6b41sEkWojbHaJ4FEIO44UbccTLa7tvNpiFCgPE7w== dependencies: "@babel/core" "^7.1.0" - "@jest/test-sequencer" "^27.1.1" + "@jest/test-sequencer" "^27.2.2" "@jest/types" "^27.1.1" - babel-jest "^27.1.1" + babel-jest "^27.2.2" chalk "^4.0.0" deepmerge "^4.2.2" glob "^7.1.1" graceful-fs "^4.2.4" is-ci "^3.0.0" - jest-circus "^27.1.1" - jest-environment-jsdom "^27.1.1" - jest-environment-node "^27.1.1" + jest-circus "^27.2.2" + jest-environment-jsdom "^27.2.2" + jest-environment-node "^27.2.2" jest-get-type "^27.0.6" - jest-jasmine2 "^27.1.1" + jest-jasmine2 "^27.2.2" jest-regex-util "^27.0.6" - jest-resolve "^27.1.1" - jest-runner "^27.1.1" - jest-util "^27.1.1" - jest-validate "^27.1.1" + jest-resolve "^27.2.2" + jest-runner "^27.2.2" + jest-util "^27.2.0" + jest-validate "^27.2.2" micromatch "^4.0.4" - pretty-format "^27.1.1" + pretty-format "^27.2.2" -jest-diff@^27.0.0, jest-diff@^27.1.1: +jest-diff@^27.0.0: version "27.1.1" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.1.1.tgz#1d1629ca2e3933b10cb27dc260e28e3dba182684" integrity sha512-m/6n5158rqEriTazqHtBpOa2B/gGgXJijX6nsEgZfbJ/3pxQcdpVXBe+FP39b1dxWHyLVVmuVXddmAwtqFO4Lg== @@ -3078,6 +3751,16 @@ jest-diff@^27.0.0, jest-diff@^27.1.1: jest-get-type "^27.0.6" pretty-format "^27.1.1" +jest-diff@^27.2.2: + version "27.2.2" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.2.2.tgz#3992fe5f55f209676c5d3fd956e3f3d4145f76b8" + integrity sha512-o3LaDbQDSaMJif4yztJAULI4xVatxbBasbKLbEw3K8CiRdDdbxMrLArS9EKDHQFYh6Tgfrm1PC2mIYR1xhu0hQ== + dependencies: + chalk "^4.0.0" + diff-sequences "^27.0.6" + jest-get-type "^27.0.6" + pretty-format "^27.2.2" + jest-docblock@^27.0.6: version "27.0.6" resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-27.0.6.tgz#cc78266acf7fe693ca462cbbda0ea4e639e4e5f3" @@ -3085,51 +3768,51 @@ jest-docblock@^27.0.6: dependencies: detect-newline "^3.0.0" -jest-each@^27.1.1: - version "27.1.1" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-27.1.1.tgz#caa1e7eed77144be346eb18712885b990389348a" - integrity sha512-r6hOsTLavUBb1xN0uDa89jdDeBmJ+K49fWpbyxeGRA2pLY46PlC4z551/cWNQzrj+IUa5/gSRsCIV/01HdNPug== +jest-each@^27.2.2: + version "27.2.2" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-27.2.2.tgz#62da8dd68b9fc61ab6e9e344692eeb1251f8c91d" + integrity sha512-ZCDhkvwHeXHsxoFxvW43fabL18iLiVDxaipG5XWG7dSd+XWXXpzMQvBWYT9Wvzhg5x4hvrLQ24LtiOKw3I09xA== dependencies: "@jest/types" "^27.1.1" chalk "^4.0.0" jest-get-type "^27.0.6" - jest-util "^27.1.1" - pretty-format "^27.1.1" + jest-util "^27.2.0" + pretty-format "^27.2.2" -jest-environment-jsdom@^27.1.1: - version "27.1.1" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-27.1.1.tgz#e53e98a16e6a764b8ee8db3b29b3a8c27db06f66" - integrity sha512-6vOnoZ6IaExuw7FvnuJhA1qFYv1DDSnN0sQowzolNwxQp7bG1YhLxj2YU1sVXAYA3IR3MbH2mbnJUsLUWfyfzw== +jest-environment-jsdom@^27.2.2: + version "27.2.2" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-27.2.2.tgz#fb3075b4be6289961dcc4942e98f1862b3a8c4cb" + integrity sha512-mzCLEdnpGWDJmNB6WIPLlZM+hpXdeiya9TryiByqcUdpliNV1O+LGC2SewzjmB4IblabGfvr3KcPN0Nme2wnDw== dependencies: - "@jest/environment" "^27.1.1" - "@jest/fake-timers" "^27.1.1" + "@jest/environment" "^27.2.2" + "@jest/fake-timers" "^27.2.2" "@jest/types" "^27.1.1" "@types/node" "*" jest-mock "^27.1.1" - jest-util "^27.1.1" + jest-util "^27.2.0" jsdom "^16.6.0" -jest-environment-node@^27.1.1: - version "27.1.1" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-27.1.1.tgz#97425d4762b2aeab15892ffba08c6cbed7653e75" - integrity sha512-OEGeZh0PwzngNIYWYgWrvTcLygopV8OJbC9HNb0j70VBKgEIsdZkYhwcFnaURX83OHACMqf1pa9Tv5Pw5jemrg== +jest-environment-node@^27.2.2: + version "27.2.2" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-27.2.2.tgz#60ba7d7fee956f68964d47a785d0195ce125aaa3" + integrity sha512-XgUscWs6H6UNqC96/QJjmUGZzzpql/JyprLSXVu7wkgM8tjbJdEkSqwrVAvJPm1yu526ImrmsIoh2BTHxkwL/g== dependencies: - "@jest/environment" "^27.1.1" - "@jest/fake-timers" "^27.1.1" + "@jest/environment" "^27.2.2" + "@jest/fake-timers" "^27.2.2" "@jest/types" "^27.1.1" "@types/node" "*" jest-mock "^27.1.1" - jest-util "^27.1.1" + jest-util "^27.2.0" jest-get-type@^27.0.6: version "27.0.6" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.0.6.tgz#0eb5c7f755854279ce9b68a9f1a4122f69047cfe" integrity sha512-XTkK5exIeUbbveehcSR8w0bhH+c0yloW/Wpl+9vZrjzztCPWrxhHwkIFpZzCt71oRBsgxmuUfxEqOYoZI2macg== -jest-haste-map@^27.1.1: - version "27.1.1" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-27.1.1.tgz#f7c646b0e417ec29b80b96cf785b57b581384adf" - integrity sha512-NGLYVAdh5C8Ezg5QBFzrNeYsfxptDBPlhvZNaicLiZX77F/rS27a9M6u9ripWAaaD54xnWdZNZpEkdjD5Eo5aQ== +jest-haste-map@^27.2.2: + version "27.2.2" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-27.2.2.tgz#81ccb57b1e1cd513aaaadf5016aad5dab0ede552" + integrity sha512-kaKiq+GbAvk6/sq++Ymor4Vzk6+lr0vbKs2HDVPdkKsHX2lIJRyvhypZG/QsNfQnROKWIZSpUpGuv2HySSosvA== dependencies: "@jest/types" "^27.1.1" "@types/graceful-fs" "^4.1.2" @@ -3139,59 +3822,59 @@ jest-haste-map@^27.1.1: graceful-fs "^4.2.4" jest-regex-util "^27.0.6" jest-serializer "^27.0.6" - jest-util "^27.1.1" - jest-worker "^27.1.1" + jest-util "^27.2.0" + jest-worker "^27.2.2" micromatch "^4.0.4" walker "^1.0.7" optionalDependencies: fsevents "^2.3.2" -jest-jasmine2@^27.1.1: - version "27.1.1" - resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-27.1.1.tgz#efb9e7b70ce834c35c91e1a2f01bb41b462fad43" - integrity sha512-0LAzUmcmvQwjIdJt0cXUVX4G5qjVXE8ELt6nbMNDzv2yAs2hYCCUtQq+Eje70GwAysWCGcS64QeYj5VPHYVxPg== +jest-jasmine2@^27.2.2: + version "27.2.2" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-27.2.2.tgz#bf87c8820a192c86b65a7c4c1a54caae52124f04" + integrity sha512-SczhZNfmZID9HbJ1GHhO4EzeL/PMRGeAUw23ddPUdd6kFijEZpT2yOxyNCBUKAsVQ/14OB60kjgnbuFOboZUNg== dependencies: "@babel/traverse" "^7.1.0" - "@jest/environment" "^27.1.1" + "@jest/environment" "^27.2.2" "@jest/source-map" "^27.0.6" - "@jest/test-result" "^27.1.1" + "@jest/test-result" "^27.2.2" "@jest/types" "^27.1.1" "@types/node" "*" chalk "^4.0.0" co "^4.6.0" - expect "^27.1.1" + expect "^27.2.2" is-generator-fn "^2.0.0" - jest-each "^27.1.1" - jest-matcher-utils "^27.1.1" - jest-message-util "^27.1.1" - jest-runtime "^27.1.1" - jest-snapshot "^27.1.1" - jest-util "^27.1.1" - pretty-format "^27.1.1" + jest-each "^27.2.2" + jest-matcher-utils "^27.2.2" + jest-message-util "^27.2.2" + jest-runtime "^27.2.2" + jest-snapshot "^27.2.2" + jest-util "^27.2.0" + pretty-format "^27.2.2" throat "^6.0.1" -jest-leak-detector@^27.1.1: - version "27.1.1" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-27.1.1.tgz#8e05ec4b339814fc4202f07d875da65189e3d7d4" - integrity sha512-gwSgzmqShoeEsEVpgObymQPrM9P6557jt1EsFW5aCeJ46Cme0EdjYU7xr6llQZ5GpWDl56eOstUaPXiZOfiTKw== +jest-leak-detector@^27.2.2: + version "27.2.2" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-27.2.2.tgz#5af54273efcf5114ad406f4448860c2396319e12" + integrity sha512-fQIYKkhXUs/4EpE4wO1AVsv7aNH3o0km1BGq3vxvSfYdwG9GLMf+b0z/ghLmBYNxb+tVpm/zv2caoKm3GfTazg== dependencies: jest-get-type "^27.0.6" - pretty-format "^27.1.1" + pretty-format "^27.2.2" -jest-matcher-utils@^27.1.1: - version "27.1.1" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.1.1.tgz#1f444d7491ccf9edca746336b056178789a59651" - integrity sha512-Q1a10w9Y4sh0wegkdP6reQOa/Dtz7nAvDqBgrat1ItZAUvk4jzXAqyhXPu/ZuEtDaXaNKpdRPRQA8bvkOh2Eaw== +jest-matcher-utils@^27.2.2: + version "27.2.2" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.2.2.tgz#a6c0a10dce6bfe8250bbed3e2f1b206568d73bde" + integrity sha512-xN3wT4p2i9DGB6zmL3XxYp5lJmq9Q6ff8XKlMtVVBS2SAshmgsPBALJFQ8dWRd2G/xf5q/N0SD0Mipt8QBA26A== dependencies: chalk "^4.0.0" - jest-diff "^27.1.1" + jest-diff "^27.2.2" jest-get-type "^27.0.6" - pretty-format "^27.1.1" + pretty-format "^27.2.2" -jest-message-util@^27.1.1: - version "27.1.1" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.1.1.tgz#980110fb72fcfa711cd9a95e8f10d335207585c6" - integrity sha512-b697BOJV93+AVGvzLRtVZ0cTVRbd59OaWnbB2D75GRaIMc4I+Z9W0wHxbfjW01JWO+TqqW4yevT0aN7Fd0XWng== +jest-message-util@^27.2.2: + version "27.2.2" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.2.2.tgz#cdbb1b82dfe5f601ae35f5c6a28bf7823e6bcf99" + integrity sha512-/iS5/m2FSF7Nn6APFoxFymJpyhB/gPf0CJa7uFSkbYaWvrADUfQ9NTsuyjpszKErOS2/huFs44ysWhlQTKvL8Q== dependencies: "@babel/code-frame" "^7.12.13" "@jest/types" "^27.1.1" @@ -3199,7 +3882,7 @@ jest-message-util@^27.1.1: chalk "^4.0.0" graceful-fs "^4.2.4" micromatch "^4.0.4" - pretty-format "^27.1.1" + pretty-format "^27.2.2" slash "^3.0.0" stack-utils "^2.0.3" @@ -3221,40 +3904,40 @@ jest-regex-util@^27.0.6: resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.0.6.tgz#02e112082935ae949ce5d13b2675db3d8c87d9c5" integrity sha512-SUhPzBsGa1IKm8hx2F4NfTGGp+r7BXJ4CulsZ1k2kI+mGLG+lxGrs76veN2LF/aUdGosJBzKgXmNCw+BzFqBDQ== -jest-resolve-dependencies@^27.1.1: - version "27.1.1" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-27.1.1.tgz#6f3e0916c1764dd1853c6111ed9d66c66c792e40" - integrity sha512-sYZR+uBjFDCo4VhYeazZf/T+ryYItvdLKu9vHatqkUqHGjDMrdEPOykiqC2iEpaCFTS+3iL/21CYiJuKdRbniw== +jest-resolve-dependencies@^27.2.2: + version "27.2.2" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-27.2.2.tgz#7467c67cb8b5630ec28e2f0c72a0b62e56668083" + integrity sha512-nvJS+DyY51HHdZnMIwXg7fimQ5ylFx4+quQXspQXde2rXYy+4v75UYoX/J65Ln8mKCNc6YF8HEhfGaRBOrxxHg== dependencies: "@jest/types" "^27.1.1" jest-regex-util "^27.0.6" - jest-snapshot "^27.1.1" + jest-snapshot "^27.2.2" -jest-resolve@^27.1.1: - version "27.1.1" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-27.1.1.tgz#3a86762f9affcad9697bc88140b0581b623add33" - integrity sha512-M41YFmWhvDVstwe7XuV21zynOiBLJB5Sk0GrIsYYgTkjfEWNLVXDjAyq1W7PHseaYNOxIc0nOGq/r5iwcZNC1A== +jest-resolve@^27.2.2: + version "27.2.2" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-27.2.2.tgz#1bad93dbc6c20edb874e6720e82e4e48900b120b" + integrity sha512-tfbHcBs/hJTb3fPQ/3hLWR+TsLNTzzK98TU+zIAsrL9nNzWfWROwopUOmiSUqmHMZW5t9au/433kSF2/Af+tTw== dependencies: "@jest/types" "^27.1.1" chalk "^4.0.0" escalade "^3.1.1" graceful-fs "^4.2.4" - jest-haste-map "^27.1.1" + jest-haste-map "^27.2.2" jest-pnp-resolver "^1.2.2" - jest-util "^27.1.1" - jest-validate "^27.1.1" + jest-util "^27.2.0" + jest-validate "^27.2.2" resolve "^1.20.0" slash "^3.0.0" -jest-runner@^27.1.1: - version "27.1.1" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-27.1.1.tgz#1991fdf13a8fe6e49cef47332db33300649357cd" - integrity sha512-lP3MBNQhg75/sQtVkC8dsAQZumvy3lHK/YIwYPfEyqGIX1qEcnYIRxP89q0ZgC5ngvi1vN2P5UFHszQxguWdng== +jest-runner@^27.2.2: + version "27.2.2" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-27.2.2.tgz#e719a8ce2a16575677105f692fdff7cd00602325" + integrity sha512-+bUFwBq+yTnvsOFuxetoQtkuOnqdAk2YuIGjlLmc7xLAXn/V1vjhXrLencgij0BUTTUvG9Aul3+5XDss4Wa8Eg== dependencies: - "@jest/console" "^27.1.1" - "@jest/environment" "^27.1.1" - "@jest/test-result" "^27.1.1" - "@jest/transform" "^27.1.1" + "@jest/console" "^27.2.2" + "@jest/environment" "^27.2.2" + "@jest/test-result" "^27.2.2" + "@jest/transform" "^27.2.2" "@jest/types" "^27.1.1" "@types/node" "*" chalk "^4.0.0" @@ -3262,30 +3945,30 @@ jest-runner@^27.1.1: exit "^0.1.2" graceful-fs "^4.2.4" jest-docblock "^27.0.6" - jest-environment-jsdom "^27.1.1" - jest-environment-node "^27.1.1" - jest-haste-map "^27.1.1" - jest-leak-detector "^27.1.1" - jest-message-util "^27.1.1" - jest-resolve "^27.1.1" - jest-runtime "^27.1.1" - jest-util "^27.1.1" - jest-worker "^27.1.1" + jest-environment-jsdom "^27.2.2" + jest-environment-node "^27.2.2" + jest-haste-map "^27.2.2" + jest-leak-detector "^27.2.2" + jest-message-util "^27.2.2" + jest-resolve "^27.2.2" + jest-runtime "^27.2.2" + jest-util "^27.2.0" + jest-worker "^27.2.2" source-map-support "^0.5.6" throat "^6.0.1" -jest-runtime@^27.1.1: - version "27.1.1" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-27.1.1.tgz#bd0a0958a11c2f7d94d2e5f6f71864ad1c65fe44" - integrity sha512-FEwy+tSzmsvuKaQpyYsUyk31KG5vMmA2r2BSTHgv0yNfcooQdm2Ke91LM9Ud8D3xz8CLDHJWAI24haMFTwrsPg== +jest-runtime@^27.2.2: + version "27.2.2" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-27.2.2.tgz#cc29d3adde948d531657a67d33c9f8d9bb58a6fc" + integrity sha512-PtTHCK5jT+KrIpKpjJsltu/dK5uGhBtTNLOk1Z+ZD2Jrxam2qQsOqDFYLszcK0jc2TLTNsrVpclqSftn7y3aXA== dependencies: - "@jest/console" "^27.1.1" - "@jest/environment" "^27.1.1" - "@jest/fake-timers" "^27.1.1" - "@jest/globals" "^27.1.1" + "@jest/console" "^27.2.2" + "@jest/environment" "^27.2.2" + "@jest/fake-timers" "^27.2.2" + "@jest/globals" "^27.2.2" "@jest/source-map" "^27.0.6" - "@jest/test-result" "^27.1.1" - "@jest/transform" "^27.1.1" + "@jest/test-result" "^27.2.2" + "@jest/transform" "^27.2.2" "@jest/types" "^27.1.1" "@types/yargs" "^16.0.0" chalk "^4.0.0" @@ -3295,14 +3978,14 @@ jest-runtime@^27.1.1: exit "^0.1.2" glob "^7.1.3" graceful-fs "^4.2.4" - jest-haste-map "^27.1.1" - jest-message-util "^27.1.1" + jest-haste-map "^27.2.2" + jest-message-util "^27.2.2" jest-mock "^27.1.1" jest-regex-util "^27.0.6" - jest-resolve "^27.1.1" - jest-snapshot "^27.1.1" - jest-util "^27.1.1" - jest-validate "^27.1.1" + jest-resolve "^27.2.2" + jest-snapshot "^27.2.2" + jest-util "^27.2.0" + jest-validate "^27.2.2" slash "^3.0.0" strip-bom "^4.0.0" yargs "^16.0.3" @@ -3315,10 +3998,10 @@ jest-serializer@^27.0.6: "@types/node" "*" graceful-fs "^4.2.4" -jest-snapshot@^27.1.1: - version "27.1.1" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-27.1.1.tgz#3b816e0ca4352fbbd1db48dc692e3d9641d2531b" - integrity sha512-Wi3QGiuRFo3lU+EbQmZnBOks0CJyAMPHvYoG7iJk00Do10jeOyuOEO0Jfoaoun8+8TDv+Nzl7Aswir/IK9+1jg== +jest-snapshot@^27.2.2: + version "27.2.2" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-27.2.2.tgz#898f0eedde658e723461d3cdcaedb404e716fa01" + integrity sha512-7ODSvULMiiOVuuLvLZpDlpqqTqX9hDfdmijho5auVu9qRYREolvrvgH4kSNOKfcqV3EZOTuLKNdqsz1PM20PQA== dependencies: "@babel/core" "^7.7.2" "@babel/generator" "^7.7.2" @@ -3326,29 +4009,29 @@ jest-snapshot@^27.1.1: "@babel/plugin-syntax-typescript" "^7.7.2" "@babel/traverse" "^7.7.2" "@babel/types" "^7.0.0" - "@jest/transform" "^27.1.1" + "@jest/transform" "^27.2.2" "@jest/types" "^27.1.1" "@types/babel__traverse" "^7.0.4" "@types/prettier" "^2.1.5" babel-preset-current-node-syntax "^1.0.0" chalk "^4.0.0" - expect "^27.1.1" + expect "^27.2.2" graceful-fs "^4.2.4" - jest-diff "^27.1.1" + jest-diff "^27.2.2" jest-get-type "^27.0.6" - jest-haste-map "^27.1.1" - jest-matcher-utils "^27.1.1" - jest-message-util "^27.1.1" - jest-resolve "^27.1.1" - jest-util "^27.1.1" + jest-haste-map "^27.2.2" + jest-matcher-utils "^27.2.2" + jest-message-util "^27.2.2" + jest-resolve "^27.2.2" + jest-util "^27.2.0" natural-compare "^1.4.0" - pretty-format "^27.1.1" + pretty-format "^27.2.2" semver "^7.3.2" -jest-util@^27.1.1: - version "27.1.1" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.1.1.tgz#2b06db1391d779ec2bd406ab3690ddc56ac728b9" - integrity sha512-zf9nEbrASWn2mC/L91nNb0K+GkhFvi4MP6XJG2HqnHzHvLYcs7ou/In68xYU1i1dSkJlrWcYfWXQE8nVR+nbOA== +jest-util@^27.2.0: + version "27.2.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.2.0.tgz#bfccb85cfafae752257319e825a5b8d4ada470dc" + integrity sha512-T5ZJCNeFpqcLBpx+Hl9r9KoxBCUqeWlJ1Htli+vryigZVJ1vuLB9j35grEBASp4R13KFkV7jM52bBGnArpJN6A== dependencies: "@jest/types" "^27.1.1" "@types/node" "*" @@ -3357,48 +4040,48 @@ jest-util@^27.1.1: is-ci "^3.0.0" picomatch "^2.2.3" -jest-validate@^27.1.1: - version "27.1.1" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-27.1.1.tgz#0783733af02c988d503995fc0a07bbdc58c7dd50" - integrity sha512-N5Er5FKav/8m2dJwn7BGnZwnoD1BSc8jx5T+diG2OvyeugvZDhPeAt5DrNaGkkaKCrSUvuE7A5E4uHyT7Vj0Mw== +jest-validate@^27.2.2: + version "27.2.2" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-27.2.2.tgz#e672118f1d9aa57b25b4c7998edc101dabd7020b" + integrity sha512-01mwTAs2kgDuX98Ua3Xhdhp5lXsLU4eyg6k56adTtrXnU/GbLd9uAsh5nc4MWVtUXMeNmHUyEiD4ibLqE8MuNw== dependencies: "@jest/types" "^27.1.1" camelcase "^6.2.0" chalk "^4.0.0" jest-get-type "^27.0.6" leven "^3.1.0" - pretty-format "^27.1.1" + pretty-format "^27.2.2" -jest-watcher@^27.1.1: - version "27.1.1" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-27.1.1.tgz#a8147e18703b5d753ada4b287451f2daf40f4118" - integrity sha512-XQzyHbxziDe+lZM6Dzs40fEt4q9akOGwitJnxQasJ9WG0bv3JGiRlsBgjw13znGapeMtFaEsyhL0Cl04IbaoWQ== +jest-watcher@^27.2.2: + version "27.2.2" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-27.2.2.tgz#8b00253d7e880c6637b402228a76f2fe5ea08132" + integrity sha512-7HJwZq06BCfM99RacCVzXO90B20/dNJvq+Ouiu/VrFdFRCpbnnqlQUEk4KAhBSllgDrTPgKu422SCF5KKBHDRA== dependencies: - "@jest/test-result" "^27.1.1" + "@jest/test-result" "^27.2.2" "@jest/types" "^27.1.1" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" - jest-util "^27.1.1" + jest-util "^27.2.0" string-length "^4.0.1" -jest-worker@^27.1.1: - version "27.1.1" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.1.1.tgz#eb5f05c4657fdcb702c36c48b20d785bd4599378" - integrity sha512-XJKCL7tu+362IUYTWvw8+3S75U7qMiYiRU6u5yqscB48bTvzwN6i8L/7wVTXiFLwkRsxARNM7TISnTvcgv9hxA== +jest-worker@^27.2.2: + version "27.2.2" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.2.2.tgz#636deeae8068abbf2b34b4eb9505f8d4e5bd625c" + integrity sha512-aG1xq9KgWB2CPC8YdMIlI8uZgga2LFNcGbHJxO8ctfXAydSaThR4EewKQGg3tBOC+kS3vhPGgymsBdi9VINjPw== dependencies: "@types/node" "*" merge-stream "^2.0.0" supports-color "^8.0.0" -jest@^27.1.1: - version "27.1.1" - resolved "https://registry.yarnpkg.com/jest/-/jest-27.1.1.tgz#49f0497fa0fb07dc78898318cc1b737b5fbf72d8" - integrity sha512-LFTEZOhoZNR/2DQM3OCaK5xC6c55c1OWhYh0njRsoHX0qd6x4nkcgenkSH0JKjsAGMTmmJAoL7/oqYHMfwhruA== +jest@^27.2.2: + version "27.2.2" + resolved "https://registry.yarnpkg.com/jest/-/jest-27.2.2.tgz#445a4c16aa4c4ae6e512d62fb6f8b2624cbd6c26" + integrity sha512-XAB/9akDTe3/V0wPNKWfP9Y/NT1QPiCqyRBYGbC66EA9EvgAzdaFEqhFGLaDJ5UP2yIyXUMtju9a9IMrlYbZTQ== dependencies: - "@jest/core" "^27.1.1" + "@jest/core" "^27.2.2" import-local "^3.0.2" - jest-cli "^27.1.1" + jest-cli "^27.2.2" jju@^1.1.0: version "1.4.0" @@ -3554,7 +4237,7 @@ kleur@^3.0.3: resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== -latest-version@^5.0.0, latest-version@^5.1.0: +latest-version@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-5.1.0.tgz#119dfe908fe38d15dfa43ecd13fa12ec8832face" integrity sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA== @@ -3805,6 +4488,11 @@ mimic-response@^1.0.0, mimic-response@^1.0.1: resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== +mimic-response@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" + integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== + minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" @@ -3884,6 +4572,13 @@ mkdirp@^1.0.3, mkdirp@^1.0.4: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== +mnemonist@0.38.3: + version "0.38.3" + resolved "https://registry.yarnpkg.com/mnemonist/-/mnemonist-0.38.3.tgz#35ec79c1c1f4357cfda2fe264659c2775ccd7d9d" + integrity sha512-2K9QYubXx/NAjv4VLq1d1Ly8pWNC5L3BrixtdkyTegXWJIqY+zLNDhhX/A+ZwWt70tB1S8H4BE8FLYEFyNoOBw== + dependencies: + obliterator "^1.6.1" + ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -3924,10 +4619,12 @@ nock@^13.1.3: lodash.set "^4.3.2" propagate "^2.0.0" -node-fetch@2, node-fetch@^2.6.2: - version "2.6.2" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.2.tgz#986996818b73785e47b1965cc34eb093a1d464d0" - integrity sha512-aLoxToI6RfZ+0NOjmWAgn9+LEd30YCkJKFSyWacNZdEKTit/ZMcKjGkTRo8uWEsnIb/hfKecNPEbln02PdWbcA== +node-fetch@^2.6.5: + version "2.6.5" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.5.tgz#42735537d7f080a7e5f78b6c549b7146be1742fd" + integrity sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ== + dependencies: + whatwg-url "^5.0.0" node-gyp@^7.1.0: version "7.1.2" @@ -3960,10 +4657,10 @@ node-releases@^1.1.75: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.75.tgz#6dd8c876b9897a1b8e5a02de26afa79bb54ebbfe" integrity sha512-Qe5OUajvqrqDSy6wrWFmMwfJ0jVgwiw4T3KqmbTcZ62qW0gQkheXYhcFM1+lOVcGUoRxcEcfyvFMAnDgaF1VWw== -nodemon@^2.0.12: - version "2.0.12" - resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-2.0.12.tgz#5dae4e162b617b91f1873b3bfea215dd71e144d5" - integrity sha512-egCTmNZdObdBxUBw6ZNwvZ/xzk24CKRs5K6d+5zbmrMr7rOpPmfPeF6OxM3DDpaRx331CQRFEktn+wrFFfBSOA== +nodemon@^2.0.13: + version "2.0.13" + resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-2.0.13.tgz#67d40d3a4d5bd840aa785c56587269cfcf5d24aa" + integrity sha512-UMXMpsZsv1UXUttCn6gv8eQPhn6DR4BW+txnL3IN5IHqrCwcrT/yWHfL35UsClGXknTH79r5xbu+6J1zNHuSyA== dependencies: chokidar "^3.2.2" debug "^3.2.6" @@ -3974,7 +4671,7 @@ nodemon@^2.0.12: supports-color "^5.5.0" touch "^3.1.0" undefsafe "^2.0.3" - update-notifier "^4.1.0" + update-notifier "^5.1.0" nopt@^5.0.0: version "5.0.0" @@ -4147,6 +4844,11 @@ object-assign@^4.1.0: resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= +obliterator@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/obliterator/-/obliterator-1.6.1.tgz#dea03e8ab821f6c4d96a299e17aef6a3af994ef3" + integrity sha512-9WXswnqINnnhOG/5SLimUlzuU1hFJUc8zkwyD59Sd+dPOMf05PmnYG/d6Q7HZ+KmgkZJa1PxRso6QdM3sTNHig== + once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -4408,10 +5110,10 @@ prepend-http@^2.0.0: resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= -prettier@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.4.0.tgz#85bdfe0f70c3e777cf13a4ffff39713ca6f64cba" - integrity sha512-DsEPLY1dE5HF3BxCRBmD4uYZ+5DCbvatnolqTqcxEgKVZnL2kUfyu7b8pPQ5+hTBkdhU9SLUmK0/pHb07RE4WQ== +prettier@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.4.1.tgz#671e11c89c14a4cfc876ce564106c4a6726c9f5c" + integrity sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA== pretty-format@^27.0.0, pretty-format@^27.1.1: version "27.1.1" @@ -4423,6 +5125,16 @@ pretty-format@^27.0.0, pretty-format@^27.1.1: ansi-styles "^5.0.0" react-is "^17.0.1" +pretty-format@^27.2.2: + version "27.2.2" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.2.2.tgz#c080f1ab7ac64302e4d438f208596fc649dbeeb3" + integrity sha512-+DdLh+rtaElc2SQOE/YPH8k2g3Rf2OXWEpy06p8Szs3hdVSYD87QOOlYRHWAeb/59XTmeVmRKvDD0svHqf6ycA== + dependencies: + "@jest/types" "^27.1.1" + ansi-regex "^5.0.1" + ansi-styles "^5.0.0" + react-is "^17.0.1" + process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" @@ -4490,7 +5202,7 @@ punycode@^2.1.0, punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== -pupa@^2.0.1, pupa@^2.1.1: +pupa@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.1.1.tgz#f5e8fd4afc2c5d97828faa523549ed8744a20d62" integrity sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A== @@ -4746,6 +5458,18 @@ semver-diff@^3.1.1: dependencies: semver "^6.3.0" +semver-regex@^4.0.5: + version "4.0.5" + resolved "https://registry.yarnpkg.com/semver-regex/-/semver-regex-4.0.5.tgz#fbfa36c7ba70461311f5debcb3928821eb4f9180" + integrity sha512-hunMQrEy1T6Jr2uEVjrAIqjwWcQTgOAcIM52C8MY1EZSD3DDNft04XzvYKPqjED65bNVVko0YI38nYeEHCX3yw== + +semver-truncate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/semver-truncate/-/semver-truncate-3.0.0.tgz#0e3b4825d4a4225d8ae6e7c72231182b42edba40" + integrity sha512-LJWA9kSvMolR51oDE6PN3kALBNaUdkxzAGcexw8gjMA8xr5zUqK0JiR3CgARSqanYF3Z1YHvsErb1KDgh+v7Rg== + dependencies: + semver "^7.3.5" + semver-utils@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/semver-utils/-/semver-utils-1.1.4.tgz#cf0405e669a57488913909fc1c3f29bf2a4871e2" @@ -4775,14 +5499,21 @@ semver@^7.2.1, semver@^7.3.2: dependencies: lru-cache "^6.0.0" -serverless-telegram@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/serverless-telegram/-/serverless-telegram-0.6.0.tgz#f6e607320b672d614f78799a5b2d25ea89f616d7" - integrity sha512-0Mnqas815C8WAUjAHBcoCoLok2QulNRqqe9cJ/I/o8+/vp3ao8WXVC4knGk+T646t/c35ZKGNKcg1NjuuMQOjA== +semver@^7.5.3: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== + dependencies: + lru-cache "^6.0.0" + +serverless-telegram@^0.8.3: + version "0.8.3" + resolved "https://registry.yarnpkg.com/serverless-telegram/-/serverless-telegram-0.8.3.tgz#9c842dcaf86f3fa6d7482d9d61c919aa855c5dac" + integrity sha512-6FszYkIEWJHcpSaLleiyWJlnzLSEYpW7VZR12butp7n9RHHxvqX1ILBpWjGH8Fu86I2rOL7S/e+ZX+ZTvh/qlg== dependencies: "@azure/functions" "^1.2.3" + "@types/node-telegram-bot-api" "^0.64.6" form-data "^4.0.0" - node-fetch "2" set-blocking@~2.0.0: version "2.0.0" @@ -4818,6 +5549,20 @@ signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== +simple-concat@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" + integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== + +simple-get@~4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-4.0.1.tgz#4a39db549287c979d352112fa03fd99fd6bc3543" + integrity sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA== + dependencies: + decompress-response "^6.0.0" + once "^1.3.1" + simple-concat "^1.0.0" + sisteransi@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" @@ -5147,11 +5892,6 @@ tar@^6.0.2, tar@^6.1.0: mkdirp "^1.0.3" yallist "^4.0.0" -term-size@^2.1.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/term-size/-/term-size-2.2.1.tgz#2a6a54840432c2fb6320fea0f415531e90189f54" - integrity sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg== - terminal-link@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" @@ -5244,6 +5984,11 @@ tr46@^2.1.0: dependencies: punycode "^2.1.1" +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= + trim-repeated@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/trim-repeated/-/trim-repeated-1.0.0.tgz#e3646a2ea4e891312bf7eace6cfb05380bc01c21" @@ -5251,11 +5996,16 @@ trim-repeated@^1.0.0: dependencies: escape-string-regexp "^1.0.2" -tslib@^1.8.1, tslib@^1.9.0: +tslib@^1.11.1, tslib@^1.8.1, tslib@^1.9.0: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== +tslib@^2.3.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" + integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== + tsutils@^3.17.1: version "3.19.1" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.19.1.tgz#d8566e0c51c82f32f9c25a4d367cd62409a547a9" @@ -5309,11 +6059,6 @@ type-fest@^0.6.0: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== -type-fest@^0.8.1: - version "0.8.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" - integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== - typedarray-to-buffer@^3.1.5: version "3.1.5" resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" @@ -5354,25 +6099,6 @@ universalify@^0.1.2: resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== -update-notifier@^4.1.0: - version "4.1.3" - resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-4.1.3.tgz#be86ee13e8ce48fb50043ff72057b5bd598e1ea3" - integrity sha512-Yld6Z0RyCYGB6ckIjffGOSOmHXj1gMeE7aROz4MG+XMkmixBX4jUngrGXNYz7wPKBmtoD4MnBa2Anu7RSKht/A== - dependencies: - boxen "^4.2.0" - chalk "^3.0.0" - configstore "^5.0.1" - has-yarn "^2.1.0" - import-lazy "^2.1.0" - is-ci "^2.0.0" - is-installed-globally "^0.3.1" - is-npm "^4.0.0" - is-yarn-global "^0.3.0" - latest-version "^5.0.0" - pupa "^2.0.1" - semver-diff "^3.1.1" - xdg-basedir "^4.0.0" - update-notifier@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-5.1.0.tgz#4ab0d7c7f36a231dd7316cf7729313f0214d9ad9" @@ -5417,6 +6143,11 @@ uuid@^3.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== +uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + v8-compile-cache@^2.0.3: version "2.2.0" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz#9471efa3ef9128d2f7c6a7ca39c4dd6b5055b132" @@ -5476,6 +6207,11 @@ walker@^1.0.7: dependencies: makeerror "1.0.x" +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= + webidl-conversions@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" @@ -5498,6 +6234,14 @@ whatwg-mimetype@^2.3.0: resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + whatwg-url@^8.0.0: version "8.4.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.4.0.tgz#50fb9615b05469591d2b2bd6dfaed2942ed72837" @@ -5639,3 +6383,14 @@ yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +youtube-dl-exec@^2.4.15: + version "2.4.15" + resolved "https://registry.yarnpkg.com/youtube-dl-exec/-/youtube-dl-exec-2.4.15.tgz#3c1a91db17aa3c2865cd313de49f85bfc562c692" + integrity sha512-zKipmeFQ9Wtn0u/j06C9up/s6/YfQxkXQKyW5aUTX8LAvNgXqdDfFy0YRcEU6TAXhTkuTyRXF9ScyWwZF1/riQ== + dependencies: + bin-version-check "~5.1.0" + dargs "~7.0.0" + execa "~5.1.1" + is-unix "~2.0.1" + simple-get "~4.0.1"