syntaxai/tdd.md · main · src / a31_edit_validation.ts
// c31 — model: validation for an admin edit submission. Pure: no I/O.
// The DB no longer stores edits (admin POST goes directly to Forgejo
// + filesystem), so this file holds only the body sanity checks that
// were previously bundled with the SQLite proposal flow.
export const MAX_EDIT_BODY_BYTES = 256 * 1024; // 256 KB
export class EditValidationError extends Error {
constructor(message: string) {
super(message);
this.name = "EditValidationError";
}
}
// Throws EditValidationError when the body is empty, too large, or
// otherwise unfit to commit. Returns the trimmed-but-otherwise-untouched
// body string on success.
export const validateEditBody = (raw: unknown): string => {
if (typeof raw !== "string") {
throw new EditValidationError("body must be a string");
}
if (raw.trim().length === 0) {
throw new EditValidationError("body cannot be empty");
}
const bytes = new TextEncoder().encode(raw).length;
if (bytes > MAX_EDIT_BODY_BYTES) {
throw new EditValidationError(
`body exceeds the ${MAX_EDIT_BODY_BYTES / 1024} KB limit (got ${Math.round(bytes / 1024)} KB)`,
);
}
return raw;
};
// Byte-identical check between current page content and the proposed
// new content. Used to skip a Forgejo round-trip when the user
// accidentally submitted without changes.
export const isNoOpEdit = (currentBody: string, newBody: string): boolean =>
currentBody === newBody;