tools/core/validators/venue.validator.ts
tools/core/validators/venue.validator.ts is a file in Coordination Surface. 200 lines of code and 66 definitions.
import { delimitersIn } from "../analyzers/fence.analyzer.ts";
import { fencedFlags } from "../predicates/fence.predicate.ts";
import { itemSpans } from "../resolvers/sweep.resolver.ts";
const POSITION_OPENER = "Position ";
const FIELD_INDENT = " ";
interface SchemaGap {
readonly record: string;
readonly field: string;
readonly declared: readonly string[];
readonly state: "absent" | "undeclared";
}
const RECORD_OPENER = "Agent ";
const RECORD_CLOSER = "└───";
const DEEPER_INDENT = `${FIELD_INDENT} `;
const fieldNameOf = function fieldNameOf(trimmed: string): string | null {
const colon = trimmed.indexOf(":");
const field = colon <= 0 ? "" : trimmed.slice(0, colon).trim();
return field.length === 0 || field.includes(" ") ? null : field;
};
const isRecordBoundary = function isRecordBoundary(trimmed: string): boolean {
return trimmed.startsWith(RECORD_OPENER) || trimmed.startsWith(RECORD_CLOSER);
};
const recordFlags = function recordFlags(lines: readonly string[], fenced: readonly boolean[]): boolean[] {
const out: boolean[] = [];
let inside = false;
for (const [index, line] of lines.entries()) {
out.push(inside);
const trimmed = line.trim();
const next: boolean = trimmed.startsWith(RECORD_OPENER) || (inside && !trimmed.startsWith(RECORD_CLOSER));
inside = fenced[index] === true ? next : inside;
}
return out;
};
export const venueFieldsFrom = function venueFieldsFrom(template: string): string[] {
const lines = template.split("\n");
const fenced = fencedFlags(template);
const inside = recordFlags(lines, fenced);
const fields = lines.flatMap((line, index) => {
const trimmed = line.trim();
const eligible = fenced[index] === true && inside[index] === true && !isRecordBoundary(trimmed);
const field = eligible && line.startsWith(FIELD_INDENT) ? fieldNameOf(trimmed) : null;
return field === null ? [] : [field];
});
return [...new Set(fields)];
};
const absentFields = function absentFields(
record: string,
seen: ReadonlySet<string>,
declared: readonly string[],
): SchemaGap[] {
if (record.length === 0) {
return [];
}
return declared.filter((field) => !seen.has(field)).map((field) => ({ declared, field, record, state: "absent" }));
};
interface RecordState {
readonly record: string;
readonly seen: ReadonlySet<string>;
}
interface RecordStep {
readonly gaps: readonly SchemaGap[];
readonly next: RecordState;
}
const NO_RECORD: RecordState = { record: "", seen: new Set() };
const venueFieldAt = function venueFieldAt(line: string, record: string): string | null {
const indented = record.length > 0 && line.startsWith(FIELD_INDENT) && !line.startsWith(DEEPER_INDENT);
return indented ? fieldNameOf(line.trim()) : null;
};
const recordStep = function recordStep(state: RecordState, line: string, declared: readonly string[]): RecordStep {
if (line.startsWith(RECORD_OPENER)) {
return {
gaps: absentFields(state.record, state.seen, declared),
next: { record: line.trim(), seen: new Set() },
};
}
if (line.startsWith(RECORD_CLOSER)) {
return { gaps: absentFields(state.record, state.seen, declared), next: NO_RECORD };
}
const field = venueFieldAt(line, state.record);
if (field === null) {
return { gaps: [], next: state };
}
if (declared.includes(field)) {
return { gaps: [], next: { ...state, seen: new Set([...state.seen, field]) } };
}
return { gaps: [{ declared, field, record: state.record, state: "undeclared" }], next: state };
};
export const venueSchemaGaps = function venueSchemaGaps(source: string, template: string): SchemaGap[] {
const declared = venueFieldsFrom(template);
if (declared.length === 0) {
return [];
}
const fenced = fencedFlags(source);
const out: SchemaGap[] = [];
let state = NO_RECORD;
for (const [index, line] of source.split("\n").entries()) {
const step = fenced[index] === true ? { gaps: [], next: state } : recordStep(state, line, declared);
out.push(...step.gaps);
state = step.next;
}
return [...out, ...absentFields(state.record, state.seen, declared)];
};
const CONTRADICTS_LEAD = "Contradicts:";
const FIELD_BLOCK_LEAD = "carries these fields";
const isLetter = function isLetter(char: string): boolean {
return (char >= "A" && char <= "Z") || (char >= "a" && char <= "z");
};
const labelWord = function labelWord(line: string): string {
const trimmed = line.trim();
const colon = trimmed.indexOf(":");
const label = colon <= 0 ? "" : trimmed.slice(0, colon);
for (const char of label) {
if (!isLetter(char)) {
return "";
}
}
return label;
};
export const positionFieldLabels = function positionFieldLabels(template: string): string[] {
const lines = template.split("\n");
const start = lines.findIndex((line) => line.includes(FIELD_BLOCK_LEAD));
if (start === -1) {
return [];
}
const words = lines.slice(start + 1).map(labelWord);
const first = words.findIndex((word) => word.length > 0 && word !== SIGNED_WORD);
const block = first === -1 ? [] : words.slice(first);
const end = block.indexOf("");
return (end === -1 ? block : block.slice(0, end)).filter((word) => word !== SIGNED_WORD);
};
const SIGNED_WORD = "Signed";
export const SIGNED_LEAD = "Signed:";
const isDigit = function isDigit(char: string): boolean {
return char >= "0" && char <= "9";
};
const citesAt = function citesAt(clause: string, index: number, letter: string): boolean {
const before = index === 0 ? " " : clause.charAt(index - 1);
const after = index + letter.length;
const cursor = clause.charAt(after) === "-" ? after + 1 : after;
return !isLetter(before) && isDigit(clause.charAt(cursor));
};
const citesOwnLetter = function citesOwnLetter(clause: string, letter: string): boolean {
let index = letter.length === 0 ? -1 : clause.indexOf(letter);
while (index !== -1) {
if (citesAt(clause, index, letter)) {
return true;
}
index = clause.indexOf(letter, index + 1);
}
return false;
};
const contradictionClause = function contradictionClause(body: string): string | null {
const opens = body.indexOf(CONTRADICTS_LEAD);
if (opens === -1) {
return null;
}
const after = body.slice(opens + CONTRADICTS_LEAD.length);
const stop = after.indexOf(SIGNED_LEAD);
return stop === -1 ? after : after.slice(0, stop);
};
export const selfCorrections = function selfCorrections(source: string): string[] {
const lines = source.split("\n");
return itemSpans(source)
.filter((span) => {
const clause = contradictionClause(lines.slice(span.from, span.through).join(" "));
return clause !== null && citesOwnLetter(clause, span.agent);
})
.map((span) => span.key);
};
interface StrayPosition {
readonly key: string;
readonly line: number;
}
const claimKeyOf = function claimKeyOf(line: string): string {
const rest = line.slice(POSITION_OPENER.length);
const dash = rest.indexOf(" —");
const key = dash === -1 ? rest : rest.slice(0, dash);
return key.trim();
};
const depthChanges = function depthChanges(source: string): Map<number, number> {
const out = new Map<number, number>();
for (const mark of delimitersIn(source)) {
out.set(mark.line, (out.get(mark.line) ?? 0) + (mark.open ? 1 : -1));
}
return out;
};
export const positionsOutsideRecords = function positionsOutsideRecords(source: string): StrayPosition[] {
const fenced = fencedFlags(source);
const changes = depthChanges(source);
const out: StrayPosition[] = [];
let depth = 0;
for (const [index, line] of source.split("\n").entries()) {
depth += changes.get(index + 1) ?? 0;
const stray = fenced[index] !== true && depth <= 0 && line.startsWith(POSITION_OPENER);
if (stray) {
out.push({ key: claimKeyOf(line), line: index + 1 });
}
}
return out;
};