import { SITE_URL, absoluteUrl } from '@/lib/site'; /** * The site's author entity. * * First name only, by choice — see /about. That makes this a weaker search * signal than a fully identified author would be, which is why the About page * carries a substantial methodology section: the credibility has to come from * stated provenance rather than from a corroborable identity. * * Everything that needs an author — the About page, every post byline — * references this one shape, so search engines resolve them all to one entity. */ export function personJsonLd() { return { '@type': 'Person', '@id': `${SITE_URL}/about#tudor`, name: 'Tudor', url: absoluteUrl('/about'), image: absoluteUrl('/brand/tudor.jpg'), description: 'Parent in south-west London who built schoolcompare while looking for a primary school.', } as const; } export function organizationJsonLd() { return { '@type': 'Organization', '@id': `${SITE_URL}#organization`, name: 'schoolcompare', url: SITE_URL, logo: absoluteUrl('/icon-512.png'), } as const; } interface PostSummary { title: string; slug: string; excerpt: string; publishedAt: string; } /** * References the Person and Organization by @id rather than repeating them, so * search engines resolve every post and the About page to the one author * entity. Repeating the shape would declare several people with one name. * * `namedAuthor` is the about_page flag. The Person entity is anchored at * /about#tudor, and that URL 404s while the flag is dark, so a post published * in that state must not claim it: an author @id resolving to nothing is a * worse signal than no named author. It falls back to the publisher, which is * always live. The parameter is required rather than defaulted because every * call site has the flag to hand and the wrong default is silent. */ export function blogPostingJsonLd( post: PostSummary, { namedAuthor }: { namedAuthor: boolean }, ) { return { '@type': 'BlogPosting', headline: post.title, description: post.excerpt, url: absoluteUrl(`/blog/${post.slug}`), datePublished: post.publishedAt, author: { '@id': namedAuthor ? `${SITE_URL}/about#tudor` : `${SITE_URL}#organization`, }, publisher: { '@id': `${SITE_URL}#organization` }, } as const; } export function breadcrumbJsonLd(post: PostSummary) { return { '@type': 'BreadcrumbList', itemListElement: [ { '@type': 'ListItem', position: 1, name: 'Blog', item: absoluteUrl('/blog'), }, { '@type': 'ListItem', position: 2, name: post.title, item: absoluteUrl(`/blog/${post.slug}`), }, ], } as const; }