tools/core/runners/archive.runner.ts

tools/core/runners/archive.runner.ts is a file in Coordination Surface. 149 lines of code and 25 definitions.

import {
    ENTRY_LEADS,
    ENTRY_MARKER,
    clauseEnd,
    declaresLead,
    entryEnd,
    filedNotice,
    promoteLeads,
} from "../analyzers/archive.analyzer.ts";
import { authorOf, closureText } from "../generators/board.generator.ts";
import {
    clauseExtended,
    clauseMissing,
    entryAdded,
    entryExists,
    entryNotFound,
    extendContended,
    extendMissingHistory,
    extendNotFound,
    handledBy,
    historyContended,
    historyMissing,
    leadUndeclared,
    leadsInPlace,
    leadsMissing,
    leadsRepaired,
    repairContended,
    repairMissingHistory,
} from "../strings/archive.strings.ts";
import { existsSync, readFileSync, writeFileSync } from "node:fs";
import { JUDGMENT_KIND } from "../constants/board.constants.ts";
import { appendSection } from "../formatters/text.formatter.ts";
import { closureRefusal } from "../validators/archive.validator.ts";
import { itemKindOf } from "../formatters/board.formatter.ts";

interface ClosureRequest {
    readonly absolute: string;
    readonly archive: string;
    readonly index: string;
    readonly closes: string;
    readonly agent: string | null;
    readonly ref: string | null;
}

interface ClosureOutcome {
    readonly refusal: string | null;
    readonly author: string;
    readonly label: string;
    readonly acknowledged: boolean;
}

interface EntryRequest {
    readonly archive: string;
    readonly heading: string;
    readonly body: string;
}

interface EntryOutcome {
    readonly message: string;
    readonly code: number;
}

interface ExtendRequest {
    readonly archive: string;
    readonly heading: string;
    readonly lead: string;
    readonly body: string;
}

export const runEntry = function runEntry(request: EntryRequest): EntryOutcome {
    if (!existsSync(request.archive)) {
        return { code: 2, message: historyMissing(request.archive) };
    }

    const before = readFileSync(request.archive, "utf8");

    if (before.includes(`${ENTRY_MARKER}${request.heading}`)) {
        return { code: 2, message: `${entryExists(request.heading, request.archive)}${filedNotice(before)}` };
    }

    const missing = ENTRY_LEADS.filter((lead) => !declaresLead(request.body, lead));
    if (missing.length > 0) {
        return { code: 2, message: leadsMissing(missing) };
    }

    const written = appendSection(before, `${ENTRY_MARKER}${request.heading}`, request.body);

    const witness = readFileSync(request.archive, "utf8");
    if (witness !== before) {
        return { code: 2, message: historyContended(request.archive) };
    }

    writeFileSync(request.archive, written, "utf8");

    return { code: 0, message: `${entryAdded(request.heading, request.archive)}${filedNotice(before)}` };
};

export const runRepair = function runRepair(request: { archive: string; heading: string }): EntryOutcome {
    if (!existsSync(request.archive)) {
        return { code: 2, message: repairMissingHistory(request.archive) };
    }

    const before = readFileSync(request.archive, "utf8");
    const lines = before.split("\n");
    const opens = lines.findIndex((line) => line.startsWith(`${ENTRY_MARKER}${request.heading}`));

    if (opens === -1) {
        return { code: 2, message: `${entryNotFound(request.heading, request.archive)}${filedNotice(before)}` };
    }

    const end = entryEnd(lines, opens);
    const promotions = lines.slice(opens + 1, end).map(promoteLeads);
    const promoted = promotions.flatMap((promotion) => promotion.promoted);
    const out = [
        ...lines.slice(0, opens + 1),
        ...promotions.flatMap((promotion) => promotion.lines),
        ...lines.slice(end),
    ];

    if (promoted.length === 0) {
        return { code: 0, message: leadsInPlace(request.heading) };
    }

    const witness = readFileSync(request.archive, "utf8");
    if (witness !== before) {
        return { code: 2, message: repairContended(request.archive) };
    }

    writeFileSync(request.archive, out.join("\n"), "utf8");

    return { code: 0, message: leadsRepaired(promoted, request.heading) };
};

export const runExtend = function runExtend(request: ExtendRequest): EntryOutcome {
    if (!existsSync(request.archive)) {
        return { code: 2, message: extendMissingHistory(request.archive) };
    }

    if (!ENTRY_LEADS.includes(request.lead)) {
        return { code: 2, message: leadUndeclared(request.lead, ENTRY_LEADS) };
    }

    const before = readFileSync(request.archive, "utf8");
    const lines = before.split("\n");
    const opens = lines.findIndex((line) => line.startsWith(`${ENTRY_MARKER}${request.heading}`));

    if (opens === -1) {
        return { code: 2, message: `${extendNotFound(request.heading, request.archive)}${filedNotice(before)}` };
    }

    const at = clauseEnd(lines, opens, request.lead);
    if (at === -1) {
        return { code: 2, message: clauseMissing(request.heading, request.lead) };
    }

    const written = [...lines.slice(0, at + 1), request.body, ...lines.slice(at + 1)].join("\n");

    const witness = readFileSync(request.archive, "utf8");
    if (witness !== before) {
        return { code: 2, message: extendContended(request.archive) };
    }

    writeFileSync(request.archive, written, "utf8");

    return { code: 0, message: clauseExtended(request.lead, request.heading, request.archive) };
};

export const runClosure = function runClosure(request: ClosureRequest): ClosureOutcome {
    const carried = existsSync(request.archive) ? readFileSync(request.archive, "utf8") : "";
    const source = readFileSync(request.absolute, "utf8");
    const index = existsSync(request.index) ? readFileSync(request.index, "utf8") : "";
    const text = closureText(request.closes, request.ref ?? "", source, request.agent ?? "", index);
    const kind = itemKindOf(source, request.closes);

    return {
        acknowledged: kind === JUDGMENT_KIND,
        author: authorOf(request.closes),
        label: handledBy(request.agent ?? ""),
        refusal: closureRefusal(request.closes, request.ref, request.agent, text, carried, kind),
    };
};