"use client"; // The client's side of POST /api/report/timeline, and the event every part of // a project page listens to after the manifest changed under it. // // The timeline editor, the structure editors and the On-screen section each // hold a manifest TOKEN; a structural write changes the file, so after one // every other holder must re-read before its next save would 409. export const MANIFEST_CHANGED = "umtool:manifest-changed"; export function announceManifestChanged(project: string) { window.dispatchEvent(new CustomEvent(MANIFEST_CHANGED, { detail: { project } })); } export type TimelineResult = { ok: boolean; status: number; json: Record & { error?: string; errors?: string[]; stale?: boolean; token?: string }; }; export async function timelineOp(project: string, token: string | null, op: string, args: Record = {}): Promise { const r = await fetch("/api/report/timeline", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ project, token, op, ...args }), cache: "no-store", }); const json = (await r.json().catch(() => ({ error: `HTTP ${r.status}` }))) as TimelineResult["json"]; return { ok: r.ok, status: r.status, json }; } /** A refusal in words: the build's sentences when there are several. */ export const refusalOf = (res: TimelineResult) => res.json.stale ? "the manifest changed since this page read it — reloaded; try again" : res.json.errors?.length ? res.json.errors.join("; ") : String(res.json.error ?? `HTTP ${res.status}`);