From 39c5298c8b6abb71bce47ef437e6e7359c3e8195 Mon Sep 17 00:00:00 2001 From: Ajima Chukwuemeka <32770340+ajimae@users.noreply.github.com> Date: Fri, 15 Nov 2024 12:22:17 +0100 Subject: [PATCH] [Feat][DEVX-487] Add `httpClient` configuration options (#848) * feat(sdk-client-v3): add http client config option - add config object to auth and http middleware options - add object validation to ensure option is truly an object - add unit and integration tests - minor refactor * chore(release-changeset): add release changeset - add release changeset for new SDK release version - add axios as development dependencies (mainly for running tests) * chore(ci-error): fix ci error - properly sort root dependencies list * chore(feedback): implement feedback - implement feedback * chore(feedback): implement feedback - implement feedback --- .changeset/serious-insects-begin.md | 6 ++ package.json | 1 + .../sdk-v3/middlewares.test.ts | 86 +++++++++++++++++++ .../test/integration-tests/test-utils.ts | 2 +- packages/sdk-client-v3/src/client/builder.ts | 1 + .../auth-middleware/anonymous-session-flow.ts | 1 + .../auth-middleware/auth-request-executor.ts | 9 +- .../client-credentials-flow.ts | 1 + .../auth-middleware/password-flow.ts | 1 + .../auth-middleware/refresh-token-flow.ts | 1 + .../src/middleware/create-http-middleware.ts | 4 +- packages/sdk-client-v3/src/types/types.d.ts | 21 +++-- packages/sdk-client-v3/src/utils/executor.ts | 11 ++- packages/sdk-client-v3/src/utils/index.ts | 2 +- packages/sdk-client-v3/src/utils/validate.ts | 10 ++- .../tests/auth/auth-request-executor.test.ts | 56 ++++++++++++ yarn.lock | 47 +++++++++- 17 files changed, 241 insertions(+), 19 deletions(-) create mode 100644 .changeset/serious-insects-begin.md diff --git a/.changeset/serious-insects-begin.md b/.changeset/serious-insects-begin.md new file mode 100644 index 000000000..ffb33078e --- /dev/null +++ b/.changeset/serious-insects-begin.md @@ -0,0 +1,6 @@ +--- +'@commercetools/ts-client': patch +--- + +- add `httpClientOptions` to all supported `authMiddlewareOption` and `httpMiddlewareOptions` +- add options used for configuring the behaviour of the supported `httpClients` (fetch and axios) diff --git a/package.json b/package.json index bca4b124e..ff7e8bee5 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "@preconstruct/cli": "2.8.8", "@types/jest": "29.5.13", "@types/node": "^20.0.0", + "axios": "^1.7.7", "babel-jest": "29.7.0", "buffer": "^6.0.3", "clean-webpack-plugin": "^4.0.0", diff --git a/packages/platform-sdk/test/integration-tests/sdk-v3/middlewares.test.ts b/packages/platform-sdk/test/integration-tests/sdk-v3/middlewares.test.ts index be50a7d0f..10cdfe3e2 100644 --- a/packages/platform-sdk/test/integration-tests/sdk-v3/middlewares.test.ts +++ b/packages/platform-sdk/test/integration-tests/sdk-v3/middlewares.test.ts @@ -20,9 +20,11 @@ import { MiddlewareRequest, Next, ClientBuilder, + type HttpMiddlewareOptions, } from '@commercetools/ts-client/src' import { createApiBuilderFromCtpClient } from '../../../src' import { randomUUID } from 'crypto' +import axios from 'axios' import * as matchers from 'jest-extended' expect.extend(matchers) @@ -187,6 +189,90 @@ describe('Concurrent Modification Middleware', () => { }) }) +describe('Http clients and http client options', () => { + it('Axios should throw error internally and cut off subsequent execution', async () => { + let isCalled = false + + const after = () => { + return (next: Next) => { + return (request: MiddlewareRequest) => { + isCalled = true + return next(request) + } + } + } + + const http: HttpMiddlewareOptions = { + ...httpMiddlewareOptionsV3, + httpClient: axios, + host: 'https://commercetools.com', // should fail (404 incorrect host) + httpClientOptions: { + validateStatus: () => false, // axios default + }, + } + + const v3Client = new ClientBuilderV3() + .withClientCredentialsFlow(authMiddlewareOptionsV3) + .withHttpMiddleware(http) + // should not be called (since axios will throw internal error) + .withAfterExecutionMiddleware({ middleware: after }) + .build() + + const api = createApiBuilderFromCtpClient(v3Client).withProjectKey({ + projectKey, + }) + + await api + .get() + .execute() + .catch(() => null) + expect(isCalled).toBe(false) + }) + + it('Axios Should not throw error internally, continue executions', async () => { + let isCalled = false + + const after = () => { + return (next: Next) => { + return (request: MiddlewareRequest) => { + isCalled = true + return next(request) + } + } + } + + const auth = { + ...authMiddlewareOptionsV3, + } + + const http: HttpMiddlewareOptions = { + ...httpMiddlewareOptionsV3, + httpClient: axios, + host: 'https://commercetools.com', // should fail (404 incorrect host) + httpClientOptions: { + validateStatus: () => true, // change axios default + }, + } + + const v3Client = new ClientBuilderV3() + .withClientCredentialsFlow(auth) + .withHttpMiddleware(http) + // should be called (since axios won't throw internal error) + .withAfterExecutionMiddleware({ middleware: after }) + .build() + + const api = createApiBuilderFromCtpClient(v3Client).withProjectKey({ + projectKey, + }) + + await api + .get() + .execute() + .catch(() => null) + expect(isCalled).toBe(true) + }) +}) + describe('Before and after execution middlewares', () => { it('should execute before execution middleware before after execution middleware', async () => { let beforeRequest diff --git a/packages/platform-sdk/test/integration-tests/test-utils.ts b/packages/platform-sdk/test/integration-tests/test-utils.ts index 9860c5fce..fb5b4c562 100644 --- a/packages/platform-sdk/test/integration-tests/test-utils.ts +++ b/packages/platform-sdk/test/integration-tests/test-utils.ts @@ -53,7 +53,7 @@ export const authMiddlewareOptions = { }, tokenCache, scopes: [`manage_project:${projectKey}`], - fetch, + httpClient: fetch, } export const authMiddlewareOptionsV3 = { diff --git a/packages/sdk-client-v3/src/client/builder.ts b/packages/sdk-client-v3/src/client/builder.ts index cb336c936..f54cf9482 100644 --- a/packages/sdk-client-v3/src/client/builder.ts +++ b/packages/sdk-client-v3/src/client/builder.ts @@ -75,6 +75,7 @@ export default class ClientBuilder { host: oauthUri, projectKey: projectKey || this.projectKey, credentials, + httpClient: httpClient || fetch, scopes, }).withHttpMiddleware({ host: baseUri, diff --git a/packages/sdk-client-v3/src/middleware/auth-middleware/anonymous-session-flow.ts b/packages/sdk-client-v3/src/middleware/auth-middleware/anonymous-session-flow.ts index 894c0a0f5..dfb3698ce 100644 --- a/packages/sdk-client-v3/src/middleware/auth-middleware/anonymous-session-flow.ts +++ b/packages/sdk-client-v3/src/middleware/auth-middleware/anonymous-session-flow.ts @@ -56,6 +56,7 @@ export default function createAuthMiddlewareForAnonymousSessionFlow( tokenCache, tokenCacheKey, httpClient: options.httpClient || fetch, + httpClientOptions: options.httpClientOptions, ...buildRequestForAnonymousSessionFlow(options), userOption: options, next, diff --git a/packages/sdk-client-v3/src/middleware/auth-middleware/auth-request-executor.ts b/packages/sdk-client-v3/src/middleware/auth-middleware/auth-request-executor.ts index 65af02e3b..27823de28 100644 --- a/packages/sdk-client-v3/src/middleware/auth-middleware/auth-request-executor.ts +++ b/packages/sdk-client-v3/src/middleware/auth-middleware/auth-request-executor.ts @@ -12,7 +12,13 @@ import { import { buildRequestForRefreshTokenFlow } from './auth-request-builder' export async function executeRequest(options: executeRequestOptions) { - const { httpClient, tokenCache, userOption, tokenCacheObject } = options + const { + httpClient, + httpClientOptions, + tokenCache, + userOption, + tokenCacheObject, + } = options let url = options.url let body = options.body @@ -62,6 +68,7 @@ export async function executeRequest(options: executeRequestOptions) { 'Content-Length': byteLength(body), }, httpClient, + httpClientOptions, body, }) diff --git a/packages/sdk-client-v3/src/middleware/auth-middleware/client-credentials-flow.ts b/packages/sdk-client-v3/src/middleware/auth-middleware/client-credentials-flow.ts index 45fb032a8..9338c2608 100644 --- a/packages/sdk-client-v3/src/middleware/auth-middleware/client-credentials-flow.ts +++ b/packages/sdk-client-v3/src/middleware/auth-middleware/client-credentials-flow.ts @@ -57,6 +57,7 @@ export default function createAuthMiddlewareForClientCredentialsFlow( tokenCacheKey, tokenCacheObject, httpClient: options.httpClient || fetch, + httpClientOptions: options.httpClientOptions, ...buildRequestForClientCredentialsFlow(options), next, } diff --git a/packages/sdk-client-v3/src/middleware/auth-middleware/password-flow.ts b/packages/sdk-client-v3/src/middleware/auth-middleware/password-flow.ts index 8735e2d0d..7efea582b 100644 --- a/packages/sdk-client-v3/src/middleware/auth-middleware/password-flow.ts +++ b/packages/sdk-client-v3/src/middleware/auth-middleware/password-flow.ts @@ -53,6 +53,7 @@ export default function createAuthMiddlewareForPasswordFlow( tokenCache, tokenCacheKey, httpClient: options.httpClient || fetch, + httpClientOptions: options.httpClientOptions, ...buildRequestForPasswordFlow(options), userOption: options, next, diff --git a/packages/sdk-client-v3/src/middleware/auth-middleware/refresh-token-flow.ts b/packages/sdk-client-v3/src/middleware/auth-middleware/refresh-token-flow.ts index e7116490b..5a0a7c827 100644 --- a/packages/sdk-client-v3/src/middleware/auth-middleware/refresh-token-flow.ts +++ b/packages/sdk-client-v3/src/middleware/auth-middleware/refresh-token-flow.ts @@ -51,6 +51,7 @@ export default function createAuthMiddlewareForRefreshTokenFlow( request, tokenCache, httpClient: options.httpClient || fetch, + httpClientOptions: options.httpClientOptions, ...buildRequestForRefreshTokenFlow(options), next, } diff --git a/packages/sdk-client-v3/src/middleware/create-http-middleware.ts b/packages/sdk-client-v3/src/middleware/create-http-middleware.ts index 00ac0ebdc..9ac6d3b45 100644 --- a/packages/sdk-client-v3/src/middleware/create-http-middleware.ts +++ b/packages/sdk-client-v3/src/middleware/create-http-middleware.ts @@ -22,7 +22,7 @@ import { getHeaders, isBuffer, maskAuthData, - validateHttpOptions, + validateHttpClientOptions, } from '../utils' async function executeRequest({ @@ -142,7 +142,7 @@ export default function createHttpMiddleware( options: HttpMiddlewareOptions ): Middleware { // validate response - validateHttpOptions(options) + validateHttpClientOptions(options) const { host, diff --git a/packages/sdk-client-v3/src/types/types.d.ts b/packages/sdk-client-v3/src/types/types.d.ts index 30c1eed7a..83f56a575 100644 --- a/packages/sdk-client-v3/src/types/types.d.ts +++ b/packages/sdk-client-v3/src/types/types.d.ts @@ -98,8 +98,9 @@ export type AuthMiddlewareOptions = { scopes?: Array // For internal usage only oauthUri?: string - httpClient?: Function tokenCache?: TokenCache + httpClient: Function + httpClientOptions?: object } export type TokenCacheOptions = { @@ -138,7 +139,8 @@ export type RefreshAuthMiddlewareOptions = { tokenCache?: TokenCache, // For internal usage only oauthUri?: string - httpClient?: Function + httpClient: Function + httpClientOptions?: object } /* Request */ @@ -147,9 +149,10 @@ type requestBaseOptions = { body: string basicAuth: string request: MiddlewareRequest - tokenCache: TokenCache, - tokenCacheKey?: TokenCacheOptions, + tokenCache: TokenCache + tokenCacheKey?: TokenCacheOptions tokenCacheObject?: TokenStore + httpClientOptions?: object } export type executeRequestOptions = requestBaseOptions & { @@ -160,11 +163,9 @@ export type executeRequestOptions = requestBaseOptions & { export type AuthMiddlewareBaseOptions = requestBaseOptions & { request: MiddlewareRequest - httpClient?: Function + httpClient: Function } -export type RequestState = boolean - export type Task = { request: MiddlewareRequest next?: Next @@ -187,7 +188,8 @@ export type PasswordAuthMiddlewareOptions = { tokenCache?: TokenCache, // For internal usage only oauthUri?: string - httpClient?: Function + httpClient: Function + httpClientOptions?: object } export type TokenInfo = { @@ -214,8 +216,8 @@ export type HttpMiddlewareOptions = { enableRetry?: boolean retryConfig?: RetryOptions httpClient: Function - getAbortController?: () => AbortController httpClientOptions?: object // will be passed as a second argument to your httpClient function for configuration + getAbortController?: () => AbortController } export type RetryOptions = RetryMiddlewareOptions @@ -297,6 +299,7 @@ export type IClientOptions = { enableRetry?: boolean retryConfig?: RetryOptions maskSensitiveHeaderData?: boolean + httpClientOptions?: object } export type HttpClientOptions = IClientOptions & Optional diff --git a/packages/sdk-client-v3/src/utils/executor.ts b/packages/sdk-client-v3/src/utils/executor.ts index 6cb44b9ba..3b8bd5fbc 100644 --- a/packages/sdk-client-v3/src/utils/executor.ts +++ b/packages/sdk-client-v3/src/utils/executor.ts @@ -56,11 +56,10 @@ export default async function executor(request: HttpClientConfig) { async function execute() { return httpClient(url, { - ...rest, ...options, + ...rest, headers: { ...rest.headers, - ...options.headers, // axios header encoding 'Accept-Encoding': 'application/json', @@ -156,7 +155,13 @@ export default async function executor(request: HttpClientConfig) { * middleware options or from * http client config */ - {} + + /** + * we want to suppress axios internal + * error handling behaviour to make it + * consistent with native fetch. + */ + { validateStatus: (status: number) => true } ) return data diff --git a/packages/sdk-client-v3/src/utils/index.ts b/packages/sdk-client-v3/src/utils/index.ts index 05df1902c..47e0cf3cb 100644 --- a/packages/sdk-client-v3/src/utils/index.ts +++ b/packages/sdk-client-v3/src/utils/index.ts @@ -20,5 +20,5 @@ export { default as userAgent } from './userAgent' export { validate, // validateUserAgentOptions, - validateClient, validateHttpOptions, validateRetryCodes + validateClient, validateHttpClientOptions, validateRetryCodes } from './validate' diff --git a/packages/sdk-client-v3/src/utils/validate.ts b/packages/sdk-client-v3/src/utils/validate.ts index 8cee5d80d..1c5d2fd49 100644 --- a/packages/sdk-client-v3/src/utils/validate.ts +++ b/packages/sdk-client-v3/src/utils/validate.ts @@ -9,7 +9,7 @@ import { * validate some essential http options * @param options */ -export function validateHttpOptions(options: HttpMiddlewareOptions) { +export function validateHttpClientOptions(options: HttpMiddlewareOptions) { if (!options.host) throw new Error( 'Request `host` or `url` is missing or invalid, please pass in a valid host e.g `host: http://a-valid-host-url`' @@ -19,6 +19,14 @@ export function validateHttpOptions(options: HttpMiddlewareOptions) { throw new Error( 'An `httpClient` is not available, please pass in a `fetch` or `axios` instance as an option or have them globally available.' ) + + if ( + options.httpClientOptions && + Object.prototype.toString.call(options.httpClientOptions) !== + '[object Object]' + ) { + throw new Error('`httpClientOptions` must be an object type') + } } /** diff --git a/packages/sdk-client-v3/tests/auth/auth-request-executor.test.ts b/packages/sdk-client-v3/tests/auth/auth-request-executor.test.ts index 92acde48e..a0ef095ca 100644 --- a/packages/sdk-client-v3/tests/auth/auth-request-executor.test.ts +++ b/packages/sdk-client-v3/tests/auth/auth-request-executor.test.ts @@ -211,5 +211,61 @@ describe('Auth request executor', () => { expect(err.message).toEqual('an error occurred.') } }) + + test('should call the httpClient using the provided `httpClientOptions`', async () => { + const options = createTestExecutorOptions({ + url: 'test-demo-uri', + tokenCache: cache(), + httpClientOptions: { name: 'httpClientOptions' }, + httpClient: jest.fn(() => ({ + statusCode: 200, + data: { + statusCode: 200, + access_token: 'test-access-token', + }, + })), + }) + + await executeRequest(options) + expect(options.httpClient).toHaveBeenCalledWith( + options.url, + expect.objectContaining({ + httpClientOptions: expect.objectContaining({ + name: 'httpClientOptions', + }), + }) + ) + }) + + test('should inject header options using `httpClientOptions`', async () => { + const options = createTestExecutorOptions({ + url: 'test-demo-uri', + tokenCache: cache(), + httpClientOptions: { + name: 'httpClientOptions', + headers: { withClientOptions: true }, + }, + httpClient: jest.fn(() => ({ + statusCode: 200, + data: { + statusCode: 200, + access_token: 'test-access-token', + }, + })), + }) + + await executeRequest(options) + expect(options.httpClient).toHaveBeenCalledWith( + options.url, + expect.objectContaining({ + httpClientOptions: expect.objectContaining({ + name: 'httpClientOptions', + headers: expect.objectContaining({ + withClientOptions: true, + }), + }), + }) + ) + }) }) }) diff --git a/yarn.lock b/yarn.lock index 0154083c8..e0bb5987b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3500,6 +3500,11 @@ async@^3.2.3: resolved "https://registry.yarnpkg.com/async/-/async-3.2.6.tgz#1b0728e14929d51b85b449b7f06e27c1145e38ce" integrity sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA== +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + at-least-node@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" @@ -3512,6 +3517,15 @@ available-typed-arrays@^1.0.7: dependencies: possible-typed-array-names "^1.0.0" +axios@^1.7.7: + version "1.7.7" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.7.tgz#2f554296f9892a72ac8d8e4c5b79c14a91d0a47f" + integrity sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q== + dependencies: + follow-redirects "^1.15.6" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + babel-jest@29.7.0, babel-jest@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.7.0.tgz#f4369919225b684c56085998ac63dbd05be020d5" @@ -4006,6 +4020,13 @@ colorette@^2.0.14, colorette@^2.0.20: resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + comma-separated-tokens@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz#4e89c9458acb61bc8fef19f4529973b2392839ee" @@ -4312,6 +4333,11 @@ del@^4.1.1: pify "^4.0.1" rimraf "^2.6.3" +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + dequal@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" @@ -4765,6 +4791,11 @@ flat@^5.0.2: resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== +follow-redirects@^1.15.6: + version "1.15.9" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1" + integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ== + for-each@^0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" @@ -4777,6 +4808,15 @@ form-data-encoder@^2.1.2: resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-2.1.4.tgz#261ea35d2a70d48d30ec7a9603130fa5515e9cd5" integrity sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw== +form-data@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.1.tgz#ba1076daaaa5bfd7e99c1a6cb02aa0a5cff90d48" + integrity sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + fs-extra@^11.0.0: version "11.2.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.2.0.tgz#e70e17dfad64232287d01929399e0ea7c86b0e5b" @@ -6399,7 +6439,7 @@ mime-db@1.52.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -mime-types@^2.1.27: +mime-types@^2.1.12, mime-types@^2.1.27: version "2.1.35" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== @@ -7102,6 +7142,11 @@ protobufjs@^7.2.5, protobufjs@^7.3.0: "@types/node" ">=13.7.0" long "^5.0.0" +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + pseudomap@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"