diff --git a/back/src/__tests__/unit/HttpPeConnectGateway.unit.test.ts b/back/src/__tests__/unit/HttpPeConnectGateway.unit.test.ts index 93cf0c0f5c..f272da7da2 100644 --- a/back/src/__tests__/unit/HttpPeConnectGateway.unit.test.ts +++ b/back/src/__tests__/unit/HttpPeConnectGateway.unit.test.ts @@ -1,5 +1,9 @@ import { ConnectionRefusedError } from "shared/src/httpClient/errors/ConnectionRefused.error"; -import { createManagedAxiosInstance } from "shared/src/httpClient/ports/axios.port"; +import { + createManagedAxiosInstance, + ErrorMapper, + TargetMapper, +} from "../shared/src/httpClient/ports/axios.adapter"; import { HttpPeConnectGateway, @@ -12,7 +16,51 @@ describe("HttpPeConnectGateway", () => { it("should have a connexion error if the httpClient could not connect", async () => { const testGateway = new HttpPeConnectGateway( {} as unknown as HttpPeConnectGatewayConfig, - createManagedAxiosInstance(), + createManagedAxiosInstance( + {} as TargetMapper<"plop">, + {} as ErrorMapper<"plop">, + ), + ); + + await expectPromiseToFailWithError( + testGateway.peAccessTokenThroughAuthorizationCode("fakeCode"), + new ConnectionRefusedError( + `Could not connect to server : ${JSON.stringify( + { + code: "ECONNREFUSED", + address: "127.0.0.1", + port: 80, + config: { + headers: { + Accept: "application/json, text/plain, */*", + "Content-Type": "application/x-www-form-urlencoded", + "User-Agent": "axios/0.26.1", + "Content-Length": 81, + }, + method: "post", + data: "grant_type=authorization_code&code=fakeCode&redirect_uri=undefined/api/pe-connect", + }, + }, + null, + 2, + )}`, + ), + ); + }); + + it.skip("url in response config shoud match full url | baseUrl should contain target", async () => { + const testGateway = new HttpPeConnectGateway( + {} as unknown as HttpPeConnectGatewayConfig, + createManagedAxiosInstance( + { + OAUTH2_ACCESS_TOKEN_STEP_2: `https://undefined/connexion/oauth2/access_token?realm=%2Findividu`, + }, + { + OAUTH2_ACCESS_TOKEN_STEP_2: { + 401: () => new Error(`J'aime le paté`), + }, + }, + ), ); await expectPromiseToFailWithError( @@ -31,7 +79,8 @@ describe("HttpPeConnectGateway", () => { "Content-Length": 81, }, method: "post", - url: "undefined/connexion/oauth2/access_token?realm=%2Findividu", + baseUrl: `OAUTH2_ACCESS_TOKEN_STEP_2`, + url: "https://undefined/connexion/oauth2/access_token?realm=%2Findividu", data: "grant_type=authorization_code&code=fakeCode&redirect_uri=undefined/api/pe-connect", }, }, diff --git a/back/src/adapters/primary/config/createGateways.ts b/back/src/adapters/primary/config/createGateways.ts index a70b113b45..9efb180181 100644 --- a/back/src/adapters/primary/config/createGateways.ts +++ b/back/src/adapters/primary/config/createGateways.ts @@ -8,8 +8,16 @@ import { CachingAccessTokenGateway } from "../../secondary/core/CachingAccessTok import { HybridEmailGateway } from "../../secondary/emailGateway/HybridEmailGateway"; import { InMemoryEmailGateway } from "../../secondary/emailGateway/InMemoryEmailGateway"; import { SendinblueEmailGateway } from "../../secondary/emailGateway/SendinblueEmailGateway"; -import { createManagedAxiosInstance } from "shared/src/httpClient/ports/axios.port"; -import { HttpPeConnectGateway } from "../../secondary/HttpPeConnectGateway"; +import { + createManagedAxiosInstance, + ErrorMapper, + TargetMapper, +} from "../shared/src/httpClient/ports/axios.adapter"; +import { + HttpPeConnectGateway, + makeApiPeConnectUrls, + PeConnectUrlTargets, +} from "../../secondary/HttpPeConnectGateway"; import { HttpsSireneGateway } from "../../secondary/HttpsSireneGateway"; import { HttpLaBonneBoiteAPI } from "../../secondary/immersionOffer/HttpLaBonneBoiteAPI"; import { HttpPassEmploiGateway } from "../../secondary/immersionOffer/HttpPassEmploiGateway"; @@ -26,6 +34,7 @@ import { MinioDocumentGateway } from "../../secondary/MinioDocumentGateway"; import { ExcelReportingGateway } from "../../secondary/reporting/ExcelReportingGateway"; import { InMemoryReportingGateway } from "../../secondary/reporting/InMemoryReportingGateway"; import { AppConfig } from "./appConfig"; +import { ManagedRedirectError } from "../helpers/redirectErrors"; const logger = createLogger(__filename); @@ -56,6 +65,15 @@ export type Gateways = ReturnType extends Promise = { + PECONNECT_ADVISORS_INFO: { + 401: () => new ManagedRedirectError("peConnectAdvisorForbiddenAccess"), + }, + OAUTH2_ACCESS_TOKEN_STEP_2: { + 400: () => new ManagedRedirectError("peConnectInvalidGrant"), + }, +}; + // eslint-disable-next-line @typescript-eslint/require-await export const createGateways = async (config: AppConfig, clock: Clock) => { logger.info({ @@ -65,6 +83,18 @@ export const createGateways = async (config: AppConfig, clock: Clock) => { romeRepository: config.romeRepository, }); + // TODO Move from here + const httpPeConnectGatewayTargetMapper: TargetMapper = + config.poleEmploiGateway === "HTTPS" + ? makeApiPeConnectUrls({ + peAuthCandidatUrl: + config.poleEmploiAccessTokenConfig.peAuthCandidatUrl, + immersionBaseUrl: + config.poleEmploiAccessTokenConfig.immersionFacileBaseUrl, + peApiUrl: config.poleEmploiAccessTokenConfig.peApiUrl, + }) + : ({} as TargetMapper); + const cachingAccessTokenGateway = [ config.laBonneBoiteGateway, config.poleEmploiGateway, @@ -110,7 +140,10 @@ export const createGateways = async (config: AppConfig, clock: Clock) => { config.peConnectGateway === "HTTPS" ? new HttpPeConnectGateway( config.poleEmploiAccessTokenConfig, - createManagedAxiosInstance(), + createManagedAxiosInstance( + httpPeConnectGatewayTargetMapper, + errorMapper, + ), /*new ExponentialBackoffRetryStrategy( 3_000, 15_0000, diff --git a/back/src/adapters/secondary/HttpPeConnectGateway.ts b/back/src/adapters/secondary/HttpPeConnectGateway.ts index 60fe9668cb..6b93418b18 100644 --- a/back/src/adapters/secondary/HttpPeConnectGateway.ts +++ b/back/src/adapters/secondary/HttpPeConnectGateway.ts @@ -47,6 +47,7 @@ export class HttpPeConnectGateway implements PeConnectGateway { private readonly config: HttpPeConnectGatewayConfig, private readonly httpClient: AxiosInstance, ) { + // TODO Extract and inject this.apiPeConnectUrls = makeApiPeConnectUrls({ peAuthCandidatUrl: config.peAuthCandidatUrl, immersionBaseUrl: config.immersionFacileBaseUrl, @@ -82,12 +83,16 @@ export class HttpPeConnectGateway implements PeConnectGateway { redirect_uri: this.apiPeConnectUrls.REGISTERED_REDIRECT_URL, }; - const response: AxiosResponse = - await getOAuthGetAccessTokenThroughAuthorizationCodeResponse( - this.httpClient, - this.apiPeConnectUrls.OAUTH2_ACCESS_TOKEN_STEP_2, + const response: AxiosResponse = await this.httpClient.post( + this.apiPeConnectUrls.OAUTH2_ACCESS_TOKEN_STEP_2, + queryParamsAsString( getAccessTokenPayload, - ); + ), + { + headers: headersUrlEncoded(), + timeout: AXIOS_TIMEOUT_FIVE_SECOND, + }, + ); const externalAccessToken: ExternalAccessToken = validateAndParseZodSchema( externalAccessTokenSchema, @@ -137,6 +142,8 @@ export class HttpPeConnectGateway implements PeConnectGateway { }, ); + // Here + const body = this.extractAdvisorsBodyFromResponse(response); const advisors: ExternalPeConnectAdvisor[] = validateAndParseZodSchema( @@ -181,14 +188,14 @@ export class HttpPeConnectGateway implements PeConnectGateway { } } -type PeConnectUrlTargets = +export type PeConnectUrlTargets = | "OAUTH2_AUTH_CODE_STEP_1" | "OAUTH2_ACCESS_TOKEN_STEP_2" | "REGISTERED_REDIRECT_URL" | "PECONNECT_USER_INFO" | "PECONNECT_ADVISORS_INFO"; -const makeApiPeConnectUrls = (params: { +export const makeApiPeConnectUrls = (params: { peAuthCandidatUrl: AbsoluteUrl; peApiUrl: AbsoluteUrl; immersionBaseUrl: AbsoluteUrl; @@ -221,11 +228,11 @@ const headersWithAuthPeAccessToken = ( const getOAuthGetAccessTokenThroughAuthorizationCodeResponse = ( axiosInstance: AxiosInstance, - url: AbsoluteUrl, + targetUrl: PeConnectUrlTargets, getAccessTokenPayload: ExternalPeConnectOAuthGetTokenWithCodeGrantPayload, ): Promise => axiosInstance.post( - url, + targetUrl, queryParamsAsString( getAccessTokenPayload, ), diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d77c5bc627..e825833fd0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -285,10 +285,14 @@ importers: shared: specifiers: + '@babel/core': ^7.17.9 + '@babel/preset-env': ^7.16.11 + '@babel/preset-typescript': ^7.16.7 '@types/jest': ^27.4.1 '@types/node': ^17.0.33 '@types/ramda': ^0.28.1 axios: ^0.26.1 + babel-jest: ^27.5.1 date-fns: ^2.28.0 jest: ^27.5.1 ramda: ^0.28.0 @@ -300,9 +304,13 @@ importers: ramda: 0.28.0 zod: 3.17.2 devDependencies: + '@babel/core': 7.18.2 + '@babel/preset-env': 7.18.0_@babel+core@7.18.2 + '@babel/preset-typescript': 7.17.12_@babel+core@7.18.2 '@types/jest': 27.5.1 '@types/node': 17.0.35 '@types/ramda': 0.28.13 + babel-jest: 27.5.1_@babel+core@7.18.2 jest: 27.5.1 typescript: 4.6.4 @@ -314,6 +322,7 @@ packages: dependencies: '@jridgewell/gen-mapping': 0.1.1 '@jridgewell/trace-mapping': 0.3.13 + dev: true /@babel/code-frame/7.16.7: resolution: {integrity: sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==} @@ -324,6 +333,7 @@ packages: /@babel/compat-data/7.17.10: resolution: {integrity: sha512-GZt/TCsG70Ms19gfZO1tM4CVnXsPgEPBCpJu+Qz3L0LUDsY5nZqFZglIoPC1kIYOtNBZlrnFT+klg12vFGZXrw==} engines: {node: '>=6.9.0'} + dev: true /@babel/core/7.12.9: resolution: {integrity: sha512-gTXYh3M5wb7FRXQy+FErKFAv90BnlOuNn1QkCK2lREoPAjrQCO49+HVSrFoe5uakFAF5eenS75KbO2vQiLrTMQ==} @@ -370,6 +380,7 @@ packages: semver: 6.3.0 transitivePeerDependencies: - supports-color + dev: true /@babel/core/7.18.2: resolution: {integrity: sha512-A8pri1YJiC5UnkdrWcmfZTJTV85b4UXTAfImGmCfYmax4TR9Cw8sDS0MOk++Gp2mE/BefVJ5nwy5yzqNJbP/DQ==} @@ -392,6 +403,7 @@ packages: semver: 6.3.0 transitivePeerDependencies: - supports-color + dev: true /@babel/generator/7.18.0: resolution: {integrity: sha512-81YO9gGx6voPXlvYdZBliFXAZU8vZ9AZ6z+CjlmcnaeOcYSFbMTpdeDUO9xD9dh/68Vq03I8ZspfUTPfitcDHg==} @@ -400,6 +412,7 @@ packages: '@babel/types': 7.18.4 '@jridgewell/gen-mapping': 0.3.1 jsesc: 2.5.2 + dev: true /@babel/generator/7.18.2: resolution: {integrity: sha512-W1lG5vUwFvfMd8HVXqdfbuG7RuaSrTCCD8cl8fP8wOivdbtbIg2Db3IWUcgvfxKbbn6ZBGYRW/Zk1MIwK49mgw==} @@ -408,6 +421,7 @@ packages: '@babel/types': 7.18.4 '@jridgewell/gen-mapping': 0.3.1 jsesc: 2.5.2 + dev: true /@babel/helper-annotate-as-pure/7.16.7: resolution: {integrity: sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==} @@ -435,18 +449,6 @@ packages: '@babel/helper-validator-option': 7.16.7 browserslist: 4.20.3 semver: 6.3.0 - - /@babel/helper-compilation-targets/7.17.10_@babel+core@7.18.2: - resolution: {integrity: sha512-gh3RxjWbauw/dFiU/7whjd0qN9K6nPJMqe6+Er7rOavFh0CQUSwhAE3IcTho2rywPJFxej6TUUHDkWcYI6gGqQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/compat-data': 7.17.10 - '@babel/core': 7.18.2 - '@babel/helper-validator-option': 7.16.7 - browserslist: 4.20.3 - semver: 6.3.0 dev: true /@babel/helper-compilation-targets/7.18.2_@babel+core@7.18.0: @@ -473,6 +475,7 @@ packages: '@babel/helper-validator-option': 7.16.7 browserslist: 4.20.3 semver: 6.3.0 + dev: true /@babel/helper-create-class-features-plugin/7.18.0_@babel+core@7.18.0: resolution: {integrity: sha512-Kh8zTGR9de3J63e5nS0rQUdRs/kbtwoeQQ0sriS0lItjC96u8XXZN6lKpuyWd2coKSU13py/y+LTmThLuVX0Pg==} @@ -556,10 +559,10 @@ packages: '@babel/core': ^7.4.0-0 dependencies: '@babel/core': 7.18.0 - '@babel/helper-compilation-targets': 7.17.10_@babel+core@7.18.0 + '@babel/helper-compilation-targets': 7.18.2_@babel+core@7.18.0 '@babel/helper-module-imports': 7.16.7 '@babel/helper-plugin-utils': 7.17.12 - '@babel/traverse': 7.18.0 + '@babel/traverse': 7.18.2 debug: 4.3.4 lodash.debounce: 4.0.8 resolve: 1.22.0 @@ -574,10 +577,10 @@ packages: '@babel/core': ^7.4.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-compilation-targets': 7.17.10_@babel+core@7.18.2 + '@babel/helper-compilation-targets': 7.18.2_@babel+core@7.18.2 '@babel/helper-module-imports': 7.16.7 '@babel/helper-plugin-utils': 7.17.12 - '@babel/traverse': 7.18.0 + '@babel/traverse': 7.18.2 debug: 4.3.4 lodash.debounce: 4.0.8 resolve: 1.22.0 @@ -591,10 +594,12 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/types': 7.18.4 + dev: true /@babel/helper-environment-visitor/7.18.2: resolution: {integrity: sha512-14GQKWkX9oJzPiQQ7/J36FTXcD4kSp8egKjO9nINlSKiHITRA9q/R74qu8S9xlc/b/yjsJItQUeeh3xnGN0voQ==} engines: {node: '>=6.9.0'} + dev: true /@babel/helper-explode-assignable-expression/7.16.7: resolution: {integrity: sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ==} @@ -609,12 +614,14 @@ packages: dependencies: '@babel/template': 7.16.7 '@babel/types': 7.18.4 + dev: true /@babel/helper-hoist-variables/7.16.7: resolution: {integrity: sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==} engines: {node: '>=6.9.0'} dependencies: '@babel/types': 7.18.4 + dev: true /@babel/helper-member-expression-to-functions/7.17.7: resolution: {integrity: sha512-thxXgnQ8qQ11W2wVUObIqDL4p148VMxkt5T/qpN5k2fboRyzFGFmKsTGViquyM5QHKUy48OZoca8kw4ajaDPyw==} @@ -643,6 +650,7 @@ packages: '@babel/types': 7.18.4 transitivePeerDependencies: - supports-color + dev: true /@babel/helper-optimise-call-expression/7.16.7: resolution: {integrity: sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w==} @@ -670,19 +678,6 @@ packages: - supports-color dev: true - /@babel/helper-replace-supers/7.16.7: - resolution: {integrity: sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-environment-visitor': 7.18.2 - '@babel/helper-member-expression-to-functions': 7.17.7 - '@babel/helper-optimise-call-expression': 7.16.7 - '@babel/traverse': 7.18.2 - '@babel/types': 7.18.4 - transitivePeerDependencies: - - supports-color - dev: true - /@babel/helper-replace-supers/7.18.2: resolution: {integrity: sha512-XzAIyxx+vFnrOxiQrToSUOzUOn0e1J2Li40ntddek1Y69AXUTXoDJ40/D5RdjFu7s7qHiaeoTiempZcbuVXh2Q==} engines: {node: '>=6.9.0'} @@ -701,6 +696,7 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/types': 7.18.4 + dev: true /@babel/helper-skip-transparent-expression-wrappers/7.16.0: resolution: {integrity: sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw==} @@ -714,6 +710,7 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/types': 7.18.4 + dev: true /@babel/helper-validator-identifier/7.16.7: resolution: {integrity: sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==} @@ -722,6 +719,7 @@ packages: /@babel/helper-validator-option/7.16.7: resolution: {integrity: sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==} engines: {node: '>=6.9.0'} + dev: true /@babel/helper-wrap-function/7.16.8: resolution: {integrity: sha512-8RpyRVIAW1RcDDGTA+GpPAwV22wXCfKOoM9bet6TLkGIFTkRQSkH1nMQ5Yet4MpoXe1ZwHPVtNasc2w0uZMqnw==} @@ -729,7 +727,7 @@ packages: dependencies: '@babel/helper-function-name': 7.17.9 '@babel/template': 7.16.7 - '@babel/traverse': 7.18.0 + '@babel/traverse': 7.18.2 '@babel/types': 7.18.4 transitivePeerDependencies: - supports-color @@ -744,6 +742,7 @@ packages: '@babel/types': 7.18.0 transitivePeerDependencies: - supports-color + dev: true /@babel/helpers/7.18.2: resolution: {integrity: sha512-j+d+u5xT5utcQSzrh9p+PaJX94h++KN+ng9b9WEJq7pkUPAd61FGqhjuUEdfknb3E/uDBb7ruwEeKkIxNJPIrg==} @@ -754,6 +753,7 @@ packages: '@babel/types': 7.18.4 transitivePeerDependencies: - supports-color + dev: true /@babel/highlight/7.17.12: resolution: {integrity: sha512-7yykMVF3hfZY2jsHZEEgLc+3x4o1O+fYyULu11GynEUQNwB6lua+IIQn1FiJxNucd5UlyJryrwsOh8PL9Sn8Qg==} @@ -769,6 +769,7 @@ packages: hasBin: true dependencies: '@babel/types': 7.18.4 + dev: true /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/7.17.12_@babel+core@7.18.0: resolution: {integrity: sha512-xCJQXl4EeQ3J9C4yOmpTrtVGmzpm2iSzyxbkZHw7UCnZBftHpF/hpII80uWVyVrc40ytIClHjgWGTG1g/yB+aw==} @@ -1226,6 +1227,7 @@ packages: dependencies: '@babel/core': 7.18.0 '@babel/helper-plugin-utils': 7.17.12 + dev: true /@babel/plugin-syntax-async-generators/7.8.4_@babel+core@7.18.2: resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} @@ -1234,6 +1236,7 @@ packages: dependencies: '@babel/core': 7.18.2 '@babel/helper-plugin-utils': 7.17.12 + dev: true /@babel/plugin-syntax-bigint/7.8.3_@babel+core@7.18.0: resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} @@ -1242,6 +1245,7 @@ packages: dependencies: '@babel/core': 7.18.0 '@babel/helper-plugin-utils': 7.17.12 + dev: true /@babel/plugin-syntax-bigint/7.8.3_@babel+core@7.18.2: resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} @@ -1250,6 +1254,7 @@ packages: dependencies: '@babel/core': 7.18.2 '@babel/helper-plugin-utils': 7.17.12 + dev: true /@babel/plugin-syntax-class-properties/7.12.13_@babel+core@7.18.0: resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} @@ -1258,6 +1263,7 @@ packages: dependencies: '@babel/core': 7.18.0 '@babel/helper-plugin-utils': 7.17.12 + dev: true /@babel/plugin-syntax-class-properties/7.12.13_@babel+core@7.18.2: resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} @@ -1266,6 +1272,7 @@ packages: dependencies: '@babel/core': 7.18.2 '@babel/helper-plugin-utils': 7.17.12 + dev: true /@babel/plugin-syntax-class-static-block/7.14.5_@babel+core@7.18.0: resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} @@ -1380,6 +1387,7 @@ packages: dependencies: '@babel/core': 7.18.0 '@babel/helper-plugin-utils': 7.17.12 + dev: true /@babel/plugin-syntax-import-meta/7.10.4_@babel+core@7.18.2: resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} @@ -1388,6 +1396,7 @@ packages: dependencies: '@babel/core': 7.18.2 '@babel/helper-plugin-utils': 7.17.12 + dev: true /@babel/plugin-syntax-json-strings/7.8.3_@babel+core@7.18.0: resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} @@ -1396,6 +1405,7 @@ packages: dependencies: '@babel/core': 7.18.0 '@babel/helper-plugin-utils': 7.17.12 + dev: true /@babel/plugin-syntax-json-strings/7.8.3_@babel+core@7.18.2: resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} @@ -1404,6 +1414,7 @@ packages: dependencies: '@babel/core': 7.18.2 '@babel/helper-plugin-utils': 7.17.12 + dev: true /@babel/plugin-syntax-jsx/7.12.1_@babel+core@7.12.9: resolution: {integrity: sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg==} @@ -1431,6 +1442,7 @@ packages: dependencies: '@babel/core': 7.18.2 '@babel/helper-plugin-utils': 7.17.12 + dev: true /@babel/plugin-syntax-logical-assignment-operators/7.10.4_@babel+core@7.18.0: resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} @@ -1439,6 +1451,7 @@ packages: dependencies: '@babel/core': 7.18.0 '@babel/helper-plugin-utils': 7.17.12 + dev: true /@babel/plugin-syntax-logical-assignment-operators/7.10.4_@babel+core@7.18.2: resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} @@ -1447,6 +1460,7 @@ packages: dependencies: '@babel/core': 7.18.2 '@babel/helper-plugin-utils': 7.17.12 + dev: true /@babel/plugin-syntax-nullish-coalescing-operator/7.8.3_@babel+core@7.18.0: resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} @@ -1455,6 +1469,7 @@ packages: dependencies: '@babel/core': 7.18.0 '@babel/helper-plugin-utils': 7.17.12 + dev: true /@babel/plugin-syntax-nullish-coalescing-operator/7.8.3_@babel+core@7.18.2: resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} @@ -1463,6 +1478,7 @@ packages: dependencies: '@babel/core': 7.18.2 '@babel/helper-plugin-utils': 7.17.12 + dev: true /@babel/plugin-syntax-numeric-separator/7.10.4_@babel+core@7.18.0: resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} @@ -1471,6 +1487,7 @@ packages: dependencies: '@babel/core': 7.18.0 '@babel/helper-plugin-utils': 7.17.12 + dev: true /@babel/plugin-syntax-numeric-separator/7.10.4_@babel+core@7.18.2: resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} @@ -1479,6 +1496,7 @@ packages: dependencies: '@babel/core': 7.18.2 '@babel/helper-plugin-utils': 7.17.12 + dev: true /@babel/plugin-syntax-object-rest-spread/7.8.3_@babel+core@7.12.9: resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} @@ -1496,6 +1514,7 @@ packages: dependencies: '@babel/core': 7.18.0 '@babel/helper-plugin-utils': 7.17.12 + dev: true /@babel/plugin-syntax-object-rest-spread/7.8.3_@babel+core@7.18.2: resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} @@ -1504,6 +1523,7 @@ packages: dependencies: '@babel/core': 7.18.2 '@babel/helper-plugin-utils': 7.17.12 + dev: true /@babel/plugin-syntax-optional-catch-binding/7.8.3_@babel+core@7.18.0: resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} @@ -1512,6 +1532,7 @@ packages: dependencies: '@babel/core': 7.18.0 '@babel/helper-plugin-utils': 7.17.12 + dev: true /@babel/plugin-syntax-optional-catch-binding/7.8.3_@babel+core@7.18.2: resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} @@ -1520,6 +1541,7 @@ packages: dependencies: '@babel/core': 7.18.2 '@babel/helper-plugin-utils': 7.17.12 + dev: true /@babel/plugin-syntax-optional-chaining/7.8.3_@babel+core@7.18.0: resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} @@ -1528,6 +1550,7 @@ packages: dependencies: '@babel/core': 7.18.0 '@babel/helper-plugin-utils': 7.17.12 + dev: true /@babel/plugin-syntax-optional-chaining/7.8.3_@babel+core@7.18.2: resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} @@ -1536,6 +1559,7 @@ packages: dependencies: '@babel/core': 7.18.2 '@babel/helper-plugin-utils': 7.17.12 + dev: true /@babel/plugin-syntax-private-property-in-object/7.14.5_@babel+core@7.18.0: resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} @@ -1565,6 +1589,7 @@ packages: dependencies: '@babel/core': 7.18.0 '@babel/helper-plugin-utils': 7.17.12 + dev: true /@babel/plugin-syntax-top-level-await/7.14.5_@babel+core@7.18.2: resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} @@ -1574,6 +1599,7 @@ packages: dependencies: '@babel/core': 7.18.2 '@babel/helper-plugin-utils': 7.17.12 + dev: true /@babel/plugin-syntax-typescript/7.17.12_@babel+core@7.18.0: resolution: {integrity: sha512-TYY0SXFiO31YXtNg3HtFwNJHjLsAyIIhAhNWkQ5whPPS7HWUFlg9z0Ta4qAQNjQbP1wsSt/oKkmZ/4/WWdMUpw==} @@ -1593,6 +1619,7 @@ packages: dependencies: '@babel/core': 7.18.2 '@babel/helper-plugin-utils': 7.17.12 + dev: true /@babel/plugin-transform-arrow-functions/7.17.12_@babel+core@7.18.0: resolution: {integrity: sha512-PHln3CNi/49V+mza4xMwrg+WGYevSF1oaiXaC2EQfdp4HWlSjRsrDXWJiQBKpP7749u6vQ9mcry2uuFOv5CXvA==} @@ -1694,7 +1721,7 @@ packages: '@babel/helper-function-name': 7.17.9 '@babel/helper-optimise-call-expression': 7.16.7 '@babel/helper-plugin-utils': 7.17.12 - '@babel/helper-replace-supers': 7.16.7 + '@babel/helper-replace-supers': 7.18.2 '@babel/helper-split-export-declaration': 7.16.7 globals: 11.12.0 transitivePeerDependencies: @@ -1713,7 +1740,7 @@ packages: '@babel/helper-function-name': 7.17.9 '@babel/helper-optimise-call-expression': 7.16.7 '@babel/helper-plugin-utils': 7.17.12 - '@babel/helper-replace-supers': 7.16.7 + '@babel/helper-replace-supers': 7.18.2 '@babel/helper-split-export-declaration': 7.16.7 globals: 11.12.0 transitivePeerDependencies: @@ -1862,7 +1889,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.0 - '@babel/helper-compilation-targets': 7.17.10_@babel+core@7.18.0 + '@babel/helper-compilation-targets': 7.18.2_@babel+core@7.18.0 '@babel/helper-function-name': 7.17.9 '@babel/helper-plugin-utils': 7.17.12 dev: true @@ -1874,7 +1901,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-compilation-targets': 7.17.10_@babel+core@7.18.2 + '@babel/helper-compilation-targets': 7.18.2_@babel+core@7.18.2 '@babel/helper-function-name': 7.17.9 '@babel/helper-plugin-utils': 7.17.12 dev: true @@ -2085,7 +2112,7 @@ packages: dependencies: '@babel/core': 7.18.0 '@babel/helper-plugin-utils': 7.17.12 - '@babel/helper-replace-supers': 7.16.7 + '@babel/helper-replace-supers': 7.18.2 transitivePeerDependencies: - supports-color dev: true @@ -2098,7 +2125,7 @@ packages: dependencies: '@babel/core': 7.18.2 '@babel/helper-plugin-utils': 7.17.12 - '@babel/helper-replace-supers': 7.16.7 + '@babel/helper-replace-supers': 7.18.2 transitivePeerDependencies: - supports-color dev: true @@ -2464,7 +2491,7 @@ packages: dependencies: '@babel/compat-data': 7.17.10 '@babel/core': 7.18.0 - '@babel/helper-compilation-targets': 7.17.10_@babel+core@7.18.0 + '@babel/helper-compilation-targets': 7.18.2_@babel+core@7.18.0 '@babel/helper-plugin-utils': 7.17.12 '@babel/helper-validator-option': 7.16.7 '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.17.12_@babel+core@7.18.0 @@ -2532,7 +2559,7 @@ packages: '@babel/plugin-transform-unicode-escapes': 7.16.7_@babel+core@7.18.0 '@babel/plugin-transform-unicode-regex': 7.16.7_@babel+core@7.18.0 '@babel/preset-modules': 0.1.5_@babel+core@7.18.0 - '@babel/types': 7.18.0 + '@babel/types': 7.18.4 babel-plugin-polyfill-corejs2: 0.3.1_@babel+core@7.18.0 babel-plugin-polyfill-corejs3: 0.5.2_@babel+core@7.18.0 babel-plugin-polyfill-regenerator: 0.3.1_@babel+core@7.18.0 @@ -2550,7 +2577,7 @@ packages: dependencies: '@babel/compat-data': 7.17.10 '@babel/core': 7.18.2 - '@babel/helper-compilation-targets': 7.17.10_@babel+core@7.18.2 + '@babel/helper-compilation-targets': 7.18.2_@babel+core@7.18.2 '@babel/helper-plugin-utils': 7.17.12 '@babel/helper-validator-option': 7.16.7 '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.17.12_@babel+core@7.18.2 @@ -2618,7 +2645,7 @@ packages: '@babel/plugin-transform-unicode-escapes': 7.16.7_@babel+core@7.18.2 '@babel/plugin-transform-unicode-regex': 7.16.7_@babel+core@7.18.2 '@babel/preset-modules': 0.1.5_@babel+core@7.18.2 - '@babel/types': 7.18.0 + '@babel/types': 7.18.4 babel-plugin-polyfill-corejs2: 0.3.1_@babel+core@7.18.2 babel-plugin-polyfill-corejs3: 0.5.2_@babel+core@7.18.2 babel-plugin-polyfill-regenerator: 0.3.1_@babel+core@7.18.2 @@ -2649,7 +2676,7 @@ packages: '@babel/helper-plugin-utils': 7.17.12 '@babel/plugin-proposal-unicode-property-regex': 7.17.12_@babel+core@7.18.0 '@babel/plugin-transform-dotall-regex': 7.16.7_@babel+core@7.18.0 - '@babel/types': 7.18.0 + '@babel/types': 7.18.4 esutils: 2.0.3 dev: true @@ -2662,7 +2689,7 @@ packages: '@babel/helper-plugin-utils': 7.17.12 '@babel/plugin-proposal-unicode-property-regex': 7.17.12_@babel+core@7.18.2 '@babel/plugin-transform-dotall-regex': 7.16.7_@babel+core@7.18.2 - '@babel/types': 7.18.0 + '@babel/types': 7.18.4 esutils: 2.0.3 dev: true @@ -2748,6 +2775,7 @@ packages: '@babel/code-frame': 7.16.7 '@babel/parser': 7.18.0 '@babel/types': 7.18.4 + dev: true /@babel/traverse/7.18.0: resolution: {integrity: sha512-oNOO4vaoIQoGjDQ84LgtF/IAlxlyqL4TUuoQ7xLkQETFaHkY1F7yazhB4Kt3VcZGL0ZF/jhrEpnXqUb0M7V3sw==} @@ -2765,6 +2793,7 @@ packages: globals: 11.12.0 transitivePeerDependencies: - supports-color + dev: true /@babel/traverse/7.18.2: resolution: {integrity: sha512-9eNwoeovJ6KH9zcCNnENY7DMFwTU9JdGCFtqNLfUAqtUHRCOsTOqWoffosP8vKmNYeSBUv3yVJXjfd8ucwOjUA==} @@ -2782,6 +2811,7 @@ packages: globals: 11.12.0 transitivePeerDependencies: - supports-color + dev: true /@babel/types/7.18.0: resolution: {integrity: sha512-vhAmLPAiC8j9K2GnsnLPCIH5wCrPpYIVBCWRBFDCB7Y/BXLqi/O+1RSTTM2bsmg6U/551+FCf9PNPxjABmxHTw==} @@ -2789,6 +2819,7 @@ packages: dependencies: '@babel/helper-validator-identifier': 7.16.7 to-fast-properties: 2.0.0 + dev: true /@babel/types/7.18.4: resolution: {integrity: sha512-ThN1mBcMq5pG/Vm2IcBmPPfyPXbd8S02rS+OBIDENdufvqC7Z/jHPCv9IcP01277aKtDI8g/2XysBN4hA8niiw==} @@ -2803,6 +2834,7 @@ packages: /@bcoe/v8-coverage/0.2.3: resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} + dev: true /@cnakazawa/watch/1.0.4: resolution: {integrity: sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==} @@ -2825,6 +2857,7 @@ packages: engines: {node: '>=12'} dependencies: '@jridgewell/trace-mapping': 0.3.9 + dev: false /@cypress/request/2.88.10: resolution: {integrity: sha512-Zp7F+R93N0yZyG34GutyTNr+okam7s/Fzc1+i3kcqOP8vk6OuajuE9qZJ6Rs+10/1JFtXFYMdyarnU1rZuJesg==} @@ -2985,6 +3018,7 @@ packages: find-root: 1.1.0 source-map: 0.5.7 stylis: 4.0.13 + dev: true /@emotion/cache/11.7.1: resolution: {integrity: sha512-r65Zy4Iljb8oyjtLeCuBH8Qjiy107dOYC6SJq7g7GV5UCQWMObY4SJDPGFjiiVpPrOJ2hmJOoBiYTC7hwx9E2A==} @@ -3052,6 +3086,7 @@ packages: '@types/react': 17.0.45 hoist-non-react-statics: 3.3.2 react: 17.0.2 + dev: true /@emotion/serialize/1.0.3: resolution: {integrity: sha512-2mSSvgLfyV3q+iVh3YWgNlUc2a9ZlDU7DjuP5MjK3AXRR0dYigCrP99aeFtaB2L/hjfEZdSThn5dsZ0ufqbvsA==} @@ -3111,6 +3146,7 @@ packages: '@emotion/utils': 1.1.0 '@types/react': 17.0.45 react: 17.0.2 + dev: true /@emotion/unitless/0.7.5: resolution: {integrity: sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==} @@ -3201,10 +3237,12 @@ packages: get-package-type: 0.1.0 js-yaml: 3.14.1 resolve-from: 5.0.0 + dev: true /@istanbuljs/schema/0.1.3: resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} engines: {node: '>=8'} + dev: true /@jest/console/27.5.1: resolution: {integrity: sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg==} @@ -3216,6 +3254,7 @@ packages: jest-message-util: 27.5.1 jest-util: 27.5.1 slash: 3.0.0 + dev: true /@jest/core/27.5.1: resolution: {integrity: sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ==} @@ -3305,6 +3344,7 @@ packages: - supports-color - ts-node - utf-8-validate + dev: true /@jest/environment/27.5.1: resolution: {integrity: sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==} @@ -3314,6 +3354,7 @@ packages: '@jest/types': 27.5.1 '@types/node': 17.0.35 jest-mock: 27.5.1 + dev: true /@jest/fake-timers/27.5.1: resolution: {integrity: sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==} @@ -3325,6 +3366,7 @@ packages: jest-message-util: 27.5.1 jest-mock: 27.5.1 jest-util: 27.5.1 + dev: true /@jest/globals/27.5.1: resolution: {integrity: sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==} @@ -3333,6 +3375,7 @@ packages: '@jest/environment': 27.5.1 '@jest/types': 27.5.1 expect: 27.5.1 + dev: true /@jest/reporters/27.5.1: resolution: {integrity: sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw==} @@ -3370,6 +3413,7 @@ packages: v8-to-istanbul: 8.1.1 transitivePeerDependencies: - supports-color + dev: true /@jest/source-map/27.5.1: resolution: {integrity: sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg==} @@ -3378,6 +3422,7 @@ packages: callsites: 3.1.0 graceful-fs: 4.2.10 source-map: 0.6.1 + dev: true /@jest/test-result/27.5.1: resolution: {integrity: sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag==} @@ -3387,6 +3432,7 @@ packages: '@jest/types': 27.5.1 '@types/istanbul-lib-coverage': 2.0.4 collect-v8-coverage: 1.0.1 + dev: true /@jest/test-sequencer/27.5.1: resolution: {integrity: sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ==} @@ -3398,6 +3444,7 @@ packages: jest-runtime: 27.5.1 transitivePeerDependencies: - supports-color + dev: true /@jest/transform/26.6.2: resolution: {integrity: sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA==} @@ -3443,6 +3490,7 @@ packages: write-file-atomic: 3.0.3 transitivePeerDependencies: - supports-color + dev: true /@jest/types/26.6.2: resolution: {integrity: sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==} @@ -3464,6 +3512,7 @@ packages: '@types/node': 17.0.35 '@types/yargs': 16.0.4 chalk: 4.1.2 + dev: true /@jridgewell/gen-mapping/0.1.1: resolution: {integrity: sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==} @@ -3471,6 +3520,7 @@ packages: dependencies: '@jridgewell/set-array': 1.1.1 '@jridgewell/sourcemap-codec': 1.4.13 + dev: true /@jridgewell/gen-mapping/0.3.1: resolution: {integrity: sha512-GcHwniMlA2z+WFPWuY8lp3fsza0I8xPFMWL5+n8LYyP6PSvPrXf4+n8stDHZY2DM0zy9sVkRDy1jDI4XGzYVqg==} @@ -3479,6 +3529,7 @@ packages: '@jridgewell/set-array': 1.1.1 '@jridgewell/sourcemap-codec': 1.4.13 '@jridgewell/trace-mapping': 0.3.13 + dev: true /@jridgewell/resolve-uri/3.0.7: resolution: {integrity: sha512-8cXDaBBHOr2pQ7j77Y6Vp5VDT2sIqWyWQ56TjEq4ih/a4iST3dItRe8Q9fp0rrIl9DoKhWQtUQz/YpOxLkXbNA==} @@ -3487,6 +3538,7 @@ packages: /@jridgewell/set-array/1.1.1: resolution: {integrity: sha512-Ct5MqZkLGEXTVmQYbGtx9SVqD2fqwvdubdps5D3djjAkgkKwT918VNOz65pEHFaYTeWcukmJmH5SwsA9Tn2ObQ==} engines: {node: '>=6.0.0'} + dev: true /@jridgewell/source-map/0.3.2: resolution: {integrity: sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==} @@ -3503,12 +3555,14 @@ packages: dependencies: '@jridgewell/resolve-uri': 3.0.7 '@jridgewell/sourcemap-codec': 1.4.13 + dev: true /@jridgewell/trace-mapping/0.3.9: resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} dependencies: '@jridgewell/resolve-uri': 3.0.7 '@jridgewell/sourcemap-codec': 1.4.13 + dev: false /@mdx-js/mdx/1.6.22: resolution: {integrity: sha512-AMxuLxPz2j5/6TpF/XSdKpQP1NlG0z11dFOlq+2IP/lSgl11GY8ji6S/rgsViN/L0BDvHvUMruRb7ub+24LUYA==} @@ -3940,11 +3994,13 @@ packages: resolution: {integrity: sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==} dependencies: type-detect: 4.0.8 + dev: true /@sinonjs/fake-timers/8.1.0: resolution: {integrity: sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==} dependencies: '@sinonjs/commons': 1.8.3 + dev: true /@storybook/addon-actions/6.5.6_sfoxds7t5ydpegc3knd667wn6m: resolution: {integrity: sha512-AGtzpWOU/B0FxcqFDM7E/KSHQyr6tMbVts77JlAKCIbwqEncD1LIQoz9CyMdbr1jynkep0Ck0JjcDdmp7CXVoQ==} @@ -5272,18 +5328,23 @@ packages: /@tootallnate/once/1.1.2: resolution: {integrity: sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==} engines: {node: '>= 6'} + dev: true /@tsconfig/node10/1.0.8: resolution: {integrity: sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==} + dev: false /@tsconfig/node12/1.0.9: resolution: {integrity: sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==} + dev: false /@tsconfig/node14/1.0.1: resolution: {integrity: sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==} + dev: false /@tsconfig/node16/1.0.2: resolution: {integrity: sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==} + dev: false /@types/archiver/5.3.1: resolution: {integrity: sha512-wKYZaSXaDvTZuInAWjCeGG7BEAgTWG2zZW0/f7IYFcoHB2X2d9lkVFnrOlXl3W6NrvO6Ml3FLLu8Uksyymcpnw==} @@ -5303,22 +5364,26 @@ packages: '@types/babel__generator': 7.6.4 '@types/babel__template': 7.4.1 '@types/babel__traverse': 7.17.1 + dev: true /@types/babel__generator/7.6.4: resolution: {integrity: sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==} dependencies: '@babel/types': 7.18.4 + dev: true /@types/babel__template/7.4.1: resolution: {integrity: sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==} dependencies: '@babel/parser': 7.18.0 '@babel/types': 7.18.4 + dev: true /@types/babel__traverse/7.17.1: resolution: {integrity: sha512-kVzjari1s2YVi77D3w1yuvohV2idweYXMCDzqBiVNN63TcDWrIlTVOYpqVrvbbyOE/IyzBoTKF0fdnLPEORFxA==} dependencies: '@babel/types': 7.18.4 + dev: true /@types/body-parser/1.19.2: resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==} @@ -5399,6 +5464,7 @@ packages: resolution: {integrity: sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==} dependencies: '@types/node': 17.0.35 + dev: true /@types/hast/2.3.4: resolution: {integrity: sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g==} @@ -5422,16 +5488,19 @@ packages: /@types/istanbul-lib-coverage/2.0.4: resolution: {integrity: sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==} + dev: true /@types/istanbul-lib-report/3.0.0: resolution: {integrity: sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==} dependencies: '@types/istanbul-lib-coverage': 2.0.4 + dev: true /@types/istanbul-reports/3.0.1: resolution: {integrity: sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==} dependencies: '@types/istanbul-lib-report': 3.0.0 + dev: true /@types/jest/27.5.1: resolution: {integrity: sha512-fUy7YRpT+rHXto1YlL+J9rs0uLGyiqVt3ZOTQR+4ROc47yNl8WLdVLgUloBRhOxP1PZvguHl44T3H0wAWxahYQ==} @@ -5555,6 +5624,7 @@ packages: /@types/prettier/2.6.1: resolution: {integrity: sha512-XFjFHmaLVifrAKaZ+EKghFHtHSUonyw8P2Qmy2/+osBnrKbH9UYtlK10zg8/kCt47MFilll/DEDKy3DHfJ0URw==} + dev: true /@types/pretty-hrtime/1.0.1: resolution: {integrity: sha512-VjID5MJb1eGKthz2qUerWT8+R4b9N+CHvGCzg9fn4kWZgaF9AhdYikQio3R7wV8YY1NsQKPaCwKz1Yff+aHNUQ==} @@ -5653,6 +5723,7 @@ packages: /@types/stack-utils/2.0.1: resolution: {integrity: sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==} + dev: true /@types/strip-bom/3.0.0: resolution: {integrity: sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ==} @@ -5722,6 +5793,7 @@ packages: /@types/yargs-parser/21.0.0: resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==} + dev: true /@types/yargs/15.0.14: resolution: {integrity: sha512-yEJzHoxf6SyQGhBhIYGXQDSCkJjB6HohDShto7m8vaKg9Yp0Yn8+71J9eakh2bnPg6BfsH9PRMhiRTZnd4eXGQ==} @@ -5733,6 +5805,7 @@ packages: resolution: {integrity: sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==} dependencies: '@types/yargs-parser': 21.0.0 + dev: true /@types/yauzl/2.10.0: resolution: {integrity: sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==} @@ -6125,6 +6198,7 @@ packages: /abab/2.0.6: resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==} + dev: true /accepts/1.3.8: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} @@ -6138,6 +6212,7 @@ packages: dependencies: acorn: 7.4.1 acorn-walk: 7.2.0 + dev: true /acorn-import-assertions/1.8.0_acorn@8.7.1: resolution: {integrity: sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==} @@ -6173,10 +6248,12 @@ packages: /acorn-walk/7.2.0: resolution: {integrity: sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==} engines: {node: '>=0.4.0'} + dev: true /acorn-walk/8.2.0: resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} engines: {node: '>=0.4.0'} + dev: false /acorn/6.4.2: resolution: {integrity: sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==} @@ -6188,6 +6265,7 @@ packages: resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==} engines: {node: '>=0.4.0'} hasBin: true + dev: true /acorn/8.7.1: resolution: {integrity: sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==} @@ -6206,6 +6284,7 @@ packages: debug: 4.3.4 transitivePeerDependencies: - supports-color + dev: true /aggregate-error/3.1.0: resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} @@ -6282,6 +6361,7 @@ packages: engines: {node: '>=8'} dependencies: type-fest: 0.21.3 + dev: true /ansi-html-community/0.0.8: resolution: {integrity: sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==} @@ -6317,6 +6397,7 @@ packages: /ansi-styles/5.2.0: resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} engines: {node: '>=10'} + dev: true /ansi-to-html/0.6.15: resolution: {integrity: sha512-28ijx2aHJGdzbs+O5SNQF65r6rrKYnkuwTYm8lZlChuoJ9P1vVzIpWO20sQTqTPDXYp6NFwk326vApTtLVFXpQ==} @@ -6341,6 +6422,7 @@ packages: dependencies: normalize-path: 3.0.0 picomatch: 2.3.1 + dev: true /app-root-dir/1.0.2: resolution: {integrity: sha512-jlpIfsOoNoafl92Sz//64uQHGSyMrD2vYG5d8o2a4qGvyNCvXur7bzIsWtAC/6flI2RYAp3kv8rsfBtaLm7w0g==} @@ -6410,6 +6492,7 @@ packages: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} dependencies: sprintf-js: 1.0.3 + dev: true /argparse/2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} @@ -6724,6 +6807,7 @@ packages: slash: 3.0.0 transitivePeerDependencies: - supports-color + dev: true /babel-jest/27.5.1_@babel+core@7.18.2: resolution: {integrity: sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg==} @@ -6817,6 +6901,7 @@ packages: test-exclude: 6.0.0 transitivePeerDependencies: - supports-color + dev: true /babel-plugin-jest-hoist/27.5.1: resolution: {integrity: sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ==} @@ -6826,6 +6911,7 @@ packages: '@babel/types': 7.18.4 '@types/babel__core': 7.1.19 '@types/babel__traverse': 7.17.1 + dev: true /babel-plugin-macros/2.8.0: resolution: {integrity: sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==} @@ -6967,6 +7053,7 @@ packages: '@babel/plugin-syntax-optional-catch-binding': 7.8.3_@babel+core@7.18.0 '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.18.0 '@babel/plugin-syntax-top-level-await': 7.14.5_@babel+core@7.18.0 + dev: true /babel-preset-current-node-syntax/1.0.1_@babel+core@7.18.2: resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==} @@ -6986,6 +7073,7 @@ packages: '@babel/plugin-syntax-optional-catch-binding': 7.8.3_@babel+core@7.18.2 '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.18.2 '@babel/plugin-syntax-top-level-await': 7.14.5_@babel+core@7.18.2 + dev: true /babel-preset-jest/27.5.1_@babel+core@7.18.0: resolution: {integrity: sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag==} @@ -6996,6 +7084,7 @@ packages: '@babel/core': 7.18.0 babel-plugin-jest-hoist: 27.5.1 babel-preset-current-node-syntax: 1.0.1_@babel+core@7.18.0 + dev: true /babel-preset-jest/27.5.1_@babel+core@7.18.2: resolution: {integrity: sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag==} @@ -7258,6 +7347,7 @@ packages: /browser-process-hrtime/1.0.0: resolution: {integrity: sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==} + dev: true /browserify-aes/1.2.0: resolution: {integrity: sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==} @@ -7319,11 +7409,13 @@ packages: escalade: 3.1.1 node-releases: 2.0.4 picocolors: 1.0.0 + dev: true /bser/2.1.1: resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} dependencies: node-int64: 0.4.0 + dev: true /buffer-crc32/0.2.13: resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} @@ -7522,13 +7614,16 @@ packages: /camelcase/5.3.1: resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} engines: {node: '>=6'} + dev: true /camelcase/6.3.0: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} + dev: true /caniuse-lite/1.0.30001342: resolution: {integrity: sha512-bn6sOCu7L7jcbBbyNhLg0qzXdJ/PMbybZTH/BA6Roet9wxYRm6Tr9D0s0uhLkOZ6MSG+QU6txUgdpr3MXIVqjA==} + dev: true /capture-exit/2.0.0: resolution: {integrity: sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==} @@ -7584,6 +7679,7 @@ packages: /char-regex/1.0.2: resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} engines: {node: '>=10'} + dev: true /character-entities-legacy/1.1.4: resolution: {integrity: sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==} @@ -7664,6 +7760,7 @@ packages: /ci-info/3.3.1: resolution: {integrity: sha512-SXgeMX9VwDe7iFFaEWkA5AstuER9YKqy4EhHqr4DVqkwmD9rpVimkMKWHdjn30Ja45txyjhSn63lVX69eVCckg==} + dev: true /cipher-base/1.0.4: resolution: {integrity: sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==} @@ -7673,6 +7770,7 @@ packages: /cjs-module-lexer/1.2.2: resolution: {integrity: sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==} + dev: true /class-utils/0.3.6: resolution: {integrity: sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==} @@ -7757,6 +7855,7 @@ packages: /co/4.6.0: resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} + dev: true /collapse-white-space/1.0.6: resolution: {integrity: sha512-jEovNnrhMuqyCcjfEJA56v0Xq8SkIoPKDyaHahwo3POf4qcSXqMYuwNcOTzp74vTsR9Tn08z4MxWqAhcekogkQ==} @@ -7764,6 +7863,7 @@ packages: /collect-v8-coverage/1.0.1: resolution: {integrity: sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==} + dev: true /collection-visit/1.0.0: resolution: {integrity: sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==} @@ -8140,15 +8240,18 @@ packages: /cssom/0.3.8: resolution: {integrity: sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==} + dev: true /cssom/0.4.4: resolution: {integrity: sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==} + dev: true /cssstyle/2.3.0: resolution: {integrity: sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==} engines: {node: '>=8'} dependencies: cssom: 0.3.8 + dev: true /csstype/3.1.0: resolution: {integrity: sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==} @@ -8228,6 +8331,7 @@ packages: abab: 2.0.6 whatwg-mimetype: 2.3.0 whatwg-url: 8.7.0 + dev: true /date-fns/2.28.0: resolution: {integrity: sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw==} @@ -8311,6 +8415,7 @@ packages: /decimal.js/10.3.1: resolution: {integrity: sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==} + dev: true /decode-uri-component/0.2.0: resolution: {integrity: sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og==} @@ -8319,6 +8424,7 @@ packages: /dedent/0.7.0: resolution: {integrity: sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==} + dev: true /deep-is/0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} @@ -8331,6 +8437,7 @@ packages: /deepmerge/4.2.2: resolution: {integrity: sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==} engines: {node: '>=0.10.0'} + dev: true /default-browser-id/1.0.4: resolution: {integrity: sha512-qPy925qewwul9Hifs+3sx1ZYn14obHxpkX+mPD369w4Rzg+YkJBgi3SOvwUq81nWSjqGUegIgEPwD8u+HUnxlw==} @@ -8425,6 +8532,7 @@ packages: /detect-newline/3.1.0: resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} engines: {node: '>=8'} + dev: true /detect-package-manager/2.0.1: resolution: {integrity: sha512-j/lJHyoLlWi6G1LDdLgvUtz60Zo5GEj+sVYtTVXnYLDPuzgC3llMxonXym9zIwhhUII8vjdw0LXxavpLqTbl1A==} @@ -8476,6 +8584,7 @@ packages: /diff-sequences/27.5.1: resolution: {integrity: sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dev: true /diff/4.0.2: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} @@ -8551,6 +8660,7 @@ packages: engines: {node: '>=8'} dependencies: webidl-conversions: 5.0.0 + dev: true /domhandler/4.3.1: resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==} @@ -8632,6 +8742,7 @@ packages: /electron-to-chromium/1.4.137: resolution: {integrity: sha512-0Rcpald12O11BUogJagX3HsCN3FE83DSqWjgXoHo5a72KUKMSfI39XBgJpgNNxS9fuGzytaFjE06kZkiVFy2qA==} + dev: true /elliptic/6.5.4: resolution: {integrity: sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==} @@ -8647,6 +8758,7 @@ packages: /emittery/0.8.1: resolution: {integrity: sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==} engines: {node: '>=10'} + dev: true /emoji-regex/8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -9021,6 +9133,7 @@ packages: /escape-string-regexp/2.0.0: resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} engines: {node: '>=8'} + dev: true /escape-string-regexp/4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} @@ -9037,6 +9150,7 @@ packages: optionator: 0.8.3 optionalDependencies: source-map: 0.6.1 + dev: true /eslint-config-prettier/8.5.0_eslint@8.16.0: resolution: {integrity: sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==} @@ -9178,6 +9292,7 @@ packages: resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} engines: {node: '>=4'} hasBin: true + dev: true /esquery/1.4.0: resolution: {integrity: sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==} @@ -9297,6 +9412,7 @@ packages: onetime: 5.1.2 signal-exit: 3.0.7 strip-final-newline: 2.0.0 + dev: true /executable/4.1.1: resolution: {integrity: sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==} @@ -9308,6 +9424,7 @@ packages: /exit/0.1.2: resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} engines: {node: '>= 0.8.0'} + dev: true /expand-brackets/2.1.4: resolution: {integrity: sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==} @@ -9332,6 +9449,7 @@ packages: jest-get-type: 27.5.1 jest-matcher-utils: 27.5.1 jest-message-util: 27.5.1 + dev: true /express-prometheus-middleware/1.2.0_5pbe54och5iqxg6nmsixssx4wm: resolution: {integrity: sha512-efSwft67rdtiW40D0im1f7Rz1TCGHGzPj6lfK0MxZDcPj6z4f/Ab5VNkWPYZEjvLqZiZ7fbS00CYzpigO8tS+g==} @@ -9521,6 +9639,7 @@ packages: resolution: {integrity: sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==} dependencies: bser: 2.1.1 + dev: true /fd-slicer/1.1.0: resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} @@ -9646,6 +9765,7 @@ packages: dependencies: locate-path: 5.0.0 path-exists: 4.0.0 + dev: true /find-up/5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} @@ -9793,6 +9913,7 @@ packages: asynckit: 0.4.0 combined-stream: 1.0.8 mime-types: 2.1.35 + dev: true /form-data/4.0.0: resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} @@ -9920,6 +10041,7 @@ packages: engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] requiresBuild: true + dev: true optional: true /fstream/1.0.12: @@ -9979,6 +10101,7 @@ packages: /gensync/1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} + dev: true /get-caller-file/2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} @@ -9994,6 +10117,7 @@ packages: /get-package-type/0.1.0: resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} engines: {node: '>=8.0.0'} + dev: true /get-stdin/4.0.1: resolution: {integrity: sha512-F5aQMywwJ2n85s4hJPTT9RPxGmubonuB10MNYo17/xph174n2MIR33HRguhzVag10O/npM7SPk73LMZNP+FaWw==} @@ -10018,6 +10142,7 @@ packages: /get-stream/6.0.1: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} + dev: true /get-symbol-description/1.0.0: resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} @@ -10110,6 +10235,7 @@ packages: /globals/11.12.0: resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} engines: {node: '>=4'} + dev: true /globals/13.15.0: resolution: {integrity: sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==} @@ -10398,6 +10524,7 @@ packages: engines: {node: '>=10'} dependencies: whatwg-encoding: 1.0.5 + dev: true /html-entities/2.3.3: resolution: {integrity: sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==} @@ -10405,6 +10532,7 @@ packages: /html-escaper/2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + dev: true /html-minifier-terser/5.1.1: resolution: {integrity: sha512-ZPr5MNObqnV/T9akshPKbVgyOqLmy+Bxo7juKCfTfnjNniTAMdy4hz21YQqoofMBJD2kdREaqPPdThoR78Tgxg==} @@ -10496,6 +10624,7 @@ packages: debug: 4.3.4 transitivePeerDependencies: - supports-color + dev: true /http-signature/1.2.0: resolution: {integrity: sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=} @@ -10527,6 +10656,7 @@ packages: debug: 4.3.4 transitivePeerDependencies: - supports-color + dev: true /human-signals/1.1.1: resolution: {integrity: sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==} @@ -10536,6 +10666,7 @@ packages: /human-signals/2.1.0: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} + dev: true /iconv-lite/0.4.24: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} @@ -10588,6 +10719,7 @@ packages: dependencies: pkg-dir: 4.2.0 resolve-cwd: 3.0.0 + dev: true /imurmurhash/0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} @@ -10852,6 +10984,7 @@ packages: /is-generator-fn/2.1.0: resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} engines: {node: '>=6'} + dev: true /is-generator-function/1.0.10: resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} @@ -10938,6 +11071,7 @@ packages: /is-potential-custom-element-name/1.0.1: resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + dev: true /is-regex/1.1.4: resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} @@ -10963,6 +11097,7 @@ packages: /is-stream/2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} + dev: true /is-string/1.0.7: resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} @@ -11080,6 +11215,7 @@ packages: /istanbul-lib-coverage/3.2.0: resolution: {integrity: sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==} engines: {node: '>=8'} + dev: true /istanbul-lib-instrument/5.2.0: resolution: {integrity: sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A==} @@ -11092,6 +11228,7 @@ packages: semver: 6.3.0 transitivePeerDependencies: - supports-color + dev: true /istanbul-lib-report/3.0.0: resolution: {integrity: sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==} @@ -11100,6 +11237,7 @@ packages: istanbul-lib-coverage: 3.2.0 make-dir: 3.1.0 supports-color: 7.2.0 + dev: true /istanbul-lib-source-maps/4.0.1: resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} @@ -11110,6 +11248,7 @@ packages: source-map: 0.6.1 transitivePeerDependencies: - supports-color + dev: true /istanbul-reports/3.1.4: resolution: {integrity: sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw==} @@ -11117,6 +11256,7 @@ packages: dependencies: html-escaper: 2.0.2 istanbul-lib-report: 3.0.0 + dev: true /iterate-iterator/1.0.2: resolution: {integrity: sha512-t91HubM4ZDQ70M9wqp+pcNpu8OyJ9UAtXntT/Bcsvp5tZMnz9vRa+IunKXeI8AnfZMTv0jNuVEmGeLSMjVvfPw==} @@ -11136,6 +11276,7 @@ packages: '@jest/types': 27.5.1 execa: 5.1.1 throat: 6.0.1 + dev: true /jest-circus/27.5.1: resolution: {integrity: sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw==} @@ -11162,6 +11303,7 @@ packages: throat: 6.0.1 transitivePeerDependencies: - supports-color + dev: true /jest-cli/27.5.1: resolution: {integrity: sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw==} @@ -11221,6 +11363,7 @@ packages: - supports-color - ts-node - utf-8-validate + dev: true /jest-config/27.5.1: resolution: {integrity: sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA==} @@ -11271,10 +11414,10 @@ packages: ts-node: optional: true dependencies: - '@babel/core': 7.18.0 + '@babel/core': 7.18.2 '@jest/test-sequencer': 27.5.1 '@jest/types': 27.5.1 - babel-jest: 27.5.1_@babel+core@7.18.0 + babel-jest: 27.5.1_@babel+core@7.18.2 chalk: 4.1.2 ci-info: 3.3.1 deepmerge: 4.2.2 @@ -11301,6 +11444,7 @@ packages: - canvas - supports-color - utf-8-validate + dev: true /jest-diff/27.5.1: resolution: {integrity: sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==} @@ -11310,12 +11454,14 @@ packages: diff-sequences: 27.5.1 jest-get-type: 27.5.1 pretty-format: 27.5.1 + dev: true /jest-docblock/27.5.1: resolution: {integrity: sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: detect-newline: 3.1.0 + dev: true /jest-each/27.5.1: resolution: {integrity: sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==} @@ -11326,6 +11472,7 @@ packages: jest-get-type: 27.5.1 jest-util: 27.5.1 pretty-format: 27.5.1 + dev: true /jest-environment-jsdom/27.5.1: resolution: {integrity: sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==} @@ -11343,6 +11490,7 @@ packages: - canvas - supports-color - utf-8-validate + dev: true /jest-environment-node/27.5.1: resolution: {integrity: sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw==} @@ -11354,10 +11502,12 @@ packages: '@types/node': 17.0.35 jest-mock: 27.5.1 jest-util: 27.5.1 + dev: true /jest-get-type/27.5.1: resolution: {integrity: sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dev: true /jest-haste-map/26.6.2: resolution: {integrity: sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w==} @@ -11400,6 +11550,7 @@ packages: walker: 1.0.8 optionalDependencies: fsevents: 2.3.2 + dev: true /jest-jasmine2/27.5.1: resolution: {integrity: sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ==} @@ -11424,6 +11575,7 @@ packages: throat: 6.0.1 transitivePeerDependencies: - supports-color + dev: true /jest-leak-detector/27.5.1: resolution: {integrity: sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ==} @@ -11431,6 +11583,7 @@ packages: dependencies: jest-get-type: 27.5.1 pretty-format: 27.5.1 + dev: true /jest-matcher-utils/27.5.1: resolution: {integrity: sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==} @@ -11440,6 +11593,7 @@ packages: jest-diff: 27.5.1 jest-get-type: 27.5.1 pretty-format: 27.5.1 + dev: true /jest-message-util/27.5.1: resolution: {integrity: sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==} @@ -11454,6 +11608,7 @@ packages: pretty-format: 27.5.1 slash: 3.0.0 stack-utils: 2.0.5 + dev: true /jest-mock/27.5.1: resolution: {integrity: sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==} @@ -11461,6 +11616,7 @@ packages: dependencies: '@jest/types': 27.5.1 '@types/node': 17.0.35 + dev: true /jest-pnp-resolver/1.2.2_jest-resolve@27.5.1: resolution: {integrity: sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==} @@ -11472,6 +11628,7 @@ packages: optional: true dependencies: jest-resolve: 27.5.1 + dev: true /jest-regex-util/26.0.0: resolution: {integrity: sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==} @@ -11481,6 +11638,7 @@ packages: /jest-regex-util/27.5.1: resolution: {integrity: sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dev: true /jest-resolve-dependencies/27.5.1: resolution: {integrity: sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg==} @@ -11491,6 +11649,7 @@ packages: jest-snapshot: 27.5.1 transitivePeerDependencies: - supports-color + dev: true /jest-resolve/27.5.1: resolution: {integrity: sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==} @@ -11506,6 +11665,7 @@ packages: resolve: 1.22.0 resolve.exports: 1.1.0 slash: 3.0.0 + dev: true /jest-runner/27.5.1: resolution: {integrity: sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ==} @@ -11537,6 +11697,7 @@ packages: - canvas - supports-color - utf-8-validate + dev: true /jest-runtime/27.5.1: resolution: {integrity: sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A==} @@ -11566,6 +11727,7 @@ packages: strip-bom: 4.0.0 transitivePeerDependencies: - supports-color + dev: true /jest-serializer/26.6.2: resolution: {integrity: sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g==} @@ -11581,6 +11743,7 @@ packages: dependencies: '@types/node': 17.0.35 graceful-fs: 4.2.10 + dev: true /jest-snapshot/27.5.1: resolution: {integrity: sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==} @@ -11610,6 +11773,7 @@ packages: semver: 7.3.7 transitivePeerDependencies: - supports-color + dev: true /jest-util/26.6.2: resolution: {integrity: sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==} @@ -11633,6 +11797,7 @@ packages: ci-info: 3.3.1 graceful-fs: 4.2.10 picomatch: 2.3.1 + dev: true /jest-validate/27.5.1: resolution: {integrity: sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ==} @@ -11644,6 +11809,7 @@ packages: jest-get-type: 27.5.1 leven: 3.1.0 pretty-format: 27.5.1 + dev: true /jest-watcher/27.5.1: resolution: {integrity: sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw==} @@ -11656,6 +11822,7 @@ packages: chalk: 4.1.2 jest-util: 27.5.1 string-length: 4.0.2 + dev: true /jest-worker/26.6.2: resolution: {integrity: sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==} @@ -11673,6 +11840,7 @@ packages: '@types/node': 17.0.35 merge-stream: 2.0.0 supports-color: 8.1.1 + dev: true /jest/27.5.1: resolution: {integrity: sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==} @@ -11714,6 +11882,7 @@ packages: - supports-color - ts-node - utf-8-validate + dev: true /joycon/3.1.1: resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} @@ -11738,6 +11907,7 @@ packages: dependencies: argparse: 1.0.10 esprima: 4.0.1 + dev: true /js-yaml/4.1.0: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} @@ -11788,9 +11958,10 @@ packages: - bufferutil - supports-color - utf-8-validate + dev: true /jsesc/0.5.0: - resolution: {integrity: sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=} + resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==} hasBin: true dev: true @@ -11803,6 +11974,7 @@ packages: resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} engines: {node: '>=4'} hasBin: true + dev: true /json-parse-better-errors/1.0.2: resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==} @@ -11843,6 +12015,7 @@ packages: resolution: {integrity: sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==} engines: {node: '>=6'} hasBin: true + dev: true /jsonfile/6.1.0: resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} @@ -11943,6 +12116,7 @@ packages: /kleur/3.0.3: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} + dev: true /klona/2.0.5: resolution: {integrity: sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==} @@ -11980,6 +12154,7 @@ packages: /leven/3.1.0: resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} engines: {node: '>=6'} + dev: true /levn/0.3.0: resolution: {integrity: sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==} @@ -11987,6 +12162,7 @@ packages: dependencies: prelude-ls: 1.1.2 type-check: 0.3.2 + dev: true /levn/0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} @@ -12086,6 +12262,7 @@ packages: engines: {node: '>=8'} dependencies: p-locate: 4.1.0 + dev: true /locate-path/6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} @@ -12254,6 +12431,7 @@ packages: engines: {node: '>=8'} dependencies: semver: 6.3.0 + dev: true /make-error/1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} @@ -12262,6 +12440,7 @@ packages: resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} dependencies: tmpl: 1.0.5 + dev: true /map-cache/0.2.2: resolution: {integrity: sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==} @@ -12383,6 +12562,7 @@ packages: /merge-stream/2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + dev: true /merge2/1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} @@ -12455,6 +12635,7 @@ packages: /mimic-fn/2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} + dev: true /min-document/2.19.0: resolution: {integrity: sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ==} @@ -12694,6 +12875,7 @@ packages: /node-int64/0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} + dev: true /node-libs-browser/2.2.1: resolution: {integrity: sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==} @@ -12739,6 +12921,7 @@ packages: /node-releases/2.0.4: resolution: {integrity: sha512-gbMzqQtTtDz/00jQzZ21PQzdI9PyLYqUSvD0p3naOhX4odFji0ZxYdnVwPTxmSwkmxhcFImpozceidSG+AgoPQ==} + dev: true /node-typescript/0.1.3: resolution: {integrity: sha512-EK34kMXvdXsVvtzLZVQN3ORdLTQCG0gSnhkQOF8vzrO8beXoXqIUNAGACF9c6N5LDDzEdM8YCXFjbuCE8Mz1Jg==} @@ -12784,6 +12967,7 @@ packages: engines: {node: '>=8'} dependencies: path-key: 3.1.1 + dev: true /npmlog/5.0.1: resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==} @@ -12806,6 +12990,7 @@ packages: /nwsapi/2.2.0: resolution: {integrity: sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==} + dev: true /oauth-sign/0.9.0: resolution: {integrity: sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==} @@ -12923,6 +13108,7 @@ packages: engines: {node: '>=6'} dependencies: mimic-fn: 2.1.0 + dev: true /open/7.4.2: resolution: {integrity: sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==} @@ -12956,6 +13142,7 @@ packages: prelude-ls: 1.1.2 type-check: 0.3.2 word-wrap: 1.2.3 + dev: true /optionator/0.9.1: resolution: {integrity: sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==} @@ -13016,6 +13203,7 @@ packages: engines: {node: '>=6'} dependencies: p-try: 2.2.0 + dev: true /p-limit/3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} @@ -13036,6 +13224,7 @@ packages: engines: {node: '>=8'} dependencies: p-limit: 2.3.0 + dev: true /p-locate/5.0.0: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} @@ -13073,6 +13262,7 @@ packages: /p-try/2.2.0: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} + dev: true /packet-reader/1.0.0: resolution: {integrity: sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==} @@ -13141,6 +13331,7 @@ packages: /parse5/6.0.1: resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==} + dev: true /parseurl/1.3.3: resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} @@ -13182,6 +13373,7 @@ packages: /path-exists/4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} + dev: true /path-is-absolute/1.0.1: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} @@ -13304,6 +13496,7 @@ packages: /picocolors/1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + dev: true /picomatch/2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} @@ -13398,6 +13591,7 @@ packages: /pirates/4.0.5: resolution: {integrity: sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==} engines: {node: '>= 6'} + dev: true /pkg-dir/3.0.0: resolution: {integrity: sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==} @@ -13411,6 +13605,7 @@ packages: engines: {node: '>=8'} dependencies: find-up: 4.1.0 + dev: true /pkg-dir/5.0.0: resolution: {integrity: sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==} @@ -13581,6 +13776,7 @@ packages: /prelude-ls/1.1.2: resolution: {integrity: sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==} engines: {node: '>= 0.8.0'} + dev: true /prelude-ls/1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} @@ -13624,6 +13820,7 @@ packages: ansi-regex: 5.0.1 ansi-styles: 5.2.0 react-is: 17.0.2 + dev: true /pretty-hrtime/1.0.3: resolution: {integrity: sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==} @@ -13724,6 +13921,7 @@ packages: dependencies: kleur: 3.0.3 sisteransi: 1.0.5 + dev: true /prop-types/15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} @@ -14372,6 +14570,7 @@ packages: engines: {node: '>=8'} dependencies: resolve-from: 5.0.0 + dev: true /resolve-from/4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} @@ -14380,6 +14579,7 @@ packages: /resolve-from/5.0.0: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} + dev: true /resolve-url/0.2.1: resolution: {integrity: sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=} @@ -14389,6 +14589,7 @@ packages: /resolve.exports/1.1.0: resolution: {integrity: sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==} engines: {node: '>=10'} + dev: true /resolve/1.22.0: resolution: {integrity: sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==} @@ -14587,6 +14788,7 @@ packages: /semver/6.3.0: resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==} hasBin: true + dev: true /semver/7.0.0: resolution: {integrity: sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==} @@ -14735,9 +14937,11 @@ packages: /signal-exit/3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + dev: true /sisteransi/1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + dev: true /slash/1.0.0: resolution: {integrity: sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=} @@ -14844,6 +15048,7 @@ packages: dependencies: buffer-from: 1.1.2 source-map: 0.6.1 + dev: true /source-map-url/0.4.1: resolution: {integrity: sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==} @@ -14857,10 +15062,12 @@ packages: /source-map/0.6.1: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} + dev: true /source-map/0.7.3: resolution: {integrity: sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==} engines: {node: '>= 8'} + dev: true /space-separated-tokens/1.1.5: resolution: {integrity: sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==} @@ -14901,6 +15108,7 @@ packages: /sprintf-js/1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + dev: true /sshpk/1.17.0: resolution: {integrity: sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==} @@ -14939,6 +15147,7 @@ packages: engines: {node: '>=10'} dependencies: escape-string-regexp: 2.0.0 + dev: true /stackframe/1.2.1: resolution: {integrity: sha512-h88QkzREN/hy8eRdyNhhsO7RSJ5oyTqxxmmn0dzBIMUclZsjpfmrsg81vp8mjjAs2vAZ72nyWxRUwSwmh0e4xg==} @@ -15002,6 +15211,7 @@ packages: dependencies: char-regex: 1.0.2 strip-ansi: 6.0.1 + dev: true /string-width/4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} @@ -15098,6 +15308,7 @@ packages: /strip-bom/4.0.0: resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} engines: {node: '>=8'} + dev: true /strip-eof/1.0.0: resolution: {integrity: sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=} @@ -15107,6 +15318,7 @@ packages: /strip-final-newline/2.0.0: resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} engines: {node: '>=6'} + dev: true /strip-indent/1.0.1: resolution: {integrity: sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=} @@ -15213,6 +15425,7 @@ packages: engines: {node: '>=10'} dependencies: has-flag: 4.0.0 + dev: true /supports-hyperlinks/2.2.0: resolution: {integrity: sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==} @@ -15220,6 +15433,7 @@ packages: dependencies: has-flag: 4.0.0 supports-color: 7.2.0 + dev: true /supports-preserve-symlinks-flag/1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} @@ -15227,6 +15441,7 @@ packages: /symbol-tree/3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + dev: true /symbol.prototype.description/1.0.5: resolution: {integrity: sha512-x738iXRYsrAt9WBhRCVG5BtIC3B7CUkFwbHW2zOvGtwM33s7JjrCDyq8V0zgMYVb5ymsL8+qkzzpANH63CPQaQ==} @@ -15330,6 +15545,7 @@ packages: dependencies: ansi-escapes: 4.3.2 supports-hyperlinks: 2.2.0 + dev: true /terser-webpack-plugin/1.4.5_webpack@4.46.0: resolution: {integrity: sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw==} @@ -15422,6 +15638,7 @@ packages: '@istanbuljs/schema': 0.1.3 glob: 7.2.3 minimatch: 3.1.2 + dev: true /text-table/0.2.0: resolution: {integrity: sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=} @@ -15433,6 +15650,7 @@ packages: /throat/6.0.1: resolution: {integrity: sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==} + dev: true /throttleit/1.0.0: resolution: {integrity: sha512-rkTVqu6IjfQ/6+uNuuc3sZek4CEYxTJom3IktzgdSxcZqdARuebbA/f4QmAxMQIxqq9ZLEUkSYqvuk1I6VKq4g==} @@ -15475,6 +15693,7 @@ packages: /tmpl/1.0.5: resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} + dev: true /to-arraybuffer/1.0.1: resolution: {integrity: sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=} @@ -15538,6 +15757,7 @@ packages: psl: 1.8.0 punycode: 2.1.1 universalify: 0.1.2 + dev: true /tr46/0.0.3: resolution: {integrity: sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=} @@ -15548,6 +15768,7 @@ packages: engines: {node: '>=8'} dependencies: punycode: 2.1.1 + dev: true /traverse/0.3.9: resolution: {integrity: sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk=} @@ -15639,6 +15860,7 @@ packages: typescript: 4.6.4 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 + dev: false /ts-node/9.1.1_typescript@4.6.4: resolution: {integrity: sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==} @@ -15717,6 +15939,7 @@ packages: engines: {node: '>= 0.8.0'} dependencies: prelude-ls: 1.1.2 + dev: true /type-check/0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} @@ -15727,6 +15950,7 @@ packages: /type-detect/4.0.8: resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} engines: {node: '>=4'} + dev: true /type-fest/0.20.2: resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} @@ -15735,6 +15959,7 @@ packages: /type-fest/0.21.3: resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} engines: {node: '>=10'} + dev: true /type-fest/0.6.0: resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} @@ -15769,6 +15994,7 @@ packages: resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} dependencies: is-typedarray: 1.0.0 + dev: true /typedarray/0.0.6: resolution: {integrity: sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=} @@ -15783,6 +16009,7 @@ packages: resolution: {integrity: sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==} engines: {node: '>=4.2.0'} hasBin: true + dev: true /uglify-js/3.15.5: resolution: {integrity: sha512-hNM5q5GbBRB5xB+PMqVRcgYe4c8jbyZ1pzZhS6jbq54/4F2gFK869ZheiE5A8/t+W5jtTNpWef/5Q9zk639FNQ==} @@ -15919,6 +16146,7 @@ packages: /universalify/0.1.2: resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} engines: {node: '>= 4.0.0'} + dev: true /universalify/2.0.0: resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==} @@ -16070,6 +16298,7 @@ packages: /v8-compile-cache-lib/3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + dev: false /v8-compile-cache/2.3.0: resolution: {integrity: sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==} @@ -16081,6 +16310,7 @@ packages: '@types/istanbul-lib-coverage': 2.0.4 convert-source-map: 1.8.0 source-map: 0.7.3 + dev: true /v8-to-istanbul/9.0.0: resolution: {integrity: sha512-HcvgY/xaRm7isYmyx+lFKA4uQmfUbN0J4M0nNItvzTvH/iQ9kW5j/t4YSR+Ge323/lrgDAWJoF46tzGQHwBHFw==} @@ -16162,17 +16392,20 @@ packages: resolution: {integrity: sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==} dependencies: browser-process-hrtime: 1.0.0 + dev: true /w3c-xmlserializer/2.0.0: resolution: {integrity: sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==} engines: {node: '>=10'} dependencies: xml-name-validator: 3.0.0 + dev: true /walker/1.0.8: resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} dependencies: makeerror: 1.0.12 + dev: true /watchpack-chokidar2/2.0.1: resolution: {integrity: sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==} @@ -16223,10 +16456,12 @@ packages: /webidl-conversions/5.0.0: resolution: {integrity: sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==} engines: {node: '>=8'} + dev: true /webidl-conversions/6.1.0: resolution: {integrity: sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==} engines: {node: '>=10.4'} + dev: true /webpack-dev-middleware/3.7.3_webpack@4.46.0: resolution: {integrity: sha512-djelc/zGiz9nZj/U7PTBi2ViorGJXEWo/3ltkPbDyxCXhhEXkW0ce99falaok4TPj+AsxLiXJR0EBOb0zh9fKQ==} @@ -16372,9 +16607,11 @@ packages: resolution: {integrity: sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==} dependencies: iconv-lite: 0.4.24 + dev: true /whatwg-mimetype/2.3.0: resolution: {integrity: sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==} + dev: true /whatwg-url/5.0.0: resolution: {integrity: sha1-lmRU6HZUYuN2RNNib2dCzotwll0=} @@ -16390,6 +16627,7 @@ packages: lodash: 4.17.21 tr46: 2.1.0 webidl-conversions: 6.1.0 + dev: true /which-boxed-primitive/1.0.2: resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} @@ -16486,6 +16724,7 @@ packages: is-typedarray: 1.0.0 signal-exit: 3.0.7 typedarray-to-buffer: 3.1.5 + dev: true /ws/7.5.7: resolution: {integrity: sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==} @@ -16498,6 +16737,7 @@ packages: optional: true utf-8-validate: optional: true + dev: true /ws/8.7.0: resolution: {integrity: sha512-c2gsP0PRwcLFzUiA8Mkr37/MI7ilIlHQxaEAtd0uNMbVMoy8puJyafRlm0bV9MbGSabUPeLrRRaqIBcFcA2Pqg==} @@ -16521,6 +16761,7 @@ packages: /xml-name-validator/3.0.0: resolution: {integrity: sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==} + dev: true /xml/1.0.1: resolution: {integrity: sha1-eLpyAgApxbyHuKgaPPzXS0ovweU=} @@ -16568,6 +16809,7 @@ packages: /yargs-parser/20.2.9: resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} engines: {node: '>=10'} + dev: true /yargs-parser/21.0.1: resolution: {integrity: sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==} @@ -16585,6 +16827,7 @@ packages: string-width: 4.2.3 y18n: 5.0.8 yargs-parser: 20.2.9 + dev: true /yargs/17.3.1: resolution: {integrity: sha512-WUANQeVgjLbNsEmGk20f+nlHgOqzRFpiGWVaBrYGYIGANIIu3lWjoyi0fNlFmJkvfhCZ6BXINe7/W2O2bV4iaA==} diff --git a/shared/babel.config.js b/shared/babel.config.js new file mode 100644 index 0000000000..dd242dc902 --- /dev/null +++ b/shared/babel.config.js @@ -0,0 +1,6 @@ +module.exports = { + presets: [ + ["@babel/preset-env", { targets: { node: "current" } }], + "@babel/preset-typescript", + ], +}; diff --git a/shared/package.json b/shared/package.json index 4700a251fa..e54d71872f 100644 --- a/shared/package.json +++ b/shared/package.json @@ -5,9 +5,13 @@ "lint": "pnpm -w run lint:shared" }, "devDependencies": { + "@types/ramda": "^0.28.1", + "@babel/core": "^7.17.9", + "@babel/preset-env": "^7.16.11", + "@babel/preset-typescript": "^7.16.7", "@types/jest": "^27.4.1", "@types/node": "^17.0.33", - "@types/ramda": "^0.28.1", + "babel-jest": "^27.5.1", "jest": "^27.5.1", "typescript": "^4.6.2" }, diff --git a/shared/src/errors/managedErrors.ts b/shared/src/errors/managedErrors.ts index b7ce23f6f9..ce334fb3e9 100644 --- a/shared/src/errors/managedErrors.ts +++ b/shared/src/errors/managedErrors.ts @@ -3,6 +3,7 @@ export const managedRedirectErrorKinds = [ "peConnectNoAuthorisation", "peConnectNoValidAdvisor", "peConnectNoValidUser", + "peConnectAdvisorForbiddenAccess", ] as const; export type ManagedRedirectErrorKinds = diff --git a/shared/src/httpClient/adapters/axios.adapter.ts b/shared/src/httpClient/adapters/axios.adapter.ts new file mode 100644 index 0000000000..77bf08e38f --- /dev/null +++ b/shared/src/httpClient/adapters/axios.adapter.ts @@ -0,0 +1,129 @@ +import axios, { + AxiosError, + AxiosInstance, + AxiosRequestConfig, + AxiosResponse, +} from "axios"; +import { + AbsoluteUrl, + ErrorMapper, + getTargetFromPredicate, + HttpClient, + HttpClientGetConfig, + HttpClientPostConfig, + HttpResponse, +} from "../httpClient"; +import { + onFullfilledDefaultResponseInterceptorMaker, + onRejectDefaultResponseInterceptorMaker, +} from "./axios.config"; + +export type AxiosErrorWithResponse = AxiosError & { response: AxiosResponse }; + +type AxiosInstanceContext = { + axiosRequestConfig: AxiosRequestConfig; + onFulfilledResponseInterceptor: (response: AxiosResponse) => AxiosResponse; + onRejectResponseInterceptor: (rawAxiosError: AxiosError) => never; +}; + +type ContextType = { + config: AxiosRequestConfig; + target: TargetUrls; + errorMapper: ErrorMapper; +}; + +export class ManagedAxios implements HttpClient { + constructor( + //prettier-ignore + private readonly targetsUrls: Record AbsoluteUrl>, + //prettier-ignore + private readonly targetsErrorMapper: ErrorMapper = {}, + //prettier-ignore + private readonly defaultRequestConfig: AxiosRequestConfig = {}, + //prettier-ignore + private readonly onFulfilledResponseInterceptorMaker: + (context: ContextType) => + (response: AxiosResponse) => AxiosResponse = + onFullfilledDefaultResponseInterceptorMaker, + //prettier-ignore + private readonly onRejectResponseInterceptorMaker: + (context: ContextType) => + (rawAxiosError: AxiosError) => never = + onRejectDefaultResponseInterceptorMaker, + ) {} + + private static workerInstance = ( + context: AxiosInstanceContext, + ): AxiosInstance => { + const axiosInstance = axios.create(context.axiosRequestConfig); + + //TODO Add request interceptors ? + /* axiosInstance.interceptors.request.use( + axiosValidRequestInterceptor(targetsUrlsMapper), + axiosErrorRequestInterceptor, + );*/ + + axiosInstance.interceptors.response.use( + context.onFulfilledResponseInterceptor, + context.onRejectResponseInterceptor, + ); + + return axiosInstance; + }; + + async get(config: HttpClientGetConfig): Promise { + const { + axiosRequestConfig, + onFulfilledResponseInterceptor, + onRejectResponseInterceptor, + } = this.clientInstanceContext(config); + + return ManagedAxios.workerInstance({ + axiosRequestConfig, + onFulfilledResponseInterceptor, + onRejectResponseInterceptor, + }).get(config.target(config.targetParams)); + } + + async post(config: HttpClientPostConfig): Promise { + const { + axiosRequestConfig, + onFulfilledResponseInterceptor, + onRejectResponseInterceptor, + } = this.clientInstanceContext(config); + + return ManagedAxios.workerInstance({ + axiosRequestConfig, + onFulfilledResponseInterceptor, + onRejectResponseInterceptor, + }).post(config.target(config.targetParams), config.data); + } + + private clientInstanceContext = ( + targetConfig: HttpClientGetConfig, + ): AxiosInstanceContext => { + const target: TargetUrls = getTargetFromPredicate( + targetConfig.target, + this.targetsUrls, + ) as TargetUrls; + const mergedConfigs = { ...this.defaultRequestConfig, ...targetConfig }; + const onFulfilledResponseInterceptor = + this.onFulfilledResponseInterceptorMaker({ + config: { ...this.defaultRequestConfig, ...targetConfig }, + target, + errorMapper: this.targetsErrorMapper, + }); + + const onRejectResponseInterceptor = this.onRejectResponseInterceptorMaker({ + config: { ...this.defaultRequestConfig, ...targetConfig }, + target, + errorMapper: this.targetsErrorMapper, + }); + + return { + axiosRequestConfig: mergedConfigs, + onFulfilledResponseInterceptor, + onRejectResponseInterceptor, + }; + }; +} diff --git a/shared/src/httpClient/adapters/axios.config.ts b/shared/src/httpClient/adapters/axios.config.ts new file mode 100644 index 0000000000..bbb600bd81 --- /dev/null +++ b/shared/src/httpClient/adapters/axios.config.ts @@ -0,0 +1,74 @@ +import axios, { AxiosError, AxiosResponse } from "axios"; +import { + toInfrastructureError, + toMappedErrorMaker, + toUnhandledError, +} from "../httpClient.mappers"; +import { InfrastructureError } from "../errors/InfrastructureError.error"; +import { ErrorMapper, isHttpError } from "../httpClient"; +import { toHttpError } from "./axios.mappers"; +import { AxiosErrorWithResponse } from "./axios.adapter"; + +export const onFullfilledDefaultResponseInterceptorMaker = + () => + (response: AxiosResponse): AxiosResponse => { + // eslint-disable-next-line no-console + console.log( + "[Axios Managed Response Status]: ", + response.status, + " - ", + response.config.url, + ); + return response; + }; + +export const onRejectDefaultResponseInterceptorMaker = < + TargetUrls extends string, +>(context: { + target: TargetUrls; + errorMapper: ErrorMapper; +}) => { + const toMappedError = toMappedErrorMaker(context.target, context.errorMapper); + + return (rawAxiosError: AxiosError): never => { + const infrastructureError: InfrastructureError | undefined = + toInfrastructureError(rawAxiosError); + if (infrastructureError) throw toMappedError(infrastructureError); + + // TODO Create Unhandled Error type + if (!axios.isAxiosError(rawAxiosError)) + throw toUnhandledError( + `error Response does not have the property isAxiosError set to true`, + rawAxiosError, + ); + + if (!isValidErrorResponse(rawAxiosError.response)) + throw toUnhandledError( + "error response objects does not have mandatory keys", + rawAxiosError, + ); + + const error = toHttpError(rawAxiosError as AxiosErrorWithResponse); + if (!isHttpError(error)) + throw toUnhandledError( + "failed to convert error to HttpClientError or HttpServerError", + rawAxiosError, + ); + + throw toMappedError(error); + }; +}; + +export const isValidErrorResponse = ( + response: AxiosResponse | undefined, +): response is AxiosResponse => + !!response && typeof response.status === "number" && !!response.data; + +// TODO Do we want to restrict statuses to a union of HttpCodes ? + +// TODO Do we have to further test what is a valid axios response format for us ? +//&& !!headers && !!config: AxiosRequestConfig + +// TODO Pole Emploi consider "" to be a valid error response body +const _poleEmploiIsValidResponseBody = (data: any): boolean => + !!data || data === ""; diff --git a/shared/src/httpClient/ports/axios.unit.test.ts b/shared/src/httpClient/adapters/axios.config.unit.test.ts similarity index 52% rename from shared/src/httpClient/ports/axios.unit.test.ts rename to shared/src/httpClient/adapters/axios.config.unit.test.ts index a937ecf49a..04d6b4dbeb 100644 --- a/shared/src/httpClient/ports/axios.unit.test.ts +++ b/shared/src/httpClient/adapters/axios.config.unit.test.ts @@ -1,38 +1,52 @@ -import { isValidAxiosErrorResponse } from "./axios.port"; +import { isValidErrorResponse } from "./axios.config"; -import axios from "axios"; - -describe("Error Response format", () => { +describe("Error Response format standart", () => { it.each([ - [{ response: {} }, false], + [null, false], + ["plop", false], + [{}, false], [ { - isAxiosError: false, + request: { status: "plop" }, }, false, ], + [[], false], + [[{}], false], + [{}, false], [ { - isAxiosError: true, + data: "", + status: 400, + }, + false, + ], + [ + { + data: "plop", + status: 400, }, true, ], [ { - response: { - status: 400, + data: { + prop: "A nested property", }, - isAxiosError: true, + status: 400, }, true, ], ])( - "isAxiosError should detect if the response has a valid format, expect: (%s to be %s)", + "isAxiosResponse should detect if the response has a valid response structure code, expect: (%s to be %s)", (raw: any, expected: boolean) => { - expect(axios.isAxiosError(raw)).toBe(expected); + expect(isValidErrorResponse(raw)).toBe(expected); }, ); +}); +// TODO PE version with data that can be equal to "" +/*describe("Error Response format standart POLE EMPLOI", () => { it.each([ [null, false], ["plop", false], @@ -72,7 +86,7 @@ describe("Error Response format", () => { ])( "isAxiosResponse should detect if the response has a valid response structure code, expect: (%s to be %s)", (raw: any, expected: boolean) => { - expect(isValidAxiosErrorResponse(raw)).toBe(expected); + expect(isValidErrorResponse(raw)).toBe(expected); }, ); -}); +});*/ diff --git a/shared/src/httpClient/adapters/axios.mappers.ts b/shared/src/httpClient/adapters/axios.mappers.ts new file mode 100644 index 0000000000..66b27557fd --- /dev/null +++ b/shared/src/httpClient/adapters/axios.mappers.ts @@ -0,0 +1,42 @@ +import { HttpClientError } from "../errors/HttpClientError.error"; +import { HttpServerError } from "../errors/HttpServerError.error"; + +import { AxiosError } from "axios"; +import { isHttpClientError } from "../httpClient"; +import { AxiosErrorWithResponse } from "./axios.adapter"; +export const toHttpError = ( + error: AxiosErrorWithResponse, +): HttpClientError | HttpServerError | undefined => { + if (isHttpClientError(error.response.status)) { + return new HttpClientError( + `4XX Status Code ${toAxiosHttpErrorString(error)}`, + error, + error.response.status, + ); + } + + if (isHttpClientError(error.response.status)) { + return new HttpServerError( + `5XX Status Code ${toAxiosHttpErrorString(error)}`, + error, + error.response.status, + ); + } +}; + +const toAxiosHttpErrorString = (error: AxiosError): string => + JSON.stringify( + { + requestConfig: { + url: error.response?.config?.url, + headers: error.response?.config?.headers, + method: error.response?.config?.method, + data: error.response?.config?.data, + timeout: error.response?.config?.timeout, + }, + data: error.response?.data, + status: error.response?.status, + }, + null, + 2, + ); diff --git a/shared/src/httpClient/adapters/axios.unit.test.ts b/shared/src/httpClient/adapters/axios.unit.test.ts new file mode 100644 index 0000000000..fc1851512a --- /dev/null +++ b/shared/src/httpClient/adapters/axios.unit.test.ts @@ -0,0 +1,33 @@ +import axios from "axios"; + +describe("Error Response format", () => { + it.each([ + [{ response: {} }, false], + [ + { + isAxiosError: false, + }, + false, + ], + [ + { + isAxiosError: true, + }, + true, + ], + [ + { + response: { + status: 400, + }, + isAxiosError: true, + }, + true, + ], + ])( + "isAxiosError should detect if the response has a valid format, expect: (%s to be %s)", + (raw: any, expected: boolean) => { + expect(axios.isAxiosError(raw)).toBe(expected); + }, + ); +}); diff --git a/shared/src/httpClient/adapters/httpClientWithAxios.manual.test.ts b/shared/src/httpClient/adapters/httpClientWithAxios.manual.test.ts new file mode 100644 index 0000000000..2e8f0cb13a --- /dev/null +++ b/shared/src/httpClient/adapters/httpClientWithAxios.manual.test.ts @@ -0,0 +1,136 @@ +import { ManagedAxios } from "./axios.adapter"; + +import { + ErrorMapper, + HttpClient, + HttpResponse, + TargetUrlsMapper, +} from "../httpClient"; +import { AbsoluteUrl } from "../../AbsoluteUrl"; +import { HttpClientError } from "../errors/HttpClientError.error"; + +describe("httpClient with axios concrete adapter", () => { + const targetToValidSearchUrl = (rawQueryString: string): AbsoluteUrl => + `https://api-adresse.data.gouv.fr/search/?q=${encodeURI( + rawQueryString, + )}&limit=1`; + + it("expect user defined function to produce absolute url", () => { + expect(targetToValidSearchUrl("18 avenue des Canuts 69120")).toBe( + `https://api-adresse.data.gouv.fr/search/?q=18%20avenue%20des%20Canuts%2069120&limit=1`, + ); + }); + + it("should call API Adresse and return 200 with data", async () => { + type TargetUrls = "ADRESS_API_ENDPOINT"; + const targetUrls: TargetUrlsMapper = { + ADRESS_API_ENDPOINT: targetToValidSearchUrl, + }; + + const httpClient: HttpClient = new ManagedAxios(targetUrls); + + const response: HttpResponse = await httpClient.get({ + target: targetUrls.ADRESS_API_ENDPOINT, + targetParams: "18 avenue des Canuts 69120", + }); + + const expectedStatus = 200; + const expectedData = { + attribution: "BAN", + features: [ + { + geometry: { + coordinates: [4.923847, 45.761134], + type: "Point", + }, + properties: { + city: "Vaulx-en-Velin", + citycode: "69256", + context: "69, Rhône, Auvergne-Rhône-Alpes", + housenumber: "18", + id: "69256_0227_00018", + importance: 0.62418, + label: "18 Avenue des Canuts 69120 Vaulx-en-Velin", + name: "18 Avenue des Canuts", + postcode: "69120", + score: 0.8749254545454545, + street: "Avenue des Canuts", + type: "housenumber", + x: 849523.68, + y: 6519769.27, + }, + type: "Feature", + }, + ], + licence: "ETALAB-2.0", + limit: 1, + query: "18 avenue des Canuts 69120", + type: "FeatureCollection", + version: "draft", + }; + + expect(response.status).toBe(expectedStatus); + expect(response.data).toStrictEqual(expectedData); + }); + + it("should call API Adresse with invalid address and throw HttpClientError", async () => { + type TargetUrls = "ADDRESS_API_SEARCH_ENDPOINT"; + + const targetToInvalidSearchUrl = (rawQueryString: string): AbsoluteUrl => + `https://api-adresse.data.gouv.fr/search/?d=${rawQueryString}&limit=1`; + + const targetUrls: TargetUrlsMapper = { + ADDRESS_API_SEARCH_ENDPOINT: targetToInvalidSearchUrl, + }; + + const httpClient: HttpClient = new ManagedAxios(targetUrls); + + const responsePromise: Promise = httpClient.get({ + target: targetUrls.ADDRESS_API_SEARCH_ENDPOINT, + targetParams: "18 avenue des Canuts 69120", + }); + + await expect(responsePromise).rejects.toThrow(HttpClientError); + }); + + it("should call API Adresse with invalid address and throw remapped CustomError", async () => { + class CustomError extends Error { + constructor(message: string, cause?: Error) { + super(message, { cause }); + } + } + + type TargetUrls = "ADDRESS_API_SEARCH_ENDPOINT"; + + const targetToInvalidSearchUrl = (rawQueryString: string): AbsoluteUrl => + `https://api-adresse.data.gouv.fr/search/?d=${rawQueryString}&limit=1`; + + const targetUrls: TargetUrlsMapper = { + ADDRESS_API_SEARCH_ENDPOINT: targetToInvalidSearchUrl, + }; + + const targetsErrorResponseOverrideMapper: ErrorMapper = { + ADDRESS_API_SEARCH_ENDPOINT: { + HttpClientError: (error) => + new CustomError("You have an invalid url you dummy dum dum !", error), + HttpServerError: (error) => + new Error( + "You have an invalid url HttpServerError you dummy dum dum !", + error, + ), + }, + }; + + const httpClient: HttpClient = new ManagedAxios( + targetUrls, + targetsErrorResponseOverrideMapper, + ); + + const responsePromise: Promise = httpClient.get({ + target: targetUrls.ADDRESS_API_SEARCH_ENDPOINT, + targetParams: "18 avenue des Canuts 69120", + }); + + await expect(responsePromise).rejects.toThrow(CustomError); + }); +}); diff --git a/shared/src/httpClient/errors/4xxClientError.error.ts b/shared/src/httpClient/errors/4xxClientError.error.ts deleted file mode 100644 index b437586cc0..0000000000 --- a/shared/src/httpClient/errors/4xxClientError.error.ts +++ /dev/null @@ -1,13 +0,0 @@ -export class HttpClientError extends Error { - constructor( - public override readonly message: string, - public override readonly cause: Error, - //TODO Restrict to valid HttpStatusCodeError - public readonly httpStatusCode: number, - ) { - super(); - Error.captureStackTrace(this, this.constructor); - this.name = this.constructor.name; - this.message = message; - } -} diff --git a/shared/src/httpClient/errors/ConfigurationError.error.ts b/shared/src/httpClient/errors/ConfigurationError.error.ts new file mode 100644 index 0000000000..33e6f889ee --- /dev/null +++ b/shared/src/httpClient/errors/ConfigurationError.error.ts @@ -0,0 +1,11 @@ +export class ConfigurationError extends Error { + constructor( + public override readonly message: string = "Invalid configuration", + public override readonly cause?: Error, + ) { + super(); + Error.captureStackTrace(this, this.constructor); + this.name = this.constructor.name; + this.message = message; + } +} \ No newline at end of file diff --git a/shared/src/httpClient/errors/ConnectionRefused.error.ts b/shared/src/httpClient/errors/ConnectionRefused.error.ts deleted file mode 100644 index d0bac6f2fe..0000000000 --- a/shared/src/httpClient/errors/ConnectionRefused.error.ts +++ /dev/null @@ -1,14 +0,0 @@ -export class ConnectionRefusedError extends Error { - constructor( - public override readonly message: string = "Could not connect to server", - public override readonly cause?: Error, - ) { - super(); - Error.captureStackTrace(this, this.constructor); - this.name = this.constructor.name; - this.message = message; - } -} - -export const isConnectionRefusedError = (error: unknown): boolean => - (error as unknown as { code: string }).code === "ECONNREFUSED"; diff --git a/shared/src/httpClient/errors/HttpClientError.error.ts b/shared/src/httpClient/errors/HttpClientError.error.ts new file mode 100644 index 0000000000..deb05b7aaa --- /dev/null +++ b/shared/src/httpClient/errors/HttpClientError.error.ts @@ -0,0 +1,27 @@ +export class HttpClientError extends Error { + constructor( + public override readonly message: string, + public override readonly cause: Error, + //TODO Restrict to valid HttpStatusCodeError + public readonly httpStatusCode: number, + ) { + super(); + Error.captureStackTrace(this, this.constructor); + this.name = this.constructor.name; + this.message = message; + } +} + +export class HttpClientForbiddenError extends HttpClientError { + //public readonly httpStatusCode = 401; + + constructor( + public override readonly message: string, + public override readonly cause: Error, //TODO Restrict to valid HttpStatusCodeError + ) { + super(message, cause, 401); + Error.captureStackTrace(this, this.constructor); + this.name = this.constructor.name; + this.message = message; + } +} diff --git a/shared/src/httpClient/errors/5xxServerError.error.ts b/shared/src/httpClient/errors/HttpServerError.error.ts similarity index 100% rename from shared/src/httpClient/errors/5xxServerError.error.ts rename to shared/src/httpClient/errors/HttpServerError.error.ts diff --git a/shared/src/httpClient/errors/InfrastructureError.error.ts b/shared/src/httpClient/errors/InfrastructureError.error.ts new file mode 100644 index 0000000000..d5e6d6b648 --- /dev/null +++ b/shared/src/httpClient/errors/InfrastructureError.error.ts @@ -0,0 +1,45 @@ +type InfrastructureErrorCodes = "ECONNREFUSED" | "ECONNRESET"; + +export class InfrastructureError extends Error { + constructor( + public override readonly message: string = "Infrastructure error", + public override readonly cause: Error, + public readonly code: InfrastructureErrorCodes, + ) { + super(); + Error.captureStackTrace(this, this.constructor); + this.name = this.constructor.name; + this.message = message; + } +} + +export class ConnectionRefusedError extends InfrastructureError { + constructor( + public override readonly message: string = "Could not connect to server", + public override readonly cause: Error, + ) { + super(message, cause, "ECONNREFUSED"); + Error.captureStackTrace(this, this.constructor); + this.name = this.constructor.name; + this.message = message; + } +} + +// TODO On veut probablement réessayer l'appel quand ça arrive +export class ConnectionResetError extends InfrastructureError { + constructor( + public override readonly message: string = "The other side of the TCP conversation abruptly closed its end of the connection", + public override readonly cause: Error, + ) { + super(message, cause, "ECONNRESET"); + Error.captureStackTrace(this, this.constructor); + this.name = this.constructor.name; + this.message = message; + } +} + +export const isConnectionRefusedError = (error: unknown): boolean => + (error as unknown as { code: string }).code === "ECONNREFUSED"; + +export const isConnectionResetError = (error: unknown): boolean => + (error as unknown as { code: string }).code === "ECONNRESET"; diff --git a/shared/src/httpClient/errors/httpClient.unit.test.ts b/shared/src/httpClient/errors/httpClient.unit.test.ts deleted file mode 100644 index 0ddbe7cdce..0000000000 --- a/shared/src/httpClient/errors/httpClient.unit.test.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { isHttpClientError, isHttpServerError } from "../httpClient"; - -describe("Http Client Errors", () => { - it.each([ - [100, false], - [308, false], - [399, false], - [400, true], - [451, true], - [499, true], - [500, false], - [502, false], - ])( - "isHttpClientErrorStatus should detect HttpClient 4XX errors, expect: (%i to be %s)", - (httpStatusCode: number, expected: boolean) => { - expect(isHttpClientError(httpStatusCode)).toBe(expected); - }, - ); -}); - -describe("Http Server Errors", () => { - it.each([ - [100, false], - [308, false], - [399, false], - [400, false], - [451, false], - [499, false], - [500, true], - [502, true], - [511, true], - [600, false], - ])( - "isHttpServerErrorStatus should detect Http Server 5XX errors, expect: (%i to be %s)", - (httpStatusCode: number, expected: boolean) => { - expect(isHttpServerError(httpStatusCode)).toBe(expected); - }, - ); -}); diff --git a/shared/src/httpClient/httpClient.mappers.ts b/shared/src/httpClient/httpClient.mappers.ts new file mode 100644 index 0000000000..bea3723b28 --- /dev/null +++ b/shared/src/httpClient/httpClient.mappers.ts @@ -0,0 +1,78 @@ +import { + InfrastructureError, + ConnectionRefusedError, + isConnectionRefusedError, + isConnectionResetError, + ConnectionResetError, +} from "./errors/InfrastructureError.error"; +import { ErrorMapper } from "./httpClient"; + +export const toMappedErrorMaker = + (target: T, errorMapper: ErrorMapper = {}) => + (error: Error): Error => { + const errorByConstructorName = errorMapper[target]; + if (!errorByConstructorName) return error; + + const newErrorMaker = errorByConstructorName[error.constructor.name]; + + if (!newErrorMaker) return error; + + return newErrorMaker(error); + }; + +export const toInfrastructureError = ( + error: Error, +): InfrastructureError | undefined => { + if (isConnectionRefusedError(error)) + return new ConnectionRefusedError( + `Could not connect to server : ${toRawErrorString(error)}`, + error, + ); + + if (isConnectionResetError(error)) + return new ConnectionResetError( + `The other side of the TCP conversation abruptly closed its end of the connection: ${toRawErrorString( + error, + )}`, + error, + ); +}; + +export const toUnhandledError = (details: string, error: Error): Error => { + let rawString: string; + try { + rawString = JSON.stringify(error); + } catch (stringifyError: any) { + const keys: string[] = Object.keys(error); + rawString = `Failed to JSON.stringify the error due to : ${ + stringifyError?.message + }. + Raw object keys : ${ + keys.length > 0 + ? keys.join("\n") + : "Object.keys(error) returned an empty array" + }`; + } + return new Error( + `Unhandled Http Client Error - ${details} - JSON Stringify tentative result -> ${rawString}`, + { cause: error }, + ); +}; + +const toRawErrorString = (error: any): string => + JSON.stringify( + { + code: error.code, + address: error.address, + port: error.port, + config: { + headers: error.config?.headers, + method: error.config?.method, + url: error.config?.url, + baseUrl: error.config?.baseUrl, + data: error.config?.data, + }, + }, + null, + 2, + ); diff --git a/shared/src/httpClient/httpClient.mappers.unit.test.ts b/shared/src/httpClient/httpClient.mappers.unit.test.ts new file mode 100644 index 0000000000..1239ca7985 --- /dev/null +++ b/shared/src/httpClient/httpClient.mappers.unit.test.ts @@ -0,0 +1,84 @@ +import { HttpClientForbiddenError } from "./errors/HttpClientError.error"; +import { ConnectionRefusedError } from "./errors/InfrastructureError.error"; +import { ErrorMapper } from "./httpClient"; +import { toMappedErrorMaker } from "./httpClient.mappers"; + +describe("toMappedErrorMaker", () => { + it("toErrorMapper should return input if mappedErrors are undefined", () => { + type TargetUrls = "ADDRESS_API_SEARCH_ENDPOINT"; + const error = new HttpClientForbiddenError("Plop", new Error("plip")); + + const errorMapper = toMappedErrorMaker( + "ADDRESS_API_SEARCH_ENDPOINT", + undefined as unknown as ErrorMapper, + ); + + expect(errorMapper(error)).toBe(error); + }); + + it("toErrorMapper should return input if the error is not in mapped errors", () => { + type TargetUrls = "ADDRESS_API_SEARCH_ENDPOINT"; + const error = new HttpClientForbiddenError("Plop", new Error("plip")); + + const errorMapper: ErrorMapper = { + ADDRESS_API_SEARCH_ENDPOINT: { + HttpBadRequestError: () => + new Error("I identify as a Bad Request Error"), + }, + }; + + const toMappedError = toMappedErrorMaker( + "ADDRESS_API_SEARCH_ENDPOINT", + errorMapper, + ); + + expect(toMappedError(error)).toBe(error); + }); + + it("toErrorMapper should return mapped http error", () => { + type TargetUrls = "ADDRESS_API_SEARCH_ENDPOINT"; + const error = new HttpClientForbiddenError("Plop", new Error("plip")); + + const expectedError = new Error( + "you though it was HttpClientForbiddenError but it was me, Dio !", + ); + + const errorMapper: ErrorMapper = { + ADDRESS_API_SEARCH_ENDPOINT: { + HttpClientForbiddenError: () => expectedError, + }, + }; + + const toMappedError = toMappedErrorMaker( + "ADDRESS_API_SEARCH_ENDPOINT", + errorMapper, + ); + + expect(toMappedError(error)).toBe(expectedError); + }); + + it("toErrorMapper should return mapped infrastructure error", () => { + type TargetUrls = "ADDRESS_API_SEARCH_ENDPOINT"; + const error = new ConnectionRefusedError( + "Could not connect to server because there is no server !", + new Error("I caused this"), + ); + + const expectedError = new Error( + "you though it was ConnectionRefusedError but it was me, Dio !", + ); + + const errorMapper: ErrorMapper = { + ADDRESS_API_SEARCH_ENDPOINT: { + ConnectionRefusedError: () => expectedError, + }, + }; + + const toMappedError = toMappedErrorMaker( + "ADDRESS_API_SEARCH_ENDPOINT", + errorMapper, + ); + + expect(toMappedError(error)).toBe(expectedError); + }); +}); diff --git a/shared/src/httpClient/httpClient.ts b/shared/src/httpClient/httpClient.ts index d3e65b091b..9951e5cd9e 100644 --- a/shared/src/httpClient/httpClient.ts +++ b/shared/src/httpClient/httpClient.ts @@ -1,7 +1,70 @@ +import { ConfigurationError } from "./errors/ConfigurationError.error"; +import { HttpClientError } from "./errors/HttpClientError.error"; +import { HttpServerError } from "./errors/HttpServerError.error"; + +export interface HttpClient { + get: (config: HttpClientGetConfig) => Promise; + post: (config: HttpClientPostConfig) => Promise; + //get$: (config: HttpClientGetConfig) => Observable; + //post$: (config: HttpClientPostConfig) => Observable; + // eslint-disable-next-line @typescript-eslint/no-unused-vars +} + +export const getTargetFromPredicate = ( + predicate: (params: any) => AbsoluteUrl, + targetsUrls: Record AbsoluteUrl>, +): string | never => { + const target: string | undefined = Object.keys(targetsUrls).find( + (targetsUrlKey) => targetsUrls[targetsUrlKey] === predicate, + ); + if (!target) + throw new ConfigurationError( + "Invalid configuration: This target predicate does not match any registered target", + ); + return target; +}; + +export const isHttpError = ( + error: any, +): error is HttpClientError | HttpServerError => + error instanceof HttpClientError || error instanceof HttpServerError; + +type Http = "http://" | "https://"; +export type AbsoluteUrl = `${Http}${string}`; + export const isHttpClientError = (status: number): boolean => status >= 400 && status < 500; export const isHttpServerError = (status: number): boolean => status >= 500 && status < 600; -// TODO Add createClient interface ? +export type TargetUrlsMapper = Record< + TargetUrls, + (params: any) => AbsoluteUrl +>; + +export type ErrorMapper = Partial< + Record Error>>> +>; + +export interface HttpResponse { + data: T; + status: number; + statusText: string; + headers: any; + config: any; + request?: any; +} + +export type HttpClientPostConfig = { + target: (params: any) => AbsoluteUrl; + targetParams?: any; + data?: string | undefined; + adapterConfig?: any; +}; + +export type HttpClientGetConfig = { + target: (params: any) => AbsoluteUrl; + targetParams?: any; + adapterConfig?: any; +}; diff --git a/shared/src/httpClient/httpClient.unit.test.ts b/shared/src/httpClient/httpClient.unit.test.ts new file mode 100644 index 0000000000..e8540b92ed --- /dev/null +++ b/shared/src/httpClient/httpClient.unit.test.ts @@ -0,0 +1,72 @@ +import { + AbsoluteUrl, + getTargetFromPredicate, + isHttpClientError, + isHttpServerError, + TargetUrlsMapper, +} from "./httpClient"; + +describe("Http Client Errors", () => { + it.each([ + [100, false], + [308, false], + [399, false], + [400, true], + [451, true], + [499, true], + [500, false], + [502, false], + ])( + "isHttpClientErrorStatus should detect HttpClient 4XX errors, expect: (%i to be %s)", + (httpStatusCode: number, expected: boolean) => { + expect(isHttpClientError(httpStatusCode)).toBe(expected); + }, + ); +}); + +describe("Http Server Errors", () => { + it.each([ + [100, false], + [308, false], + [399, false], + [400, false], + [451, false], + [499, false], + [500, true], + [502, true], + [511, true], + [600, false], + ])( + "isHttpServerErrorStatus should detect Http Server 5XX errors, expect: (%i to be %s)", + (httpStatusCode: number, expected: boolean) => { + expect(isHttpServerError(httpStatusCode)).toBe(expected); + }, + ); +}); + +describe("find target from callback", () => { + it("getTargetFromPredicate should return", () => { + type TargetUrls = "ADDRESS_API_SEARCH" | "ADDRESS_API_GEOLOCATE"; + + const targetToValidSearchUrl = (rawQueryString: string): AbsoluteUrl => + `https://api-adresse.data.gouv.fr/search/?q=${encodeURI( + rawQueryString, + )}&limit=1`; + + const targetToGeolocateUrl = (): AbsoluteUrl => + `https://geo.api.gouv.fr/communes`; + + const targetUrls: TargetUrlsMapper = { + ADDRESS_API_SEARCH: targetToValidSearchUrl, + ADDRESS_API_GEOLOCATE: targetToGeolocateUrl, + }; + + expect(getTargetFromPredicate(targetToValidSearchUrl, targetUrls)).toBe( + "ADDRESS_API_SEARCH", + ); + + expect(getTargetFromPredicate(targetToGeolocateUrl, targetUrls)).toBe( + "ADDRESS_API_GEOLOCATE", + ); + }); +}); diff --git a/shared/src/httpClient/ports/axios.port.ts b/shared/src/httpClient/ports/axios.port.ts deleted file mode 100644 index c69a8ac95e..0000000000 --- a/shared/src/httpClient/ports/axios.port.ts +++ /dev/null @@ -1,177 +0,0 @@ -//TODO Découpler les notions entre axios et client http générique ? - -import axios, { - AxiosError, - AxiosInstance, - AxiosRequestConfig, - AxiosResponse, -} from "axios"; -import { HttpClientError } from "../errors/4xxClientError.error"; -import { HttpServerError } from "../errors/5xxServerError.error"; -import { - ConnectionRefusedError, - isConnectionRefusedError, -} from "../errors/ConnectionRefused.error"; -import { isHttpClientError, isHttpServerError } from "../httpClient"; - -type AxiosErrorWithResponse = AxiosError & { response: AxiosResponse }; - -// TODO For now directly use this -export const createManagedAxiosInstance = ( - axiosConfig: AxiosRequestConfig = {}, -): AxiosInstance => { - const axiosInstance = axios.create(axiosConfig); - axiosInstance.interceptors.request.use( - (request) => request, - axiosErrorRequestInterceptor, - ); - - axiosInstance.interceptors.response.use((validResponse) => { - // eslint-disable-next-line no-console - console.log("[Axios Managed Request url]: ", validResponse.config.url); - // eslint-disable-next-line no-console - console.log("[Axios Managed Response Status]: ", validResponse.status); - return validResponse; - }, axiosErrorResponseInterceptor); - - return axiosInstance; -}; - -const axiosErrorResponseInterceptor = (rawAxiosError: AxiosError): never => { - handleConnectionRefused(rawAxiosError); - - const error: AxiosErrorWithResponse = - validateAxiosErrorResponse(rawAxiosError); - - handleHttpClientError(error); - handleServerError(error); - - throw Error(`We should never reach here`, error); -}; - -const axiosErrorRequestInterceptor = (error: any): never => { - throw new Error("Invalid request", { cause: error }); -}; - -export const isValidAxiosErrorResponse = ( - response: AxiosResponse | undefined, -): response is AxiosResponse => - !!response && - isValidResponseBody(response.data) && - typeof response.status === "number"; - -// TODO Do we want to restrict statuses to a union of HttpCodes ? - -// TODO Do we have to further test what is a valid axios response format for us ? -//&& !!headers && !!config: AxiosRequestConfig - -const isValidResponseBody = (data: any): boolean => !!data || data === ""; - -const handleHttpClientError = (error: AxiosErrorWithResponse): never | void => { - // TODO Manage some specific status here ? (429) - if (isHttpClientError(error.response.status)) throwClientError(error); -}; - -const handleServerError = (error: AxiosErrorWithResponse): never | void => { - if (isHttpServerError(error.response.status)) throwServerError(error); -}; - -const throwClientError = (error: AxiosErrorWithResponse): never => { - throw new HttpClientError( - `4XX Status Code ${toAxiosHttpErrorString(error)}`, - error, - error.response.status, - ); -}; - -const throwServerError = (error: AxiosErrorWithResponse): never => { - throw new HttpServerError( - `5XX Status Code ${toAxiosHttpErrorString(error)}`, - error, - error.response.status, - ); -}; - -const handleConnectionRefused = (error: AxiosError): never | void => { - if (isConnectionRefusedError(error)) - throw new ConnectionRefusedError( - `Could not connect to server : ${toRawErrorString(error)}`, - error, - ); -}; - -const validateAxiosErrorResponse = ( - error: AxiosError, -): AxiosErrorWithResponse => { - if (!axios.isAxiosError(error)) - throwUnhandledError( - `error Response does not have the property isAxiosError set to true`, - error, - ); - - if (!isValidAxiosErrorResponse(error.response)) - throwUnhandledError( - "error response objects does not have mandatory keys", - error, - ); - - return error as AxiosErrorWithResponse; -}; - -export const throwUnhandledError = ( - details: string, - error: AxiosError, -): never => { - let rawString: string; - try { - rawString = JSON.stringify(error); - } catch (stringifyError: any) { - const keys: string[] = Object.keys(error); - rawString = `Failed to JSON.stringify the error due to : ${ - stringifyError?.message - }. - Raw object keys : ${ - keys.length > 0 - ? keys.join("\n") - : "Object.keys(error) returned an empty array" - }`; - } - throw new Error( - `Unhandled Http Client Error - ${details} - JSON Stringify tentative result -> ${rawString}`, - { cause: error }, - ); -}; - -const toAxiosHttpErrorString = (error: AxiosError): string => - JSON.stringify( - { - requestConfig: { - url: error.response?.config?.url, - headers: error.response?.config?.headers, - method: error.response?.config?.method, - data: error.response?.config?.data, - timeout: error.response?.config?.timeout, - }, - data: error.response?.data, - status: error.response?.status, - }, - null, - 2, - ); - -const toRawErrorString = (error: any): string => - JSON.stringify( - { - code: error.code, - address: error.address, - port: error.port, - config: { - headers: error.config?.headers, - method: error.config?.method, - url: error.config?.url, - data: error.config?.data, - }, - }, - null, - 2, - );