core/validators/alias.validator.ts

core/validators/alias.validator.ts is a file in GovLab Context. 183 lines of code and 20 definitions.

import {
    ALIAS_EMPTY,
    ALIAS_FOLDED,
    ALIAS_REPEATS_NAME,
    aliasCollides,
} from "#configuration/strings/validation.strings";
import type { AliasDefect, AliasHolder } from "#types/validation.types";
import { nameKeyOf, slugify } from "#core/converters/identifier.converter";
import type { DistinctDeclaration } from "#types/field.types";
import type { Faces } from "#types/context.types";

const KIND_SEPARATOR = ":";

const holder = function holder(
    collection: string,
    ref: string,
    names: readonly (string | undefined)[],
    aliases: readonly string[] | undefined,
    distinct: readonly DistinctDeclaration[] | undefined,
): AliasHolder {
    return {
        aliases: aliases ?? [],
        collection,
        distinct: new Set((distinct ?? []).map((entry) => entry.id)),
        names: names.filter((name): name is string => name !== undefined && name.length > 0),
        ref,
    };
};

const reasonHolders = function reasonHolders(faces: Pick<Faces, "reason">): AliasHolder[] {
    const { reason } = faces;
    const kinded = <T extends { id: string; aliases?: string[] }>(
        kind: string,
        records: readonly T[],
        nameOf: (record: T) => string | undefined,
    ): AliasHolder[] =>
        records.map((record) =>
            holder(
                kind,
                `reasoning${KIND_SEPARATOR}${kind}${KIND_SEPARATOR}${record.id}`,
                [record.id, nameOf(record)],
                record.aliases,
                [],
            ),
        );
    return [
        ...kinded("node", reason.nodes(), (record) => record.name),
        ...kinded("invariant", reason.invariants(), (record) => record.name),
        ...kinded("failure-shape", reason.failureShapes(), (record) => record.name),
        ...kinded("layer", reason.layers(), (record) => record.label),
        ...kinded("lens", reason.lenses(), (record) => record.label),
        ...kinded("representation", reason.representations(), (record) => record.label),
        ...kinded("pattern-type", reason.patternTypes(), (record) => record.label),
        ...kinded("axis", reason.axes(), () => {}),
        ...kinded("dimension", reason.dimensions(), () => {}),
        ...kinded("math-type", reason.mathTypes(), () => {}),
        ...kinded("math-domain", reason.mathDomains(), () => {}),
        ...kinded("mode", reason.modes(), () => {}),
        ...kinded("model", reason.models(), () => {}),
        ...kinded("universal-axis", reason.universalAxes(), () => {}),
        ...kinded("technique", reason.techniques(), () => {}),
        ...kinded("test-surface", reason.testSurfaces(), () => {}),
        ...kinded("substrate-node", reason.substrate().nodes, (record) => record.name),
    ];
};

const pagHolders = function pagHolders(faces: Pick<Faces, "pag">): AliasHolder[] {
    const { pag } = faces;
    const at = (kind: string, key: string): string => `pag${KIND_SEPARATOR}${kind}${KIND_SEPARATOR}${key}`;
    return [
        ...pag
            .keywords()
            .map((record) =>
                holder("keyword", at("keyword", record.keyword), [record.keyword], record.aliases, record.distinctFrom),
            ),
        ...pag
            .documentTypes()
            .map((record) =>
                holder("document-type", at("document-type", record.type), [record.type], record.aliases, []),
            ),
        ...pag
            .productions()
            .map((record) => holder("production", at("production", record.lhs), [record.lhs], record.aliases, [])),
        ...pag
            .templates()
            .map((record) =>
                holder("template", at("template", record.type), [record.type, record.title], record.aliases, []),
            ),
    ];
};

export const aliasHoldersOf = function aliasHoldersOf(
    faces: Pick<Faces, "algo" | "arch" | "lex" | "pag" | "reason">,
): AliasHolder[] {
    return [
        ...faces.arch
            .all()
            .map((record) =>
                holder(
                    "architecture",
                    `architecture:${record.id}`,
                    [record.id, record.name],
                    record.aliases,
                    record.distinctFrom,
                ),
            ),
        ...faces.lex
            .all()
            .map((record) =>
                holder(
                    "lexicon",
                    `lexicon:${record.id}`,
                    [record.id, record.name],
                    record.aliases,
                    record.distinctFrom,
                ),
            ),
        ...faces.algo
            .all()
            .map((record) =>
                holder(
                    "algorithms",
                    `algorithms:${record.id}`,
                    [record.id, record.title],
                    record.aliases,
                    record.distinctFrom,
                ),
            ),
        ...pagHolders(faces),
        ...reasonHolders(faces),
    ];
};

const scopedKey = function scopedKey(entry: AliasHolder, phrase: string): string {
    return `${entry.collection}${KIND_SEPARATOR}${nameKeyOf(phrase)}`;
};

const keyedHolders = function keyedHolders(holders: readonly AliasHolder[]): Map<string, AliasHolder[]> {
    const keyed = new Map<string, AliasHolder[]>();
    for (const entry of holders) {
        for (const scoped of new Set([...entry.names, ...entry.aliases].map((phrase) => scopedKey(entry, phrase)))) {
            keyed.set(scoped, [...(keyed.get(scoped) ?? []), entry]);
        }
    }
    return keyed;
};

const collisionOf = function collisionOf(
    entry: AliasHolder,
    phrase: string,
    keyed: Map<string, AliasHolder[]>,
): string | null {
    const others = (keyed.get(scopedKey(entry, phrase)) ?? []).filter(
        (other) => other.ref !== entry.ref && !(entry.distinct.has(other.ref) && other.distinct.has(entry.ref)),
    );
    return others.length === 0 ? null : aliasCollides(others.map((other) => other.ref));
};

const defectOf = function defectOf(
    entry: AliasHolder,
    alias: string,
    keyed: Map<string, AliasHolder[]>,
): string | null {
    const slug = slugify(alias);
    if (slug.length === 0) {
        return ALIAS_EMPTY;
    }
    if (entry.names.some((name) => slugify(name) === slug)) {
        return ALIAS_REPEATS_NAME;
    }
    if (entry.names.some((name) => nameKeyOf(name) === nameKeyOf(alias))) {
        return ALIAS_FOLDED;
    }
    return collisionOf(entry, alias, keyed);
};

const nameDefectsOf = function nameDefectsOf(entry: AliasHolder, keyed: Map<string, AliasHolder[]>): AliasDefect[] {
    const byKey = new Map(entry.names.map((name) => [nameKeyOf(name), name]));
    return [...byKey.values()].flatMap((name) => {
        const reason = collisionOf(entry, name, keyed);
        return reason === null ? [] : [{ alias: name, reason, ref: entry.ref }];
    });
};

export const aliasDefectsOf = function aliasDefectsOf(holders: readonly AliasHolder[]): AliasDefect[] {
    const keyed = keyedHolders(holders);
    return holders.flatMap((entry) => [
        ...nameDefectsOf(entry, keyed),
        ...entry.aliases.flatMap((alias) => {
            const reason = defectOf(entry, alias, keyed);
            return reason === null ? [] : [{ alias, reason, ref: entry.ref }];
        }),
    ]);
};