tools/core/validators/channel.validator.ts
tools/core/validators/channel.validator.ts is a file in Coordination Surface. 81 lines of code and 19 definitions.
import { existsSync, readFileSync, readdirSync, rmSync } from "node:fs";
import { fieldOf, tryParse } from "../readers/json.reader.ts";
import { GENERATED_DIR } from "../constants/path.constants.ts";
import { channelScopeOf } from "../reporters/rule.reporter.ts";
import { resolve } from "node:path";
interface Channel {
readonly name: string;
readonly scope: string;
readonly declared: string | null;
}
interface StaleChannel extends Channel {
readonly removable: boolean;
}
const isChannelBody = function isChannelBody(parsed: unknown): boolean {
if (typeof parsed !== "object" || parsed === null) {
return false;
}
return fieldOf(parsed, "tool") === "govern" && fieldOf(parsed, "authoritative") === false;
};
const declaredScope = function declaredScope(parsed: unknown): string | null {
const value = typeof parsed === "object" && parsed !== null ? fieldOf(parsed, "scope") : null;
return typeof value === "string" && value.length > 0 ? value : null;
};
const channelOf = function channelOf(dir: string, entry: string): Channel[] {
const scope = channelScopeOf(entry);
const parsed = scope === null ? null : tryParse(readFileSync(resolve(dir, entry), "utf8"))?.value;
return scope !== null && isChannelBody(parsed) ? [{ declared: declaredScope(parsed), name: entry, scope }] : [];
};
export const channelsIn = function channelsIn(repoRoot: string): Channel[] {
const dir = resolve(repoRoot, GENERATED_DIR);
return existsSync(dir) ? readdirSync(dir).flatMap((entry) => channelOf(dir, entry)) : [];
};
export const staleChannels = function staleChannels(
repoRoot: string,
resolves: (scope: string) => boolean,
): StaleChannel[] {
return channelsIn(repoRoot)
.filter((channel) => !resolves(channel.scope))
.map((channel) => ({ ...channel, removable: true }));
};
interface NamingBreach {
readonly name: string;
readonly named: string;
readonly declared: string;
}
interface ScopeCollision {
readonly scope: string;
readonly names: readonly string[];
}
export const misnamedChannels = function misnamedChannels(repoRoot: string): NamingBreach[] {
const out: NamingBreach[] = [];
for (const channel of channelsIn(repoRoot)) {
if (channel.declared === null || channel.declared === channel.scope) {
continue;
}
out.push({ declared: channel.declared, name: channel.name, named: channel.scope });
}
return out;
};
export const collidingScopes = function collidingScopes(repoRoot: string): ScopeCollision[] {
const byScope = new Map<string, string[]>();
for (const channel of channelsIn(repoRoot)) {
if (channel.declared === null) {
continue;
}
const held = byScope.get(channel.declared) ?? [];
held.push(channel.name);
byScope.set(channel.declared, held);
}
const out: ScopeCollision[] = [];
for (const [scope, names] of byScope) {
if (names.length < 2) {
continue;
}
out.push({ names: names.toSorted((left, right) => left.localeCompare(right, "en")), scope });
}
return out;
};
export const removeChannel = function removeChannel(repoRoot: string, name: string): void {
rmSync(resolve(repoRoot, GENERATED_DIR, name), { force: true });
};