`lib/blog.ts:158` types `getPost(slug: string)` and compares raw against entry slugs. `app/blog/[slug]/page.tsx:30,56` passes the un-narrowed `params.slug` straight in.
Today this is safe because `output: "export"` + `dynamicParams = false` means the framework only routes pre-validated slugs to this handler. But the function signature invites a regression if a future caller invokes it from an unguarded path (RSS feed, search, new route).
Fix
Call `parseSlug` at the top and return `null` on failure — matches the `Slug` brand already defined in the same file, costs ~3 lines, and locks the invariant at the function boundary instead of relying on transitive framework guarantees.
```ts
export function getPost(slug: string): PostSource | null {
const valid = parseSlug(slug);
if (!valid) return null;
return readEntries().find((e) => e.slug === valid) ?? null;
}
```
Found during PR review of #61.
`lib/blog.ts:158` types `getPost(slug: string)` and compares raw against entry slugs. `app/blog/[slug]/page.tsx:30,56` passes the un-narrowed `params.slug` straight in.
Today this is safe because `output: "export"` + `dynamicParams = false` means the framework only routes pre-validated slugs to this handler. But the function signature invites a regression if a future caller invokes it from an unguarded path (RSS feed, search, new route).
Fix
Call `parseSlug` at the top and return `null` on failure — matches the `Slug` brand already defined in the same file, costs ~3 lines, and locks the invariant at the function boundary instead of relying on transitive framework guarantees.
```ts
export function getPost(slug: string): PostSource | null {
const valid = parseSlug(slug);
if (!valid) return null;
return readEntries().find((e) => e.slug === valid) ?? null;
}
```
Found during PR review of #61.