diff --git a/next-env.d.ts b/next-env.d.ts index 0ad0784..1fab158 100644 --- a/next-env.d.ts +++ b/next-env.d.ts @@ -1,3 +1,15 @@ /// /// /// + +declare global { + namespace NodeJS { + interface ProcessEnv { + AWS_REGION?: string; + AWS_ACCESS_KEY_ID?: string; + AWS_SECRET_ACCESS_KEY?: string; + AWS_S3_BUCKET_NAME?: string; + LAMBDA_PRESIGN_UPLOAD_ENDPOINT?: string; + } + } +} diff --git a/package.json b/package.json index d6d3a3f..fa11025 100644 --- a/package.json +++ b/package.json @@ -3,10 +3,11 @@ "version": "2.2.1", "private": true, "dependencies": { - "@dotkomonline/design-system": "^0.22.0", + "@dotkomonline/design-system": "^0.22.2", "@reduxjs/toolkit": "^1.4.0", "@sentry/browser": "^5.0.3", "@sentry/node": "^5.4.3", + "@types/aws-sdk": "^2.7.0", "@types/file-saver": "^2.0.0", "@types/get-stream": "^3.0.2", "@types/jsdom": "^16.2.4", @@ -19,6 +20,7 @@ "@types/react-dom": "16.9.8", "@types/react-redux": "^7.1.0", "@types/styled-components": "^5.1.4", + "aws-sdk": "^2.1560.0", "core-js": "^3.0.1", "file-saver": "^2.0.1", "get-stream": "^6.0.1", diff --git a/src/constants/backend.ts b/src/constants/backend.ts index 55c9e4c..d360591 100644 --- a/src/constants/backend.ts +++ b/src/constants/backend.ts @@ -1,3 +1,4 @@ export const LAMBDA_ENDPOINT = '/api/generate-receipt'; +export const LAMBDA_PRESIGN_UPLOAD_ENDPOINT = '/api/presign-upload-url'; export const OW4_ADDRESS = process.env.NEXT_PUBLIC_OW4_ADDRESS || 'https://online.ntnu.no'; diff --git a/src/form/state.ts b/src/form/state.ts index e7b8101..141b9be 100644 --- a/src/form/state.ts +++ b/src/form/state.ts @@ -2,6 +2,8 @@ import { ApiBodyError } from './../lambda/errors'; import { Group } from 'models/groups'; import { readDataUrlAsFile2 } from 'utils/readDataUrlAsFile'; import { readFileAsDataUrl } from 'utils/readFileAsDataUrl'; +import { uploadFile } from "../utils/uploadFile"; +import { downloadFileFromS3Bucket } from "../utils/downloadFileFromS3Bucket"; export type ReceiptType = 'card' | 'deposit'; export type SendMode = 'download' | 'email' | 'teapot'; @@ -56,7 +58,9 @@ export interface IDeserializedState { } export const deserializeReceipt = async (state: IState): Promise => { - const attachments = await Promise.all(state.attachments.map(async (file) => readFileAsDataUrl(file))); + const attachments = await Promise.all( + state.attachments.map(async (file) => uploadFile(file)) + ); const signature = await readFileAsDataUrl(state.signature || new File([], 'newfile')); return { ...state, @@ -67,9 +71,11 @@ export const deserializeReceipt = async (state: IState): Promise => { try { - const attachments = await Promise.all( - deserializedState.attachments.map(async (dataUrl) => readDataUrlAsFile2(dataUrl)) - ); + const illegalAttachment = deserializedState.attachments.find((attachment) => !attachment.startsWith("uploads/")); + if (illegalAttachment) { + throw new TypeError('Illegal attachment'); + } + const attachments = await Promise.all(deserializedState.attachments.map(downloadFileFromS3Bucket)); const signature = await readDataUrlAsFile2(deserializedState.signature); return { ...deserializedState, diff --git a/src/form/validation.ts b/src/form/validation.ts index 1c97ff9..7fec576 100644 --- a/src/form/validation.ts +++ b/src/form/validation.ts @@ -29,8 +29,8 @@ export const ACCOUNT_NUMBER_REGEX = new RegExp(/^\d{4} \d{2} \d{5}$/); export const COMMITTEE_EMAIL_REGEX = new RegExp(/^.{2,50}@online\.ntnu\.no$/); export const EMAIL_REGEX = new RegExp(/^[a-zA-Z0-9.!#$%&’*+/=?^_{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/); export const CARD_DETAIL_REGEX = new RegExp(/^.{5,30}$/); -export const FILE_SIZE_WARN = 7 * 1024 * 1024; // 7 MB -export const FILE_SIZE_MAX = 9 * 1024 * 1024; // 9 MB +export const FILE_SIZE_WARN = 20 * 1024 * 1024; // 20 MB +export const FILE_SIZE_MAX = 25 * 1024 * 1024; // 25 MB export const STATE_VALIDATION: StateValidators = { fullname: [ diff --git a/src/lambda/handler.ts b/src/lambda/handler.ts index df88c4a..2febc8e 100755 --- a/src/lambda/handler.ts +++ b/src/lambda/handler.ts @@ -10,6 +10,7 @@ import { readFileAsDataUrl } from 'utils/readFileAsDataUrl'; import { sendEmail } from './sendEmail'; import { ApiBodyError, ApiValidationError } from './errors'; import { pdfGenerator } from './browserlessGenerator'; +import { uploadFileToS3Bucket } from "../utils/uploadFileToS3Bucket"; export interface SuccessBody { message: string; @@ -48,12 +49,16 @@ export const generateReceipt = async (data: IDeserializedState | null): Promise< } const validState = state as NonNullableState; const pdf = await pdfGenerator(validState); - const pdfFile = new File([pdf], 'receipt.pdf', { type: 'application/pdf' }); - const pdfString = await readFileAsDataUrl(pdfFile); if (state.mode === 'download') { - return DOWNLOAD_SUCCESS_MESSAGE(pdfString); + const dato = `${new Date().toISOString().split('T')[0]}`; + const filename = `kvittering-${dato}-${+Date.now()}.pdf`; + const downloadUrl = await uploadFileToS3Bucket(pdf, `receipts/${filename}`); + return DOWNLOAD_SUCCESS_MESSAGE(downloadUrl); } else if (state.mode === 'email') { + const pdfFile = new File([pdf], 'receipt.pdf', { type: 'application/pdf' }); + const pdfString = await readFileAsDataUrl(pdfFile); + await sendEmail(pdfString, state); return EMAIL_SUCCESS_MESSAGE; } else if (state.mode === 'teapot') { diff --git a/src/pages/api/presign-upload-url.ts b/src/pages/api/presign-upload-url.ts new file mode 100644 index 0000000..068d141 --- /dev/null +++ b/src/pages/api/presign-upload-url.ts @@ -0,0 +1,27 @@ +import { NextApiRequest, NextApiResponse, PageConfig } from 'next'; + +import { SuccessBody } from 'lambda/handler'; +import { sentryMiddleware } from 'lambda/sentry'; +import { ErrorData } from 'lambda/errors'; +import { getPresignedS3URL } from "../../utils/getPresignedS3URL"; + +const handler = async (req: NextApiRequest, res: NextApiResponse) => { + const { filename, contentType } = req.body; + + try { + const data = await getPresignedS3URL(filename, contentType); + res.status(200).json({ message: "Presigned URL", data: JSON.stringify(data) }); + } catch (error) { + res.status(500).json({ message: "Failed to get presigned URL", data: error }); + } +}; + +export const config: PageConfig = { + api: { + bodyParser: { + sizeLimit: '25mb', + }, + }, +}; + +export default sentryMiddleware(handler); diff --git a/src/redux/actions/authActions.ts b/src/redux/actions/authActions.ts index d2b52f5..fc2ec5d 100644 --- a/src/redux/actions/authActions.ts +++ b/src/redux/actions/authActions.ts @@ -68,7 +68,6 @@ export const loginAction = createAsyncThunk('user/login', async (_, { dispatch, const user: User | null = await getManager().getUser(); if (user) { const newForm = await processUser(user, form); - console.log({ newForm }); updateForm(dispatch, newForm); } else { logInRedirect(form); @@ -96,6 +95,7 @@ export const catchCallbackAction = createAsyncThunk('user/catchCallback', async window.location.hash = ''; } catch (err) { /** Do nothing if no user data is present */ + window.location.hash = ''; return; } }); diff --git a/src/redux/actions/submitActions.ts b/src/redux/actions/submitActions.ts index 8f6c910..eca2014 100644 --- a/src/redux/actions/submitActions.ts +++ b/src/redux/actions/submitActions.ts @@ -3,9 +3,7 @@ import { createAsyncThunk } from '@reduxjs/toolkit'; import { NonNullableState } from 'lambda/generatePDF'; import { getFileName } from 'lambda/tools/format'; -import { downloadFile } from 'utils/download'; import { postReceipt } from 'utils/postReceipt'; -import { readDataUrlAsFile } from 'utils/readDataUrlAsFile'; import { downloadFinished, downloadStarted, loadingDone, setResponse } from 'redux/reducers/statusReducer'; import { State } from 'redux/store'; import { SuccessBody } from 'lambda/handler'; @@ -14,10 +12,13 @@ const handleDownload = async (response: SuccessBody, state: NonNullableState) => if (response.data) { /** Use the same filename that would be generated when sending a mail */ const fileName = getFileName(state); - const pdfFile = await readDataUrlAsFile(response.data, fileName); - if (pdfFile) { - downloadFile(pdfFile); - } + // response.data is a URL to the file + + const a = document.createElement('a'); + document.body.appendChild(a); + a.href = response.data; + a.download = fileName; + a.click(); } }; diff --git a/src/utils/downloadFileFromS3Bucket.ts b/src/utils/downloadFileFromS3Bucket.ts new file mode 100644 index 0000000..ef4923e --- /dev/null +++ b/src/utils/downloadFileFromS3Bucket.ts @@ -0,0 +1,32 @@ +import AWS from "aws-sdk"; + +const credentials = (process.env.AWS_ACCESS_KEY_ID && process.env.AWS_SECRET_ACCESS_KEY) ? + { + accessKeyId: process.env.AWS_ACCESS_KEY_ID, + secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, + } : + undefined; + +AWS.config.update({ + region: process.env.AWS_REGION, + credentials, +}) + +export async function downloadFileFromS3Bucket(key: string): Promise { + if (!process.env.AWS_S3_BUCKET_NAME) { + throw new Error('AWS_S3_BUCKET_NAME is not set'); + } + + const s3 = new AWS.S3({ + apiVersion: '2006-03-01', + }); + + const params = { + Bucket: process.env.AWS_S3_BUCKET_NAME, + Key: key, + }; + + const data = await s3.getObject(params).promise(); + + return new File([data.Body as Blob], key, { type: data.ContentType }); +} diff --git a/src/utils/getPresignedS3URL.ts b/src/utils/getPresignedS3URL.ts new file mode 100644 index 0000000..0f0fa30 --- /dev/null +++ b/src/utils/getPresignedS3URL.ts @@ -0,0 +1,41 @@ +import AWS from "aws-sdk"; + +const credentials = (process.env.AWS_ACCESS_KEY_ID && process.env.AWS_SECRET_ACCESS_KEY) ? + { + accessKeyId: process.env.AWS_ACCESS_KEY_ID, + secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, + } : + undefined; + +AWS.config.update({ + region: process.env.AWS_REGION, + credentials, +}) + +const s3 = new AWS.S3({ + apiVersion: '2006-03-01', +}); + +const createPresignedPost = (params: AWS.S3.PresignedPost.Params): Promise => new Promise((resolve, reject) => { + s3.createPresignedPost(params, function (err, data) { + if (err) { + reject(err); + } else { + resolve(data) + } + }); +}); + +export const getPresignedS3URL = async (name: string, contentType: string): Promise<{ url: string, fields: {[key: string]: string}}> => { + return await createPresignedPost({ + Bucket: "receipt-form", + Fields: { + key: `uploads/${+new Date()}-${name}`, + "Content-Type": contentType, + }, + Conditions: [ + ["content-length-range", 0, 1024 * 1024 * 10], + ], + Expires: 60, + }) +} \ No newline at end of file diff --git a/src/utils/uploadFile.ts b/src/utils/uploadFile.ts new file mode 100644 index 0000000..a49d734 --- /dev/null +++ b/src/utils/uploadFile.ts @@ -0,0 +1,42 @@ +import { LAMBDA_PRESIGN_UPLOAD_ENDPOINT } from "constants/backend"; + +export const uploadFile = async (file: File): Promise => { + if (!LAMBDA_PRESIGN_UPLOAD_ENDPOINT) { + throw new Error('LAMBDA_PRESIGN_UPLOAD_ENDPOINT is not set'); + } + + // Request to get the presigned POST data + const response = await fetch(LAMBDA_PRESIGN_UPLOAD_ENDPOINT, { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ filename: file.name, contentType: file.type }), + }); + + const body = await response.json(); + if (!response.ok) { + console.error(await response.text()); + throw new Error('Failed to get presigned post data'); + } + + const { url, fields }: {url: string, fields: {[key: string]: string}} = JSON.parse(body.data); + + const formData = new FormData(); + for (const key in fields) { + formData.append(key, fields[key]); + } + formData.append('file', file); + + const uploadResponse = await fetch(url, { + method: 'POST', + body: formData, + }); + + if (!uploadResponse.ok) { + throw new Error('Failed to upload file'); + } + + return fields.key +}; diff --git a/src/utils/uploadFileToS3Bucket.ts b/src/utils/uploadFileToS3Bucket.ts new file mode 100644 index 0000000..4ddd445 --- /dev/null +++ b/src/utils/uploadFileToS3Bucket.ts @@ -0,0 +1,35 @@ +import AWS from "aws-sdk"; + +const credentials = (process.env.AWS_ACCESS_KEY_ID && process.env.AWS_SECRET_ACCESS_KEY) ? + { + accessKeyId: process.env.AWS_ACCESS_KEY_ID, + secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, + } : + undefined; + +AWS.config.update({ + region: process.env.AWS_REGION, + credentials, +}) + +// upload file to S3 bucket and make it publicly downloadable +export async function uploadFileToS3Bucket(file: Uint8Array, key: string): Promise { + if (!process.env.AWS_S3_BUCKET_NAME) { + throw new Error('AWS_S3_BUCKET_NAME is not set'); + } + const s3 = new AWS.S3({ + apiVersion: '2006-03-01', + params: { Bucket: process.env.AWS_S3_BUCKET_NAME }, + }); + + const params = { + Bucket: process.env.AWS_S3_BUCKET_NAME, + Key: key, + Body: file, + ACL: 'public-read', + }; + + const result = await s3.upload(params).promise(); + + return result.Location; +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index bf9bdaa..0524794 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1475,10 +1475,10 @@ resolved "https://registry.yarnpkg.com/@commitlint/types/-/types-11.0.0.tgz#719cf05fcc1abb6533610a2e0f5dd1e61eac14fe" integrity sha512-VoNqai1vR5anRF5Tuh/+SWDFk7xi7oMwHrHrbm1BprYXjB2RJsWLhUrStMssDxEl5lW/z3EUdg8RvH/IUBccSQ== -"@dotkomonline/design-system@^0.22.0": - version "0.22.0" - resolved "https://registry.yarnpkg.com/@dotkomonline/design-system/-/design-system-0.22.0.tgz#a111004f6d464283ebbb6f8a21ea1e2b1fed4e9d" - integrity sha512-uQxutJjJNcqsHW2ycosFDq94KNVbrZsXwqarlo0o+xdlubt+b8VTEtm6A4+z6Ej/OIS/K32ERFpcYqpf1RfE9g== +"@dotkomonline/design-system@^0.22.2": + version "0.22.2" + resolved "https://registry.yarnpkg.com/@dotkomonline/design-system/-/design-system-0.22.2.tgz#9595dbf33c45323155bd6ef201823724c6e43f22" + integrity sha512-nX5+3nZKOYGuq5bQ0w8j4PzAYs6gJ3yEz5Zx8uSBx8HuVx9/rK0ySs9R7vllq2IyewE4OuwQFoPRVBoI4i8l6w== dependencies: "@storybook/addon-a11y" "^5.3.12" "@storybook/addon-actions" "^5.3.12" @@ -2951,6 +2951,13 @@ resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-4.2.0.tgz#14264692a9d6e2fa4db3df5e56e94b5e25647ac0" integrity sha512-iIgQNzCm0v7QMhhe4Jjn9uRh+I6GoPmt03CbEtwx3ao8/EfoQcmgtqH4vQ5Db/lxiIGaWDv6nwvunuh0RyX0+A== +"@types/aws-sdk@^2.7.0": + version "2.7.0" + resolved "https://registry.yarnpkg.com/@types/aws-sdk/-/aws-sdk-2.7.0.tgz#83588b3d14ebdca1d4ce5e023387577568ce82f3" + integrity sha512-bF6brnwPN9+kheqdKCpinMgCkj+sJIUEj+0v0LPug9OQwL5/1jy+kiJwl+Nkw4Kh+7oaL1phhC4gMz6Oq60jMg== + dependencies: + aws-sdk "*" + "@types/babel__core@^7.0.0", "@types/babel__core@^7.1.7": version "7.1.12" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.12.tgz#4d8e9e51eb265552a7e4f1ff2219ab6133bdfb2d" @@ -4287,6 +4294,43 @@ autoprefixer@^9.7.2: postcss "^7.0.32" postcss-value-parser "^4.1.0" +available-typed-arrays@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.6.tgz#ac812d8ce5a6b976d738e1c45f08d0b00bc7d725" + integrity sha512-j1QzY8iPNPG4o4xmO3ptzpRxTciqD3MgEHtifP/YnJpIo58Xu+ne4BejlbkuaLfXn/nz6HFiw29bLpj2PNMdGg== + +aws-sdk@*: + version "2.1561.0" + resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1561.0.tgz#cfe24c92818f9f4bd4a91a0f45aca26c8688faf6" + integrity sha512-YbYQOyvy9mfEGRI4JDZjw6J0zW6bjyV7H3WMWeq69qETvZlkq8koy5CTPMCjnL8i7boDjyW9FuhQzICBbeNgLg== + dependencies: + buffer "4.9.2" + events "1.1.1" + ieee754 "1.1.13" + jmespath "0.16.0" + querystring "0.2.0" + sax "1.2.1" + url "0.10.3" + util "^0.12.4" + uuid "8.0.0" + xml2js "0.6.2" + +aws-sdk@^2.1560.0: + version "2.1560.0" + resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1560.0.tgz#f43b2b4d212ecf1da86ce1b1f4f0c5308c74f9e5" + integrity sha512-nakTZHytnhKWZpwu9d1crqjoegBRG+j1/rflsVnckXxoIwlKM0D/v/NIe+BJmRnCA2aCdwuMx3dtkgLz/AB6VA== + dependencies: + buffer "4.9.2" + events "1.1.1" + ieee754 "1.1.13" + jmespath "0.16.0" + querystring "0.2.0" + sax "1.2.1" + url "0.10.3" + util "^0.12.4" + uuid "8.0.0" + xml2js "0.6.2" + aws-sign2@~0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" @@ -5052,15 +5096,7 @@ buffer-xor@^1.0.3: resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= -buffer@5.6.0: - version "5.6.0" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.6.0.tgz#a31749dc7d81d84db08abf937b6b8c4033f62786" - integrity sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw== - dependencies: - base64-js "^1.0.2" - ieee754 "^1.1.4" - -buffer@^4.3.0: +buffer@4.9.2, buffer@^4.3.0: version "4.9.2" resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8" integrity sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg== @@ -5069,6 +5105,14 @@ buffer@^4.3.0: ieee754 "^1.1.4" isarray "^1.0.0" +buffer@5.6.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.6.0.tgz#a31749dc7d81d84db08abf937b6b8c4033f62786" + integrity sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw== + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + buffer@^5.2.1, buffer@^5.5.0: version "5.7.1" resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" @@ -5185,6 +5229,17 @@ cache-base@^1.0.1: union-value "^1.0.0" unset-value "^1.0.0" +call-bind@^1.0.5: + version "1.0.7" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" + integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + set-function-length "^1.2.1" + call-limit@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/call-limit/-/call-limit-1.1.1.tgz#ef15f2670db3f1992557e2d965abc459e6e358d4" @@ -6509,6 +6564,15 @@ defaults@^1.0.3: dependencies: clone "^1.0.2" +define-data-property@^1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + gopd "^1.0.1" + define-properties@^1.1.2, define-properties@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" @@ -7114,6 +7178,18 @@ es-array-method-boxes-properly@^1.0.0: resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e" integrity sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA== +es-define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" + integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== + dependencies: + get-intrinsic "^1.2.4" + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + es-get-iterator@^1.0.2: version "1.1.0" resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.0.tgz#bb98ad9d6d63b31aacdc8f89d5d0ee57bcb5b4c8" @@ -7470,6 +7546,11 @@ eventemitter3@^4.0.0: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== +events@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" + integrity sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw== + events@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/events/-/events-3.2.0.tgz#93b87c18f8efcd4202a461aec4dfc0556b639379" @@ -7967,6 +8048,13 @@ fontkit@^1.8.1: unicode-properties "^1.2.2" unicode-trie "^0.3.0" +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" + for-in@^0.1.3: version "0.1.8" resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.8.tgz#d8773908e31256109952b1fdb9b3fa867d2775e1" @@ -8146,6 +8234,11 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + function.prototype.name@^1.1.0: version "1.1.2" resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.2.tgz#5cdf79d7c05db401591dfde83e3b70c5123e9a45" @@ -8233,6 +8326,17 @@ get-caller-file@^2.0.1: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== +get-intrinsic@^1.1.3, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" + integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + get-package-type@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" @@ -8448,6 +8552,13 @@ good-listener@^1.2.2: dependencies: delegate "^3.1.2" +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + got@^6.7.1: version "6.7.1" resolved "https://registry.yarnpkg.com/got/-/got-6.7.1.tgz#240cd05785a9a18e561dc1b44b41c763ef1e8db0" @@ -8540,11 +8651,35 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== +has-property-descriptors@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== + dependencies: + es-define-property "^1.0.0" + +has-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" + integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== + has-symbols@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== +has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +has-tostringtag@^1.0.0, has-tostringtag@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== + dependencies: + has-symbols "^1.0.3" + has-unicode@^2.0.0, has-unicode@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" @@ -8615,6 +8750,13 @@ hash.js@^1.0.0, hash.js@^1.0.3: inherits "^2.0.3" minimalistic-assert "^1.0.1" +hasown@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.1.tgz#26f48f039de2c0f8d3356c223fb8d50253519faa" + integrity sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA== + dependencies: + function-bind "^1.1.2" + hast-to-hyperscript@^9.0.0: version "9.0.0" resolved "https://registry.yarnpkg.com/hast-to-hyperscript/-/hast-to-hyperscript-9.0.0.tgz#768fb557765fe28749169c885056417342d71e83" @@ -8950,16 +9092,16 @@ identity-obj-proxy@^3.0.0: dependencies: harmony-reflect "^1.4.6" +ieee754@1.1.13, ieee754@^1.1.4: + version "1.1.13" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" + integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== + ieee754@^1.1.13: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== -ieee754@^1.1.4: - version "1.1.13" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" - integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== - iferr@^0.1.5: version "0.1.5" resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" @@ -9277,6 +9419,11 @@ is-buffer@^2.0.0: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.4.tgz#3e572f23c8411a5cfd9557c849e3665e0b290623" integrity sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A== +is-callable@^1.1.3: + version "1.2.7" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== + is-callable@^1.1.4, is-callable@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.2.tgz#c7c6715cd22d4ddb48d3e19970223aceabb080d9" @@ -9441,6 +9588,13 @@ is-generator-fn@^2.0.0: resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== +is-generator-function@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" + integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== + dependencies: + has-tostringtag "^1.0.0" + is-glob@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" @@ -9634,6 +9788,13 @@ is-text-path@^1.0.1: dependencies: text-extensions "^1.0.0" +is-typed-array@^1.1.3: + version "1.1.13" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.13.tgz#d6c5ca56df62334959322d7d7dd1cca50debe229" + integrity sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw== + dependencies: + which-typed-array "^1.1.14" + is-typedarray@^1.0.0, is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" @@ -10266,6 +10427,11 @@ jest@^26.6.3: import-local "^3.0.2" jest-cli "^26.6.3" +jmespath@0.16.0: + version "0.16.0" + resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.16.0.tgz#b15b0a85dfd4d930d43e69ed605943c802785076" + integrity sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw== + js-string-escape@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/js-string-escape/-/js-string-escape-1.0.1.tgz#e2625badbc0d67c7533e9edc1068c587ae4137ef" @@ -14628,6 +14794,16 @@ sass-loader@10.0.2: schema-utils "^2.7.1" semver "^7.3.2" +sax@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a" + integrity sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA== + +sax@>=0.6.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.3.0.tgz#a5dbe77db3be05c9d1ee7785dbd3ea9de51593d0" + integrity sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA== + sax@~1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" @@ -14831,6 +15007,18 @@ set-blocking@^2.0.0, set-blocking@~2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= +set-function-length@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.1.tgz#47cc5945f2c771e2cf261c6737cf9684a2a5e425" + integrity sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g== + dependencies: + define-data-property "^1.1.2" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.3" + gopd "^1.0.1" + has-property-descriptors "^1.0.1" + set-value@^2.0.0, set-value@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" @@ -16622,6 +16810,14 @@ url-parse@^1.4.3: querystringify "^2.1.1" requires-port "^1.0.0" +url@0.10.3: + version "0.10.3" + resolved "https://registry.yarnpkg.com/url/-/url-0.10.3.tgz#021e4d9c7705f21bbf37d03ceb58767402774c64" + integrity sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ== + dependencies: + punycode "1.3.2" + querystring "0.2.0" + url@^0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" @@ -16704,6 +16900,17 @@ util@^0.11.0: dependencies: inherits "2.0.3" +util@^0.12.4: + version "0.12.5" + resolved "https://registry.yarnpkg.com/util/-/util-0.12.5.tgz#5f17a6059b73db61a875668781a1c2b136bd6fbc" + integrity sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA== + dependencies: + inherits "^2.0.3" + is-arguments "^1.0.4" + is-generator-function "^1.0.7" + is-typed-array "^1.1.3" + which-typed-array "^1.1.2" + utila@~0.4: version "0.4.0" resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" @@ -16714,6 +16921,11 @@ utils-merge@1.0.1: resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= +uuid@8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.0.0.tgz#bc6ccf91b5ff0ac07bbcdbf1c7c4e150db4dbb6c" + integrity sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw== + uuid@^3.3.2, uuid@^3.3.3: version "3.4.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" @@ -17082,6 +17294,17 @@ which-pm-runs@^1.0.0: resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.0.0.tgz#670b3afbc552e0b55df6b7780ca74615f23ad1cb" integrity sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs= +which-typed-array@^1.1.14, which-typed-array@^1.1.2: + version "1.1.14" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.14.tgz#1f78a111aee1e131ca66164d8bdc3ab062c95a06" + integrity sha512-VnXFiIW8yNn9kIHN88xvZ4yOWchftKDsRJ8fEPacX/wl1lOvBrhsJ/OeJCXq7B0AaijRuqgzSKalJoPk+D8MPg== + dependencies: + available-typed-arrays "^1.0.6" + call-bind "^1.0.5" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.1" + which@^1.2.9, which@^1.3.0, which@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" @@ -17232,6 +17455,19 @@ xml-name-validator@^3.0.0: resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== +xml2js@0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.6.2.tgz#dd0b630083aa09c161e25a4d0901e2b2a929b499" + integrity sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA== + dependencies: + sax ">=0.6.0" + xmlbuilder "~11.0.0" + +xmlbuilder@~11.0.0: + version "11.0.1" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" + integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== + xmlchars@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb"