import { withEditNotes } from "@/lib/report/guard"; import { ChromeRefused, StaleToken, manifestToken, updateChrome } from "@/lib/report/manifest.mjs"; import { resolveReport } from "@/lib/report/serve.mjs"; import { DECK_DEFAULTS, deckOn, resolveDeck, validateChrome } from "umtool-report-to-video/deck"; export const dynamic = "force-dynamic"; // The deck's settings: `render.chrome` in the manifest. // // The client sends a project id, the WHOLE chrome block (or null to turn the // deck off) and the token it was given when it read the manifest. It never // sends a path. Validation is deck.mjs's validateChrome, run by the writer, so // a block this route accepts is one the build accepts; a refusal comes back // as the same sentences the build would print, one per problem. type Body = Record; const noStore = { "cache-control": "no-store" }; /** What the settings form starts from: the block as written, and with every default filled. */ export async function GET(request: Request) { const url = new URL(request.url); const r = await resolveReport(url.searchParams.get("project") ?? ""); if ("error" in r) return Response.json({ error: r.error }, { status: r.status }); const render = (r.manifest.render ?? {}) as Record; const { chrome = null, ...rest } = render; return Response.json( { chrome, deckOn: deckOn(render), deck: chrome ? resolveDeck(render) : null, defaults: DECK_DEFAULTS, // A block somebody hand-edited into a state the build would refuse. The // form shows these rather than pretending the saved settings are fine. errors: chrome ? validateChrome(chrome, rest) : [], token: await manifestToken(r.project.dir), }, { headers: noStore }, ); } export async function PUT(request: Request) { let body: Body; try { body = await request.json(); } catch { return Response.json({ error: "expected JSON" }, { status: 400 }); } if (!("chrome" in body)) { return Response.json({ error: "chrome is required — an object, or null to turn the deck off" }, { status: 400 }); } const r = await resolveReport(String(body.project ?? "")); if ("error" in r) return Response.json({ error: r.error }, { status: r.status }); try { const { result: res, editNotes } = await withEditNotes(r.project, () => updateChrome(r.project.dir, (body.chrome ?? null) as Record | null, { token: body.token === undefined ? null : String(body.token), }), ); return Response.json( { ok: true, chrome: res.chrome, deck: res.chrome ? resolveDeck({ ...(r.manifest.render ?? {}), chrome: res.chrome }) : null, token: res.token, editNotes, }, { headers: noStore }, ); } catch (e) { if (e instanceof StaleToken) { return Response.json( { error: e.message, expected: e.expected, got: e.got, stale: true }, { status: 409 }, ); } if (e instanceof ChromeRefused) { return Response.json({ error: e.message, errors: e.errors }, { status: 400 }); } return Response.json({ error: e instanceof Error ? e.message : String(e) }, { status: 400 }); } }