From b8998e7ead84a2714d13678aaf1e349e648eb90a Mon Sep 17 00:00:00 2001 From: Kamil Kisiela Date: Thu, 27 Jun 2024 16:00:30 +0200 Subject: [PATCH] Shared HTTP client with retries (#5097) --- .changeset/good-hairs-deliver.md | 6 ++ .changeset/selfish-shoes-pump.md | 5 ++ .changeset/tidy-worms-add.md | 5 ++ packages/libraries/apollo/src/index.ts | 71 +++++++++--------- .../libraries/apollo/tests/apollo.spec.ts | 43 ++++++----- packages/libraries/cli/package.json | 1 - packages/libraries/cli/src/base-command.ts | 14 ++-- .../cli/src/commands/artifact/fetch.ts | 10 ++- packages/libraries/core/package.json | 1 + .../libraries/core/src/client/gateways.ts | 63 ++++++++-------- .../libraries/core/src/client/http-client.ts | 72 ++++++++++++++++--- packages/libraries/core/src/index.ts | 2 +- packages/libraries/core/src/version.ts | 2 +- .../libraries/core/tests/gateways.spec.ts | 4 +- pnpm-lock.yaml | 39 +++++----- 15 files changed, 207 insertions(+), 131 deletions(-) create mode 100644 .changeset/good-hairs-deliver.md create mode 100644 .changeset/selfish-shoes-pump.md create mode 100644 .changeset/tidy-worms-add.md diff --git a/.changeset/good-hairs-deliver.md b/.changeset/good-hairs-deliver.md new file mode 100644 index 0000000000..be0b08950e --- /dev/null +++ b/.changeset/good-hairs-deliver.md @@ -0,0 +1,6 @@ +--- +'@graphql-hive/apollo': patch +'@graphql-hive/yoga': patch +--- + +Use built-in retry of http client of the core package diff --git a/.changeset/selfish-shoes-pump.md b/.changeset/selfish-shoes-pump.md new file mode 100644 index 0000000000..f79bf6a311 --- /dev/null +++ b/.changeset/selfish-shoes-pump.md @@ -0,0 +1,5 @@ +--- +'@graphql-hive/cli': patch +--- + +Retry up to 3 times a GET request in the artifact:fetch command diff --git a/.changeset/tidy-worms-add.md b/.changeset/tidy-worms-add.md new file mode 100644 index 0000000000..0e001c600d --- /dev/null +++ b/.changeset/tidy-worms-add.md @@ -0,0 +1,5 @@ +--- +'@graphql-hive/core': minor +--- + +Add retry mechanism to the http client diff --git a/packages/libraries/apollo/src/index.ts b/packages/libraries/apollo/src/index.ts index a24f875c7b..92c7fc8aec 100644 --- a/packages/libraries/apollo/src/index.ts +++ b/packages/libraries/apollo/src/index.ts @@ -29,7 +29,7 @@ export function createSupergraphSDLFetcher(options: SupergraphSDLFetcherOptions) ? options.endpoint : joinUrl(options.endpoint, 'supergraph'); - return function supergraphSDLFetcher() { + return function supergraphSDLFetcher(): Promise<{ id: string; supergraphSdl: string }> { const headers: { [key: string]: string; } = { @@ -41,49 +41,42 @@ export function createSupergraphSDLFetcher(options: SupergraphSDLFetcherOptions) headers['If-None-Match'] = cacheETag; } - let retryCount = 0; - - const retry = (status: number) => { - if (retryCount >= 10 || status < 499) { - return Promise.reject(new Error(`Failed to fetch [${status}]`)); - } - - retryCount = retryCount + 1; - - return fetchWithRetry(); - }; - - const fetchWithRetry = (): Promise<{ id: string; supergraphSdl: string }> => { - return http - .get(endpoint, { - headers, - }) - .then(async response => { - if (response.ok) { - const supergraphSdl = await response.text(); - const result = { - id: await createHash('SHA-256').update(supergraphSdl).digest('base64'), - supergraphSdl, - }; - - const etag = response.headers.get('etag'); - if (etag) { - cached = result; - cacheETag = etag; - } + return http + .get(endpoint, { + headers, + retry: { + retryWhen: response => response.status >= 500, + okWhen: response => response.status === 304, + retries: 10, + maxTimeout: 200, + minTimeout: 1, + }, + }) + .then(async response => { + if (response.ok) { + const supergraphSdl = await response.text(); + const result = { + id: await createHash('SHA-256').update(supergraphSdl).digest('base64'), + supergraphSdl, + }; - return result; + const etag = response.headers.get('etag'); + if (etag) { + cached = result; + cacheETag = etag; } - if (response.status === 304 && cached !== null) { - return cached; - } + return result; + } - return retry(response.status); - }); - }; + if (response.status === 304 && cached !== null) { + return cached; + } - return fetchWithRetry(); + throw new Error( + `Failed to GET ${endpoint}, received: ${response.status} ${response.statusText ?? 'Internal Server Error'}`, + ); + }); }; } diff --git a/packages/libraries/apollo/tests/apollo.spec.ts b/packages/libraries/apollo/tests/apollo.spec.ts index 7398873da9..18c92132de 100644 --- a/packages/libraries/apollo/tests/apollo.spec.ts +++ b/packages/libraries/apollo/tests/apollo.spec.ts @@ -11,6 +11,7 @@ import { ApolloServer } from '@apollo/server'; import { expressMiddleware } from '@apollo/server/express4'; import { ApolloServerPluginDrainHttpServer } from '@apollo/server/plugin/drainHttpServer'; import { startStandaloneServer } from '@apollo/server/standalone'; +import { http } from '@graphql-hive/core'; import { makeExecutableSchema } from '@graphql-tools/schema'; import { createHive, createSupergraphSDLFetcher, useHive } from '../src'; import { version } from '../src/version'; @@ -139,21 +140,23 @@ test('should capture client name and version headers', async () => { await startStandaloneServer(apollo); - await fetch('http://localhost:4000/graphql', { - method: 'POST', - body: JSON.stringify({ + await http.post( + 'http://localhost:4000/graphql', + JSON.stringify({ query: /* GraphQL */ ` { hello } `, }), - headers: { - 'content-type': 'application/json', - 'x-graphql-client-name': 'vitest', - 'x-graphql-client-version': '1.0.0', + { + headers: { + 'content-type': 'application/json', + 'x-graphql-client-name': 'vitest', + 'x-graphql-client-version': '1.0.0', + }, }, - }); + ); await waitFor(50); await apollo.stop(); @@ -295,7 +298,9 @@ describe('supergraph SDL fetcher', async () => { try { await fetcher(); } catch (err) { - expect(err).toMatchInlineSnapshot(`[Error: Failed to fetch [500]]`); + expect(err).toMatchInlineSnapshot( + `[Error: Failed to fetch http://localhost/supergraph, received: 500 Internal Server Error]`, + ); } }); }); @@ -667,17 +672,19 @@ describe('built-in HTTP usage reporting', async () => { }); (async () => { - const response = await fetch(url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'x-graphql-client-version': '4.2.0', - 'x-graphql-client-name': 'apollo-client', - }, - body: JSON.stringify({ + const response = await http.post( + url, + JSON.stringify({ query: '{hi}', }), - }); + { + headers: { + 'Content-Type': 'application/json', + 'x-graphql-client-version': '4.2.0', + 'x-graphql-client-name': 'apollo-client', + }, + }, + ); expect(response.status).toBe(200); expect(await response.json()).toEqual({ diff --git a/packages/libraries/cli/package.json b/packages/libraries/cli/package.json index 8a7b82b7d4..e393d3a96a 100644 --- a/packages/libraries/cli/package.json +++ b/packages/libraries/cli/package.json @@ -57,7 +57,6 @@ "@oclif/core": "^3.26.6", "@oclif/plugin-help": "6.0.22", "@oclif/plugin-update": "4.2.13", - "@whatwg-node/fetch": "0.9.18", "colors": "1.4.0", "env-ci": "7.3.0", "graphql": "^16.8.1", diff --git a/packages/libraries/cli/src/base-command.ts b/packages/libraries/cli/src/base-command.ts index 975955997c..ab5fbcb9ce 100644 --- a/packages/libraries/cli/src/base-command.ts +++ b/packages/libraries/cli/src/base-command.ts @@ -1,9 +1,9 @@ import colors from 'colors'; import { print, type GraphQLError } from 'graphql'; import type { ExecutionResult } from 'graphql'; +import { http } from '@graphql-hive/core'; import type { TypedDocumentNode } from '@graphql-typed-document-node/core'; import { Command, Errors, Config as OclifConfig } from '@oclif/core'; -import { fetch } from '@whatwg-node/fetch'; import { Config, GetConfigurationValueType, ValidConfigurationKeys } from './helpers/config'; type OmitNever = { [K in keyof T as T[K] extends never ? never : K]: T[K] }; @@ -166,14 +166,16 @@ export default abstract class extends Command { operation: TypedDocumentNode, ...[variables]: TVariables extends Record ? [] : [TVariables] ): Promise { - const response = await fetch(endpoint, { - headers: requestHeaders, - method: 'POST', - body: JSON.stringify({ + const response = await http.post( + endpoint, + JSON.stringify({ query: typeof operation === 'string' ? operation : print(operation), variables, }), - }); + { + headers: requestHeaders, + }, + ); if (!response.ok) { throw new Error(`Invalid status code for HTTP call: ${response.status}`); diff --git a/packages/libraries/cli/src/commands/artifact/fetch.ts b/packages/libraries/cli/src/commands/artifact/fetch.ts index 456bca2569..fa898445d4 100644 --- a/packages/libraries/cli/src/commands/artifact/fetch.ts +++ b/packages/libraries/cli/src/commands/artifact/fetch.ts @@ -1,5 +1,5 @@ +import { http, URL } from '@graphql-hive/core'; import { Flags } from '@oclif/core'; -import { fetch, URL } from '@whatwg-node/fetch'; import Command from '../../base-command'; export default class ArtifactsFetch extends Command { @@ -40,11 +40,17 @@ export default class ArtifactsFetch extends Command { const url = new URL(`${cdnEndpoint}/${artifactType}`); - const response = await fetch(url.toString(), { + const response = await http.get(url.toString(), { headers: { 'x-hive-cdn-key': token, 'User-Agent': `hive-cli/${this.config.version}`, }, + retry: { + retries: 3, + retryWhen(response) { + return response.status >= 500; + }, + }, }); if (response.status >= 300) { diff --git a/packages/libraries/core/package.json b/packages/libraries/core/package.json index 3811e34d34..573c6a114a 100644 --- a/packages/libraries/core/package.json +++ b/packages/libraries/core/package.json @@ -54,6 +54,7 @@ "devDependencies": { "@apollo/federation": "0.38.1", "@apollo/subgraph": "2.8.1", + "@types/async-retry": "1.4.8", "@types/lodash.sortby": "4.7.9", "graphql": "16.9.0", "nock": "14.0.0-beta.7", diff --git a/packages/libraries/core/src/client/gateways.ts b/packages/libraries/core/src/client/gateways.ts index 167e8d5fc8..763692336d 100644 --- a/packages/libraries/core/src/client/gateways.ts +++ b/packages/libraries/core/src/client/gateways.ts @@ -33,45 +33,38 @@ function createFetcher(options: SchemaFetcherOptions & ServicesFetcherOptions) { headers['If-None-Match'] = cacheETag; } - let retryCount = 0; - - const retry = (status: number) => { - if (retryCount >= 10 || status < 499) { - return Promise.reject(new Error(`Failed to fetch [${status}]`)); - } - - retryCount = retryCount + 1; - - return fetchWithRetry(); - }; - - const fetchWithRetry = (): Promise => { - return http - .get(endpoint, { - headers, - }) - .then(async response => { - if (response.ok) { - const result = await response.json(); - - const etag = response.headers.get('etag'); - if (etag) { - cached = result; - cacheETag = etag; - } - - return result; + return http + .get(endpoint, { + headers, + retry: { + retryWhen: response => response.status >= 500, + okWhen: response => response.status === 304, + retries: 10, + maxTimeout: 200, + minTimeout: 1, + }, + }) + .then(async response => { + if (response.ok) { + const result = await response.json(); + + const etag = response.headers.get('etag'); + if (etag) { + cached = result; + cacheETag = etag; } - if (response.status === 304 && cached !== null) { - return cached; - } + return result; + } - return retry(response.status); - }); - }; + if (response.status === 304 && cached !== null) { + return cached; + } - return fetchWithRetry(); + throw new Error( + `Failed to GET ${endpoint}, received: ${response.status} ${response.statusText ?? 'Internal Server Error'}`, + ); + }); }; } diff --git a/packages/libraries/core/src/client/http-client.ts b/packages/libraries/core/src/client/http-client.ts index dc0b606468..0cd72f7644 100644 --- a/packages/libraries/core/src/client/http-client.ts +++ b/packages/libraries/core/src/client/http-client.ts @@ -1,4 +1,10 @@ -import { fetch } from '@whatwg-node/fetch'; +import asyncRetry from 'async-retry'; +import { fetch, URL } from '@whatwg-node/fetch'; + +type RetryOptions = Parameters[1] & { + retryWhen(response: Response): boolean; + okWhen?(response: Response): boolean; +}; function get( endpoint: string, @@ -6,12 +12,14 @@ function get( headers: Record; timeout?: number; fetchImplementation?: typeof fetch; + retry?: RetryOptions; }, ) { return makeFetchCall(endpoint, { method: 'GET', headers: config.headers, timeout: config.timeout, + retry: config.retry, fetchImplementation: config.fetchImplementation, }); } @@ -22,6 +30,7 @@ function post( config: { headers: Record; timeout?: number; + retry?: RetryOptions; fetchImplementation?: typeof fetch; }, ) { @@ -30,6 +39,7 @@ function post( method: 'POST', headers: config.headers, timeout: config.timeout, + retry: config.retry, fetchImplementation: config.fetchImplementation, }); } @@ -46,24 +56,68 @@ async function makeFetchCall( method: 'GET' | 'POST'; headers: Record; timeout?: number; + retry?: RetryOptions; fetchImplementation?: typeof fetch; }, ) { const controller = new AbortController(); let timeoutId: ReturnType | undefined = undefined; - const responsePromise = (config.fetchImplementation ?? fetch)(endpoint, { - method: config.method, - body: config.body, - headers: config.headers, - signal: controller.signal, - }); if (config.timeout) { timeoutId = setTimeout(() => controller.abort(), config.timeout); } try { - return await responsePromise; + const retryOptions = config.retry; + if (!retryOptions) { + return await (config.fetchImplementation ?? fetch)(endpoint, { + method: config.method, + body: config.body, + headers: config.headers, + signal: controller.signal, + }); + } + + const result = await asyncRetry( + async bail => { + const res = await (config.fetchImplementation ?? fetch)(endpoint, { + method: config.method, + body: config.body, + headers: config.headers, + signal: controller.signal, + }); + + if (res.ok || retryOptions.okWhen?.(res)) { + return res; + } + + if (!retryOptions.retryWhen(res)) { + bail( + new Error( + `Failed to fetch ${endpoint}, received: ${res.status} ${res.statusText ?? 'Internal Server Error'}`, + ), + ); + return; + } + + throw new Error( + `Failed to fetch ${endpoint}, received: ${res.status} ${res.statusText ?? 'Internal Server Error'}`, + ); + }, + { + ...retryOptions, + retries: retryOptions?.retries ?? 5, + minTimeout: retryOptions?.minTimeout ?? 200, + maxTimeout: retryOptions?.maxTimeout ?? 2000, + factor: retryOptions?.factor ?? 1.2, + }, + ); + + if (result === undefined) { + throw new Error('Failed to bail out of retry.'); + } + + return result; } catch (error) { if (isAggregateError(error)) { throw new Error(error.errors.map(e => e.message).join(', '), { @@ -85,3 +139,5 @@ interface AggregateError extends Error { function isAggregateError(error: unknown): error is AggregateError { return !!error && typeof error === 'object' && 'errors' in error && Array.isArray(error.errors); } + +export { URL }; diff --git a/packages/libraries/core/src/index.ts b/packages/libraries/core/src/index.ts index ef4bfb4f37..594a6d51d1 100644 --- a/packages/libraries/core/src/index.ts +++ b/packages/libraries/core/src/index.ts @@ -4,4 +4,4 @@ export { createSchemaFetcher, createServicesFetcher } from './client/gateways.js export { createHive, autoDisposeSymbol } from './client/client.js'; export { atLeastOnceSampler } from './client/samplers.js'; export { isHiveClient, isAsyncIterable, createHash, joinUrl } from './client/utils.js'; -export { http } from './client/http-client.js'; +export { http, URL } from './client/http-client.js'; diff --git a/packages/libraries/core/src/version.ts b/packages/libraries/core/src/version.ts index c1e6b487ab..e5471b87be 100644 --- a/packages/libraries/core/src/version.ts +++ b/packages/libraries/core/src/version.ts @@ -1 +1 @@ -export const version = '0.3.0'; +export const version = '0.3.1'; diff --git a/packages/libraries/core/tests/gateways.spec.ts b/packages/libraries/core/tests/gateways.spec.ts index 20cde22036..bc61ba8316 100644 --- a/packages/libraries/core/tests/gateways.spec.ts +++ b/packages/libraries/core/tests/gateways.spec.ts @@ -289,6 +289,8 @@ test('fail in case of unexpected CDN status code (nRetryCount=11)', async () => try { await fetcher(); } catch (e) { - expect(e).toMatchInlineSnapshot(`[Error: Failed to fetch [500]]`); + expect(e).toMatchInlineSnapshot( + `[Error: Failed to fetch http://localhost/services, received: 500 Internal Server Error]`, + ); } }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 769877d2de..668c98bfde 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -412,9 +412,6 @@ importers: '@oclif/plugin-update': specifier: 4.2.13 version: 4.2.13 - '@whatwg-node/fetch': - specifier: 0.9.18 - version: 0.9.18 colors: specifier: 1.4.0 version: 1.4.0 @@ -477,6 +474,9 @@ importers: '@apollo/subgraph': specifier: 2.8.1 version: 2.8.1(graphql@16.9.0) + '@types/async-retry': + specifier: 1.4.8 + version: 1.4.8 '@types/lodash.sortby': specifier: 4.7.9 version: 4.7.9 @@ -4148,6 +4148,7 @@ packages: '@fastify/vite@6.0.6': resolution: {integrity: sha512-FsWJC92murm5tjeTezTTvMLyZido/ZWy0wYWpVkh/bDe1gAUAabYLB7Vp8hokXGsRE/mOpqYVsRDAKENY2qPUQ==} + bundledDependencies: [] '@floating-ui/core@1.2.6': resolution: {integrity: sha512-EvYTiXet5XqweYGClEmpu3BoxmsQ4hkj3QaYA6qEnigCWffTP3vNRwBReTdrwDwo7OoJ3wM8Uoe9Uk4n+d4hfg==} @@ -17507,10 +17508,10 @@ snapshots: '@aws-crypto/sha1-browser': 5.2.0 '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.600.0(@aws-sdk/client-sts@3.600.0) - '@aws-sdk/client-sts': 3.600.0 + '@aws-sdk/client-sso-oidc': 3.600.0 + '@aws-sdk/client-sts': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0) '@aws-sdk/core': 3.598.0 - '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0) + '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0(@aws-sdk/client-sso-oidc@3.600.0)) '@aws-sdk/middleware-bucket-endpoint': 3.598.0 '@aws-sdk/middleware-expect-continue': 3.598.0 '@aws-sdk/middleware-flexible-checksums': 3.598.0 @@ -17610,13 +17611,13 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sso-oidc@3.600.0(@aws-sdk/client-sts@3.600.0)': + '@aws-sdk/client-sso-oidc@3.600.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sts': 3.600.0 + '@aws-sdk/client-sts': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0) '@aws-sdk/core': 3.598.0 - '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0) + '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0(@aws-sdk/client-sso-oidc@3.600.0)) '@aws-sdk/middleware-host-header': 3.598.0 '@aws-sdk/middleware-logger': 3.598.0 '@aws-sdk/middleware-recursion-detection': 3.598.0 @@ -17653,7 +17654,6 @@ snapshots: '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 transitivePeerDependencies: - - '@aws-sdk/client-sts' - aws-crt '@aws-sdk/client-sso@3.592.0': @@ -17788,13 +17788,13 @@ snapshots: - '@aws-sdk/client-sso-oidc' - aws-crt - '@aws-sdk/client-sts@3.600.0': + '@aws-sdk/client-sts@3.600.0(@aws-sdk/client-sso-oidc@3.600.0)': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.600.0(@aws-sdk/client-sts@3.600.0) + '@aws-sdk/client-sso-oidc': 3.600.0 '@aws-sdk/core': 3.598.0 - '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0) + '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0(@aws-sdk/client-sso-oidc@3.600.0)) '@aws-sdk/middleware-host-header': 3.598.0 '@aws-sdk/middleware-logger': 3.598.0 '@aws-sdk/middleware-recursion-detection': 3.598.0 @@ -17831,6 +17831,7 @@ snapshots: '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 transitivePeerDependencies: + - '@aws-sdk/client-sso-oidc' - aws-crt '@aws-sdk/core@3.592.0': @@ -17909,9 +17910,9 @@ snapshots: - '@aws-sdk/client-sso-oidc' - aws-crt - '@aws-sdk/credential-provider-ini@3.598.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0)': + '@aws-sdk/credential-provider-ini@3.598.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0(@aws-sdk/client-sso-oidc@3.600.0))': dependencies: - '@aws-sdk/client-sts': 3.600.0 + '@aws-sdk/client-sts': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0) '@aws-sdk/credential-provider-env': 3.598.0 '@aws-sdk/credential-provider-http': 3.598.0 '@aws-sdk/credential-provider-process': 3.598.0 @@ -17946,11 +17947,11 @@ snapshots: - '@aws-sdk/client-sts' - aws-crt - '@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0)': + '@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0(@aws-sdk/client-sso-oidc@3.600.0))': dependencies: '@aws-sdk/credential-provider-env': 3.598.0 '@aws-sdk/credential-provider-http': 3.598.0 - '@aws-sdk/credential-provider-ini': 3.598.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0) + '@aws-sdk/credential-provider-ini': 3.598.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0(@aws-sdk/client-sso-oidc@3.600.0)) '@aws-sdk/credential-provider-process': 3.598.0 '@aws-sdk/credential-provider-sso': 3.598.0(@aws-sdk/client-sso-oidc@3.600.0) '@aws-sdk/credential-provider-web-identity': 3.598.0(@aws-sdk/client-sts@3.600.0) @@ -18017,7 +18018,7 @@ snapshots: '@aws-sdk/credential-provider-web-identity@3.598.0(@aws-sdk/client-sts@3.600.0)': dependencies: - '@aws-sdk/client-sts': 3.600.0 + '@aws-sdk/client-sts': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0) '@aws-sdk/types': 3.598.0 '@smithy/property-provider': 3.1.2 '@smithy/types': 3.2.0 @@ -18190,7 +18191,7 @@ snapshots: '@aws-sdk/token-providers@3.598.0(@aws-sdk/client-sso-oidc@3.600.0)': dependencies: - '@aws-sdk/client-sso-oidc': 3.600.0(@aws-sdk/client-sts@3.600.0) + '@aws-sdk/client-sso-oidc': 3.600.0 '@aws-sdk/types': 3.598.0 '@smithy/property-provider': 3.1.2 '@smithy/shared-ini-file-loader': 3.1.2