From 7d6a64b0544edf669c7a63d31895e4c22d466faf Mon Sep 17 00:00:00 2001 From: Diego Rodrigues de Sa e Souza Date: Tue, 4 Aug 2026 21:36:47 -0300 Subject: [PATCH] fix(mcp): break circular import between googApiKeyAuth.ts and auth.ts (#9297) --- changelog.d/fixes/9297-fix.plan.md | 1 + src/sse/services/auth.ts | 33 +++--------------------- src/sse/services/googApiKeyAuth.ts | 4 +-- src/sse/services/headerReader.ts | 40 ++++++++++++++++++++++++++++++ 4 files changed, 46 insertions(+), 32 deletions(-) create mode 100644 changelog.d/fixes/9297-fix.plan.md create mode 100644 src/sse/services/headerReader.ts diff --git a/changelog.d/fixes/9297-fix.plan.md b/changelog.d/fixes/9297-fix.plan.md new file mode 100644 index 0000000000..67679de6ee --- /dev/null +++ b/changelog.d/fixes/9297-fix.plan.md @@ -0,0 +1 @@ +- fix(mcp): break circular import between googApiKeyAuth.ts and auth.ts to fix esbuild SyntaxError in MCP server bundle (#9297) diff --git a/src/sse/services/auth.ts b/src/sse/services/auth.ts index e0c03fdbea..65d0578c5d 100644 --- a/src/sse/services/auth.ts +++ b/src/sse/services/auth.ts @@ -79,6 +79,7 @@ import { getNoAuthHydrationProviderIds } from "./noAuthProviderSiblings"; import { getResource404Bypass } from "./requestResourceHealth"; import * as log from "../utils/logger"; import { fisherYatesShuffle, getNextFromDeckSync } from "@/shared/utils/shuffleDeck"; +import { readHeaderValue, type AuthRequestHeaders } from "./headerReader.ts"; type JsonRecord = Record; interface RecoverableConnectionState { @@ -143,33 +144,6 @@ function toBooleanOrDefault(value: unknown, fallback: boolean): boolean { return typeof value === "boolean" ? value : fallback; } -export function readHeaderValue( - headers: - | Headers - | { get?: (name: string) => string | null } - | Record - | null - | undefined, - name: string -): string | null { - if (!headers) return null; - - if (typeof (headers as Headers).get === "function") { - const value = (headers as Headers).get(name) || (headers as Headers).get(name.toLowerCase()); - return typeof value === "string" && value.trim().length > 0 ? value.trim() : null; - } - - const recordHeaders = headers as Record; - const value = - recordHeaders[name] || recordHeaders[name.toLowerCase()] || recordHeaders[name.toUpperCase()]; - - if (Array.isArray(value)) { - return typeof value[0] === "string" && value[0].trim().length > 0 ? value[0].trim() : null; - } - - return typeof value === "string" && value.trim().length > 0 ? value.trim() : null; -} - function normalizeSessionKey(value: unknown, prefix: string): string | null { if (typeof value !== "string" || value.trim().length === 0) return null; const trimmed = value.trim(); @@ -946,6 +920,9 @@ const markMutexes = new Map>(); // auth.ts uses getNextFromDeckSync inside the provider-scoped selection mutex. // Re-export for backwards compat with existing test imports. export { fisherYatesShuffle, getNextFromDeckSync as getNextFromDeck }; +// Re-export readHeaderValue and AuthRequestHeaders from headerReader.ts for +// backwards compat with existing imports (e.g. googApiKeyAuth.ts). +export { readHeaderValue, type AuthRequestHeaders } from "./headerReader.ts"; const PROVIDER_SEARCH_PAIRS: string[][] = [ ["nvidia", "nvidia_nim"], @@ -2380,8 +2357,6 @@ export async function clearRecoveredProviderState( return { applied: true }; } -type AuthRequestHeaders = Headers | Record; - type AuthRequestLike = { headers?: AuthRequestHeaders | null; url?: string | null; diff --git a/src/sse/services/googApiKeyAuth.ts b/src/sse/services/googApiKeyAuth.ts index aa5244953b..f92e6442ea 100644 --- a/src/sse/services/googApiKeyAuth.ts +++ b/src/sse/services/googApiKeyAuth.ts @@ -1,6 +1,4 @@ -import { readHeaderValue } from "./auth.ts"; - -type AuthRequestHeaders = Headers | Record; +import { readHeaderValue, type AuthRequestHeaders } from "./headerReader.ts"; /** * Issue #7034: `gemini-cli` (and any `@google/genai`-based client) sends its diff --git a/src/sse/services/headerReader.ts b/src/sse/services/headerReader.ts new file mode 100644 index 0000000000..6daf84f7ba --- /dev/null +++ b/src/sse/services/headerReader.ts @@ -0,0 +1,40 @@ +export type AuthRequestHeaders = Headers | Record; + +/** + * Safely read a header value from various request-like objects. + * + * Accepts: + * - `Headers` (Web API / Fetch API) + * - Objects with a `.get()` method (e.g. `IncomingMessage.headers`) + * - Plain `Record` objects + * + * Extracted to its own module to break the circular import between + * `./auth.ts` and `./googApiKeyAuth.ts` — both import this function + * without creating a cycle. + */ +export function readHeaderValue( + headers: + | Headers + | { get?: (name: string) => string | null } + | Record + | null + | undefined, + name: string +): string | null { + if (!headers) return null; + + if (typeof (headers as Headers).get === "function") { + const value = (headers as Headers).get(name) || (headers as Headers).get(name.toLowerCase()); + return typeof value === "string" && value.trim().length > 0 ? value.trim() : null; + } + + const recordHeaders = headers as Record; + const value = + recordHeaders[name] || recordHeaders[name.toLowerCase()] || recordHeaders[name.toUpperCase()]; + + if (Array.isArray(value)) { + return typeof value[0] === "string" && value[0].trim().length > 0 ? value[0].trim() : null; + } + + return typeof value === "string" && value.trim().length > 0 ? value.trim() : null; +} \ No newline at end of file