Luigit
repositories / pi-ext

pi-ext

bugabingas pi extensions

owned by admin

extensions/the-system/plan-status.ts

Raw
import { readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
import path from "node:path";
import {
	parseAuthoredDocument,
	resolveGovernedPath,
	SystemDocumentError,
	validateBundleCandidate,
} from "./documents.ts";
import type { ProjectIndex } from "./project-index.ts";

export type PlanStatus = "draft" | "approved";

const FRONTMATTER = /^(---\r?\n)([\s\S]*?)(\r?\n---(?:\r?\n|$))/;
const STATUS_LINE = /^status:[^\r\n]*$/gm;

export function planStatusSource(source: string, status: PlanStatus): string {
	const block = FRONTMATTER.exec(source);
	if (!block)
		throw new SystemDocumentError(
			"status.frontmatter",
			"Plan requires one leading YAML frontmatter block",
		);
	const open = block[1] ?? "";
	const body = block[2] ?? "";
	const close = block[3] ?? "";
	const matches = body.match(STATUS_LINE) ?? [];
	if (matches.length !== 1)
		throw new SystemDocumentError(
			"status.line",
			`Plan frontmatter requires exactly one status line; found ${matches.length}`,
		);
	const updated = body.replace(STATUS_LINE, `status: ${status}`);
	return `${open}${updated}${close}${source.slice(block[0].length)}`;
}

export function writePlanStatus(
	index: ProjectIndex,
	id: string,
	status: PlanStatus,
): void {
	const document = index.documents.find(
		(candidate) => candidate.metadata.id === id,
	);
	if (!document)
		throw new SystemDocumentError(
			"status.missing-item",
			`No System document: ${id}`,
		);
	if (document.metadata.type !== "plan")
		throw new SystemDocumentError(
			"status.item-type",
			`Only plans carry a status: ${id}`,
		);
	if (document.metadata.status === status) return;
	const target = resolveGovernedPath(index.root, document.path);
	const markdown = planStatusSource(readFileSync(target, "utf8"), status);
	const diagnostics = validateBundleCandidate(
		index.documents.map((candidate) => ({
			path: candidate.path,
			markdown: candidate.markdown,
		})),
		{ path: document.path, markdown },
		index.prefix,
	);
	if (diagnostics.length > 0)
		throw new SystemDocumentError(
			"status.invalid",
			`Plan would become invalid: ${diagnostics.map((item) => item.message).join("; ")}`,
		);
	const written = parseAuthoredDocument(markdown, index.prefix).metadata;
	if (written.type !== "plan" || written.status !== status)
		throw new SystemDocumentError(
			"status.invalid",
			`Plan status did not change to ${status}: ${id}`,
		);
	const temporary = path.join(
		path.dirname(target),
		`.status-${process.pid}-${Date.now()}.md`,
	);
	writeFileSync(temporary, markdown, { encoding: "utf8", flag: "wx" });
	try {
		renameSync(temporary, target);
	} finally {
		rmSync(temporary, { force: true });
	}
}