import 'server-only'; import { api, safe } from '@/lib/api'; import { routes, SITE_URL, TYPE_PATH } from '@/lib/site'; export const SITEMAP_HEADERS = { 'content-type': 'application/xml; charset=utf-8', 'cache-control': 'public, s-maxage=3600, stale-while-revalidate=86400' }; export const SHARD = 5000; const STATIC = ['/', '/models', '/companies', '/papers', '/providers', '/benchmarks', '/hardware', '/frameworks', '/datasets', '/tools', '/changes', '/timeline', '/compare', '/explore', '/methodology', '/sources', '/about', '/developers', '/bot']; /** Types with their own sitemap series (in this order); everything else goes into the "other" series. */ const SERIES = ['model', 'company', 'paper', 'provider', 'benchmark', 'hardware', 'framework', 'dataset', 'tool', 'repository'] as const; function esc(s: string): string { return s.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); } export function toIndex(locs: string[]): string { return `\n\n${locs.map((l) => ` ${esc(SITE_URL + l)}`).join('\n')}\n\n`; } export function toUrlset(entries: { loc: string; lastmod?: string }[]): string { return `\n\n${entries.map((e) => ` ${esc(SITE_URL + e.loc)}${e.lastmod ? `${e.lastmod.slice(0, 10)}` : ''}`).join('\n')}\n\n`; } /** Shard ids: `static`, then `-` per type with entities. */ export async function shardIds(): Promise { const ids = ['static']; const stats = await safe(api.stats()); for (const t of SERIES) { const n = Number(stats?.entities?.[t] ?? 0); const shards = n > 0 ? Math.ceil(n / SHARD) : 0; for (let i = 0; i < shards; i++) ids.push(`${t}-${i}`); } return ids; } export async function shardEntries(id: string): Promise<{ loc: string; lastmod?: string }[] | null> { if (id === 'static') return STATIC.map((loc) => ({ loc })); const m = /^([a-z_]+)-(\d+)$/.exec(id); if (!m) return null; const type = m[1] as string; const n = Number(m[2]); if (!(SERIES as readonly string[]).includes(type) || !TYPE_PATH[type]) return null; const res = await safe(api.sitemap(type, SHARD, n * SHARD)); if (!res) return []; return res.items.map((it) => ({ loc: routes.entity({ entity_type: it.entity_type, slug: it.slug }), lastmod: it.updated_at })); }