mirror of
https://github.com/nuxt/ui.git
synced 2026-09-14 19:51:10 +08:00
5fd94e1365
Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Benjamin Canac <canacb1@gmail.com> Co-authored-by: Cursor <cursoragent@cursor.com>
33 lines
1.4 KiB
TypeScript
33 lines
1.4 KiB
TypeScript
type GoogleFont = {
|
|
name: string
|
|
category: string
|
|
}
|
|
|
|
/**
|
|
* The Google Fonts catalog, slimmed to what the theme studio's font picker
|
|
* needs (family name + category, popularity-ordered). Proxied because the
|
|
* metadata endpoint sends no CORS headers, and cached, the catalog moves
|
|
* a few families a month at most.
|
|
*/
|
|
export default defineCachedEventHandler(async (): Promise<GoogleFont[]> => {
|
|
const text = await $fetch<string>('https://fonts.google.com/metadata/fonts', { responseType: 'text' })
|
|
// Google JSON endpoints traditionally lead with an anti-hijacking prefix
|
|
let data: any
|
|
try {
|
|
data = JSON.parse(text.replace(/^\)\]\}'/, ''))
|
|
} catch {
|
|
// an HTML error page, handled by the shape check below
|
|
}
|
|
|
|
// an HTML error page or a renamed field must not become a cached crash
|
|
if (!Array.isArray(data?.familyMetadataList)) {
|
|
throw createError({ statusCode: 502, statusMessage: 'Unexpected fonts metadata shape' })
|
|
}
|
|
|
|
return (data.familyMetadataList as Array<{ family: string, category: string, popularity?: number }>)
|
|
.sort((a, b) => (a.popularity ?? Number.MAX_SAFE_INTEGER) - (b.popularity ?? Number.MAX_SAFE_INTEGER))
|
|
.map(font => ({ name: font.family, category: font.category }))
|
|
// one cache entry no matter the query string: the default key hashes the
|
|
// full URL, letting `?anything` bypass the cache straight to Google
|
|
}, { name: 'google-fonts', getKey: () => 'catalog', maxAge: 60 * 60 * 24, swr: true })
|