SPB Git forge

spb/ai-atlas

Public
41commits 1branches 0releases
4.6 MBsize
maindefault branch
12 days agolast push
HTML 77.2% TypeScript 10.5% Python 9.6% JavaScript 2.5%
2.4 KB · 64 lines typescript
Raw Blame History
1import 'server-only';2import type { Metadata } from 'next';3import { notFound } from 'next/navigation';4import { api, ApiError, safe } from '@/lib/api';5import { PATH_TYPES, routes, SITE_NAME, SITE_URL, TYPE_PATH, typeLabel } from '@/lib/site';6import type { EntityDetail } from '@/lib/types';7import { describeEntity } from './entity-page';89/**10 * Load an entity for a typed path (`models`, `companies`, `providers`…). 404 when the API says so or when the entity's11 * type is not accepted under this path (so /tools/<framework-slug> is a 404, not a duplicate canonical URL).12 */13export async function loadEntity(typePath: string, slug: string): Promise<EntityDetail> {14  const def = PATH_TYPES[typePath];15  if (!def) notFound();16  try {17    const d = def.api === 'entities' ? await api.entity(slug) : await api.entityOfType(def.api, slug);18    if (!def.types.includes(d.entity_type)) notFound();19    return d;20  } catch (e) {21    if (e instanceof ApiError && e.notFound) notFound();22    throw e;23  }24}2526/** Load for the /explore/<type>/<slug> fallback: any type that has no dedicated path. */27export async function loadAnyEntity(type: string, slug: string): Promise<EntityDetail> {28  try {29    const d = await api.entity(slug);30    if (d.entity_type !== type) notFound();31    return d;32  } catch (e) {33    if (e instanceof ApiError && e.notFound) notFound();34    throw e;35  }36}3738export async function entityMetadata(typePath: string, slug: string): Promise<Metadata> {39  const def = PATH_TYPES[typePath];40  const d = def ? await safe(def.api === 'entities' ? api.entity(slug) : api.entityOfType(def.api, slug)) : null;41  if (!d || (def && !def.types.includes(d.entity_type))) return { title: def?.singular ?? 'Entity', robots: { index: false } };42  return buildMetadata(d);43}4445export function buildMetadata(d: EntityDetail): Metadata {46  const canonical = routes.entity(d);47  const label = typeLabel(d.entity_type);48  const title = d.organization && d.entity_type === 'model' ? `${d.name} — ${d.organization.name} ${label.toLowerCase()}` : `${d.name} — ${label}`;49  const description = describeEntity(d);50  return {51    title,52    description,53    alternates: { canonical },54    openGraph: { title: `${title} | ${SITE_NAME}`, description, url: `${SITE_URL}${canonical}`, type: 'article', siteName: SITE_NAME },55    twitter: { card: 'summary_large_image', title, description },56  };57}5859export function canonicalFor(d: EntityDetail): string {60  return routes.entity(d);61}6263export { TYPE_PATH };64