SPB Git

spb/ai-atlas

Public
2.5 KB · 45 lines typescript
1import 'server-only';2import { api, safe } from '@/lib/api';3import { routes, SITE_URL, TYPE_PATH } from '@/lib/site';45export const SITEMAP_HEADERS = { 'content-type': 'application/xml; charset=utf-8', 'cache-control': 'public, s-maxage=3600, stale-while-revalidate=86400' };6export const SHARD = 5000;7const STATIC = ['/', '/models', '/companies', '/papers', '/providers', '/benchmarks', '/hardware', '/frameworks', '/datasets', '/tools', '/changes', '/timeline', '/compare', '/explore', '/methodology', '/sources', '/about', '/developers', '/bot'];8/** Types with their own sitemap series (in this order); everything else goes into the "other" series. */9const SERIES = ['model', 'company', 'paper', 'provider', 'benchmark', 'hardware', 'framework', 'dataset', 'tool', 'repository'] as const;1011function esc(s: string): string {12  return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');13}1415export function toIndex(locs: string[]): string {16  return `<?xml version="1.0" encoding="UTF-8"?>\n<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n${locs.map((l) => `  <sitemap><loc>${esc(SITE_URL + l)}</loc></sitemap>`).join('\n')}\n</sitemapindex>\n`;17}18export function toUrlset(entries: { loc: string; lastmod?: string }[]): string {19  return `<?xml version="1.0" encoding="UTF-8"?>\n<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n${entries.map((e) => `  <url><loc>${esc(SITE_URL + e.loc)}</loc>${e.lastmod ? `<lastmod>${e.lastmod.slice(0, 10)}</lastmod>` : ''}</url>`).join('\n')}\n</urlset>\n`;20}2122/** Shard ids: `static`, then `<type>-<n>` per type with entities. */23export async function shardIds(): Promise<string[]> {24  const ids = ['static'];25  const stats = await safe(api.stats());26  for (const t of SERIES) {27    const n = Number(stats?.entities?.[t] ?? 0);28    const shards = n > 0 ? Math.ceil(n / SHARD) : 0;29    for (let i = 0; i < shards; i++) ids.push(`${t}-${i}`);30  }31  return ids;32}3334export async function shardEntries(id: string): Promise<{ loc: string; lastmod?: string }[] | null> {35  if (id === 'static') return STATIC.map((loc) => ({ loc }));36  const m = /^([a-z_]+)-(\d+)$/.exec(id);37  if (!m) return null;38  const type = m[1] as string;39  const n = Number(m[2]);40  if (!(SERIES as readonly string[]).includes(type) || !TYPE_PATH[type]) return null;41  const res = await safe(api.sitemap(type, SHARD, n * SHARD));42  if (!res) return [];43  return res.items.map((it) => ({ loc: routes.entity({ entity_type: it.entity_type, slug: it.slug }), lastmod: it.updated_at }));44}45
Files · 9f1db171c786cf4a3b99436329f29b145c02aeec
Menu

Theme