fix: address review feedback and code scanning alerts

This commit is contained in:
Cita
2026-03-22 23:41:45 +08:00
parent 32d7037629
commit 5d78658af9
38 changed files with 929 additions and 344 deletions
@@ -27,6 +27,24 @@ describe('geminiGenerateContentTransformer.inbound', () => {
expect(geminiGenerateContentOutbound.resolveActionUrl).toBe(resolveGeminiGenerateContentUrl);
});
it('preserves base-url query params when resolving Gemini endpoints', () => {
expect(
resolveGeminiNativeBaseUrl('https://example.com/native?alt=sse', 'v1beta'),
).toBe('https://example.com/native/v1beta?alt=sse');
expect(
resolveGeminiModelsUrl('https://example.com/native?alt=sse', 'v1beta', 'api-key'),
).toBe('https://example.com/native/v1beta/models?alt=sse&key=api-key');
expect(
resolveGeminiGenerateContentUrl(
'https://example.com/native?alt=sse',
'v1beta',
'/models/gemini-2.5-pro:generateContent',
'api-key',
'?trace=1',
),
).toBe('https://example.com/native/v1beta/models/gemini-2.5-pro:generateContent?alt=sse&trace=1&key=api-key');
});
it('parses native Gemini requests into canonical envelopes', () => {
const result = geminiGenerateContentTransformer.parseRequest({
model: 'gemini-2.5-pro',
@@ -1,15 +1,33 @@
function normalizeBaseUrl(baseUrl: string): string {
return (baseUrl || '').replace(/\/+$/, '');
function normalizePathname(pathname: string): string {
let normalized = pathname || '';
while (normalized.endsWith('/')) {
normalized = normalized.slice(0, -1);
}
return normalized;
}
function baseIncludesVersion(baseUrl: string): boolean {
return /\/v\d+(?:beta)?(?:\/|$)/i.test(baseUrl);
function splitBaseUrl(baseUrl: string): { path: string; query: string } {
const raw = baseUrl || '';
const queryIndex = raw.indexOf('?');
if (queryIndex < 0) {
return { path: normalizePathname(raw), query: '' };
}
return {
path: normalizePathname(raw.slice(0, queryIndex)),
query: raw.slice(queryIndex + 1),
};
}
function baseIncludesVersion(path: string): boolean {
return /\/v\d+(?:beta)?(?:\/|$)/i.test(path);
}
export function resolveGeminiNativeBaseUrl(baseUrl: string, apiVersion: string): string {
const normalized = normalizeBaseUrl(baseUrl);
if (baseIncludesVersion(normalized)) return normalized;
return `${normalized}/${apiVersion}`;
const { path, query } = splitBaseUrl(baseUrl);
const normalizedPath = baseIncludesVersion(path)
? path
: `${path}/${apiVersion.replace(/^\/+/, '')}`;
return `${normalizedPath}${query ? `?${query}` : ''}`;
}
export function resolveGeminiModelsUrl(
@@ -18,8 +36,10 @@ export function resolveGeminiModelsUrl(
apiKey: string,
): string {
const base = resolveGeminiNativeBaseUrl(baseUrl, apiVersion);
const separator = base.includes('?') ? '&' : '?';
return `${base}/models${separator}key=${encodeURIComponent(apiKey)}`;
const [path, query = ''] = base.split('?', 2);
const params = new URLSearchParams(query);
params.set('key', apiKey);
return `${path}/models?${params.toString()}`;
}
export function resolveGeminiGenerateContentUrl(
@@ -31,8 +51,13 @@ export function resolveGeminiGenerateContentUrl(
): string {
const base = resolveGeminiNativeBaseUrl(baseUrl, apiVersion);
const normalizedAction = modelActionPath.replace(/^\/+/, '');
const params = new URLSearchParams(search.startsWith('?') ? search.slice(1) : search);
const [path, baseQuery = ''] = base.split('?', 2);
const params = new URLSearchParams(baseQuery);
const extraParams = new URLSearchParams(search.startsWith('?') ? search.slice(1) : search);
for (const [key, value] of extraParams) {
params.set(key, value);
}
params.set('key', apiKey);
const query = params.toString();
return `${base}/${normalizedAction}${query ? `?${query}` : ''}`;
return `${path}/${normalizedAction}${query ? `?${query}` : ''}`;
}