From c6dadff541d82cfc4afa75773f0fdd8c2fa81260 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Nov 2023 19:35:48 +0000 Subject: [PATCH 01/12] fix(deps): bump type-fest from 4.5.0 to 4.7.1 in /shared (#6883) Bumps [type-fest](https://github.com/sindresorhus/type-fest) from 4.5.0 to 4.7.1. - [Release notes](https://github.com/sindresorhus/type-fest/releases) - [Commits](https://github.com/sindresorhus/type-fest/compare/v4.5.0...v4.7.1) --- updated-dependencies: - dependency-name: type-fest dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- shared/package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/shared/package-lock.json b/shared/package-lock.json index 5bc2bed9d8..f54e5db163 100644 --- a/shared/package-lock.json +++ b/shared/package-lock.json @@ -865,9 +865,9 @@ } }, "node_modules/type-fest": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.5.0.tgz", - "integrity": "sha512-diLQivFzddJl4ylL3jxSkEc39Tpw7o1QeEHIPxVwryDK2lpB7Nqhzhuo6v5/Ls08Z0yPSAhsyAWlv1/H0ciNmw==", + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.7.1.tgz", + "integrity": "sha512-iWr8RUmzAJRfhZugX9O7nZE6pCxDU8CZ3QxsLuTnGcBLJpCaP2ll3s4eMTBoFnU/CeXY/5rfQSuAEsTGJO4y8A==", "engines": { "node": ">=16" }, @@ -1478,9 +1478,9 @@ } }, "type-fest": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.5.0.tgz", - "integrity": "sha512-diLQivFzddJl4ylL3jxSkEc39Tpw7o1QeEHIPxVwryDK2lpB7Nqhzhuo6v5/Ls08Z0yPSAhsyAWlv1/H0ciNmw==" + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.7.1.tgz", + "integrity": "sha512-iWr8RUmzAJRfhZugX9O7nZE6pCxDU8CZ3QxsLuTnGcBLJpCaP2ll3s4eMTBoFnU/CeXY/5rfQSuAEsTGJO4y8A==" }, "util-deprecate": { "version": "1.0.2", From 2b813a494d70501d0b73e7f0c091e06029d6a6ae Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 10 Nov 2023 22:59:21 +0000 Subject: [PATCH 02/12] fix(deps): bump axios from 1.2.1 to 1.6.0 (#6886) Bumps [axios](https://github.com/axios/axios) from 1.2.1 to 1.6.0. - [Release notes](https://github.com/axios/axios/releases) - [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md) - [Commits](https://github.com/axios/axios/compare/v1.2.1...v1.6.0) --- updated-dependencies: - dependency-name: axios dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index bf836c7b5e..b7fa60b13c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,7 +24,7 @@ "abortcontroller-polyfill": "^1.7.5", "aws-info": "^1.2.0", "aws-sdk": "^2.1354.0", - "axios": "^1.2.1", + "axios": "^1.6.0", "bcrypt": "^5.1.0", "bluebird": "^3.5.2", "body-parser": "^1.20.1", @@ -9778,9 +9778,9 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.2.1.tgz", - "integrity": "sha512-I88cFiGu9ryt/tfVEi4kX2SITsvDddTajXTOFmt2uK1ZVA8LytjtdeyefdQWEf5PU8w+4SSJDoYnggflB5tW4A==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.0.tgz", + "integrity": "sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==", "dependencies": { "follow-redirects": "^1.15.0", "form-data": "^4.0.0", @@ -38955,9 +38955,9 @@ "dev": true }, "axios": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.2.1.tgz", - "integrity": "sha512-I88cFiGu9ryt/tfVEi4kX2SITsvDddTajXTOFmt2uK1ZVA8LytjtdeyefdQWEf5PU8w+4SSJDoYnggflB5tW4A==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.0.tgz", + "integrity": "sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==", "requires": { "follow-redirects": "^1.15.0", "form-data": "^4.0.0", diff --git a/package.json b/package.json index 331c1b3531..d2dac61ccf 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "abortcontroller-polyfill": "^1.7.5", "aws-info": "^1.2.0", "aws-sdk": "^2.1354.0", - "axios": "^1.2.1", + "axios": "^1.6.0", "bcrypt": "^5.1.0", "bluebird": "^3.5.2", "body-parser": "^1.20.1", From 61318c3c03516550a96d7eb0b6838bdda2c796f5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 10 Nov 2023 23:19:04 +0000 Subject: [PATCH 03/12] chore(deps): bump axios from 1.2.1 to 1.6.0 in /frontend (#6887) Bumps [axios](https://github.com/axios/axios) from 1.2.1 to 1.6.0. - [Release notes](https://github.com/axios/axios/releases) - [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md) - [Commits](https://github.com/axios/axios/compare/v1.2.1...v1.6.0) --- updated-dependencies: - dependency-name: axios dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- frontend/package-lock.json | 14 +++++++------- frontend/package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index f2e12da404..c9bb2a0692 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -20,7 +20,7 @@ "@stablelib/base64": "^1.0.1", "@stripe/react-stripe-js": "^1.15.0", "@stripe/stripe-js": "^1.44.1", - "axios": "^1.2.1", + "axios": "^1.6.0", "broadcast-channel": "^4.13.0", "browser-image-compression": "^2.0.2", "comlink": "^4.3.1", @@ -17324,9 +17324,9 @@ } }, "node_modules/axios": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.2.1.tgz", - "integrity": "sha512-I88cFiGu9ryt/tfVEi4kX2SITsvDddTajXTOFmt2uK1ZVA8LytjtdeyefdQWEf5PU8w+4SSJDoYnggflB5tW4A==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.0.tgz", + "integrity": "sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==", "dependencies": { "follow-redirects": "^1.15.0", "form-data": "^4.0.0", @@ -61200,9 +61200,9 @@ "dev": true }, "axios": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.2.1.tgz", - "integrity": "sha512-I88cFiGu9ryt/tfVEi4kX2SITsvDddTajXTOFmt2uK1ZVA8LytjtdeyefdQWEf5PU8w+4SSJDoYnggflB5tW4A==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.0.tgz", + "integrity": "sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==", "requires": { "follow-redirects": "^1.15.0", "form-data": "^4.0.0", diff --git a/frontend/package.json b/frontend/package.json index 74bd289e2b..a155f67aad 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -15,7 +15,7 @@ "@stablelib/base64": "^1.0.1", "@stripe/react-stripe-js": "^1.15.0", "@stripe/stripe-js": "^1.44.1", - "axios": "^1.2.1", + "axios": "^1.6.0", "broadcast-channel": "^4.13.0", "browser-image-compression": "^2.0.2", "comlink": "^4.3.1", From 7baacefced6be0f13301582687e55dc8bc2a8693 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 12 Nov 2023 20:03:06 +0000 Subject: [PATCH 04/12] chore(deps-dev): bump @types/lodash from 4.14.200 to 4.14.201 in /shared (#6888) Bumps [@types/lodash](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/lodash) from 4.14.200 to 4.14.201. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/lodash) --- updated-dependencies: - dependency-name: "@types/lodash" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- shared/package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/shared/package-lock.json b/shared/package-lock.json index f54e5db163..1c52d642de 100644 --- a/shared/package-lock.json +++ b/shared/package-lock.json @@ -102,9 +102,9 @@ "dev": true }, "node_modules/@types/lodash": { - "version": "4.14.200", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.200.tgz", - "integrity": "sha512-YI/M/4HRImtNf3pJgbF+W6FrXovqj+T+/HpENLTooK9PnkacBsDpeP3IpHab40CClUfhNmdM2WTNP2sa2dni5Q==", + "version": "4.14.201", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.201.tgz", + "integrity": "sha512-y9euML0cim1JrykNxADLfaG0FgD1g/yTHwUs/Jg9ZIU7WKj2/4IW9Lbb1WZbvck78W/lfGXFfe+u2EGfIJXdLQ==", "dev": true }, "node_modules/@types/semver": { @@ -960,9 +960,9 @@ "dev": true }, "@types/lodash": { - "version": "4.14.200", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.200.tgz", - "integrity": "sha512-YI/M/4HRImtNf3pJgbF+W6FrXovqj+T+/HpENLTooK9PnkacBsDpeP3IpHab40CClUfhNmdM2WTNP2sa2dni5Q==", + "version": "4.14.201", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.201.tgz", + "integrity": "sha512-y9euML0cim1JrykNxADLfaG0FgD1g/yTHwUs/Jg9ZIU7WKj2/4IW9Lbb1WZbvck78W/lfGXFfe+u2EGfIJXdLQ==", "dev": true }, "@types/semver": { From d5bbb4d30454a4d6050c0bda9a041bac2928ec3e Mon Sep 17 00:00:00 2001 From: tshuli <63710093+tshuli@users.noreply.github.com> Date: Tue, 14 Nov 2023 23:46:20 +0800 Subject: [PATCH 05/12] chore: remove eb shift frontend feature flags (#6869) * chore: remove virus scanner feature flag * chore: remove enable encryption boundary shift feature flag * chore: remove growthbook in public form provider * chore: remove unused storage submission modes * chore: fetch fallback should also use virus scanning * chore: update mutation * chore: move fallback to catch block * chore: add back network error check * chore: remove unused import and const * chore: remove clear word --- .../public-form/PublicFormProvider.tsx | 145 +++------------- .../features/public-form/PublicFormService.ts | 162 ++---------------- .../src/features/public-form/mutations.ts | 107 ++++++++---- shared/constants/form.ts | 1 - 4 files changed, 112 insertions(+), 303 deletions(-) diff --git a/frontend/src/features/public-form/PublicFormProvider.tsx b/frontend/src/features/public-form/PublicFormProvider.tsx index fc3feeaffb..ecc8b777ff 100644 --- a/frontend/src/features/public-form/PublicFormProvider.tsx +++ b/frontend/src/features/public-form/PublicFormProvider.tsx @@ -11,11 +11,6 @@ import { SubmitHandler } from 'react-hook-form' import { useNavigate } from 'react-router-dom' import { useDisclosure } from '@chakra-ui/react' import { datadogLogs } from '@datadog/browser-logs' -import { - useFeatureIsOn, - useFeatureValue, - useGrowthBook, -} from '@growthbook/growthbook-react' import { differenceInMilliseconds, isPast } from 'date-fns' import get from 'lodash/get' import simplur from 'simplur' @@ -135,23 +130,6 @@ export const PublicFormProvider = ({ /* enabled= */ !submissionData, ) - const growthbook = useGrowthBook() - - useEffect(() => { - if (growthbook) { - growthbook.setAttributes({ - // Only update the `formId` attribute, keep the rest the same - ...growthbook.getAttributes(), - formId, - }) - } - }, [growthbook, formId]) - - const enableEncryptionBoundaryShift = useFeatureValue( - featureFlags.encryptionBoundaryShift, - true, - ) - // Scroll to top of page when user has finished their submission. useLayoutEffect(() => { if (submissionData) { @@ -269,18 +247,11 @@ export const PublicFormProvider = ({ } }, [data?.form.form_fields, toast, vfnToastIdRef]) - const enableVirusScanner = useFeatureIsOn( - featureFlags.encryptionBoundaryShiftVirusScanner, - ) - const { submitEmailModeFormMutation, - submitStorageModeFormMutation, submitEmailModeFormFetchMutation, - submitStorageModeFormFetchMutation, - submitStorageModeClearFormMutation, - submitStorageModeClearFormFetchMutation, - submitStorageModeClearFormWithVirusScanningMutation, + submitStorageModeFormWithVirusScanningFetchMutation, + submitStorageModeFormWithVirusScanningMutation, } = usePublicFormMutations(formId, submissionData?.id ?? '') const { handleLogoutMutation } = usePublicAuthMutations(formId) @@ -484,9 +455,7 @@ export const PublicFormProvider = ({ : {}), } - const submitStorageFormWithFetch = function ( - routeToNewStorageModeSubmission: boolean, - ) { + const submitStorageFormWithFetch = function () { datadogLogs.logger.info(`handleSubmitForm: submitting via fetch`, { meta: { ...logMeta, @@ -495,16 +464,11 @@ export const PublicFormProvider = ({ }, }) - return ( - routeToNewStorageModeSubmission - ? submitStorageModeClearFormFetchMutation - : submitStorageModeFormFetchMutation - ) + return submitStorageModeFormWithVirusScanningFetchMutation .mutateAsync( { ...formData, ...formPaymentData, - publicKey: form.publicKey, }, { onSuccess: ({ @@ -546,7 +510,7 @@ export const PublicFormProvider = ({ // TODO (#5826): Toggle to use fetch for submissions instead of axios. If enabled, this is used for testing and to use fetch instead of axios by default if testing shows fetch is more stable. Remove once network error is resolved if (useFetchForSubmissions) { - return submitStorageFormWithFetch(enableEncryptionBoundaryShift) + return submitStorageFormWithFetch() } datadogLogs.logger.info(`handleSubmitForm: submitting via axios`, { meta: { @@ -556,9 +520,8 @@ export const PublicFormProvider = ({ }, }) - // TODO (FRM-1413): Move to main return statement once virus scanner has been fully rolled out - if (enableEncryptionBoundaryShift && enableVirusScanner) { - return submitStorageModeClearFormWithVirusScanningMutation.mutateAsync( + return submitStorageModeFormWithVirusScanningMutation + .mutateAsync( { ...formData, ...formPaymentData, @@ -582,86 +545,27 @@ export const PublicFormProvider = ({ timestamp, }) }, - onError: (error) => { - // TODO(#5826): Remove when we have resolved the Network Error - datadogLogs.logger.warn( - `handleSubmitForm: submit with virus scan`, - { - meta: { - ...logMeta, - responseMode: 'storage', - method: 'axios', - error, - }, - }, - ) - - // defaults to the safest option of storage submission without virus scanning - return submitStorageFormWithFetch( - enableEncryptionBoundaryShift, - ) - }, }, ) - } - - return ( - ( - enableEncryptionBoundaryShift - ? submitStorageModeClearFormMutation - : submitStorageModeFormMutation - ) - .mutateAsync( - { - ...formData, - ...formPaymentData, - publicKey: form.publicKey, - }, + .catch(async (error) => { + datadogLogs.logger.warn( + `handleSubmitForm: submit with virus scan`, { - onSuccess: ({ - submissionId, - timestamp, - // payment forms will have non-empty paymentData field - paymentData, - }) => { - trackSubmitForm(form) - - if (paymentData) { - navigate(getPaymentPageUrl(formId, paymentData.paymentId)) - storePaymentMemory(paymentData.paymentId) - return - } - setSubmissionData({ - id: submissionId, - timestamp, - }) - }, - }, - ) - // Using catch since we are using mutateAsync and react-hook-form will continue bubbling this up. - .catch(async (error) => { - // TODO(#5826): Remove when we have resolved the Network Error - datadogLogs.logger.warn(`handleSubmitForm: ${error.message}`, { meta: { ...logMeta, responseMode: 'storage', method: 'axios', - error: { - message: error.message, - stack: error.stack, - }, + error, }, - }) - - if (/Network Error/i.test(error.message)) { - axiosDebugFlow() - return submitStorageFormWithFetch( - enableEncryptionBoundaryShift, - ) - } - showErrorToast(error, form) - }) - ) + }, + ) + if (/Network Error/i.test(error.message)) { + axiosDebugFlow() + // fallback to fetch + return submitStorageFormWithFetch() + } + showErrorToast(error, form) + }) } } }, @@ -678,16 +582,11 @@ export const PublicFormProvider = ({ getCaptchaResponse, submitEmailModeFormFetchMutation, submitEmailModeFormMutation, - enableEncryptionBoundaryShift, - enableVirusScanner, - submitStorageModeClearFormMutation, - submitStorageModeFormMutation, - submitStorageModeClearFormFetchMutation, - submitStorageModeFormFetchMutation, + submitStorageModeFormWithVirusScanningMutation, + submitStorageModeFormWithVirusScanningFetchMutation, navigate, formId, storePaymentMemory, - submitStorageModeClearFormWithVirusScanningMutation, ], ) diff --git a/frontend/src/features/public-form/PublicFormService.ts b/frontend/src/features/public-form/PublicFormService.ts index 1ae78819d0..9cab540bee 100644 --- a/frontend/src/features/public-form/PublicFormService.ts +++ b/frontend/src/features/public-form/PublicFormService.ts @@ -1,10 +1,7 @@ import { PresignedPost } from 'aws-sdk/clients/s3' import axios from 'axios' -import { - ENCRYPTION_BOUNDARY_SHIFT_SUBMISSION_VERSION, - VIRUS_SCANNER_SUBMISSION_VERSION, -} from '~shared/constants' +import { VIRUS_SCANNER_SUBMISSION_VERSION } from '~shared/constants' import { SubmitFormIssueBodyDto, SuccessMessageDto } from '~shared/types' import { AttachmentPresignedPostDataMapType, @@ -39,7 +36,6 @@ import { FormFieldValues } from '~templates/Field' import { createClearSubmissionFormData, createClearSubmissionWithVirusScanningFormData, - createEncryptedSubmissionData, getAttachmentsMap, } from './utils/createSubmission' import { filterHiddenInputs } from './utils/filterHiddenInputs' @@ -155,46 +151,9 @@ export const submitEmailModeForm = async ({ ).then(({ data }) => data) } -export const submitStorageModeForm = async ({ - formFields, - formLogics, - formInputs, - formId, - publicKey, - captchaResponse = null, - captchaType = '', - paymentReceiptEmail, - responseMetadata, - paymentProducts, - payments, -}: SubmitStorageFormArgs) => { - const filteredInputs = filterHiddenInputs({ - formFields, - formInputs, - formLogics, - }) - const submissionContent = await createEncryptedSubmissionData({ - formFields, - formInputs: filteredInputs, - publicKey, - responseMetadata, - paymentReceiptEmail, - payments, - paymentProducts, - }) - return ApiService.post( - `${PUBLIC_FORMS_ENDPOINT}/${formId}/submissions/encrypt`, - submissionContent, - { - params: { - captchaResponse: String(captchaResponse), - captchaType, - }, - }, - ).then(({ data }) => data) -} - -export const submitStorageModeClearForm = async ({ +// TODO (#5826): Fallback mutation using Fetch. Remove once network error is resolved +// Submit storage mode form with virus scanning (storage v2.1+) +export const submitStorageModeFormWithVirusScanningWithFetch = async ({ formFields, formLogics, formInputs, @@ -205,63 +164,26 @@ export const submitStorageModeClearForm = async ({ responseMetadata, paymentProducts, payments, -}: SubmitStorageFormClearArgs) => { + fieldIdToQuarantineKeyMap, +}: SubmitStorageFormWithVirusScanningArgs) => { const filteredInputs = filterHiddenInputs({ formFields, formInputs, formLogics, }) - const formData = createClearSubmissionFormData({ - formFields, - formInputs: filteredInputs, - responseMetadata, - paymentReceiptEmail, - paymentProducts, - payments, - version: ENCRYPTION_BOUNDARY_SHIFT_SUBMISSION_VERSION, - }) - - return ApiService.post( - `${PUBLIC_FORMS_ENDPOINT}/${formId}/submissions/storage`, - formData, + const formData = createClearSubmissionWithVirusScanningFormData( { - params: { - captchaResponse: String(captchaResponse), - captchaType: captchaType, - }, + formFields, + formInputs: filteredInputs, + responseMetadata, + paymentReceiptEmail, + paymentProducts, + payments, + version: VIRUS_SCANNER_SUBMISSION_VERSION, }, - ).then(({ data }) => data) -} - -// TODO (#5826): Fallback mutation using Fetch. Remove once network error is resolved -export const submitStorageModeClearFormWithFetch = async ({ - formFields, - formLogics, - formInputs, - formId, - captchaResponse = null, - captchaType = '', - paymentReceiptEmail, - responseMetadata, - paymentProducts, - payments, -}: SubmitStorageFormClearArgs) => { - const filteredInputs = filterHiddenInputs({ - formFields, - formInputs, - formLogics, - }) - - const formData = createClearSubmissionFormData({ - formFields, - formInputs: filteredInputs, - responseMetadata, - paymentReceiptEmail, - paymentProducts, - payments, - version: ENCRYPTION_BOUNDARY_SHIFT_SUBMISSION_VERSION, - }) + fieldIdToQuarantineKeyMap, + ) // Add captcha response to query string const queryString = new URLSearchParams({ @@ -284,7 +206,7 @@ export const submitStorageModeClearFormWithFetch = async ({ } // Submit storage mode form with virus scanning (storage v2.1+) -export const submitStorageModeClearFormWithVirusScanning = async ({ +export const submitStorageModeFormWithVirusScanning = async ({ formFields, formLogics, formInputs, @@ -369,56 +291,6 @@ export const submitEmailModeFormWithFetch = async ({ return processFetchResponse(response) } -// TODO (#5826): Fallback mutation using Fetch. Remove once network error is resolved -export const submitStorageModeFormWithFetch = async ({ - formFields, - formLogics, - formInputs, - formId, - publicKey, - captchaResponse = null, - captchaType = '', - paymentReceiptEmail, - responseMetadata, - paymentProducts, - payments, -}: SubmitStorageFormArgs) => { - const filteredInputs = filterHiddenInputs({ - formFields, - formInputs, - formLogics, - }) - const submissionContent = await createEncryptedSubmissionData({ - formFields, - formInputs: filteredInputs, - publicKey, - responseMetadata, - paymentReceiptEmail, - payments, - paymentProducts, - }) - - // Add captcha response to query string - const queryString = new URLSearchParams({ - captchaResponse: String(captchaResponse), - captchaType, - }).toString() - - const response = await fetch( - `${API_BASE_URL}${PUBLIC_FORMS_ENDPOINT}/${formId}/submissions/encrypt?${queryString}`, - { - method: 'POST', - body: JSON.stringify(submissionContent), - headers: { - 'Content-Type': 'application/json', - Accept: 'application/json', - }, - }, - ) - - return processFetchResponse(response) -} - /** * Post feedback for a given form. * @param formId the id of the form to post feedback for diff --git a/frontend/src/features/public-form/mutations.ts b/frontend/src/features/public-form/mutations.ts index 1975644ce7..e39c2561f4 100644 --- a/frontend/src/features/public-form/mutations.ts +++ b/frontend/src/features/public-form/mutations.ts @@ -20,13 +20,9 @@ import { submitEmailModeFormWithFetch, submitFormFeedback, submitFormIssue, - SubmitStorageFormArgs, SubmitStorageFormClearArgs, - submitStorageModeClearForm, - submitStorageModeClearFormWithFetch, - submitStorageModeClearFormWithVirusScanning, - submitStorageModeForm, - submitStorageModeFormWithFetch, + submitStorageModeFormWithVirusScanning, + submitStorageModeFormWithVirusScanningWithFetch, uploadAttachmentToQuarantine, } from './PublicFormService' @@ -82,18 +78,6 @@ export const usePublicFormMutations = ( }, ) - const submitStorageModeFormMutation = useMutation( - (args: Omit) => { - return submitStorageModeForm({ ...args, formId }) - }, - ) - - const submitStorageModeClearFormMutation = useMutation( - (args: Omit) => { - return submitStorageModeClearForm({ ...args, formId }) - }, - ) - // TODO (#5826): Fallback mutation using Fetch. Remove once network error is resolved const submitEmailModeFormFetchMutation = useMutation( (args: Omit) => { @@ -101,15 +85,73 @@ export const usePublicFormMutations = ( }, ) - const submitStorageModeFormFetchMutation = useMutation( - (args: Omit) => { - return submitStorageModeFormWithFetch({ ...args, formId }) - }, - ) + const submitStorageModeFormWithVirusScanningFetchMutation = useMutation( + async (args: Omit) => { + const attachmentSizes = await getAttachmentSizes(args) + // If there are no attachments, submit form without virus scanning by passing in empty list + if (attachmentSizes.length === 0) { + return submitStorageModeFormWithVirusScanningWithFetch({ + ...args, + fieldIdToQuarantineKeyMap: [], + formId, + }) + } + + // Step 1: Get presigned post data for all attachment fields + return ( + getAttachmentPresignedPostData({ ...args, formId, attachmentSizes }) + .then( + // Step 2: Upload attachments to quarantine bucket asynchronously + (fieldToPresignedPostDataMap) => + Promise.all( + fieldToPresignedPostDataMap.map( + async (fieldToPresignedPostData) => { + const attachmentFile = + args.formInputs[fieldToPresignedPostData.id] - const submitStorageModeClearFormFetchMutation = useMutation( - (args: Omit) => { - return submitStorageModeClearFormWithFetch({ ...args, formId }) + // Check if response is a File object (from an attachment field) + if (!(attachmentFile instanceof File)) + throw new Error('Field is not attachment') + + const uploadResponse = await uploadAttachmentToQuarantine( + fieldToPresignedPostData.presignedPostData, + attachmentFile, + ) + + // If status code is not 200-299, throw error + if ( + uploadResponse.status < 200 || + uploadResponse.status > 299 + ) + throw new Error( + `Attachment upload failed - ${uploadResponse.statusText}`, + ) + + const quarantineBucketKey = + fieldToPresignedPostData.presignedPostData.fields.key + + if (!quarantineBucketKey) + throw new Error( + 'key is not defined in presigned post data', + ) + + return { + fieldId: fieldToPresignedPostData.id, + quarantineBucketKey, + } as FieldIdToQuarantineKeyType + }, + ), + ), + ) + // Step 3: Submit form with keys to quarantine bucket attachments + .then((fieldIdToQuarantineKeyMap) => { + return submitStorageModeFormWithVirusScanningWithFetch({ + ...args, + fieldIdToQuarantineKeyMap, + formId, + }) + }) + ) }, ) @@ -123,12 +165,12 @@ export const usePublicFormMutations = ( }, ) - const submitStorageModeClearFormWithVirusScanningMutation = useMutation( + const submitStorageModeFormWithVirusScanningMutation = useMutation( async (args: Omit) => { const attachmentSizes = await getAttachmentSizes(args) // If there are no attachments, submit form without virus scanning by passing in empty list if (attachmentSizes.length === 0) { - return submitStorageModeClearFormWithVirusScanning({ + return submitStorageModeFormWithVirusScanning({ ...args, fieldIdToQuarantineKeyMap: [], formId, @@ -182,7 +224,7 @@ export const usePublicFormMutations = ( ) // Step 3: Submit form with keys to quarantine bucket attachments .then((fieldIdToQuarantineKeyMap) => { - return submitStorageModeClearFormWithVirusScanning({ + return submitStorageModeFormWithVirusScanning({ ...args, fieldIdToQuarantineKeyMap, formId, @@ -194,13 +236,10 @@ export const usePublicFormMutations = ( return { submitEmailModeFormMutation, - submitStorageModeFormMutation, submitFormFeedbackMutation, - submitStorageModeFormFetchMutation, submitEmailModeFormFetchMutation, - submitStorageModeClearFormMutation, - submitStorageModeClearFormFetchMutation, - submitStorageModeClearFormWithVirusScanningMutation, + submitStorageModeFormWithVirusScanningFetchMutation, + submitStorageModeFormWithVirusScanningMutation, } } diff --git a/shared/constants/form.ts b/shared/constants/form.ts index a6a26085e1..6b355aa7b6 100644 --- a/shared/constants/form.ts +++ b/shared/constants/form.ts @@ -68,5 +68,4 @@ export const PAYMENT_VARIABLE_INPUT_AMOUNT_FIELD_ID = export const E2EE_SUBMISSION_VERSION = 1 // Encryption boundary shift RFC: https://docs.google.com/document/d/1VmNXS_xYY2Yg30AwVqzdndBp5yRJGSDsyjBnH51ktyc/edit?usp=sharing // Encryption boundary shift implementation PR: https://github.com/opengovsg/FormSG/pull/6587 -export const ENCRYPTION_BOUNDARY_SHIFT_SUBMISSION_VERSION = 2 export const VIRUS_SCANNER_SUBMISSION_VERSION = 2.1 From 432ebdfccf33c1402d5cbc9f32177a8be8c1656b Mon Sep 17 00:00:00 2001 From: wanlingt <56983748+wanlingt@users.noreply.github.com> Date: Wed, 15 Nov 2023 14:47:31 +0800 Subject: [PATCH 06/12] feat: announcement modal and what's new for myinfo storage-mode (#6892) * feat: add myinfo storage mode announcement * feat: add graphic and amend copy * fix: update copy --- frontend/src/constants/localStorage.ts | 2 +- .../components/AnnouncementsFeatureList.tsx | 11 + .../features/whats-new/FeatureUpdateList.ts | 11 + .../whats-new/assets/6-myinfo-storage.svg | 229 ++++++++++++++++++ 4 files changed, 252 insertions(+), 1 deletion(-) create mode 100644 frontend/src/features/whats-new/assets/6-myinfo-storage.svg diff --git a/frontend/src/constants/localStorage.ts b/frontend/src/constants/localStorage.ts index 3af13425f8..126ff0ecb5 100644 --- a/frontend/src/constants/localStorage.ts +++ b/frontend/src/constants/localStorage.ts @@ -17,7 +17,7 @@ export const LOCAL_STORAGE_EVENT = 'local-storage' * Key to store whether a user has seen the rollout announcements before. */ export const ROLLOUT_ANNOUNCEMENT_KEY_PREFIX = - 'has-seen-rollout-announcement-20231026-' + 'has-seen-rollout-announcement-20231116-' /** * Key to store whether the admin has seen the feature tour in localStorage. diff --git a/frontend/src/features/rollout-announcement/components/AnnouncementsFeatureList.tsx b/frontend/src/features/rollout-announcement/components/AnnouncementsFeatureList.tsx index f59e440a4f..5800d0e472 100644 --- a/frontend/src/features/rollout-announcement/components/AnnouncementsFeatureList.tsx +++ b/frontend/src/features/rollout-announcement/components/AnnouncementsFeatureList.tsx @@ -2,6 +2,7 @@ import { GUIDE_PAYMENTS_ENTRY } from '~constants/links' import { FeatureUpdateImage } from '~features/whats-new/FeatureUpdateList' +import myInfoStorageMode from '../../whats-new/assets/6-myinfo-storage.svg' import foldersDashboard from '../../whats-new/assets/folders_dashboard.svg' import PaymentsAnnouncementGraphic from '../assets/payments_announcement.svg' @@ -14,6 +15,16 @@ export interface NewFeature { // When updating this, remember to update the ROLLOUT_ANNOUNCEMENT_KEY_PREFIX with the new date // so admins will see new announcements. export const NEW_FEATURES: NewFeature[] = [ + { + // Announcement date: 2023-11-16 + title: 'Myinfo fields for Storage mode forms', + description: + 'Get verified data from respondents by adding Myinfo fields to your Storage mode form. To enable Myinfo fields, select one of our Myinfo-enabled authentication options in your form’s settings.', + image: { + url: myInfoStorageMode, + alt: 'Myinfo fields for Storage mode forms', + }, + }, { // Announcement date: 2023-10-31 title: 'Introducing Folders!', diff --git a/frontend/src/features/whats-new/FeatureUpdateList.ts b/frontend/src/features/whats-new/FeatureUpdateList.ts index 673801d6b4..60de1adb92 100644 --- a/frontend/src/features/whats-new/FeatureUpdateList.ts +++ b/frontend/src/features/whats-new/FeatureUpdateList.ts @@ -6,6 +6,7 @@ import { GUIDE_PAYMENTS_ENTRY } from '~constants/links' import Animation2 from './assets/2-payments.json' import Animation3 from './assets/3-search-and-filter.json' import Animation4 from './assets/4-dnd.json' +import MyInfoStorageMode from './assets/6-myinfo-storage.svg' import foldersDashboard from './assets/folders_dashboard.svg' // image can either be a static image (using url) or an animation (using animationData) @@ -34,6 +35,16 @@ export const FEATURE_UPDATE_LIST: FeatureUpdateList = { // Update version whenever a new feature is added. version: 4, features: [ + { + title: 'Myinfo fields for Storage mode forms', + date: new Date('16 Nov 2023 GMT+8'), + description: + 'Get verified data from respondents by adding Myinfo fields to your Storage mode form. To enable Myinfo fields, select one of our Myinfo-enabled authentication options in your form’s settings.', + image: { + url: MyInfoStorageMode, + alt: 'Myinfo fields for Storage mode forms', + }, + }, { title: 'Introducing Folders!', date: new Date('31 Oct 2023 GMT+8'), diff --git a/frontend/src/features/whats-new/assets/6-myinfo-storage.svg b/frontend/src/features/whats-new/assets/6-myinfo-storage.svg new file mode 100644 index 0000000000..3ba311231c --- /dev/null +++ b/frontend/src/features/whats-new/assets/6-myinfo-storage.svg @@ -0,0 +1,229 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 3e8b06ab542776ec9503746af1b825cf4b7eeb0a Mon Sep 17 00:00:00 2001 From: wanlingt <56983748+wanlingt@users.noreply.github.com> Date: Wed, 15 Nov 2023 15:10:31 +0800 Subject: [PATCH 07/12] feat: myinfo for storage-mode (#6870) * feat: allow myinfo field to be added to storage-mode form * feat: remove FormAuthType.MyInfo check from submitEncryptModeForm * fix: add MyInfo case for authType validator * fix: use set instead of validator for form.server.model * feat: validate myinfo response in backend * fix: clean up imports and comments * fix: refine error * test: remove MyInfo test * test: add myinfo e2e test * feat: add sgid myinfo * ref: refactor authtypes * fix: remove console.log * feat: allow storage mode in myinfo form duplication * ref: remove unused declaration * feat: process each child subfield as a new field * feat: rename session cookie to formsg.connect.sid in local dev * feat: remove containsMyInfoFields from create and dupe form wizards * ref: use common FORM_AUTHTYPES for email and storage mode forms * fix: use triple = for authType check * feat: remove check for authType in validation * test: add MyInfo and SGID MyInfo test cases * fix: use ADMIN_LOGIN_SESSION_COOKIE_NAME constant for tests * ref: extract common functions getMyInfoPrefix and getAnswersForChild to shared submission.utils * fix: error handling in validateStorageSubmission * fix: clean up constants and unused imports --- __tests__/e2e/encrypt-submission.spec.ts | 37 ++++++++- __tests__/integration/helpers/express-auth.ts | 5 +- .../integration/helpers/express-setup.ts | 2 +- .../field-panels/MyInfoPanel.tsx | 23 +----- .../AuthSettingsSection.tsx | 23 +++--- .../AuthSettingsSection/constants.ts | 25 ++---- .../UseTemplateWizardProvider.tsx | 20 +---- .../CreateFormDetailsScreen.tsx | 15 +--- .../FormResponseOptions.tsx | 4 +- .../CreateFormWizardContext.tsx | 1 - .../CreateFormWizardProvider.tsx | 2 - .../DupeFormWizardProvider.tsx | 13 +--- shared/types/response.ts | 11 +++ src/app/loaders/express/session.ts | 7 +- .../__tests__/form.server.model.spec.ts | 33 +++++--- src/app/models/field/baseField.ts | 13 +--- src/app/models/form.server.model.ts | 20 +---- src/app/modules/auth/auth.controller.ts | 3 +- .../email-submission.types.ts | 5 ++ .../email-submission/email-submission.util.ts | 65 +--------------- .../encrypt-submission.controller.ts | 59 ++++++++++---- .../encrypt-submission.middleware.ts | 76 ++++++++++++++++++- .../encrypt-submission.utils.ts | 28 +++++++ .../modules/submission/submission.utils.ts | 73 +++++++++++++++++- .../verified-content.service.ts | 2 + .../verified-content.types.ts | 7 +- .../api/v3/auth/__tests__/auth.routes.spec.ts | 11 +-- 27 files changed, 348 insertions(+), 235 deletions(-) diff --git a/__tests__/e2e/encrypt-submission.spec.ts b/__tests__/e2e/encrypt-submission.spec.ts index 4d42ce557d..8b6a65fadc 100644 --- a/__tests__/e2e/encrypt-submission.spec.ts +++ b/__tests__/e2e/encrypt-submission.spec.ts @@ -1,6 +1,11 @@ import mongoose from 'mongoose' import { featureFlags } from 'shared/constants/feature-flags' -import { BasicField, FormResponseMode } from 'shared/types' +import { + BasicField, + FormAuthType, + FormResponseMode, + MyInfoAttribute, +} from 'shared/types' import { IFeatureFlagModel, IFormModel } from 'src/types' @@ -19,6 +24,7 @@ import { } from './helpers' import { createBlankVersion, + createMyInfoField, createOptionalVersion, deleteDocById, getSettings, @@ -143,4 +149,33 @@ test.describe('Storage form submission', () => { ) await deleteDocById(Form, form._id) }) + + test('Create and submit storage mode form with MyInfo fields', async ({ + page, + }) => { + // Define + const formFields = [ + // Short answer + createMyInfoField(MyInfoAttribute.Name, 'LIM YONG XIANG', true), + // Dropdown + createMyInfoField(MyInfoAttribute.Sex, 'MALE', true), + // Date + createMyInfoField(MyInfoAttribute.DateOfBirth, '06/10/1980', true), + // Mobile + createMyInfoField(MyInfoAttribute.MobileNo, '97399245', false), + // Unverified + createMyInfoField(MyInfoAttribute.WorkpassStatus, 'Live', false), + ] + const formLogics = NO_LOGIC + const formSettings = getSettings({ + authType: FormAuthType.MyInfo, + }) + + // Test + await runEncryptSubmissionTest(page, Form, { + formFields, + formLogics, + formSettings, + }) + }) }) diff --git a/__tests__/integration/helpers/express-auth.ts b/__tests__/integration/helpers/express-auth.ts index d8545efa48..14b4fb27ee 100644 --- a/__tests__/integration/helpers/express-auth.ts +++ b/__tests__/integration/helpers/express-auth.ts @@ -6,6 +6,7 @@ import * as OtpUtils from 'src/app/utils/otp' const MOCK_VALID_OTP = '123456' const MOCK_OTP_PREFIX = 'ABC' +const ADMIN_LOGIN_SESSION_COOKIE_NAME = 'formsg.connect.sid' /** * Integration test helper to create an authenticated session where the user @@ -50,7 +51,7 @@ export const createAuthedSession = async ( // Assert // Should have session cookie returned. const sessionCookie = request.cookies.find( - (cookie) => cookie.name === 'connect.sid', + (cookie) => cookie.name === ADMIN_LOGIN_SESSION_COOKIE_NAME, ) expect(sessionCookie).toBeDefined() @@ -68,7 +69,7 @@ export const logoutSession = async (request: Session): Promise => { expect(response.status).toEqual(200) const sessionCookie = request.cookies.find( - (cookie) => cookie.name === 'connect.sid', + (cookie) => cookie.name === ADMIN_LOGIN_SESSION_COOKIE_NAME, ) expect(sessionCookie).not.toBeDefined() diff --git a/__tests__/integration/helpers/express-setup.ts b/__tests__/integration/helpers/express-setup.ts index 106b393a2c..6b2afebe97 100644 --- a/__tests__/integration/helpers/express-setup.ts +++ b/__tests__/integration/helpers/express-setup.ts @@ -17,7 +17,7 @@ const testSessionMiddlewares = () => { saveUninitialized: false, resave: false, secret: 'test-session-secret', - name: 'connect.sid', + name: 'formsg.connect.sid', store: new session.MemoryStore(), }) diff --git a/frontend/src/features/admin-form/create/builder-and-design/BuilderAndDesignDrawer/FieldListDrawer/field-panels/MyInfoPanel.tsx b/frontend/src/features/admin-form/create/builder-and-design/BuilderAndDesignDrawer/FieldListDrawer/field-panels/MyInfoPanel.tsx index 60d36bde4c..5d728ddc30 100644 --- a/frontend/src/features/admin-form/create/builder-and-design/BuilderAndDesignDrawer/FieldListDrawer/field-panels/MyInfoPanel.tsx +++ b/frontend/src/features/admin-form/create/builder-and-design/BuilderAndDesignDrawer/FieldListDrawer/field-panels/MyInfoPanel.tsx @@ -5,12 +5,7 @@ import { Box, Text } from '@chakra-ui/react' import { useFeatureIsOn, useGrowthBook } from '@growthbook/growthbook-react' import { featureFlags } from '~shared/constants' -import { - AdminFormDto, - FormAuthType, - FormResponseMode, - MyInfoAttribute, -} from '~shared/types' +import { AdminFormDto, FormAuthType, MyInfoAttribute } from '~shared/types' import { GUIDE_EMAIL_MODE } from '~constants/links' import { ADMINFORM_SETTINGS_SINGPASS_SUBROUTE } from '~constants/routes' @@ -113,14 +108,12 @@ export const MyInfoFieldPanel = () => { ) // myInfo should be disabled if - // 1. form response mode is not email mode - // 2. form auth type is not myInfo - // 3. # of myInfo fields >= 30 + // 1. form auth type is not myInfo + // 2. # of myInfo fields >= 30 const isMyInfoDisabled = useMemo( () => form ? form.form_fields.filter(isMyInfo).length >= 30 || - form.responseMode !== FormResponseMode.Email || (form.authType !== FormAuthType.MyInfo && form.authType !== FormAuthType.SGID_MyInfo) : true, @@ -232,14 +225,10 @@ export const MyInfoFieldPanel = () => { ) } -type MyInfoTextProps = Pick< - AdminFormDto, - 'authType' | 'responseMode' | 'form_fields' -> +type MyInfoTextProps = Pick const MyInfoText = ({ authType, - responseMode, form_fields, }: MyInfoTextProps): JSX.Element => { const isMyInfoDisabled = @@ -249,10 +238,6 @@ const MyInfoText = ({ [form_fields], ) - if (responseMode !== FormResponseMode.Email) { - return MyInfo fields are not available in Storage mode forms. - } - if (isMyInfoDisabled) { return ( diff --git a/frontend/src/features/admin-form/settings/components/AuthSettingsSection/AuthSettingsSection.tsx b/frontend/src/features/admin-form/settings/components/AuthSettingsSection/AuthSettingsSection.tsx index f824086b98..6cb0e880d3 100644 --- a/frontend/src/features/admin-form/settings/components/AuthSettingsSection/AuthSettingsSection.tsx +++ b/frontend/src/features/admin-form/settings/components/AuthSettingsSection/AuthSettingsSection.tsx @@ -21,7 +21,7 @@ import { isMyInfo } from '~features/myinfo/utils' import { useMutateFormSettings } from '../../mutations' -import { AUTHTYPE_TO_TEXT, STORAGE_MODE_AUTHTYPES } from './constants' +import { FORM_AUTHTYPES } from './constants' import { EsrvcIdBox } from './EsrvcIdBox' const esrvcidRequired = (authType: FormAuthType) => { @@ -42,13 +42,11 @@ interface AuthSettingsSectionProps { export const AuthSettingsSectionSkeleton = (): JSX.Element => { return ( - {Object.entries(STORAGE_MODE_AUTHTYPES).map( - ([authType, textToRender]) => ( - - {textToRender} - - ), - )} + {Object.entries(FORM_AUTHTYPES).map(([authType, textToRender]) => ( + + {textToRender} + + ))} ) } @@ -116,12 +114,9 @@ export const AuthSettingsSection = ({ [isDisabled, mutateFormAuthType, settings.authType], ) - const radioOptions: [FormAuthType, string][] = useMemo(() => { - return Object.entries(AUTHTYPE_TO_TEXT[settings.responseMode]) as [ - FormAuthType, - string, - ][] - }, [settings.responseMode]) + const radioOptions: [FormAuthType, string][] = Object.entries( + FORM_AUTHTYPES, + ) as [FormAuthType, string][] return ( diff --git a/frontend/src/features/admin-form/settings/components/AuthSettingsSection/constants.ts b/frontend/src/features/admin-form/settings/components/AuthSettingsSection/constants.ts index 6a4357d815..01a6e5837e 100644 --- a/frontend/src/features/admin-form/settings/components/AuthSettingsSection/constants.ts +++ b/frontend/src/features/admin-form/settings/components/AuthSettingsSection/constants.ts @@ -1,21 +1,12 @@ -import { FormAuthType, FormResponseMode } from '~shared/types/form' +import { FormAuthType } from '~shared/types/form' export type EmailFormAuthType = FormAuthType -export type StorageFormAuthType = - | FormAuthType.NIL - | FormAuthType.SP - | FormAuthType.CP - | FormAuthType.SGID +export type StorageFormAuthType = FormAuthType -export const STORAGE_MODE_AUTHTYPES: Record = { - [FormAuthType.NIL]: 'None', - [FormAuthType.SGID]: 'Singpass App-only Login', - [FormAuthType.SP]: 'Singpass', - [FormAuthType.CP]: 'Singpass (Corporate)', -} - -// Not using STORAGE_MODE_AUTHTYPES due to wanting a different order. -export const EMAIL_MODE_AUTHTYPES: Record = { +export const FORM_AUTHTYPES: Record< + StorageFormAuthType | EmailFormAuthType, + string +> = { [FormAuthType.NIL]: 'None', [FormAuthType.SGID]: 'Singpass App-only Login', [FormAuthType.SGID_MyInfo]: 'Singpass App-only with Myinfo', @@ -23,7 +14,3 @@ export const EMAIL_MODE_AUTHTYPES: Record = { [FormAuthType.MyInfo]: 'Singpass with Myinfo', [FormAuthType.CP]: 'Singpass (Corporate)', } -export const AUTHTYPE_TO_TEXT = { - [FormResponseMode.Email]: EMAIL_MODE_AUTHTYPES, - [FormResponseMode.Encrypt]: STORAGE_MODE_AUTHTYPES, -} diff --git a/frontend/src/features/admin-form/template/UseTemplateModal/UseTemplateWizardProvider.tsx b/frontend/src/features/admin-form/template/UseTemplateModal/UseTemplateWizardProvider.tsx index a4f3b1f0ea..b39b8fc4d6 100644 --- a/frontend/src/features/admin-form/template/UseTemplateModal/UseTemplateWizardProvider.tsx +++ b/frontend/src/features/admin-form/template/UseTemplateModal/UseTemplateWizardProvider.tsx @@ -1,9 +1,8 @@ -import { useEffect, useMemo } from 'react' +import { useEffect } from 'react' import { FormResponseMode } from '~shared/types' import { useFormTemplate } from '~features/admin-form/common/queries' -import { isMyInfo } from '~features/myinfo/utils' import { CreateFormFlowStates, CreateFormWizardContext, @@ -23,11 +22,6 @@ export const useUseTemplateWizardContext = ( /* enabled= */ !!formId, ) - const containsMyInfoFields = useMemo( - () => !!templateFormData?.form.form_fields.find((ff) => isMyInfo(ff)), - [templateFormData?.form.form_fields], - ) - const { formMethods, currentStep, direction, keypair, setCurrentStep } = useCommonFormWizardProvider() @@ -41,18 +35,9 @@ export const useUseTemplateWizardContext = ( reset({ ...getValues(), - responseMode: containsMyInfoFields - ? FormResponseMode.Email - : FormResponseMode.Encrypt, title: `[Template] ${templateFormData?.form.title}`, }) - }, [ - reset, - getValues, - containsMyInfoFields, - isTemplateFormLoading, - templateFormData?.form.title, - ]) + }, [reset, getValues, isTemplateFormLoading, templateFormData?.form.title]) const { handleSubmit } = formMethods @@ -98,7 +83,6 @@ export const useUseTemplateWizardContext = ( formMethods, handleDetailsSubmit, handleCreateStorageModeForm, - containsMyInfoFields, modalHeader: 'Duplicate form', } } diff --git a/frontend/src/features/workspace/components/CreateFormModal/CreateFormModalContent/CreateFormDetailsScreen.tsx b/frontend/src/features/workspace/components/CreateFormModal/CreateFormModalContent/CreateFormDetailsScreen.tsx index 9a5800bf43..f581e3a42c 100644 --- a/frontend/src/features/workspace/components/CreateFormModal/CreateFormModalContent/CreateFormDetailsScreen.tsx +++ b/frontend/src/features/workspace/components/CreateFormModal/CreateFormModalContent/CreateFormDetailsScreen.tsx @@ -17,7 +17,6 @@ import Button from '~components/Button' import FormErrorMessage from '~components/FormControl/FormErrorMessage' import FormFieldMessage from '~components/FormControl/FormFieldMessage' import FormLabel from '~components/FormControl/FormLabel' -import InlineMessage from '~components/InlineMessage' import Input from '~components/Input' import { useCreateFormWizard } from '../CreateFormWizardContext' @@ -35,7 +34,6 @@ export const CreateFormDetailsScreen = (): JSX.Element => { isLoading, isFetching, modalHeader, - containsMyInfoFields, } = useCreateFormWizard() const { register, @@ -79,23 +77,12 @@ export const CreateFormDetailsScreen = (): JSX.Element => { ( - - )} + render={({ field }) => } rules={{ required: 'Please select a form response mode' }} /> {errors.responseMode?.message} - {containsMyInfoFields && ( - - {`This form contains MyInfo fields. Only **Email** mode is supported at - this point.`} - - )} {responseModeValue === FormResponseMode.Email && ( void value: FormResponseMode } @@ -29,7 +28,7 @@ const OptionDescription = ({ listItems = [] }: { listItems: string[] }) => { export const FormResponseOptions = forwardRef< FormResponseOptionsProps, 'button' ->(({ value, onChange, containsMyInfoFields }, ref) => { +>(({ value, onChange }, ref) => { return ( Recommended} isActive={value === FormResponseMode.Encrypt} - isDisabled={containsMyInfoFields} onClick={() => onChange(FormResponseMode.Encrypt)} isFullWidth flex={1} diff --git a/frontend/src/features/workspace/components/CreateFormModal/CreateFormWizardContext.tsx b/frontend/src/features/workspace/components/CreateFormModal/CreateFormWizardContext.tsx index 1af1f663ed..11284dc68c 100644 --- a/frontend/src/features/workspace/components/CreateFormModal/CreateFormWizardContext.tsx +++ b/frontend/src/features/workspace/components/CreateFormModal/CreateFormWizardContext.tsx @@ -34,7 +34,6 @@ export type CreateFormWizardContextReturn = { isFetching: boolean isLoading: boolean modalHeader: string - containsMyInfoFields: boolean } export const CreateFormWizardContext = createContext< diff --git a/frontend/src/features/workspace/components/CreateFormModal/CreateFormWizardProvider.tsx b/frontend/src/features/workspace/components/CreateFormModal/CreateFormWizardProvider.tsx index 3b985d4a6e..bfe69cd1ea 100644 --- a/frontend/src/features/workspace/components/CreateFormModal/CreateFormWizardProvider.tsx +++ b/frontend/src/features/workspace/components/CreateFormModal/CreateFormWizardProvider.tsx @@ -105,8 +105,6 @@ const useCreateFormWizardContext = (): CreateFormWizardContextReturn => { handleDetailsSubmit, handleCreateStorageModeForm, modalHeader: 'Set up your form', - // Creation will never contain any fields. - containsMyInfoFields: false, } } diff --git a/frontend/src/features/workspace/components/DuplicateFormModal/DupeFormWizardProvider.tsx b/frontend/src/features/workspace/components/DuplicateFormModal/DupeFormWizardProvider.tsx index 5dd7d23be7..31d4cab2bb 100644 --- a/frontend/src/features/workspace/components/DuplicateFormModal/DupeFormWizardProvider.tsx +++ b/frontend/src/features/workspace/components/DuplicateFormModal/DupeFormWizardProvider.tsx @@ -1,9 +1,8 @@ -import { useEffect, useMemo } from 'react' +import { useEffect } from 'react' import { FormResponseMode } from '~shared/types' import { usePreviewForm } from '~features/admin-form/common/queries' -import { isMyInfo } from '~features/myinfo/utils' import { useDuplicateFormMutations } from '~features/workspace/mutations' import { useDashboard } from '~features/workspace/queries' import { makeDuplicateFormTitle } from '~features/workspace/utils/createDuplicateFormTitle' @@ -27,11 +26,6 @@ export const useDupeFormWizardContext = (): CreateFormWizardContextReturn => { /* enabled= */ !!activeFormMeta, ) - const containsMyInfoFields = useMemo( - () => !!previewFormData?.form.form_fields.find((ff) => isMyInfo(ff)), - [previewFormData?.form.form_fields], - ) - const { formMethods, currentStep, direction, keypair, setCurrentStep } = useCommonFormWizardProvider() @@ -50,9 +44,6 @@ export const useDupeFormWizardContext = (): CreateFormWizardContextReturn => { reset({ ...getValues(), - responseMode: containsMyInfoFields - ? FormResponseMode.Email - : FormResponseMode.Encrypt, title: makeDuplicateFormTitle(previewFormData.form.title, dashboardForms), }) }, [ @@ -62,7 +53,6 @@ export const useDupeFormWizardContext = (): CreateFormWizardContextReturn => { isPreviewFormLoading, isWorkspaceLoading, dashboardForms, - containsMyInfoFields, ]) const { handleSubmit } = formMethods @@ -116,7 +106,6 @@ export const useDupeFormWizardContext = (): CreateFormWizardContextReturn => { formMethods, handleDetailsSubmit, handleCreateStorageModeForm, - containsMyInfoFields, modalHeader: 'Duplicate form', } } diff --git a/shared/types/response.ts b/shared/types/response.ts index c42bd29e0a..de3ab7ad59 100644 --- a/shared/types/response.ts +++ b/shared/types/response.ts @@ -144,6 +144,16 @@ export type ChildBirthRecordsResponse = z.infer< typeof ChildBirthRecordsResponse > +// These fieldTypes are used for the child fields in MYINFO_ATTRIBUTE_MAP +export const SingleChildSubRecordResponse = MyInfoableSingleResponse.extend({ + fieldType: z.literal( + BasicField.Children || BasicField.ShortText || BasicField.Dropdown, + ), +}) +export type SingleChildSubRecordResponse = z.infer< + typeof SingleChildSubRecordResponse +> + export type FieldResponse = | HeaderResponse | EmailResponse @@ -165,3 +175,4 @@ export type FieldResponse = | TableResponse | UenResponse | ChildBirthRecordsResponse + | SingleChildSubRecordResponse diff --git a/src/app/loaders/express/session.ts b/src/app/loaders/express/session.ts index 707c3a199b..dd9eccb36b 100644 --- a/src/app/loaders/express/session.ts +++ b/src/app/loaders/express/session.ts @@ -6,6 +6,10 @@ import { Connection } from 'mongoose' import config from '../../config/config' +export const ADMIN_LOGIN_SESSION_COOKIE_NAME = config.isDev + ? 'formsg.connect.sid' + : 'connect.sid' + const sessionMiddlewares = (connection: Connection): RequestHandler[] => { // Configure express-session and connect to mongo const expressSession = session({ @@ -13,7 +17,8 @@ const sessionMiddlewares = (connection: Connection): RequestHandler[] => { resave: false, secret: config.sessionSecret, cookie: config.cookieSettings, - name: 'connect.sid', + // TODO: FRM-1512: Standardise cookie name across environments + name: ADMIN_LOGIN_SESSION_COOKIE_NAME, store: MongoStore.create({ client: connection.getClient(), }), diff --git a/src/app/models/__tests__/form.server.model.spec.ts b/src/app/models/__tests__/form.server.model.spec.ts index bd2e6bfe0c..4760c83612 100644 --- a/src/app/models/__tests__/form.server.model.spec.ts +++ b/src/app/models/__tests__/form.server.model.spec.ts @@ -694,31 +694,46 @@ describe('Form Model', () => { ) }) - it('should set authType to NIL when given authType is MyInfo', async () => { + // Ensure that encrypted sgID forms can be created since they could not before + it('should set authType to SGID when given authType is SGID', async () => { // Arrange - const malformedParams = merge({}, MOCK_ENCRYPTED_FORM_PARAMS, { + const encryptFormParams = merge({}, MOCK_ENCRYPTED_FORM_PARAMS, { + authType: FormAuthType.SGID, + }) + + // Act + const sgidForm = await EncryptedForm.create(encryptFormParams) + + // Assert + await expect(sgidForm.authType).toBe(FormAuthType.SGID) + }) + + // Ensure that encrypted MyInfo forms can be created since they could not before + it('should set authType to MyInfo when given authType is MyInfo', async () => { + // Arrange + const encryptFormParams = merge({}, MOCK_ENCRYPTED_FORM_PARAMS, { authType: FormAuthType.MyInfo, }) // Act - const invalidForm = await EncryptedForm.create(malformedParams) + const myInfoForm = await EncryptedForm.create(encryptFormParams) // Assert - await expect(invalidForm.authType).toBe(FormAuthType.NIL) + await expect(myInfoForm.authType).toBe(FormAuthType.MyInfo) }) - // Ensure that encrypted sgID forms can be created since they could not before - it('should set authType to SGID when given authType is SGID', async () => { + // Ensure that encrypted SGID MyInfo forms can be created since they could not before + it('should set authType to SGID MyInfo when given authType is SGID MyInfo', async () => { // Arrange const encryptFormParams = merge({}, MOCK_ENCRYPTED_FORM_PARAMS, { - authType: FormAuthType.SGID, + authType: FormAuthType.SGID_MyInfo, }) // Act - const sgidForm = await EncryptedForm.create(encryptFormParams) + const sgidMyInfoForm = await EncryptedForm.create(encryptFormParams) // Assert - await expect(sgidForm.authType).toBe(FormAuthType.SGID) + await expect(sgidMyInfoForm.authType).toBe(FormAuthType.SGID_MyInfo) }) it('should save with default payments settings', async () => { diff --git a/src/app/models/field/baseField.ts b/src/app/models/field/baseField.ts index 524f651135..444d6e8a81 100644 --- a/src/app/models/field/baseField.ts +++ b/src/app/models/field/baseField.ts @@ -1,11 +1,7 @@ import { Schema } from 'mongoose' import UIDGenerator from 'uid-generator' -import { - BasicField, - FormResponseMode, - MyInfoAttribute, -} from '../../../../shared/types' +import { BasicField, MyInfoAttribute } from '../../../../shared/types' import { IFieldSchema, IMyInfoSchema, ITableFieldSchema } from '../../../types' const uidgen3 = new UIDGenerator(256, UIDGenerator.BASE62) @@ -62,13 +58,6 @@ BaseFieldSchema.pre('validate', function (next) { return next(Error('Field type is incorrect or unspecified')) } - // Prevent MyInfo fields from being set in encrypt mode. - if (this.parent().responseMode === FormResponseMode.Encrypt) { - if (this.myInfo?.attr) { - return next(Error('MyInfo fields are not allowed for storage mode forms')) - } - } - // No errors. return next() }) diff --git a/src/app/models/form.server.model.ts b/src/app/models/form.server.model.ts index 0f4f887fa0..4526073c4c 100644 --- a/src/app/models/form.server.model.ts +++ b/src/app/models/form.server.model.ts @@ -311,13 +311,12 @@ const compileFormModel = (db: Mongoose): IFormModel => { return ( myInfoFieldCount === 0 || ((this.authType === FormAuthType.MyInfo || - this.authType == FormAuthType.SGID_MyInfo) && - this.responseMode === FormResponseMode.Email && + this.authType === FormAuthType.SGID_MyInfo) && myInfoFieldCount <= 30) ) }, message: - 'Check that your form is MyInfo-authenticated, is an email mode form and has 30 or fewer MyInfo fields.', + 'Check that your form is MyInfo-authenticated and has 30 or fewer MyInfo fields.', }, }, form_logics: { @@ -457,21 +456,6 @@ const compileFormModel = (db: Mongoose): IFormModel => { // Do not allow authType to be changed if form is published if (this.authType !== v && this.status === FormStatus.Public) { return this.authType - // Singpass/Corppass/SGID authentication is available for both email - // and storage mode - // Important - this case must come before the MyInfo + storage - // mode case, or else we may accidentally set Singpass/Corppass/SGID - // storage mode forms to FormAuthType.NIL - } else if ( - [FormAuthType.SP, FormAuthType.CP, FormAuthType.SGID].includes(v) - ) { - return v - } else if ( - this.responseMode === FormResponseMode.Encrypt && - // MyInfo is not available for storage mode - (v === FormAuthType.MyInfo || v === FormAuthType.SGID_MyInfo) - ) { - return FormAuthType.NIL } else { return v } diff --git a/src/app/modules/auth/auth.controller.ts b/src/app/modules/auth/auth.controller.ts index fb51f93fbd..edc996b9bd 100644 --- a/src/app/modules/auth/auth.controller.ts +++ b/src/app/modules/auth/auth.controller.ts @@ -4,6 +4,7 @@ import { SendOtpResponseDto } from 'shared/types/user' import { SUPPORT_FORM_LINK } from '../../../../shared/constants/links' import { createLoggerWithLabel } from '../../config/logger' +import { ADMIN_LOGIN_SESSION_COOKIE_NAME } from '../../loaders/express/session' import MailService from '../../services/mail/mail.service' import { createReqMeta, getRequestIp } from '../../utils/request' import { ControllerHandler } from '../core/core.types' @@ -256,7 +257,7 @@ export const handleSignout: ControllerHandler = async (req, res) => { } // No error. - res.clearCookie('connect.sid') + res.clearCookie(ADMIN_LOGIN_SESSION_COOKIE_NAME) return res.status(StatusCodes.OK).json({ message: 'Sign out successful' }) }) } diff --git a/src/app/modules/submission/email-submission/email-submission.types.ts b/src/app/modules/submission/email-submission/email-submission.types.ts index 198334e0f2..eab814517e 100644 --- a/src/app/modules/submission/email-submission/email-submission.types.ts +++ b/src/app/modules/submission/email-submission/email-submission.types.ts @@ -26,3 +26,8 @@ export interface IPopulatedEmailFormWithResponsesAndHash { parsedResponses: ParsedResponsesObject hashedFields?: Set } + +export interface IPopulatedStorageFormWithResponsesAndHash { + parsedResponses: ParsedResponsesObject + hashedFields?: Set +} diff --git a/src/app/modules/submission/email-submission/email-submission.util.ts b/src/app/modules/submission/email-submission/email-submission.util.ts index 5ac3609369..09f5074893 100644 --- a/src/app/modules/submission/email-submission/email-submission.util.ts +++ b/src/app/modules/submission/email-submission/email-submission.util.ts @@ -1,12 +1,7 @@ import { StatusCodes } from 'http-status-codes' import { compact } from 'lodash' -import { MYINFO_ATTRIBUTE_MAP } from '../../../../../shared/constants/field/myinfo' -import { - BasicField, - FormAuthType, - MyInfoAttribute, -} from '../../../../../shared/types' +import { BasicField, FormAuthType } from '../../../../../shared/types' import { EmailAdminDataField, EmailDataCollationToolField, @@ -58,7 +53,6 @@ import { MyInfoMissingLoginCookieError, } from '../../myinfo/myinfo.errors' import { MyInfoKey } from '../../myinfo/myinfo.types' -import { getMyInfoChildHashKey } from '../../myinfo/myinfo.util' import { SgidInvalidJwtError, SgidMissingJwtError, @@ -80,14 +74,13 @@ import { } from '../submission.errors' import { ProcessedCheckboxResponse, - ProcessedChildrenResponse, ProcessedFieldResponse, ProcessedTableResponse, } from '../submission.types' +import { getAnswersForChild, getMyInfoPrefix } from '../submission.utils' import { ATTACHMENT_PREFIX, - MYINFO_PREFIX, TABLE_PREFIX, VERIFIED_PREFIX, } from './email-submission.constants' @@ -96,22 +89,6 @@ import { ResponseFormattedForEmail } from './email-submission.types' const logger = createLoggerWithLabel(module) -/** - * Determines the prefix for a question based on whether it is verified - * by MyInfo. - * @param response - * @param hashedFields Field ids of hashed fields. - * @returns the prefix - */ -const getMyInfoPrefix = ( - response: ResponseFormattedForEmail, - hashedFields: Set, -): string => { - return !!response.myInfo?.attr && hashedFields.has(response._id) - ? MYINFO_PREFIX - : '' -} - /** * Determines the prefix for a question based on whether it was verified * by a user during form submission. @@ -214,44 +191,6 @@ export const getAnswerForCheckbox = ( } } -export const getAnswersForChild = ( - response: ProcessedChildrenResponse, -): ResponseFormattedForEmail[] => { - const subFields = response.childSubFieldsArray - const qnChildIdx = response.childIdx ?? 0 - if (!subFields) { - return [] - } - return response.answerArray.flatMap((arr, childIdx) => { - // First array element is always child name - const childName = arr[0] - return arr.map((answer, idx) => { - const subfield = subFields[idx] - return { - _id: getMyInfoChildHashKey( - response._id, - subFields[idx], - childIdx, - childName, - ), - fieldType: response.fieldType, - // qnChildIdx represents the index of the MyInfo field - // childIdx represents the index of the child in this MyInfo field - // as there might be >1 child for each MyInfo child field if "Add another child" is used - question: `Child ${qnChildIdx + childIdx + 1} ${ - MYINFO_ATTRIBUTE_MAP[subfield].description - }`, - myInfo: { - attr: subFields[idx] as unknown as MyInfoAttribute, - }, - isVisible: response.isVisible, - isUserVerified: response.isUserVerified, - answer, - } - }) - }) -} - /** * Formats the response for sending to the submitter (autoReplyData), * the table that is sent to the admin (formData), diff --git a/src/app/modules/submission/encrypt-submission/encrypt-submission.controller.ts b/src/app/modules/submission/encrypt-submission/encrypt-submission.controller.ts index 1e5c77bdc2..88a8e09ab4 100644 --- a/src/app/modules/submission/encrypt-submission/encrypt-submission.controller.ts +++ b/src/app/modules/submission/encrypt-submission/encrypt-submission.controller.ts @@ -39,11 +39,12 @@ import * as TurnstileMiddleware from '../../../services/turnstile/turnstile.midd import { Pipeline } from '../../../utils/pipeline-middleware' import { createReqMeta } from '../../../utils/request' import { getFormAfterPermissionChecks } from '../../auth/auth.service' -import { MalformedParametersError } from '../../core/core.errors' import { ControllerHandler } from '../../core/core.types' import { setFormTags } from '../../datadog/datadog.utils' import { getFeatureFlag } from '../../feature-flags/feature-flags.service' import { PermissionLevel } from '../../form/admin-form/admin-form.types' +import { MyInfoService } from '../../myinfo/myinfo.service' +import { extractMyInfoLoginJwt } from '../../myinfo/myinfo.util' import { SgidService } from '../../sgid/sgid.service' import { getOidcService } from '../../spcp/spcp.oidc.service' import { getPopulatedUserById } from '../../user/user.service' @@ -146,19 +147,6 @@ const submitEncryptModeForm = async ( let userInfo const { authType } = formDef switch (authType) { - case FormAuthType.MyInfo: { - logger.error({ - message: - 'Storage mode form is not allowed to have MyInfo authorisation', - meta: logMeta, - }) - const { errorMessage, statusCode } = mapRouteError( - new MalformedParametersError( - 'Storage mode form is not allowed to have MyInfo authType', - ), - ) - return res.status(statusCode).json({ message: errorMessage }) - } case FormAuthType.SP: { const oidcService = getOidcService(FormAuthType.SP) const jwtPayloadResult = await oidcService @@ -204,6 +192,45 @@ const submitEncryptModeForm = async ( userInfo = jwtPayloadResult.value.userInfo break } + case FormAuthType.SGID_MyInfo: + case FormAuthType.MyInfo: { + const jwtPayloadResult = await extractMyInfoLoginJwt( + req.cookies, + authType, + ) + .andThen(MyInfoService.verifyLoginJwt) + .map(({ uinFin }) => { + return uinFin + }) + .mapErr((error) => { + logger.error({ + message: `Error verifying MyInfo${ + authType === FormAuthType.SGID_MyInfo ? '(over SGID)' : '' + } hashes`, + meta: logMeta, + error, + }) + return error + }) + if (jwtPayloadResult.isErr()) { + const { statusCode, errorMessage } = mapRouteError( + jwtPayloadResult.error, + ) + logger.error({ + message: `Failed to verify ${ + authType === FormAuthType.SGID_MyInfo ? 'SGID' : 'Singpass' + } JWT with auth client`, + meta: logMeta, + error: jwtPayloadResult.error, + }) + return res.status(statusCode).json({ + message: errorMessage, + spcpSubmissionFailure: true, + }) + } + uinFin = jwtPayloadResult.value + break + } case FormAuthType.SGID: { const jwtPayloadResult = SgidService.extractSgidSingpassJwtPayload( req.cookies.jwtSgid, @@ -232,7 +259,9 @@ const submitEncryptModeForm = async ( if ( form.authType === FormAuthType.SP || form.authType === FormAuthType.CP || - form.authType === FormAuthType.SGID + form.authType === FormAuthType.SGID || + form.authType === FormAuthType.MyInfo || + form.authType === FormAuthType.SGID_MyInfo ) { const encryptVerifiedContentResult = VerifiedContentService.getVerifiedContent({ diff --git a/src/app/modules/submission/encrypt-submission/encrypt-submission.middleware.ts b/src/app/modules/submission/encrypt-submission/encrypt-submission.middleware.ts index 3ae11e7310..c9e2de26c8 100644 --- a/src/app/modules/submission/encrypt-submission/encrypt-submission.middleware.ts +++ b/src/app/modules/submission/encrypt-submission/encrypt-submission.middleware.ts @@ -11,6 +11,7 @@ import { } from '../../../../../shared/constants' import { BasicField, + FormAuthType, StorageModeAttachment, StorageModeAttachmentsMap, } from '../../../../../shared/types' @@ -29,6 +30,9 @@ import { createReqMeta } from '../../../utils/request' import * as FeatureFlagService from '../../feature-flags/feature-flags.service' import { JoiPaymentProduct } from '../../form/admin-form/admin-form.payments.constants' import * as FormService from '../../form/form.service' +import { MyInfoService } from '../../myinfo/myinfo.service' +import { extractMyInfoLoginJwt } from '../../myinfo/myinfo.util' +import { IPopulatedStorageFormWithResponsesAndHash } from '../email-submission/email-submission.types' import ParsedResponsesObject from '../ParsedResponsesObject.class' import { sharedSubmissionParams } from '../submission.constants' import * as SubmissionService from '../submission.service' @@ -58,7 +62,10 @@ import { StorageSubmissionMiddlewareHandlerType, ValidateSubmissionMiddlewareHandlerRequest, } from './encrypt-submission.types' -import { mapRouteError } from './encrypt-submission.utils' +import { + formatMyInfoStorageResponseData, + mapRouteError, +} from './encrypt-submission.utils' import IncomingEncryptSubmission from './IncomingEncryptSubmission.class' const logger = createLoggerWithLabel(module) @@ -388,6 +395,7 @@ export const validateStorageSubmission = async ( next: NextFunction, ) => { const formDef = req.formsg.formDef + let spcpSubmissionFailure: undefined | true const logMeta = { action: 'validateStorageSubmission', @@ -413,12 +421,14 @@ export const validateStorageSubmission = async ( else { // eslint-disable-next-line @typescript-eslint/no-unused-vars const { filename: __, content: ___, ...restAttachments } = rest - responses.push({ ...restAttachments } as EncryptAttachmentResponse) + responses.push({ + ...restAttachments, + } as EncryptAttachmentResponse) } } } req.formsg.filteredResponses = responses - return next() + return { parsedResponses, form: formDef } }) .mapErr((error) => { // TODO(FRM-1318): Set DB flag to true to harden submission validation after validation has similar error rates as email mode forms. @@ -444,8 +454,68 @@ export const validateStorageSubmission = async ( error, }) req.formsg.filteredResponses = req.body.responses + return error + }) + .andThen(({ parsedResponses, form }) => { + // Validate MyInfo responses + const { authType } = form + switch (authType) { + case FormAuthType.SGID_MyInfo: + case FormAuthType.MyInfo: { + return extractMyInfoLoginJwt(req.cookies, authType) + .andThen(MyInfoService.verifyLoginJwt) + .asyncAndThen(({ uinFin }) => + MyInfoService.fetchMyInfoHashes(uinFin, form._id) + .andThen((hashes) => + MyInfoService.checkMyInfoHashes( + parsedResponses.responses, + hashes, + ), + ) + .map( + (hashedFields) => ({ + hashedFields, + parsedResponses, + }), + ), + ) + .mapErr((error) => { + spcpSubmissionFailure = true + logger.error({ + message: `Error verifying MyInfo${ + authType === FormAuthType.SGID_MyInfo ? '(over SGID)' : '' + } hashes`, + meta: logMeta, + error, + }) + return error + }) + } + default: + return ok({ + parsedResponses, + }) + } + }) + .map(({ parsedResponses, hashedFields }) => { + const storageFormData = formatMyInfoStorageResponseData( + parsedResponses.getAllResponses(), + hashedFields, + ) + req.body.responses = storageFormData return next() }) + .mapErr((error) => { + logger.error({ + message: 'Error saving responses in req.body', + meta: logMeta, + error, + }) + return res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({ + message: 'Error saving responses in req.body', + spcpSubmissionFailure, + }) + }) } const encryptAttachment = async ( diff --git a/src/app/modules/submission/encrypt-submission/encrypt-submission.utils.ts b/src/app/modules/submission/encrypt-submission/encrypt-submission.utils.ts index 631d04e0d9..e31cde8c88 100644 --- a/src/app/modules/submission/encrypt-submission/encrypt-submission.utils.ts +++ b/src/app/modules/submission/encrypt-submission/encrypt-submission.utils.ts @@ -11,6 +11,7 @@ import { SubmissionType, } from '../../../../../shared/types' import { calculatePrice } from '../../../../../shared/utils/paymentProductPrice' +import { isProcessedChildResponse } from '../../../../app/utils/field-validation/field-validation.guards' import { IEncryptedSubmissionSchema, IPopulatedEncryptedForm, @@ -48,6 +49,7 @@ import { FormNotFoundError, PrivateFormError, } from '../../form/form.errors' +import { MyInfoKey } from '../../myinfo/myinfo.types' import { PaymentNotFoundError } from '../../payments/payments.errors' import { SgidInvalidJwtError, @@ -71,6 +73,8 @@ import { SubmissionNotFoundError, ValidateFieldError, } from '../submission.errors' +import { ProcessedFieldResponse } from '../submission.types' +import { getAnswersForChild, getMyInfoPrefix } from '../submission.utils' import { AttachmentSizeLimitExceededError, @@ -359,3 +363,27 @@ export const getPaymentIntentDescription = ( } } } + +export const formatMyInfoStorageResponseData = ( + parsedResponses: ProcessedFieldResponse[], + hashedFields?: Set, +) => { + if (!hashedFields) { + return parsedResponses + } else { + return parsedResponses.flatMap((response) => { + if (isProcessedChildResponse(response)) { + return getAnswersForChild(response).map((childField) => { + const myInfoPrefix = getMyInfoPrefix(childField, hashedFields) + childField.question = `${myInfoPrefix}${childField.question}` + return childField + }) + } else { + // Obtain prefix for question based on whether it is verified by MyInfo. + const myInfoPrefix = getMyInfoPrefix(response, hashedFields) + response.question = `${myInfoPrefix}${response.question}` + return response + } + }) + } +} diff --git a/src/app/modules/submission/submission.utils.ts b/src/app/modules/submission/submission.utils.ts index 1266ad634b..4e6c63b300 100644 --- a/src/app/modules/submission/submission.utils.ts +++ b/src/app/modules/submission/submission.utils.ts @@ -9,10 +9,12 @@ import { import { err, ok, Result } from 'neverthrow' import { FIELDS_TO_REJECT } from '../../../../shared/constants/field/basic' +import { MYINFO_ATTRIBUTE_MAP } from '../../../../shared/constants/field/myinfo' import { BasicField, FormField, FormResponseMode, + MyInfoAttribute, } from '../../../../shared/types' import * as FileValidation from '../../../../shared/utils/file-validation' import { @@ -26,9 +28,18 @@ import { ParsedClearFormFieldResponse, } from '../../../types/api' import { AutoReplyMailData } from '../../services/mail/mail.types' +import { MyInfoKey } from '../myinfo/myinfo.types' +import { getMyInfoChildHashKey } from '../myinfo/myinfo.util' +import { MYINFO_PREFIX } from './email-submission/email-submission.constants' +import { ResponseFormattedForEmail } from './email-submission/email-submission.types' import { ConflictError } from './submission.errors' -import { FilteredResponse } from './submission.types' +import { + FilteredResponse, + ProcessedChildrenResponse, + ProcessedFieldResponse, + ProcessedSingleAnswerResponse, +} from './submission.types' type ResponseModeFilterParam = { fieldType: BasicField @@ -266,3 +277,63 @@ export const mapAttachmentsFromResponses = ( content: response.content, })) } + +/** + * Determines the prefix for a question based on whether it is verified + * by MyInfo. + * @param response + * @param hashedFields Field ids of hashed fields. + * @returns the prefix + */ +export const getMyInfoPrefix = ( + response: ResponseFormattedForEmail | ProcessedFieldResponse, + hashedFields: Set, +): string => { + return !!response.myInfo?.attr && hashedFields.has(response._id) + ? MYINFO_PREFIX + : '' +} + +/** + * Expands child subfields into individual fields, so that they are no longer nested under + * 1 parent field. + * @param response + * @returns + */ +export const getAnswersForChild = ( + response: ProcessedChildrenResponse, +): ProcessedSingleAnswerResponse[] => { + const subFields = response.childSubFieldsArray + const qnChildIdx = response.childIdx ?? 0 + if (!subFields) { + return [] + } + return response.answerArray.flatMap((arr, childIdx) => { + // First array element is always child name + const childName = arr[0] + return arr.map((answer, idx) => { + const subfield = subFields[idx] + return { + _id: getMyInfoChildHashKey( + response._id, + subFields[idx], + childIdx, + childName, + ), + fieldType: response.fieldType, + // qnChildIdx represents the index of the MyInfo field + // childIdx represents the index of the child in this MyInfo field + // as there might be >1 child for each MyInfo child field if "Add another child" is used + question: `Child ${qnChildIdx + childIdx + 1} ${ + MYINFO_ATTRIBUTE_MAP[subfield].description + }`, + myInfo: { + attr: subFields[idx] as unknown as MyInfoAttribute, + }, + isVisible: response.isVisible, + isUserVerified: response.isUserVerified, + answer, + } + }) + }) +} diff --git a/src/app/modules/verified-content/verified-content.service.ts b/src/app/modules/verified-content/verified-content.service.ts index 826cf00db8..dafded0e66 100644 --- a/src/app/modules/verified-content/verified-content.service.ts +++ b/src/app/modules/verified-content/verified-content.service.ts @@ -34,10 +34,12 @@ export const getVerifiedContent = ({ CpVerifiedContent | SpVerifiedContent | SgidVerifiedContent > => { switch (type) { + case FormAuthType.MyInfo: case FormAuthType.SP: return getSpVerifiedContent(data) case FormAuthType.CP: return getCpVerifiedContent(data) + case FormAuthType.SGID_MyInfo: case FormAuthType.SGID: return getSgidVerifiedContent(data) } diff --git a/src/app/modules/verified-content/verified-content.types.ts b/src/app/modules/verified-content/verified-content.types.ts index c7aecc77fb..896861bd60 100644 --- a/src/app/modules/verified-content/verified-content.types.ts +++ b/src/app/modules/verified-content/verified-content.types.ts @@ -41,6 +41,11 @@ export type EncryptVerificationContentParams = { } export type GetVerifiedContentParams = { - type: FormAuthType.SP | FormAuthType.CP | FormAuthType.SGID + type: + | FormAuthType.SP + | FormAuthType.CP + | FormAuthType.SGID + | FormAuthType.MyInfo + | FormAuthType.SGID_MyInfo data: Record } diff --git a/src/app/routes/api/v3/auth/__tests__/auth.routes.spec.ts b/src/app/routes/api/v3/auth/__tests__/auth.routes.spec.ts index 1c27619e83..63279c91bb 100644 --- a/src/app/routes/api/v3/auth/__tests__/auth.routes.spec.ts +++ b/src/app/routes/api/v3/auth/__tests__/auth.routes.spec.ts @@ -6,6 +6,7 @@ import { errAsync, okAsync } from 'neverthrow' import supertest, { Session } from 'supertest-session' import validator from 'validator' +import { ADMIN_LOGIN_SESSION_COOKIE_NAME } from 'src/app/loaders/express/session' import MailService from 'src/app/services/mail/mail.service' import { HashingError } from 'src/app/utils/hash' import * as OtpUtils from 'src/app/utils/otp' @@ -511,7 +512,7 @@ describe('auth.routes', () => { }) // Should have session cookie returned. const sessionCookie = request.cookies.find( - (cookie) => cookie.name === 'connect.sid', + (cookie) => cookie.name === ADMIN_LOGIN_SESSION_COOKIE_NAME, ) expect(sessionCookie).toBeDefined() }) @@ -538,7 +539,7 @@ describe('auth.routes', () => { }) // Should have session cookie returned. const sessionCookie = request.cookies.find( - (cookie) => cookie.name === 'connect.sid', + (cookie) => cookie.name === ADMIN_LOGIN_SESSION_COOKIE_NAME, ) expect(sessionCookie).toBeDefined() }) @@ -591,9 +592,9 @@ describe('auth.routes', () => { // Assert expect(response.status).toEqual(200) expect(response.body).toEqual({ message: 'Sign out successful' }) - // connect.sid should now be empty. + // Login cookie should now be empty. expect(response.header['set-cookie'][0]).toEqual( - expect.stringContaining('connect.sid=;'), + expect.stringContaining(`${ADMIN_LOGIN_SESSION_COOKIE_NAME}=;`), ) }) @@ -629,7 +630,7 @@ describe('auth.routes', () => { // Assert // Should have session cookie returned. const sessionCookie = request.cookies.find( - (cookie) => cookie.name === 'connect.sid', + (cookie) => cookie.name === ADMIN_LOGIN_SESSION_COOKIE_NAME, ) expect(sessionCookie).toBeDefined() return response.body From ded4926b558935c69d1455b4cd59de8f3c9961c8 Mon Sep 17 00:00:00 2001 From: Justyn Oh Date: Wed, 15 Nov 2023 15:28:28 +0800 Subject: [PATCH 08/12] chore: security upgrade axios to 1.6.2 (#6893) chore: upgrade axios to 1.6.0 --- frontend/package-lock.json | 14 +- frontend/package.json | 2 +- package-lock.json | 14 +- package.json | 2 +- serverless/virus-scanner/package-lock.json | 1192 ++++++++++---------- serverless/virus-scanner/package.json | 4 +- 6 files changed, 610 insertions(+), 618 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index c9bb2a0692..599fcb5566 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -20,7 +20,7 @@ "@stablelib/base64": "^1.0.1", "@stripe/react-stripe-js": "^1.15.0", "@stripe/stripe-js": "^1.44.1", - "axios": "^1.6.0", + "axios": "^1.6.2", "broadcast-channel": "^4.13.0", "browser-image-compression": "^2.0.2", "comlink": "^4.3.1", @@ -17324,9 +17324,9 @@ } }, "node_modules/axios": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.0.tgz", - "integrity": "sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz", + "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==", "dependencies": { "follow-redirects": "^1.15.0", "form-data": "^4.0.0", @@ -61200,9 +61200,9 @@ "dev": true }, "axios": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.0.tgz", - "integrity": "sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz", + "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==", "requires": { "follow-redirects": "^1.15.0", "form-data": "^4.0.0", diff --git a/frontend/package.json b/frontend/package.json index a155f67aad..162f127ab9 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -15,7 +15,7 @@ "@stablelib/base64": "^1.0.1", "@stripe/react-stripe-js": "^1.15.0", "@stripe/stripe-js": "^1.44.1", - "axios": "^1.6.0", + "axios": "^1.6.2", "broadcast-channel": "^4.13.0", "browser-image-compression": "^2.0.2", "comlink": "^4.3.1", diff --git a/package-lock.json b/package-lock.json index b7fa60b13c..e8f4169600 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,7 +24,7 @@ "abortcontroller-polyfill": "^1.7.5", "aws-info": "^1.2.0", "aws-sdk": "^2.1354.0", - "axios": "^1.6.0", + "axios": "^1.6.2", "bcrypt": "^5.1.0", "bluebird": "^3.5.2", "body-parser": "^1.20.1", @@ -9778,9 +9778,9 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.0.tgz", - "integrity": "sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz", + "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==", "dependencies": { "follow-redirects": "^1.15.0", "form-data": "^4.0.0", @@ -38955,9 +38955,9 @@ "dev": true }, "axios": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.0.tgz", - "integrity": "sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz", + "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==", "requires": { "follow-redirects": "^1.15.0", "form-data": "^4.0.0", diff --git a/package.json b/package.json index d2dac61ccf..1dcf9047cd 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "abortcontroller-polyfill": "^1.7.5", "aws-info": "^1.2.0", "aws-sdk": "^2.1354.0", - "axios": "^1.6.0", + "axios": "^1.6.2", "bcrypt": "^5.1.0", "bluebird": "^3.5.2", "body-parser": "^1.20.1", diff --git a/serverless/virus-scanner/package-lock.json b/serverless/virus-scanner/package-lock.json index f6475eca16..08279ae749 100644 --- a/serverless/virus-scanner/package-lock.json +++ b/serverless/virus-scanner/package-lock.json @@ -12,7 +12,7 @@ "@aws-sdk/client-s3": "^3.363.0", "@aws-sdk/client-ssm": "^3.363.0", "aws-lambda-ric": "^2.1.0", - "axios": "^0.27.2", + "axios": "^1.6.2", "clamscan": "^2.1.2", "convict": "^6.2.4", "http-status-codes": "^2.2.0", @@ -33,7 +33,7 @@ "jest-environment-jsdom": "^29.5.0", "jest-extended": "^3.2.4", "jest-localstorage-mock": "^2.4.26", - "jest-mock-axios": "^4.7.2", + "jest-mock-axios": "^4.7.3", "serverless": "^3.19.0", "serverless-dotenv-plugin": "^4.0.1", "ts-jest": "^29.0.5", @@ -1396,9 +1396,9 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", - "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz", + "integrity": "sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -1498,9 +1498,9 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", - "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz", + "integrity": "sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -1642,16 +1642,16 @@ } }, "node_modules/@jest/console": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.6.4.tgz", - "integrity": "sha512-wNK6gC0Ha9QeEPSkeJedQuTQqxZYnDPuDcDhVuVatRvMkL4D0VTvFVZj+Yuh6caG2aOfzkUZ36KtCmLNtR02hw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", "dev": true, "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", - "jest-message-util": "^29.6.3", - "jest-util": "^29.6.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", "slash": "^3.0.0" }, "engines": { @@ -1659,15 +1659,15 @@ } }, "node_modules/@jest/core": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.6.4.tgz", - "integrity": "sha512-U/vq5ccNTSVgYH7mHnodHmCffGWHJnz/E1BEWlLuK5pM4FZmGfBn/nrJGLjUsSmyx3otCeqc1T31F4y08AMDLg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", "dev": true, "dependencies": { - "@jest/console": "^29.6.4", - "@jest/reporters": "^29.6.4", - "@jest/test-result": "^29.6.4", - "@jest/transform": "^29.6.4", + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", "ansi-escapes": "^4.2.1", @@ -1675,21 +1675,21 @@ "ci-info": "^3.2.0", "exit": "^0.1.2", "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.6.3", - "jest-config": "^29.6.4", - "jest-haste-map": "^29.6.4", - "jest-message-util": "^29.6.3", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.6.4", - "jest-resolve-dependencies": "^29.6.4", - "jest-runner": "^29.6.4", - "jest-runtime": "^29.6.4", - "jest-snapshot": "^29.6.4", - "jest-util": "^29.6.3", - "jest-validate": "^29.6.3", - "jest-watcher": "^29.6.4", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", "micromatch": "^4.0.4", - "pretty-format": "^29.6.3", + "pretty-format": "^29.7.0", "slash": "^3.0.0", "strip-ansi": "^6.0.0" }, @@ -1706,37 +1706,37 @@ } }, "node_modules/@jest/environment": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.6.4.tgz", - "integrity": "sha512-sQ0SULEjA1XUTHmkBRl7A1dyITM9yb1yb3ZNKPX3KlTd6IG7mWUe3e2yfExtC2Zz1Q+mMckOLHmL/qLiuQJrBQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", "dev": true, "dependencies": { - "@jest/fake-timers": "^29.6.4", + "@jest/fake-timers": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", - "jest-mock": "^29.6.3" + "jest-mock": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/expect": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.6.4.tgz", - "integrity": "sha512-Warhsa7d23+3X5bLbrbYvaehcgX5TLYhI03JKoedTiI8uJU4IhqYBWF7OSSgUyz4IgLpUYPkK0AehA5/fRclAA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", "dev": true, "dependencies": { - "expect": "^29.6.4", - "jest-snapshot": "^29.6.4" + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/expect-utils": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.6.4.tgz", - "integrity": "sha512-FEhkJhqtvBwgSpiTrocquJCdXPsyvNKcl/n7A3u7X4pVoF4bswm11c9d4AV+kfq2Gpv/mM8x7E7DsRvH+djkrg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", "dev": true, "dependencies": { "jest-get-type": "^29.6.3" @@ -1746,47 +1746,47 @@ } }, "node_modules/@jest/fake-timers": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.6.4.tgz", - "integrity": "sha512-6UkCwzoBK60edXIIWb0/KWkuj7R7Qq91vVInOe3De6DSpaEiqjKcJw4F7XUet24Wupahj9J6PlR09JqJ5ySDHw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", "dev": true, "dependencies": { "@jest/types": "^29.6.3", "@sinonjs/fake-timers": "^10.0.2", "@types/node": "*", - "jest-message-util": "^29.6.3", - "jest-mock": "^29.6.3", - "jest-util": "^29.6.3" + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/globals": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.6.4.tgz", - "integrity": "sha512-wVIn5bdtjlChhXAzVXavcY/3PEjf4VqM174BM3eGL5kMxLiZD5CLnbmkEyA1Dwh9q8XjP6E8RwjBsY/iCWrWsA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", "dev": true, "dependencies": { - "@jest/environment": "^29.6.4", - "@jest/expect": "^29.6.4", + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", "@jest/types": "^29.6.3", - "jest-mock": "^29.6.3" + "jest-mock": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/reporters": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.6.4.tgz", - "integrity": "sha512-sxUjWxm7QdchdrD3NfWKrL8FBsortZeibSJv4XLjESOOjSUOkjQcb0ZHJwfhEGIvBvTluTzfG2yZWZhkrXJu8g==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", "dev": true, "dependencies": { "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.6.4", - "@jest/test-result": "^29.6.4", - "@jest/transform": "^29.6.4", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", "@jest/types": "^29.6.3", "@jridgewell/trace-mapping": "^0.3.18", "@types/node": "*", @@ -1800,9 +1800,9 @@ "istanbul-lib-report": "^3.0.0", "istanbul-lib-source-maps": "^4.0.0", "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.6.3", - "jest-util": "^29.6.3", - "jest-worker": "^29.6.4", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", "slash": "^3.0.0", "string-length": "^4.0.1", "strip-ansi": "^6.0.0", @@ -1847,12 +1847,12 @@ } }, "node_modules/@jest/test-result": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.6.4.tgz", - "integrity": "sha512-uQ1C0AUEN90/dsyEirgMLlouROgSY+Wc/JanVVk0OiUKa5UFh7sJpMEM3aoUBAz2BRNvUJ8j3d294WFuRxSyOQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", "dev": true, "dependencies": { - "@jest/console": "^29.6.4", + "@jest/console": "^29.7.0", "@jest/types": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "collect-v8-coverage": "^1.0.0" @@ -1862,14 +1862,14 @@ } }, "node_modules/@jest/test-sequencer": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.6.4.tgz", - "integrity": "sha512-E84M6LbpcRq3fT4ckfKs9ryVanwkaIB0Ws9bw3/yP4seRLg/VaCZ/LgW0MCq5wwk4/iP/qnilD41aj2fsw2RMg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", "dev": true, "dependencies": { - "@jest/test-result": "^29.6.4", + "@jest/test-result": "^29.7.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.4", + "jest-haste-map": "^29.7.0", "slash": "^3.0.0" }, "engines": { @@ -1877,9 +1877,9 @@ } }, "node_modules/@jest/transform": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.6.4.tgz", - "integrity": "sha512-8thgRSiXUqtr/pPGY/OsyHuMjGyhVnWrFAwoxmIemlBuiMyU1WFs0tXoNxzcr4A4uErs/ABre76SGmrr5ab/AA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", "dev": true, "dependencies": { "@babel/core": "^7.11.6", @@ -1890,9 +1890,9 @@ "convert-source-map": "^2.0.0", "fast-json-stable-stringify": "^2.1.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.4", + "jest-haste-map": "^29.7.0", "jest-regex-util": "^29.6.3", - "jest-util": "^29.6.3", + "jest-util": "^29.7.0", "micromatch": "^4.0.4", "pirates": "^4.0.4", "slash": "^3.0.0", @@ -3009,9 +3009,9 @@ "peer": true }, "node_modules/@types/graceful-fs": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", - "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", "dev": true, "dependencies": { "@types/node": "*" @@ -3122,9 +3122,9 @@ } }, "node_modules/@types/stack-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", - "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", "dev": true }, "node_modules/@types/strip-bom": { @@ -3789,21 +3789,22 @@ } }, "node_modules/axios": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", - "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz", + "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==", "dependencies": { - "follow-redirects": "^1.14.9", - "form-data": "^4.0.0" + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" } }, "node_modules/babel-jest": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.6.4.tgz", - "integrity": "sha512-meLj23UlSLddj6PC+YTOFRgDAtjnZom8w/ACsrx0gtPtv5cJZk0A5Unk5bV4wixD7XaPCN1fQvpww8czkZURmw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", "dev": true, "dependencies": { - "@jest/transform": "^29.6.4", + "@jest/transform": "^29.7.0", "@types/babel__core": "^7.1.14", "babel-plugin-istanbul": "^6.1.1", "babel-preset-jest": "^29.6.3", @@ -4906,6 +4907,27 @@ "node": ">= 6" } }, + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", @@ -5828,16 +5850,16 @@ } }, "node_modules/expect": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.6.4.tgz", - "integrity": "sha512-F2W2UyQ8XYyftHT57dtfg8Ue3X5qLgm2sSug0ivvLRH/VKNRL/pDxg/TH7zVzbQB0tu80clNFy6LU7OS/VSEKA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", "dev": true, "dependencies": { - "@jest/expect-utils": "^29.6.4", + "@jest/expect-utils": "^29.7.0", "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.6.4", - "jest-message-util": "^29.6.3", - "jest-util": "^29.6.3" + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -6979,9 +7001,9 @@ } }, "node_modules/istanbul-lib-instrument": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.0.tgz", - "integrity": "sha512-x58orMzEVfzPUKqlbLd1hXCnySCxKdDKa6Rjg97CwuLLRI4g3FHTdnExu1OqffVFay6zeMW+T6/DowFLndWnIw==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.1.tgz", + "integrity": "sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==", "dev": true, "dependencies": { "@babel/core": "^7.12.3", @@ -7063,15 +7085,15 @@ } }, "node_modules/jest": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.6.4.tgz", - "integrity": "sha512-tEFhVQFF/bzoYV1YuGyzLPZ6vlPrdfvDmmAxudA1dLEuiztqg2Rkx20vkKY32xiDROcD2KXlgZ7Cu8RPeEHRKw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, "dependencies": { - "@jest/core": "^29.6.4", + "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", "import-local": "^3.0.2", - "jest-cli": "^29.6.4" + "jest-cli": "^29.7.0" }, "bin": { "jest": "bin/jest.js" @@ -7089,13 +7111,13 @@ } }, "node_modules/jest-changed-files": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.6.3.tgz", - "integrity": "sha512-G5wDnElqLa4/c66ma5PG9eRjE342lIbF6SUnTJi26C3J28Fv2TVY2rOyKB9YGbSA5ogwevgmxc4j4aVjrEK6Yg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", "dev": true, "dependencies": { "execa": "^5.0.0", - "jest-util": "^29.6.3", + "jest-util": "^29.7.0", "p-limit": "^3.1.0" }, "engines": { @@ -7103,28 +7125,28 @@ } }, "node_modules/jest-circus": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.6.4.tgz", - "integrity": "sha512-YXNrRyntVUgDfZbjXWBMPslX1mQ8MrSG0oM/Y06j9EYubODIyHWP8hMUbjbZ19M3M+zamqEur7O80HODwACoJw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", "dev": true, "dependencies": { - "@jest/environment": "^29.6.4", - "@jest/expect": "^29.6.4", - "@jest/test-result": "^29.6.4", + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "co": "^4.6.0", "dedent": "^1.0.0", "is-generator-fn": "^2.0.0", - "jest-each": "^29.6.3", - "jest-matcher-utils": "^29.6.4", - "jest-message-util": "^29.6.3", - "jest-runtime": "^29.6.4", - "jest-snapshot": "^29.6.4", - "jest-util": "^29.6.3", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", "p-limit": "^3.1.0", - "pretty-format": "^29.6.3", + "pretty-format": "^29.7.0", "pure-rand": "^6.0.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" @@ -7134,22 +7156,21 @@ } }, "node_modules/jest-cli": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.6.4.tgz", - "integrity": "sha512-+uMCQ7oizMmh8ZwRfZzKIEszFY9ksjjEQnTEMTaL7fYiL3Kw4XhqT9bYh+A4DQKUb67hZn2KbtEnDuHvcgK4pQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", "dev": true, "dependencies": { - "@jest/core": "^29.6.4", - "@jest/test-result": "^29.6.4", + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", "@jest/types": "^29.6.3", "chalk": "^4.0.0", + "create-jest": "^29.7.0", "exit": "^0.1.2", - "graceful-fs": "^4.2.9", "import-local": "^3.0.2", - "jest-config": "^29.6.4", - "jest-util": "^29.6.3", - "jest-validate": "^29.6.3", - "prompts": "^2.0.1", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", "yargs": "^17.3.1" }, "bin": { @@ -7168,31 +7189,31 @@ } }, "node_modules/jest-config": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.6.4.tgz", - "integrity": "sha512-JWohr3i9m2cVpBumQFv2akMEnFEPVOh+9L2xIBJhJ0zOaci2ZXuKJj0tgMKQCBZAKA09H049IR4HVS/43Qb19A==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", "dev": true, "dependencies": { "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.6.4", + "@jest/test-sequencer": "^29.7.0", "@jest/types": "^29.6.3", - "babel-jest": "^29.6.4", + "babel-jest": "^29.7.0", "chalk": "^4.0.0", "ci-info": "^3.2.0", "deepmerge": "^4.2.2", "glob": "^7.1.3", "graceful-fs": "^4.2.9", - "jest-circus": "^29.6.4", - "jest-environment-node": "^29.6.4", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", "jest-get-type": "^29.6.3", "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.6.4", - "jest-runner": "^29.6.4", - "jest-util": "^29.6.3", - "jest-validate": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", "micromatch": "^4.0.4", "parse-json": "^5.2.0", - "pretty-format": "^29.6.3", + "pretty-format": "^29.7.0", "slash": "^3.0.0", "strip-json-comments": "^3.1.1" }, @@ -7213,24 +7234,24 @@ } }, "node_modules/jest-diff": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.6.4.tgz", - "integrity": "sha512-9F48UxR9e4XOEZvoUXEHSWY4qC4zERJaOfrbBg9JpbJOO43R1vN76REt/aMGZoY6GD5g84nnJiBIVlscegefpw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", "dev": true, "dependencies": { "chalk": "^4.0.0", "diff-sequences": "^29.6.3", "jest-get-type": "^29.6.3", - "pretty-format": "^29.6.3" + "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-docblock": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.6.3.tgz", - "integrity": "sha512-2+H+GOTQBEm2+qFSQ7Ma+BvyV+waiIFxmZF5LdpBsAEjWX8QYjSCa4FrkIYtbfXUJJJnFCYrOtt6TZ+IAiTjBQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", "dev": true, "dependencies": { "detect-newline": "^3.0.0" @@ -7240,16 +7261,16 @@ } }, "node_modules/jest-each": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.6.3.tgz", - "integrity": "sha512-KoXfJ42k8cqbkfshW7sSHcdfnv5agDdHCPA87ZBdmHP+zJstTJc0ttQaJ/x7zK6noAL76hOuTIJ6ZkQRS5dcyg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", "dev": true, "dependencies": { "@jest/types": "^29.6.3", "chalk": "^4.0.0", "jest-get-type": "^29.6.3", - "jest-util": "^29.6.3", - "pretty-format": "^29.6.3" + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -7283,17 +7304,17 @@ } }, "node_modules/jest-environment-node": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.6.4.tgz", - "integrity": "sha512-i7SbpH2dEIFGNmxGCpSc2w9cA4qVD+wfvg2ZnfQ7XVrKL0NA5uDVBIiGH8SR4F0dKEv/0qI5r+aDomDf04DpEQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", "dev": true, "dependencies": { - "@jest/environment": "^29.6.4", - "@jest/fake-timers": "^29.6.4", + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", - "jest-mock": "^29.6.3", - "jest-util": "^29.6.3" + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -7330,9 +7351,9 @@ } }, "node_modules/jest-haste-map": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.6.4.tgz", - "integrity": "sha512-12Ad+VNTDHxKf7k+M65sviyynRoZYuL1/GTuhEVb8RYsNSNln71nANRb/faSyWvx0j+gHcivChXHIoMJrGYjog==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", "dev": true, "dependencies": { "@jest/types": "^29.6.3", @@ -7342,8 +7363,8 @@ "fb-watchman": "^2.0.0", "graceful-fs": "^4.2.9", "jest-regex-util": "^29.6.3", - "jest-util": "^29.6.3", - "jest-worker": "^29.6.4", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", "micromatch": "^4.0.4", "walker": "^1.0.8" }, @@ -7355,13 +7376,13 @@ } }, "node_modules/jest-leak-detector": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.6.3.tgz", - "integrity": "sha512-0kfbESIHXYdhAdpLsW7xdwmYhLf1BRu4AA118/OxFm0Ho1b2RcTmO4oF6aAMaxpxdxnJ3zve2rgwzNBD4Zbm7Q==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", "dev": true, "dependencies": { "jest-get-type": "^29.6.3", - "pretty-format": "^29.6.3" + "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -7377,24 +7398,24 @@ } }, "node_modules/jest-matcher-utils": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.6.4.tgz", - "integrity": "sha512-KSzwyzGvK4HcfnserYqJHYi7sZVqdREJ9DMPAKVbS98JsIAvumihaNUbjrWw0St7p9IY7A9UskCW5MYlGmBQFQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", "dev": true, "dependencies": { "chalk": "^4.0.0", - "jest-diff": "^29.6.4", + "jest-diff": "^29.7.0", "jest-get-type": "^29.6.3", - "pretty-format": "^29.6.3" + "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-message-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.6.3.tgz", - "integrity": "sha512-FtzaEEHzjDpQp51HX4UMkPZjy46ati4T5pEMyM6Ik48ztu4T9LQplZ6OsimHx7EuM9dfEh5HJa6D3trEftu3dA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", "dev": true, "dependencies": { "@babel/code-frame": "^7.12.13", @@ -7403,7 +7424,7 @@ "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "micromatch": "^4.0.4", - "pretty-format": "^29.6.3", + "pretty-format": "^29.7.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" }, @@ -7412,56 +7433,30 @@ } }, "node_modules/jest-mock": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.6.3.tgz", - "integrity": "sha512-Z7Gs/mOyTSR4yPsaZ72a/MtuK6RnC3JYqWONe48oLaoEcYwEDxqvbXz85G4SJrm2Z5Ar9zp6MiHF4AlFlRM4Pg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", "dev": true, "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", - "jest-util": "^29.6.3" + "jest-util": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-mock-axios": { - "version": "4.7.2", - "resolved": "https://registry.npmjs.org/jest-mock-axios/-/jest-mock-axios-4.7.2.tgz", - "integrity": "sha512-8xrkCuEsscOLSw4Noi40Pq5tQMlKg3tbDZ0XNkf3Mv0Yq+z6+qS2tToimNs/3RIsEzoPXHTf2+mcySaNaYXIyw==", + "version": "4.7.3", + "resolved": "https://registry.npmjs.org/jest-mock-axios/-/jest-mock-axios-4.7.3.tgz", + "integrity": "sha512-RHHdCZWreeX1EAl77u46yqYJG5aKX9l4zsCwf6wsIb3uy3w/XaEC5n4wbyluNujXQSZfNH1ir8OXinoewYQkUw==", "dev": true, "dependencies": { - "@jest/globals": "^29.5.0", - "jest": "~29.5.0", + "@jest/globals": "^29.7.0", + "jest": "~29.7.0", "synchronous-promise": "^2.0.17" } }, - "node_modules/jest-mock-axios/node_modules/jest": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.5.0.tgz", - "integrity": "sha512-juMg3he2uru1QoXX078zTa7pO85QyB9xajZc6bU+d9yEGwrKX6+vGmJQ3UdVZsvTEUARIdObzH68QItim6OSSQ==", - "dev": true, - "dependencies": { - "@jest/core": "^29.5.0", - "@jest/types": "^29.5.0", - "import-local": "^3.0.2", - "jest-cli": "^29.5.0" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, "node_modules/jest-pnp-resolver": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", @@ -7489,17 +7484,17 @@ } }, "node_modules/jest-resolve": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.6.4.tgz", - "integrity": "sha512-fPRq+0vcxsuGlG0O3gyoqGTAxasagOxEuyoxHeyxaZbc9QNek0AmJWSkhjlMG+mTsj+8knc/mWb3fXlRNVih7Q==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", "dev": true, "dependencies": { "chalk": "^4.0.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.4", + "jest-haste-map": "^29.7.0", "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.6.3", - "jest-validate": "^29.6.3", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", "resolve": "^1.20.0", "resolve.exports": "^2.0.0", "slash": "^3.0.0" @@ -7509,43 +7504,43 @@ } }, "node_modules/jest-resolve-dependencies": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.6.4.tgz", - "integrity": "sha512-7+6eAmr1ZBF3vOAJVsfLj1QdqeXG+WYhidfLHBRZqGN24MFRIiKG20ItpLw2qRAsW/D2ZUUmCNf6irUr/v6KHA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", "dev": true, "dependencies": { "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.6.4" + "jest-snapshot": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-runner": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.6.4.tgz", - "integrity": "sha512-SDaLrMmtVlQYDuG0iSPYLycG8P9jLI+fRm8AF/xPKhYDB2g6xDWjXBrR5M8gEWsK6KVFlebpZ4QsrxdyIX1Jaw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", "dev": true, "dependencies": { - "@jest/console": "^29.6.4", - "@jest/environment": "^29.6.4", - "@jest/test-result": "^29.6.4", - "@jest/transform": "^29.6.4", + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "emittery": "^0.13.1", "graceful-fs": "^4.2.9", - "jest-docblock": "^29.6.3", - "jest-environment-node": "^29.6.4", - "jest-haste-map": "^29.6.4", - "jest-leak-detector": "^29.6.3", - "jest-message-util": "^29.6.3", - "jest-resolve": "^29.6.4", - "jest-runtime": "^29.6.4", - "jest-util": "^29.6.3", - "jest-watcher": "^29.6.4", - "jest-worker": "^29.6.4", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", "p-limit": "^3.1.0", "source-map-support": "0.5.13" }, @@ -7554,17 +7549,17 @@ } }, "node_modules/jest-runtime": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.6.4.tgz", - "integrity": "sha512-s/QxMBLvmwLdchKEjcLfwzP7h+jsHvNEtxGP5P+Fl1FMaJX2jMiIqe4rJw4tFprzCwuSvVUo9bn0uj4gNRXsbA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", "dev": true, "dependencies": { - "@jest/environment": "^29.6.4", - "@jest/fake-timers": "^29.6.4", - "@jest/globals": "^29.6.4", + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.6.4", - "@jest/transform": "^29.6.4", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", @@ -7572,13 +7567,13 @@ "collect-v8-coverage": "^1.0.0", "glob": "^7.1.3", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.4", - "jest-message-util": "^29.6.3", - "jest-mock": "^29.6.3", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.6.4", - "jest-snapshot": "^29.6.4", - "jest-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", "slash": "^3.0.0", "strip-bom": "^4.0.0" }, @@ -7587,9 +7582,9 @@ } }, "node_modules/jest-snapshot": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.6.4.tgz", - "integrity": "sha512-VC1N8ED7+4uboUKGIDsbvNAZb6LakgIPgAF4RSpF13dN6YaMokfRqO+BaqK4zIh6X3JffgwbzuGqDEjHm/MrvA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", "dev": true, "dependencies": { "@babel/core": "^7.11.6", @@ -7597,20 +7592,20 @@ "@babel/plugin-syntax-jsx": "^7.7.2", "@babel/plugin-syntax-typescript": "^7.7.2", "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.6.4", - "@jest/transform": "^29.6.4", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", "@jest/types": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0", "chalk": "^4.0.0", - "expect": "^29.6.4", + "expect": "^29.7.0", "graceful-fs": "^4.2.9", - "jest-diff": "^29.6.4", + "jest-diff": "^29.7.0", "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.6.4", - "jest-message-util": "^29.6.3", - "jest-util": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", "natural-compare": "^1.4.0", - "pretty-format": "^29.6.3", + "pretty-format": "^29.7.0", "semver": "^7.5.3" }, "engines": { @@ -7618,9 +7613,9 @@ } }, "node_modules/jest-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.6.3.tgz", - "integrity": "sha512-QUjna/xSy4B32fzcKTSz1w7YYzgiHrjjJjevdRf61HYk998R5vVMMNmrHESYZVDS5DSWs+1srPLPKxXPkeSDOA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", "dev": true, "dependencies": { "@jest/types": "^29.6.3", @@ -7635,9 +7630,9 @@ } }, "node_modules/jest-validate": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.6.3.tgz", - "integrity": "sha512-e7KWZcAIX+2W1o3cHfnqpGajdCs1jSM3DkXjGeLSNmCazv1EeI1ggTeK5wdZhF+7N+g44JI2Od3veojoaumlfg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", "dev": true, "dependencies": { "@jest/types": "^29.6.3", @@ -7645,7 +7640,7 @@ "chalk": "^4.0.0", "jest-get-type": "^29.6.3", "leven": "^3.1.0", - "pretty-format": "^29.6.3" + "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -7664,18 +7659,18 @@ } }, "node_modules/jest-watcher": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.6.4.tgz", - "integrity": "sha512-oqUWvx6+On04ShsT00Ir9T4/FvBeEh2M9PTubgITPxDa739p4hoQweWPRGyYeaojgT0xTpZKF0Y/rSY1UgMxvQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", "dev": true, "dependencies": { - "@jest/test-result": "^29.6.4", + "@jest/test-result": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", "emittery": "^0.13.1", - "jest-util": "^29.6.3", + "jest-util": "^29.7.0", "string-length": "^4.0.1" }, "engines": { @@ -7683,13 +7678,13 @@ } }, "node_modules/jest-worker": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.6.4.tgz", - "integrity": "sha512-6dpvFV4WjcWbDVGgHTWo/aupl8/LbBx2NSKfiwqf79xC/yeJjKHT1+StcKy/2KTmW16hE68ccKVOtXf+WZGz7Q==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", "dev": true, "dependencies": { "@types/node": "*", - "jest-util": "^29.6.3", + "jest-util": "^29.7.0", "merge-stream": "^2.0.0", "supports-color": "^8.0.0" }, @@ -9132,9 +9127,9 @@ } }, "node_modules/pretty-format": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.3.tgz", - "integrity": "sha512-ZsBgjVhFAj5KeK+nHfF1305/By3lechHQSMWCTl8iHSbfOm2TN5nHEtFc/+W7fAyUeCs2n5iow72gld4gW0xDw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, "dependencies": { "@jest/schemas": "^29.6.3", @@ -9222,6 +9217,11 @@ "node": ">= 6" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", @@ -9254,9 +9254,9 @@ } }, "node_modules/pure-rand": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.2.tgz", - "integrity": "sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ==", + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.4.tgz", + "integrity": "sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==", "dev": true, "funding": [ { @@ -11082,25 +11082,19 @@ "dev": true }, "node_modules/v8-to-istanbul": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz", - "integrity": "sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==", + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.3.tgz", + "integrity": "sha512-9lDD+EVI2fjFsMWXc6dy5JJzBsVTcQ2fVkfBvncZ6xJWG9wtBhOldG+mHkSL0+V1K/xgZz0JDO5UT5hFwHUghg==", "dev": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.12", "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0" + "convert-source-map": "^2.0.0" }, "engines": { "node": ">=10.12.0" } }, - "node_modules/v8-to-istanbul/node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - }, "node_modules/validate-npm-package-name": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz", @@ -12705,9 +12699,9 @@ } }, "@babel/plugin-syntax-jsx": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", - "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz", + "integrity": "sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5" @@ -12777,9 +12771,9 @@ } }, "@babel/plugin-syntax-typescript": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", - "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz", + "integrity": "sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5" @@ -12898,29 +12892,29 @@ "dev": true }, "@jest/console": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.6.4.tgz", - "integrity": "sha512-wNK6gC0Ha9QeEPSkeJedQuTQqxZYnDPuDcDhVuVatRvMkL4D0VTvFVZj+Yuh6caG2aOfzkUZ36KtCmLNtR02hw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", "dev": true, "requires": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", - "jest-message-util": "^29.6.3", - "jest-util": "^29.6.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", "slash": "^3.0.0" } }, "@jest/core": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.6.4.tgz", - "integrity": "sha512-U/vq5ccNTSVgYH7mHnodHmCffGWHJnz/E1BEWlLuK5pM4FZmGfBn/nrJGLjUsSmyx3otCeqc1T31F4y08AMDLg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", "dev": true, "requires": { - "@jest/console": "^29.6.4", - "@jest/reporters": "^29.6.4", - "@jest/test-result": "^29.6.4", - "@jest/transform": "^29.6.4", + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", "ansi-escapes": "^4.2.1", @@ -12928,92 +12922,92 @@ "ci-info": "^3.2.0", "exit": "^0.1.2", "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.6.3", - "jest-config": "^29.6.4", - "jest-haste-map": "^29.6.4", - "jest-message-util": "^29.6.3", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.6.4", - "jest-resolve-dependencies": "^29.6.4", - "jest-runner": "^29.6.4", - "jest-runtime": "^29.6.4", - "jest-snapshot": "^29.6.4", - "jest-util": "^29.6.3", - "jest-validate": "^29.6.3", - "jest-watcher": "^29.6.4", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", "micromatch": "^4.0.4", - "pretty-format": "^29.6.3", + "pretty-format": "^29.7.0", "slash": "^3.0.0", "strip-ansi": "^6.0.0" } }, "@jest/environment": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.6.4.tgz", - "integrity": "sha512-sQ0SULEjA1XUTHmkBRl7A1dyITM9yb1yb3ZNKPX3KlTd6IG7mWUe3e2yfExtC2Zz1Q+mMckOLHmL/qLiuQJrBQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", "dev": true, "requires": { - "@jest/fake-timers": "^29.6.4", + "@jest/fake-timers": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", - "jest-mock": "^29.6.3" + "jest-mock": "^29.7.0" } }, "@jest/expect": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.6.4.tgz", - "integrity": "sha512-Warhsa7d23+3X5bLbrbYvaehcgX5TLYhI03JKoedTiI8uJU4IhqYBWF7OSSgUyz4IgLpUYPkK0AehA5/fRclAA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", "dev": true, "requires": { - "expect": "^29.6.4", - "jest-snapshot": "^29.6.4" + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" } }, "@jest/expect-utils": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.6.4.tgz", - "integrity": "sha512-FEhkJhqtvBwgSpiTrocquJCdXPsyvNKcl/n7A3u7X4pVoF4bswm11c9d4AV+kfq2Gpv/mM8x7E7DsRvH+djkrg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", "dev": true, "requires": { "jest-get-type": "^29.6.3" } }, "@jest/fake-timers": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.6.4.tgz", - "integrity": "sha512-6UkCwzoBK60edXIIWb0/KWkuj7R7Qq91vVInOe3De6DSpaEiqjKcJw4F7XUet24Wupahj9J6PlR09JqJ5ySDHw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", "dev": true, "requires": { "@jest/types": "^29.6.3", "@sinonjs/fake-timers": "^10.0.2", "@types/node": "*", - "jest-message-util": "^29.6.3", - "jest-mock": "^29.6.3", - "jest-util": "^29.6.3" + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" } }, "@jest/globals": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.6.4.tgz", - "integrity": "sha512-wVIn5bdtjlChhXAzVXavcY/3PEjf4VqM174BM3eGL5kMxLiZD5CLnbmkEyA1Dwh9q8XjP6E8RwjBsY/iCWrWsA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", "dev": true, "requires": { - "@jest/environment": "^29.6.4", - "@jest/expect": "^29.6.4", + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", "@jest/types": "^29.6.3", - "jest-mock": "^29.6.3" + "jest-mock": "^29.7.0" } }, "@jest/reporters": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.6.4.tgz", - "integrity": "sha512-sxUjWxm7QdchdrD3NfWKrL8FBsortZeibSJv4XLjESOOjSUOkjQcb0ZHJwfhEGIvBvTluTzfG2yZWZhkrXJu8g==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", "dev": true, "requires": { "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.6.4", - "@jest/test-result": "^29.6.4", - "@jest/transform": "^29.6.4", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", "@jest/types": "^29.6.3", "@jridgewell/trace-mapping": "^0.3.18", "@types/node": "*", @@ -13027,9 +13021,9 @@ "istanbul-lib-report": "^3.0.0", "istanbul-lib-source-maps": "^4.0.0", "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.6.3", - "jest-util": "^29.6.3", - "jest-worker": "^29.6.4", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", "slash": "^3.0.0", "string-length": "^4.0.1", "strip-ansi": "^6.0.0", @@ -13057,33 +13051,33 @@ } }, "@jest/test-result": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.6.4.tgz", - "integrity": "sha512-uQ1C0AUEN90/dsyEirgMLlouROgSY+Wc/JanVVk0OiUKa5UFh7sJpMEM3aoUBAz2BRNvUJ8j3d294WFuRxSyOQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", "dev": true, "requires": { - "@jest/console": "^29.6.4", + "@jest/console": "^29.7.0", "@jest/types": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "collect-v8-coverage": "^1.0.0" } }, "@jest/test-sequencer": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.6.4.tgz", - "integrity": "sha512-E84M6LbpcRq3fT4ckfKs9ryVanwkaIB0Ws9bw3/yP4seRLg/VaCZ/LgW0MCq5wwk4/iP/qnilD41aj2fsw2RMg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", "dev": true, "requires": { - "@jest/test-result": "^29.6.4", + "@jest/test-result": "^29.7.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.4", + "jest-haste-map": "^29.7.0", "slash": "^3.0.0" } }, "@jest/transform": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.6.4.tgz", - "integrity": "sha512-8thgRSiXUqtr/pPGY/OsyHuMjGyhVnWrFAwoxmIemlBuiMyU1WFs0tXoNxzcr4A4uErs/ABre76SGmrr5ab/AA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", "dev": true, "requires": { "@babel/core": "^7.11.6", @@ -13094,9 +13088,9 @@ "convert-source-map": "^2.0.0", "fast-json-stable-stringify": "^2.1.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.4", + "jest-haste-map": "^29.7.0", "jest-regex-util": "^29.6.3", - "jest-util": "^29.6.3", + "jest-util": "^29.7.0", "micromatch": "^4.0.4", "pirates": "^4.0.4", "slash": "^3.0.0", @@ -14023,9 +14017,9 @@ "peer": true }, "@types/graceful-fs": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", - "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", "dev": true, "requires": { "@types/node": "*" @@ -14135,9 +14129,9 @@ } }, "@types/stack-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", - "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", "dev": true }, "@types/strip-bom": { @@ -14700,21 +14694,22 @@ } }, "axios": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", - "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz", + "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==", "requires": { - "follow-redirects": "^1.14.9", - "form-data": "^4.0.0" + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" } }, "babel-jest": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.6.4.tgz", - "integrity": "sha512-meLj23UlSLddj6PC+YTOFRgDAtjnZom8w/ACsrx0gtPtv5cJZk0A5Unk5bV4wixD7XaPCN1fQvpww8czkZURmw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", "dev": true, "requires": { - "@jest/transform": "^29.6.4", + "@jest/transform": "^29.7.0", "@types/babel__core": "^7.1.14", "babel-plugin-istanbul": "^6.1.1", "babel-preset-jest": "^29.6.3", @@ -15539,6 +15534,21 @@ } } }, + "create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + } + }, "create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", @@ -16280,16 +16290,16 @@ "dev": true }, "expect": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.6.4.tgz", - "integrity": "sha512-F2W2UyQ8XYyftHT57dtfg8Ue3X5qLgm2sSug0ivvLRH/VKNRL/pDxg/TH7zVzbQB0tu80clNFy6LU7OS/VSEKA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", "dev": true, "requires": { - "@jest/expect-utils": "^29.6.4", + "@jest/expect-utils": "^29.7.0", "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.6.4", - "jest-message-util": "^29.6.3", - "jest-util": "^29.6.3" + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" } }, "ext": { @@ -17130,9 +17140,9 @@ "dev": true }, "istanbul-lib-instrument": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.0.tgz", - "integrity": "sha512-x58orMzEVfzPUKqlbLd1hXCnySCxKdDKa6Rjg97CwuLLRI4g3FHTdnExu1OqffVFay6zeMW+T6/DowFLndWnIw==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.1.tgz", + "integrity": "sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==", "dev": true, "requires": { "@babel/core": "^7.12.3", @@ -17195,138 +17205,137 @@ } }, "jest": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.6.4.tgz", - "integrity": "sha512-tEFhVQFF/bzoYV1YuGyzLPZ6vlPrdfvDmmAxudA1dLEuiztqg2Rkx20vkKY32xiDROcD2KXlgZ7Cu8RPeEHRKw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, "requires": { - "@jest/core": "^29.6.4", + "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", "import-local": "^3.0.2", - "jest-cli": "^29.6.4" + "jest-cli": "^29.7.0" } }, "jest-changed-files": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.6.3.tgz", - "integrity": "sha512-G5wDnElqLa4/c66ma5PG9eRjE342lIbF6SUnTJi26C3J28Fv2TVY2rOyKB9YGbSA5ogwevgmxc4j4aVjrEK6Yg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", "dev": true, "requires": { "execa": "^5.0.0", - "jest-util": "^29.6.3", + "jest-util": "^29.7.0", "p-limit": "^3.1.0" } }, "jest-circus": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.6.4.tgz", - "integrity": "sha512-YXNrRyntVUgDfZbjXWBMPslX1mQ8MrSG0oM/Y06j9EYubODIyHWP8hMUbjbZ19M3M+zamqEur7O80HODwACoJw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", "dev": true, "requires": { - "@jest/environment": "^29.6.4", - "@jest/expect": "^29.6.4", - "@jest/test-result": "^29.6.4", + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "co": "^4.6.0", "dedent": "^1.0.0", "is-generator-fn": "^2.0.0", - "jest-each": "^29.6.3", - "jest-matcher-utils": "^29.6.4", - "jest-message-util": "^29.6.3", - "jest-runtime": "^29.6.4", - "jest-snapshot": "^29.6.4", - "jest-util": "^29.6.3", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", "p-limit": "^3.1.0", - "pretty-format": "^29.6.3", + "pretty-format": "^29.7.0", "pure-rand": "^6.0.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" } }, "jest-cli": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.6.4.tgz", - "integrity": "sha512-+uMCQ7oizMmh8ZwRfZzKIEszFY9ksjjEQnTEMTaL7fYiL3Kw4XhqT9bYh+A4DQKUb67hZn2KbtEnDuHvcgK4pQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", "dev": true, "requires": { - "@jest/core": "^29.6.4", - "@jest/test-result": "^29.6.4", + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", "@jest/types": "^29.6.3", "chalk": "^4.0.0", + "create-jest": "^29.7.0", "exit": "^0.1.2", - "graceful-fs": "^4.2.9", "import-local": "^3.0.2", - "jest-config": "^29.6.4", - "jest-util": "^29.6.3", - "jest-validate": "^29.6.3", - "prompts": "^2.0.1", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", "yargs": "^17.3.1" } }, "jest-config": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.6.4.tgz", - "integrity": "sha512-JWohr3i9m2cVpBumQFv2akMEnFEPVOh+9L2xIBJhJ0zOaci2ZXuKJj0tgMKQCBZAKA09H049IR4HVS/43Qb19A==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", "dev": true, "requires": { "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.6.4", + "@jest/test-sequencer": "^29.7.0", "@jest/types": "^29.6.3", - "babel-jest": "^29.6.4", + "babel-jest": "^29.7.0", "chalk": "^4.0.0", "ci-info": "^3.2.0", "deepmerge": "^4.2.2", "glob": "^7.1.3", "graceful-fs": "^4.2.9", - "jest-circus": "^29.6.4", - "jest-environment-node": "^29.6.4", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", "jest-get-type": "^29.6.3", "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.6.4", - "jest-runner": "^29.6.4", - "jest-util": "^29.6.3", - "jest-validate": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", "micromatch": "^4.0.4", "parse-json": "^5.2.0", - "pretty-format": "^29.6.3", + "pretty-format": "^29.7.0", "slash": "^3.0.0", "strip-json-comments": "^3.1.1" } }, "jest-diff": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.6.4.tgz", - "integrity": "sha512-9F48UxR9e4XOEZvoUXEHSWY4qC4zERJaOfrbBg9JpbJOO43R1vN76REt/aMGZoY6GD5g84nnJiBIVlscegefpw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", "dev": true, "requires": { "chalk": "^4.0.0", "diff-sequences": "^29.6.3", "jest-get-type": "^29.6.3", - "pretty-format": "^29.6.3" + "pretty-format": "^29.7.0" } }, "jest-docblock": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.6.3.tgz", - "integrity": "sha512-2+H+GOTQBEm2+qFSQ7Ma+BvyV+waiIFxmZF5LdpBsAEjWX8QYjSCa4FrkIYtbfXUJJJnFCYrOtt6TZ+IAiTjBQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", "dev": true, "requires": { "detect-newline": "^3.0.0" } }, "jest-each": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.6.3.tgz", - "integrity": "sha512-KoXfJ42k8cqbkfshW7sSHcdfnv5agDdHCPA87ZBdmHP+zJstTJc0ttQaJ/x7zK6noAL76hOuTIJ6ZkQRS5dcyg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", "dev": true, "requires": { "@jest/types": "^29.6.3", "chalk": "^4.0.0", "jest-get-type": "^29.6.3", - "jest-util": "^29.6.3", - "pretty-format": "^29.6.3" + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" } }, "jest-environment-jsdom": { @@ -17346,17 +17355,17 @@ } }, "jest-environment-node": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.6.4.tgz", - "integrity": "sha512-i7SbpH2dEIFGNmxGCpSc2w9cA4qVD+wfvg2ZnfQ7XVrKL0NA5uDVBIiGH8SR4F0dKEv/0qI5r+aDomDf04DpEQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", "dev": true, "requires": { - "@jest/environment": "^29.6.4", - "@jest/fake-timers": "^29.6.4", + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", - "jest-mock": "^29.6.3", - "jest-util": "^29.6.3" + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" } }, "jest-extended": { @@ -17376,9 +17385,9 @@ "dev": true }, "jest-haste-map": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.6.4.tgz", - "integrity": "sha512-12Ad+VNTDHxKf7k+M65sviyynRoZYuL1/GTuhEVb8RYsNSNln71nANRb/faSyWvx0j+gHcivChXHIoMJrGYjog==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", "dev": true, "requires": { "@jest/types": "^29.6.3", @@ -17389,20 +17398,20 @@ "fsevents": "^2.3.2", "graceful-fs": "^4.2.9", "jest-regex-util": "^29.6.3", - "jest-util": "^29.6.3", - "jest-worker": "^29.6.4", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", "micromatch": "^4.0.4", "walker": "^1.0.8" } }, "jest-leak-detector": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.6.3.tgz", - "integrity": "sha512-0kfbESIHXYdhAdpLsW7xdwmYhLf1BRu4AA118/OxFm0Ho1b2RcTmO4oF6aAMaxpxdxnJ3zve2rgwzNBD4Zbm7Q==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", "dev": true, "requires": { "jest-get-type": "^29.6.3", - "pretty-format": "^29.6.3" + "pretty-format": "^29.7.0" } }, "jest-localstorage-mock": { @@ -17412,21 +17421,21 @@ "dev": true }, "jest-matcher-utils": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.6.4.tgz", - "integrity": "sha512-KSzwyzGvK4HcfnserYqJHYi7sZVqdREJ9DMPAKVbS98JsIAvumihaNUbjrWw0St7p9IY7A9UskCW5MYlGmBQFQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", "dev": true, "requires": { "chalk": "^4.0.0", - "jest-diff": "^29.6.4", + "jest-diff": "^29.7.0", "jest-get-type": "^29.6.3", - "pretty-format": "^29.6.3" + "pretty-format": "^29.7.0" } }, "jest-message-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.6.3.tgz", - "integrity": "sha512-FtzaEEHzjDpQp51HX4UMkPZjy46ati4T5pEMyM6Ik48ztu4T9LQplZ6OsimHx7EuM9dfEh5HJa6D3trEftu3dA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", "dev": true, "requires": { "@babel/code-frame": "^7.12.13", @@ -17435,45 +17444,31 @@ "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "micromatch": "^4.0.4", - "pretty-format": "^29.6.3", + "pretty-format": "^29.7.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" } }, "jest-mock": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.6.3.tgz", - "integrity": "sha512-Z7Gs/mOyTSR4yPsaZ72a/MtuK6RnC3JYqWONe48oLaoEcYwEDxqvbXz85G4SJrm2Z5Ar9zp6MiHF4AlFlRM4Pg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", "dev": true, "requires": { "@jest/types": "^29.6.3", "@types/node": "*", - "jest-util": "^29.6.3" + "jest-util": "^29.7.0" } }, "jest-mock-axios": { - "version": "4.7.2", - "resolved": "https://registry.npmjs.org/jest-mock-axios/-/jest-mock-axios-4.7.2.tgz", - "integrity": "sha512-8xrkCuEsscOLSw4Noi40Pq5tQMlKg3tbDZ0XNkf3Mv0Yq+z6+qS2tToimNs/3RIsEzoPXHTf2+mcySaNaYXIyw==", + "version": "4.7.3", + "resolved": "https://registry.npmjs.org/jest-mock-axios/-/jest-mock-axios-4.7.3.tgz", + "integrity": "sha512-RHHdCZWreeX1EAl77u46yqYJG5aKX9l4zsCwf6wsIb3uy3w/XaEC5n4wbyluNujXQSZfNH1ir8OXinoewYQkUw==", "dev": true, "requires": { - "@jest/globals": "^29.5.0", - "jest": "~29.5.0", + "@jest/globals": "^29.7.0", + "jest": "~29.7.0", "synchronous-promise": "^2.0.17" - }, - "dependencies": { - "jest": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.5.0.tgz", - "integrity": "sha512-juMg3he2uru1QoXX078zTa7pO85QyB9xajZc6bU+d9yEGwrKX6+vGmJQ3UdVZsvTEUARIdObzH68QItim6OSSQ==", - "dev": true, - "requires": { - "@jest/core": "^29.5.0", - "@jest/types": "^29.5.0", - "import-local": "^3.0.2", - "jest-cli": "^29.5.0" - } - } } }, "jest-pnp-resolver": { @@ -17490,73 +17485,73 @@ "dev": true }, "jest-resolve": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.6.4.tgz", - "integrity": "sha512-fPRq+0vcxsuGlG0O3gyoqGTAxasagOxEuyoxHeyxaZbc9QNek0AmJWSkhjlMG+mTsj+8knc/mWb3fXlRNVih7Q==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", "dev": true, "requires": { "chalk": "^4.0.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.4", + "jest-haste-map": "^29.7.0", "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.6.3", - "jest-validate": "^29.6.3", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", "resolve": "^1.20.0", "resolve.exports": "^2.0.0", "slash": "^3.0.0" } }, "jest-resolve-dependencies": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.6.4.tgz", - "integrity": "sha512-7+6eAmr1ZBF3vOAJVsfLj1QdqeXG+WYhidfLHBRZqGN24MFRIiKG20ItpLw2qRAsW/D2ZUUmCNf6irUr/v6KHA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", "dev": true, "requires": { "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.6.4" + "jest-snapshot": "^29.7.0" } }, "jest-runner": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.6.4.tgz", - "integrity": "sha512-SDaLrMmtVlQYDuG0iSPYLycG8P9jLI+fRm8AF/xPKhYDB2g6xDWjXBrR5M8gEWsK6KVFlebpZ4QsrxdyIX1Jaw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", "dev": true, "requires": { - "@jest/console": "^29.6.4", - "@jest/environment": "^29.6.4", - "@jest/test-result": "^29.6.4", - "@jest/transform": "^29.6.4", + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "emittery": "^0.13.1", "graceful-fs": "^4.2.9", - "jest-docblock": "^29.6.3", - "jest-environment-node": "^29.6.4", - "jest-haste-map": "^29.6.4", - "jest-leak-detector": "^29.6.3", - "jest-message-util": "^29.6.3", - "jest-resolve": "^29.6.4", - "jest-runtime": "^29.6.4", - "jest-util": "^29.6.3", - "jest-watcher": "^29.6.4", - "jest-worker": "^29.6.4", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", "p-limit": "^3.1.0", "source-map-support": "0.5.13" } }, "jest-runtime": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.6.4.tgz", - "integrity": "sha512-s/QxMBLvmwLdchKEjcLfwzP7h+jsHvNEtxGP5P+Fl1FMaJX2jMiIqe4rJw4tFprzCwuSvVUo9bn0uj4gNRXsbA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", "dev": true, "requires": { - "@jest/environment": "^29.6.4", - "@jest/fake-timers": "^29.6.4", - "@jest/globals": "^29.6.4", + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.6.4", - "@jest/transform": "^29.6.4", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", @@ -17564,21 +17559,21 @@ "collect-v8-coverage": "^1.0.0", "glob": "^7.1.3", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.4", - "jest-message-util": "^29.6.3", - "jest-mock": "^29.6.3", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.6.4", - "jest-snapshot": "^29.6.4", - "jest-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", "slash": "^3.0.0", "strip-bom": "^4.0.0" } }, "jest-snapshot": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.6.4.tgz", - "integrity": "sha512-VC1N8ED7+4uboUKGIDsbvNAZb6LakgIPgAF4RSpF13dN6YaMokfRqO+BaqK4zIh6X3JffgwbzuGqDEjHm/MrvA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", "dev": true, "requires": { "@babel/core": "^7.11.6", @@ -17586,27 +17581,27 @@ "@babel/plugin-syntax-jsx": "^7.7.2", "@babel/plugin-syntax-typescript": "^7.7.2", "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.6.4", - "@jest/transform": "^29.6.4", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", "@jest/types": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0", "chalk": "^4.0.0", - "expect": "^29.6.4", + "expect": "^29.7.0", "graceful-fs": "^4.2.9", - "jest-diff": "^29.6.4", + "jest-diff": "^29.7.0", "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.6.4", - "jest-message-util": "^29.6.3", - "jest-util": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", "natural-compare": "^1.4.0", - "pretty-format": "^29.6.3", + "pretty-format": "^29.7.0", "semver": "^7.5.3" } }, "jest-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.6.3.tgz", - "integrity": "sha512-QUjna/xSy4B32fzcKTSz1w7YYzgiHrjjJjevdRf61HYk998R5vVMMNmrHESYZVDS5DSWs+1srPLPKxXPkeSDOA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", "dev": true, "requires": { "@jest/types": "^29.6.3", @@ -17618,9 +17613,9 @@ } }, "jest-validate": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.6.3.tgz", - "integrity": "sha512-e7KWZcAIX+2W1o3cHfnqpGajdCs1jSM3DkXjGeLSNmCazv1EeI1ggTeK5wdZhF+7N+g44JI2Od3veojoaumlfg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", "dev": true, "requires": { "@jest/types": "^29.6.3", @@ -17628,7 +17623,7 @@ "chalk": "^4.0.0", "jest-get-type": "^29.6.3", "leven": "^3.1.0", - "pretty-format": "^29.6.3" + "pretty-format": "^29.7.0" }, "dependencies": { "camelcase": { @@ -17640,29 +17635,29 @@ } }, "jest-watcher": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.6.4.tgz", - "integrity": "sha512-oqUWvx6+On04ShsT00Ir9T4/FvBeEh2M9PTubgITPxDa739p4hoQweWPRGyYeaojgT0xTpZKF0Y/rSY1UgMxvQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", "dev": true, "requires": { - "@jest/test-result": "^29.6.4", + "@jest/test-result": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", "emittery": "^0.13.1", - "jest-util": "^29.6.3", + "jest-util": "^29.7.0", "string-length": "^4.0.1" } }, "jest-worker": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.6.4.tgz", - "integrity": "sha512-6dpvFV4WjcWbDVGgHTWo/aupl8/LbBx2NSKfiwqf79xC/yeJjKHT1+StcKy/2KTmW16hE68ccKVOtXf+WZGz7Q==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", "dev": true, "requires": { "@types/node": "*", - "jest-util": "^29.6.3", + "jest-util": "^29.7.0", "merge-stream": "^2.0.0", "supports-color": "^8.0.0" } @@ -18766,9 +18761,9 @@ } }, "pretty-format": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.3.tgz", - "integrity": "sha512-ZsBgjVhFAj5KeK+nHfF1305/By3lechHQSMWCTl8iHSbfOm2TN5nHEtFc/+W7fAyUeCs2n5iow72gld4gW0xDw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, "requires": { "@jest/schemas": "^29.6.3", @@ -18837,6 +18832,11 @@ "sisteransi": "^1.0.5" } }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", @@ -18866,9 +18866,9 @@ "dev": true }, "pure-rand": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.2.tgz", - "integrity": "sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ==", + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.4.tgz", + "integrity": "sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==", "dev": true }, "qs": { @@ -20206,22 +20206,14 @@ "dev": true }, "v8-to-istanbul": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz", - "integrity": "sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==", + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.3.tgz", + "integrity": "sha512-9lDD+EVI2fjFsMWXc6dy5JJzBsVTcQ2fVkfBvncZ6xJWG9wtBhOldG+mHkSL0+V1K/xgZz0JDO5UT5hFwHUghg==", "dev": true, "requires": { "@jridgewell/trace-mapping": "^0.3.12", "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0" - }, - "dependencies": { - "convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - } + "convert-source-map": "^2.0.0" } }, "validate-npm-package-name": { diff --git a/serverless/virus-scanner/package.json b/serverless/virus-scanner/package.json index 973f4ab9ad..1b387997e7 100644 --- a/serverless/virus-scanner/package.json +++ b/serverless/virus-scanner/package.json @@ -16,7 +16,7 @@ "@aws-sdk/client-s3": "^3.363.0", "@aws-sdk/client-ssm": "^3.363.0", "aws-lambda-ric": "^2.1.0", - "axios": "^0.27.2", + "axios": "^1.6.2", "clamscan": "^2.1.2", "convict": "^6.2.4", "http-status-codes": "^2.2.0", @@ -37,7 +37,7 @@ "jest-environment-jsdom": "^29.5.0", "jest-extended": "^3.2.4", "jest-localstorage-mock": "^2.4.26", - "jest-mock-axios": "^4.7.2", + "jest-mock-axios": "^4.7.3", "serverless": "^3.19.0", "serverless-dotenv-plugin": "^4.0.1", "ts-jest": "^29.0.5", From 9d426d4d729aaaa8e203ed981c6d066f0bb3a479 Mon Sep 17 00:00:00 2001 From: wanlingt <56983748+wanlingt@users.noreply.github.com> Date: Wed, 15 Nov 2023 16:32:24 +0800 Subject: [PATCH 09/12] fix: add learn more link (#6897) --- .../components/AnnouncementsFeatureList.tsx | 3 ++- frontend/src/features/whats-new/FeatureUpdateList.ts | 5 ++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/src/features/rollout-announcement/components/AnnouncementsFeatureList.tsx b/frontend/src/features/rollout-announcement/components/AnnouncementsFeatureList.tsx index 5800d0e472..0b301d7221 100644 --- a/frontend/src/features/rollout-announcement/components/AnnouncementsFeatureList.tsx +++ b/frontend/src/features/rollout-announcement/components/AnnouncementsFeatureList.tsx @@ -1,4 +1,4 @@ -import { GUIDE_PAYMENTS_ENTRY } from '~constants/links' +import { GUIDE_PAYMENTS_ENTRY, GUIDE_SPCP_ESRVCID } from '~constants/links' import { FeatureUpdateImage } from '~features/whats-new/FeatureUpdateList' @@ -20,6 +20,7 @@ export const NEW_FEATURES: NewFeature[] = [ title: 'Myinfo fields for Storage mode forms', description: 'Get verified data from respondents by adding Myinfo fields to your Storage mode form. To enable Myinfo fields, select one of our Myinfo-enabled authentication options in your form’s settings.', + learnMoreLink: GUIDE_SPCP_ESRVCID, image: { url: myInfoStorageMode, alt: 'Myinfo fields for Storage mode forms', diff --git a/frontend/src/features/whats-new/FeatureUpdateList.ts b/frontend/src/features/whats-new/FeatureUpdateList.ts index 60de1adb92..04131e44e4 100644 --- a/frontend/src/features/whats-new/FeatureUpdateList.ts +++ b/frontend/src/features/whats-new/FeatureUpdateList.ts @@ -1,7 +1,7 @@ import dedent from 'dedent' import { JsonObject, RequireExactlyOne } from 'type-fest' -import { GUIDE_PAYMENTS_ENTRY } from '~constants/links' +import { GUIDE_PAYMENTS_ENTRY, GUIDE_SPCP_ESRVCID } from '~constants/links' import Animation2 from './assets/2-payments.json' import Animation3 from './assets/3-search-and-filter.json' @@ -38,8 +38,7 @@ export const FEATURE_UPDATE_LIST: FeatureUpdateList = { { title: 'Myinfo fields for Storage mode forms', date: new Date('16 Nov 2023 GMT+8'), - description: - 'Get verified data from respondents by adding Myinfo fields to your Storage mode form. To enable Myinfo fields, select one of our Myinfo-enabled authentication options in your form’s settings.', + description: `Get verified data from respondents by adding Myinfo fields to your Storage mode form. To enable Myinfo fields, select one of our Myinfo-enabled authentication options in your form’s settings. [Learn more](${GUIDE_SPCP_ESRVCID})`, image: { url: MyInfoStorageMode, alt: 'Myinfo fields for Storage mode forms', From 1665a48b0ff133378c7126d3fedf8f1145f67111 Mon Sep 17 00:00:00 2001 From: wanlingt Date: Wed, 15 Nov 2023 16:38:26 +0800 Subject: [PATCH 10/12] chore: bump version to v6.89.0 --- CHANGELOG.md | 23 +++++++++++++++++++++++ frontend/package-lock.json | 4 ++-- frontend/package.json | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 5 files changed, 29 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0572f78013..1804b378fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,12 +4,35 @@ All notable changes to this project will be documented in this file. Dates are d Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). +#### [v6.89.0](https://github.com/opengovsg/FormSG/compare/v6.89.0...v6.89.0) + +- fix: add learn more link [`#6897`](https://github.com/opengovsg/FormSG/pull/6897) + +#### [v6.89.0](https://github.com/opengovsg/FormSG/compare/v6.88.0...v6.89.0) + +> 15 November 2023 + +- chore: security upgrade axios to 1.6.2 [`#6893`](https://github.com/opengovsg/FormSG/pull/6893) +- feat: myinfo for storage-mode [`#6870`](https://github.com/opengovsg/FormSG/pull/6870) +- feat: announcement modal and what's new for myinfo storage-mode [`#6892`](https://github.com/opengovsg/FormSG/pull/6892) +- chore: remove eb shift frontend feature flags [`#6869`](https://github.com/opengovsg/FormSG/pull/6869) +- chore(deps-dev): bump @types/lodash from 4.14.200 to 4.14.201 in /shared [`#6888`](https://github.com/opengovsg/FormSG/pull/6888) +- chore(deps): bump axios from 1.2.1 to 1.6.0 in /frontend [`#6887`](https://github.com/opengovsg/FormSG/pull/6887) +- fix(deps): bump axios from 1.2.1 to 1.6.0 [`#6886`](https://github.com/opengovsg/FormSG/pull/6886) +- fix(deps): bump type-fest from 4.5.0 to 4.7.1 in /shared [`#6883`](https://github.com/opengovsg/FormSG/pull/6883) +- build: merge release 6.88.0 into develop [`#6882`](https://github.com/opengovsg/FormSG/pull/6882) +- build: release v6.88.0 [`#6881`](https://github.com/opengovsg/FormSG/pull/6881) +- chore: bump version to v6.89.0 [`e511d27`](https://github.com/opengovsg/FormSG/commit/e511d27cee4cf0ad5f5687046dfb25577125942d) + #### [v6.88.0](https://github.com/opengovsg/FormSG/compare/v6.87.0...v6.88.0) +> 9 November 2023 + - chore: add note to update guide to sync with file exts [`#6876`](https://github.com/opengovsg/FormSG/pull/6876) - fix(datepicker): webkit-related stylings [`#6875`](https://github.com/opengovsg/FormSG/pull/6875) - build: merge release 6.87.0 into develop [`#6879`](https://github.com/opengovsg/FormSG/pull/6879) - build: release v6.87.0 [`#6878`](https://github.com/opengovsg/FormSG/pull/6878) +- chore: bump version to v6.88.0 [`352ae5c`](https://github.com/opengovsg/FormSG/commit/352ae5c49ed30d73450364f8b4d6048e21f72ee2) #### [v6.87.0](https://github.com/opengovsg/FormSG/compare/v6.86.0...v6.87.0) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 599fcb5566..6dae895358 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "form-frontend", - "version": "6.88.0", + "version": "6.89.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "form-frontend", - "version": "6.88.0", + "version": "6.89.0", "hasInstallScript": true, "dependencies": { "@chakra-ui/react": "^1.8.6", diff --git a/frontend/package.json b/frontend/package.json index 162f127ab9..26368530f7 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "form-frontend", - "version": "6.88.0", + "version": "6.89.0", "homepage": ".", "private": true, "dependencies": { diff --git a/package-lock.json b/package-lock.json index e8f4169600..9cca008f95 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "FormSG", - "version": "6.88.0", + "version": "6.89.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "FormSG", - "version": "6.88.0", + "version": "6.89.0", "hasInstallScript": true, "dependencies": { "@aws-sdk/client-cloudwatch-logs": "^3.347.1", diff --git a/package.json b/package.json index 1dcf9047cd..9df9a1e8e7 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "FormSG", "description": "Form Manager for Government", - "version": "6.88.0", + "version": "6.89.0", "homepage": "https://form.gov.sg", "authors": [ "FormSG " From 5a45c980dbe3fc8c15eacb3ff1827f3003fcbfc4 Mon Sep 17 00:00:00 2001 From: wanlingt Date: Wed, 15 Nov 2023 17:39:35 +0800 Subject: [PATCH 11/12] fix: remove email mode from myinfo limit message --- frontend/src/components/InlineMessage/InlineMessage.stories.tsx | 2 +- .../FieldListDrawer/field-panels/MyInfoPanel.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/InlineMessage/InlineMessage.stories.tsx b/frontend/src/components/InlineMessage/InlineMessage.stories.tsx index 2181f21d0d..a74b3fa60d 100644 --- a/frontend/src/components/InlineMessage/InlineMessage.stories.tsx +++ b/frontend/src/components/InlineMessage/InlineMessage.stories.tsx @@ -41,6 +41,6 @@ Warning.args = { export const Error = InlineMessageTemplate.bind({}) Error.args = { variant: 'error', - children: `Only 30 MyInfo fields are allowed in Email mode (30/30). [Learn more](http://localhost:6006)`, + children: `Only 30 MyInfo fields are allowed (30/30). [Learn more](http://localhost:6006)`, useMarkdown: true, } diff --git a/frontend/src/features/admin-form/create/builder-and-design/BuilderAndDesignDrawer/FieldListDrawer/field-panels/MyInfoPanel.tsx b/frontend/src/features/admin-form/create/builder-and-design/BuilderAndDesignDrawer/FieldListDrawer/field-panels/MyInfoPanel.tsx index 5d728ddc30..9502d22aa8 100644 --- a/frontend/src/features/admin-form/create/builder-and-design/BuilderAndDesignDrawer/FieldListDrawer/field-panels/MyInfoPanel.tsx +++ b/frontend/src/features/admin-form/create/builder-and-design/BuilderAndDesignDrawer/FieldListDrawer/field-panels/MyInfoPanel.tsx @@ -252,7 +252,7 @@ const MyInfoText = ({ return ( - {`Only 30 MyInfo fields are allowed in Email mode (${numMyInfoFields}/30). `} + {`Only 30 MyInfo fields are allowed (${numMyInfoFields}/30). `} Learn more From 8f753e625be00a8d11220d15d9482ed43441dc17 Mon Sep 17 00:00:00 2001 From: wanlingt <56983748+wanlingt@users.noreply.github.com> Date: Thu, 16 Nov 2023 12:07:03 +0800 Subject: [PATCH 12/12] fix: remove myinfo child from storage mode (#6901) * fix: remove myinfo child from storage mode * fix: re-add the formatting --- .../FieldListDrawer/field-panels/MyInfoPanel.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/frontend/src/features/admin-form/create/builder-and-design/BuilderAndDesignDrawer/FieldListDrawer/field-panels/MyInfoPanel.tsx b/frontend/src/features/admin-form/create/builder-and-design/BuilderAndDesignDrawer/FieldListDrawer/field-panels/MyInfoPanel.tsx index 9502d22aa8..b53afc0eb7 100644 --- a/frontend/src/features/admin-form/create/builder-and-design/BuilderAndDesignDrawer/FieldListDrawer/field-panels/MyInfoPanel.tsx +++ b/frontend/src/features/admin-form/create/builder-and-design/BuilderAndDesignDrawer/FieldListDrawer/field-panels/MyInfoPanel.tsx @@ -5,7 +5,12 @@ import { Box, Text } from '@chakra-ui/react' import { useFeatureIsOn, useGrowthBook } from '@growthbook/growthbook-react' import { featureFlags } from '~shared/constants' -import { AdminFormDto, FormAuthType, MyInfoAttribute } from '~shared/types' +import { + AdminFormDto, + FormAuthType, + FormResponseMode, + MyInfoAttribute, +} from '~shared/types' import { GUIDE_EMAIL_MODE } from '~constants/links' import { ADMINFORM_SETTINGS_SINGPASS_SUBROUTE } from '~constants/routes' @@ -200,7 +205,8 @@ export const MyInfoFieldPanel = () => { )} - {user?.betaFlags?.children ? ( + {user?.betaFlags?.children && + form?.responseMode === FormResponseMode.Email ? ( {(provided) => (