syntaxai/tdd.md · main · src / b32_edit_resolve.ts
// c32 — pure logic: given a (section, slug) tuple from a /edit/<...>
// URL, resolve it to the editable resource (page URL, file path on
// disk, page title) — or return null if the combination doesn't map
// to an existing doc page. Pure: no I/O. The handler does the actual
// fs read for the current body.
//
// Editable sections are the ones that already have a registry: sama,
// guides, blog. Slug must match an entry in the corresponding registry
// — this prevents arbitrary file writes via /edit/../../etc/passwd
// style inputs.
import { ALL_SAMA } from "./a31_sama.ts";
import { ALL_GUIDES } from "./a31_guides.ts";
import { ALL_POSTS } from "./a31_blog.ts";
import { SITE_NAV } from "./a31_docs_nav.ts";
export type EditableSection = "sama" | "guides" | "blog";
export interface ResolvedEdit {
section: EditableSection;
slug: string;
pageUrl: string;
filePath: string;
title: string;
}
const SECTIONS = new Set<EditableSection>(["sama", "guides", "blog"]);
const isValidSection = (s: string): s is EditableSection => SECTIONS.has(s as EditableSection);
const SAFE_SLUG = /^[a-z0-9][a-z0-9-]*$/;
const lookupTitle = (section: EditableSection, slug: string): string | null => {
if (section === "sama") {
const e = ALL_SAMA.find((d) => d.slug === slug);
if (e) return `${e.letter} — ${e.title}`;
} else if (section === "guides") {
const e = ALL_GUIDES.find((g) => g.slug === slug);
if (e) return e.title;
} else {
const e = ALL_POSTS.find((p) => p.slug === slug);
if (e) return e.title;
}
// Fallback to SITE_NAV: nav-only editable pages (e.g. /sama/skill)
// have a content/<...>.md backing file but no entry in the discipline
// / guide / blog registries. They're listed in SITE_NAV with a
// non-null editPath, which is the single source of truth for
// "this docs page is editable".
const navSection = SITE_NAV.find((s) => s.id === section);
const link = navSection?.links.find(
(l) => l.href === `/${section}/${slug}` && l.editPath !== null,
);
return link?.label ?? null;
};
export const resolveEdit = (section: string, slug: string): ResolvedEdit | null => {
if (!isValidSection(section)) return null;
if (!SAFE_SLUG.test(slug)) return null;
const title = lookupTitle(section, slug);
if (title === null) return null;
// /sama discipline pages live under /sama/discipline/<slug> as of
// PR #53. /blog pages live under /blog/<yyyy-mm>/<slug> as of PR #55.
// Other sections keep the flat /<section>/<slug> shape.
let pageUrl: string;
if (section === "sama" && slug !== "skill" && slug !== "v2") {
pageUrl = `/sama/discipline/${slug}`;
} else if (section === "blog") {
const post = ALL_POSTS.find((p) => p.slug === slug);
pageUrl = post
? `/blog/${post.date.slice(0, 7)}/${slug}`
: `/blog/${slug}`;
} else {
pageUrl = `/${section}/${slug}`;
}
return {
section,
slug,
pageUrl,
filePath: `content/${section}/${slug}.md`,
title,
};
};