@ai-sdk/provider-utils
Version:
74 lines (68 loc) • 2.54 kB
text/typescript
/**
* Checks if the given URL is supported natively by the model.
*
* @param mediaType - The media type of the URL. Case-insensitive. May be a full
* `type/subtype`, a wildcard `type/*`, or just the
* top-level segment (e.g. `image`).
* @param url - The URL to check.
* @param supportedUrls - A record where keys are case-insensitive media types (or '*')
* and values are arrays of RegExp patterns for URLs.
*
* @returns `true` if the URL matches a pattern under the specific media type
* or the wildcard '*', `false` otherwise.
*/
export function isUrlSupported({
mediaType,
url,
supportedUrls,
}: {
mediaType: string;
url: string;
supportedUrls: Record<string, RegExp[]>;
}): boolean {
// standardize media type to lower case
mediaType = mediaType.toLowerCase();
const isTopLevelOnly = !mediaType.includes('/');
return (
Object.entries(supportedUrls)
// standardize supported url map into lowercase prefixes:
.map(([key, value]) => {
const mediaType = key.toLowerCase();
return mediaType === '*' || mediaType === '*/*'
? { mediaTypePrefix: '', regexes: value }
: { mediaTypePrefix: mediaType.replace(/\*/, ''), regexes: value };
})
// gather all regexp pattern from matched media type prefixes:
.filter(({ mediaTypePrefix }) => {
if (mediaTypePrefix === '') {
return true;
}
// For a top-level-only media type (e.g. `image`), we cannot determine
// whether a specific subtype (e.g. `image/png`) would apply, so we
// only match the corresponding `type/*` prefix exactly.
if (isTopLevelOnly) {
return `${mediaType}/` === mediaTypePrefix;
}
return mediaTypePrefix.endsWith('/')
? mediaType.startsWith(mediaTypePrefix)
: mediaType === mediaTypePrefix;
})
.flatMap(({ regexes }) => regexes)
// check if any pattern matches the url:
.some(pattern => testRegExpFromStart(pattern, url))
);
}
function testRegExpFromStart(pattern: RegExp, value: string): boolean {
if (!pattern.global && !pattern.sticky) {
return pattern.test(value);
}
// Global and sticky regexes retain match state in lastIndex.
// Evaluate from the start without changing caller-owned state.
const lastIndex = pattern.lastIndex;
pattern.lastIndex = 0;
try {
return pattern.test(value);
} finally {
pattern.lastIndex = lastIndex;
}
}