import { notFound, redirect } from 'next/navigation' /** * Minimal shape of a blog client's slug lookup. Matches both the shared * `@startsimpli/api/blog` client (`createBlogClient().fetchPostBySlug`) and the * legacy per-app `blog-api-server` modules, so callers can pass whichever they * already have without migrating their data layer first. */ export type ArticleSlugFetcher = (slug: string) => Promise export interface ResolveArticleOptions { /** * List/index route a missing article falls back to, e.g. `/resources` or * `/articles`. Dead, stale, or mistyped article links land here. */ indexPath: string /** * Behaviour when no post matches the slug. * - `'redirect'` (default): 307 to `indexPath` — the smart landing that keeps * visitors (and crawlers following old links) inside the content instead of * on a bare 404. * - `'notFound'`: trigger the nearest `not-found.tsx` (404 status). Use for * routes where preserving the 404 signal matters more than the soft landing. */ onMissing?: 'redirect' | 'notFound' } /** * Resolve a blog post by slug, applying the shared missing-article default. * * Every marketing-site article route (`/resources/[slug]`, `/articles/[slug]`, * `/guides/[guide]`, …) should call this instead of hand-rolling * `if (!post) notFound()` — so a non-existent or removed article URL * smart-redirects to its list page by default, consistently across every app * that shares the blog layer. * * Never returns a nullish value: it either returns the resolved post or throws * the redirect / not-found control-flow signal that Next.js expects. Call it at * the top level of a Server Component (do not wrap in try/catch, or the signal * will be swallowed). */ export async function resolveArticleOrFallback( fetchPostBySlug: ArticleSlugFetcher, slug: string, { indexPath, onMissing = 'redirect' }: ResolveArticleOptions, ): Promise { const post = await fetchPostBySlug(slug) if (post) return post if (onMissing === 'notFound') notFound() redirect(indexPath) }