fix: finish review-driven single-source cleanup
This commit is contained in:
@@ -17,6 +17,12 @@ function normalizeValue(value) {
|
||||
return (value || '').trim().toLowerCase();
|
||||
}
|
||||
|
||||
function normalizeMimeType(value) {
|
||||
const normalized = normalizeValue(value);
|
||||
const [essence] = normalized.split(';');
|
||||
return (essence || '').trim();
|
||||
}
|
||||
|
||||
export function inferConversationFileMimeType(filename) {
|
||||
const normalized = normalizeValue(filename);
|
||||
if (normalized.endsWith('.pdf')) return 'application/pdf';
|
||||
@@ -41,7 +47,7 @@ export function inferConversationFileMimeType(filename) {
|
||||
}
|
||||
|
||||
export function resolveConversationFileMimeType(mimeType, filename) {
|
||||
const normalizedMimeType = normalizeValue(mimeType);
|
||||
const normalizedMimeType = normalizeMimeType(mimeType);
|
||||
if (normalizedMimeType && !GENERIC_MIME_TYPES.has(normalizedMimeType)) {
|
||||
return normalizedMimeType;
|
||||
}
|
||||
@@ -49,14 +55,14 @@ export function resolveConversationFileMimeType(mimeType, filename) {
|
||||
}
|
||||
|
||||
export function classifyConversationFileMimeType(mimeType) {
|
||||
const normalized = normalizeValue(mimeType);
|
||||
const normalized = normalizeMimeType(mimeType);
|
||||
if (normalized.startsWith('image/')) return 'image';
|
||||
if (normalized.startsWith('audio/')) return 'audio';
|
||||
return 'document';
|
||||
}
|
||||
|
||||
export function detectConversationFileKind(file) {
|
||||
const mimeType = normalizeValue(file?.mimeType);
|
||||
const mimeType = normalizeMimeType(file?.mimeType);
|
||||
if (mimeType) {
|
||||
if (mimeType.startsWith('image/')) return 'image';
|
||||
if (mimeType.startsWith('audio/')) return 'audio';
|
||||
@@ -73,7 +79,7 @@ export function detectConversationFileKind(file) {
|
||||
}
|
||||
|
||||
export function isSupportedConversationFileMimeType(mimeType) {
|
||||
const normalized = normalizeValue(mimeType);
|
||||
const normalized = normalizeMimeType(mimeType);
|
||||
return DOCUMENT_MIME_TYPES.has(normalized)
|
||||
|| normalized.startsWith('image/')
|
||||
|| normalized.startsWith('audio/');
|
||||
|
||||
@@ -42,4 +42,14 @@ describe('conversationFileTypes', () => {
|
||||
mimeType: 'application/octet-stream',
|
||||
})).toBe('document');
|
||||
});
|
||||
|
||||
it('strips MIME parameters before matching support and kind rules', () => {
|
||||
expect(resolveConversationFileMimeType('text/plain; charset=utf-8', 'notes.txt')).toBe('text/plain');
|
||||
expect(classifyConversationFileMimeType('text/plain; charset=utf-8')).toBe('document');
|
||||
expect(detectConversationFileKind({
|
||||
filename: 'notes.txt',
|
||||
mimeType: 'text/plain; charset=utf-8',
|
||||
})).toBe('document');
|
||||
expect(isSupportedConversationFileMimeType('text/plain; charset=utf-8')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -86,6 +86,11 @@ function isSafeRegexPatternBody(body) {
|
||||
continue;
|
||||
}
|
||||
if (ch === '\\') {
|
||||
const next = body[index + 1];
|
||||
if (!next) return false;
|
||||
if (/[a-z]/i.test(next) && next !== 'd') {
|
||||
return false;
|
||||
}
|
||||
escaped = true;
|
||||
continue;
|
||||
}
|
||||
@@ -99,10 +104,7 @@ function isSafeRegexPatternBody(body) {
|
||||
}
|
||||
if (ch === '(') {
|
||||
if (body[index + 1] === '?') {
|
||||
if (body[index + 2] !== ':') return false;
|
||||
groupStack.push({ hasInnerQuantifier: false, hasAlternation: false });
|
||||
index += 2;
|
||||
continue;
|
||||
return false;
|
||||
}
|
||||
groupStack.push({ hasInnerQuantifier: false, hasAlternation: false });
|
||||
continue;
|
||||
|
||||
@@ -21,6 +21,18 @@ describe('token route pattern helpers', () => {
|
||||
expect(matchesTokenRouteModelPattern('claude-sonnet-4-6', 're:(?=claude)')).toBe(false);
|
||||
});
|
||||
|
||||
it('rejects regex syntax that the lightweight parser does not implement', async () => {
|
||||
const {
|
||||
matchesTokenRouteModelPattern,
|
||||
parseTokenRouteRegexPattern,
|
||||
} = await import('./tokenRoutePatterns.js');
|
||||
|
||||
expect(parseTokenRouteRegexPattern('re:^(?:gpt|claude)-5$').regex).toBeNull();
|
||||
expect(matchesTokenRouteModelPattern('gpt-5', 're:^(?:gpt|claude)-5$')).toBe(false);
|
||||
expect(parseTokenRouteRegexPattern('re:^gpt-\\s+$').regex).toBeNull();
|
||||
expect(matchesTokenRouteModelPattern('gpt- ', 're:^gpt-\\s+$')).toBe(false);
|
||||
});
|
||||
|
||||
it('supports exact, glob, and safe regex route matches', async () => {
|
||||
const { matchesTokenRouteModelPattern } = await import('./tokenRoutePatterns.js');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user