# configuration/strings/catalog.strings.ts

> 172 lines of code and 39 definitions.

Tree: Site tree
Language: typescript
Layer: product
Canonical: https://banes-lab.com/anatomy/tree#file-configuration-strings-catalog-strings-ts
Source text: https://banes-lab.com/source/tree/configuration/strings/catalog.strings.ts.txt

Listed in [configuration/strings](https://banes-lab.com/api/source/tree/configuration/strings.md), after [configuration/strings/card.strings.ts](https://banes-lab.com/source/tree/configuration/strings/card.strings.ts.md) and before [configuration/strings/clipboard.strings.ts](https://banes-lab.com/source/tree/configuration/strings/clipboard.strings.ts.md).

## Definitions

- `spacedOf` (lexical_declaration, line 123)
- `groupTitleOf` (lexical_declaration, line 143, exported)
- `neighboursClause` (lexical_declaration, line 62)
- `capitalised` (lexical_declaration, line 131)
- `countOf` (lexical_declaration, line 135)
- `namesOf` (lexical_declaration, line 139)
- `nodeSummary` (lexical_declaration, line 186, exported)
- `forceTitleOf` (lexical_declaration, line 154, exported)
- `routeStopSentence` (lexical_declaration, line 69, exported)
- `listedInSentence` (lexical_declaration, line 90, exported)
- `groupSummary` (lexical_declaration, line 158, exported)
- `forceSummary` (lexical_declaration, line 171, exported)
- `substrateSummary` (lexical_declaration, line 190, exported)
- `loopSummary` (lexical_declaration, line 199, exported)
- `INDEX_GROUP` (lexical_declaration, line 20)
- `COLLECTION_TITLES` (lexical_declaration, line 22, exported)
- `ADDRESS_GROUP_TITLES` (lexical_declaration, line 40, exported)
- `recordCountPhrase` (lexical_declaration, line 46, exported)
- `facetTitle` (lexical_declaration, line 50, exported)
- `addressGroupTitle` (lexical_declaration, line 54, exported)
- `nameShardTitle` (lexical_declaration, line 58, exported)
- `after` (lexical_declaration, line 63)
- `before` (lexical_declaration, line 64)
- `neighbors` (lexical_declaration, line 65)
- `GROUNDED_RECORDS_TITLE` (lexical_declaration, line 78, exported)
- `CHECKED_RECORDS_TITLE` (lexical_declaration, line 80, exported)
- `facetCollectionTitle` (lexical_declaration, line 82, exported)
- `facetFieldTitle` (lexical_declaration, line 86, exported)
- `GROUP_TITLES` (lexical_declaration, line 98)
- `ID_SEPARATORS` (lexical_declaration, line 121)
- `out` (lexical_declaration, line 124)
- `title` (lexical_declaration, line 147, exported)
- `PRINCIPLE_MEMBER` (lexical_declaration, line 167, exported)
- `TERM_MEMBER` (lexical_declaration, line 168, exported)
- `CONTRACT_MEMBER` (lexical_declaration, line 169, exported)
- `parts` (lexical_declaration, line 172, exported)
- `verb` (lexical_declaration, line 176, exported)
- `relationSummary` (lexical_declaration, line 180, exported)
- `base` (lexical_declaration, line 195, exported)

## Used by

- [domain/converters/contract.converter.ts](https://banes-lab.com/source/tree/domain/converters/contract.converter.ts.md)
- [domain/converters/lexicon.converter.ts](https://banes-lab.com/source/tree/domain/converters/lexicon.converter.ts.md)
- [domain/converters/principle.converter.ts](https://banes-lab.com/source/tree/domain/converters/principle.converter.ts.md)

## Source

```typescript
import {
    ALGO_DOMAIN_FACE,
    ALGO_FACE,
    ARCH_CATEGORY_FACE,
    ARCH_FACE,
    FORCE_FACE,
    KIND_FACE,
    LAYER_FACE,
    LEX_CATEGORY_FACE,
    LEX_FACE,
    PAG_FACE,
    REASON_FACE,
    RELATION_FACE,
    STAGE_FACE,
    TENSION_FACE,
    VOCABULARY_FACE,
} from "@govlab/constants";
import { ANATOMY_FACE, CHAPTER_FACE } from "#configuration/constants/vocabulary.constants";

const INDEX_GROUP = "api";

export const COLLECTION_TITLES: ReadonlyMap<string, string> = new Map([
    [ALGO_FACE, "Algorithm contracts"],
    [ARCH_FACE, "Architecture principles"],
    [LEX_FACE, "Lexicon terms"],
    [REASON_FACE, "Reasoning records"],
    [STAGE_FACE, "Loop stages"],
    [LAYER_FACE, "Layers"],
    [TENSION_FACE, "Tensions"],
    [ALGO_DOMAIN_FACE, "Algorithm domains"],
    [ARCH_CATEGORY_FACE, "Principle categories"],
    [FORCE_FACE, "Forces"],
    [KIND_FACE, "Record kinds"],
    [LEX_CATEGORY_FACE, "Term categories"],
    [RELATION_FACE, "Relations"],
    [PAG_FACE, "Grammar records"],
    [VOCABULARY_FACE, "Controlled vocabularies"],
]);

export const ADDRESS_GROUP_TITLES: ReadonlyMap<string, string> = new Map([
    [CHAPTER_FACE, "sections"],
    [ANATOMY_FACE, "source files and folders"],
    [INDEX_GROUP, "indexes"],
]);

export const recordCountPhrase = function recordCountPhrase(count: number): string {
    return `${String(count)} ${count === 1 ? "record" : "records"}`;
};

export const facetTitle = function facetTitle(collection: string, field: string, value: string): string {
    return `${collection} whose ${field} is ${value}`;
};

export const addressGroupTitle = function addressGroupTitle(group: string): string {
    return `Every published address for ${group}`;
};

export const nameShardTitle = function nameShardTitle(letter: string): string {
    return `Name lookup entries for names that start with ${letter}`;
};

const neighboursClause = function neighboursClause(previous: string | null, next: string | null): string {
    const after = previous === null ? null : `after ${previous}`;
    const before = next === null ? null : `before ${next}`;
    const neighbors = [after, before].filter((part): part is string => part !== null).join(" and ");
    return neighbors.length === 0 ? "" : `, ${neighbors}`;
};

export const routeStopSentence = function routeStopSentence(
    position: number,
    total: number,
    previous: string | null,
    next: string | null,
): string {
    return `This section is stop ${String(position)} of ${String(total)} in the learning route${neighboursClause(previous, next)}.`;
};

export const GROUNDED_RECORDS_TITLE = "Records this file grounds";

export const CHECKED_RECORDS_TITLE = "Records this file checks";

export const facetCollectionTitle = function facetCollectionTitle(collection: string): string {
    return `${collection} by field`;
};

export const facetFieldTitle = function facetFieldTitle(collection: string, field: string): string {
    return `${collection} by ${field}`;
};

export const listedInSentence = function listedInSentence(
    index: string,
    previous: string | null,
    next: string | null,
): string {
    return `Listed in ${index}${neighboursClause(previous, next)}.`;
};

const GROUP_TITLES: ReadonlyMap<string, string> = new Map([
    ["agent-creation", "Agent creation"],
    ["agent-workflow", "Agent workflow"],
    ["anti-patterns", "Anti-patterns"],
    ["arch-relationships", "Architecture relationships"],
    ["architecture", "Architecture"],
    ["automation", "Automation"],
    ["centralization", "Centralization"],
    ["checklist-creation", "Checklist creation"],
    ["codebase-verification", "Codebase verification"],
    ["context-verification", "Context verification"],
    ["coordination", "Coordination"],
    ["css-cascade", "CSS cascade"],
    ["governed-plan-loop", "Governed plan loop"],
    ["living-profile", "Living profile"],
    ["mode-driven-response-schema", "Mode-driven response schema"],
    ["pag", "Pattern Abstract Grammar"],
    ["pattern-distillation", "Pattern distillation"],
    ["quality-engine", "Quality engine"],
    ["taxonomy", "Taxonomy"],
    ["test-coverage", "Test coverage"],
]);

const ID_SEPARATORS: ReadonlySet<string> = new Set(["_", "-"]);

const spacedOf = function spacedOf(id: string): string {
    let out = "";
    for (const char of id) {
        out += ID_SEPARATORS.has(char) ? " " : char;
    }
    return out;
};

const capitalised = function capitalised(text: string): string {
    return text.charAt(0).toUpperCase() + text.slice(1);
};

const countOf = function countOf(count: number, singular: string, plural: string): string {
    return `${String(count)} ${count === 1 ? singular : plural}`;
};

const namesOf = function namesOf(names: readonly string[]): string {
    return names.length < 2 ? names.join("") : `${names.slice(0, -1).join(", ")} and ${names.at(-1) ?? ""}`;
};

export const groupTitleOf = function groupTitleOf(id: string, name: string): string {
    if (name !== id) {
        return name;
    }
    const title = GROUP_TITLES.get(id);
    if (title === undefined) {
        throw new Error(`catalog: the group "${id}" has no title`);
    }
    return title;
};

export const forceTitleOf = function forceTitleOf(id: string): string {
    return capitalised(spacedOf(id));
};

export const groupSummary = function groupSummary(
    name: string,
    group: "category" | "domain",
    count: number,
    member: readonly [string, string],
): string {
    return `The ${name} ${group} holds ${countOf(count, member[0], member[1])}.`;
};

export const PRINCIPLE_MEMBER: readonly [string, string] = ["architecture principle", "architecture principles"];
export const TERM_MEMBER: readonly [string, string] = ["lexicon term", "lexicon terms"];
export const CONTRACT_MEMBER: readonly [string, string] = ["algorithm contract", "algorithm contracts"];

export const forceSummary = function forceSummary(contracts: number, principles: number): string {
    const parts = [
        contracts === 0 ? null : countOf(contracts, CONTRACT_MEMBER[0], CONTRACT_MEMBER[1]),
        principles === 0 ? null : countOf(principles, PRINCIPLE_MEMBER[0], PRINCIPLE_MEMBER[1]),
    ].filter((part): part is string => part !== null);
    const verb = contracts + principles === 1 ? "names" : "name";
    return `${capitalised(parts.join(" and "))} ${verb} this force.`;
};

export const relationSummary = function relationSummary(kinds: readonly string[]): string {
    return kinds.length === 1
        ? `This relation points only at ${kinds[0] ?? ""} records.`
        : `This relation can point at ${String(kinds.length)} kinds of record.`;
};

export const nodeSummary = function nodeSummary(concept: string, axis: string, mathType: string): string {
    return `This node covers the ${spacedOf(concept)} concept on the ${spacedOf(axis)} axis. Its mathematical type is ${spacedOf(mathType)}.`;
};

export const substrateSummary = function substrateSummary(
    layer: string,
    mathType: string,
    models: readonly string[],
): string {
    const base = `This node sits on the ${spacedOf(layer)} layer. Its mathematical type is ${spacedOf(mathType)}.`;
    return models.length === 0 ? base : `${base} The ${namesOf(models)} models build on it.`;
};

export const loopSummary = function loopSummary(name: string, stages: readonly string[]): string {
    return `The ${name.toLowerCase()} runs through ${countOf(stages.length, "stage", "stages")}, from ${stages[0] ?? ""} to ${stages.at(-1) ?? ""}.`;
};
```
