# tools/core/runners/index.runner.ts

> 153 lines of code and 32 definitions.

Tree: Coordination tree
Language: typescript
Canonical: https://banes-lab.com/anatomy/coordination#file-coordination-tools-core-runners-index-runner-ts
Source text: https://banes-lab.com/assets/sources/source.cda1f4c867b5cebe594e5e8ac79d4e768062a0fa3ebae007cc73895d027c6706.generated.txt

## Definitions

- `refused` (lexical_declaration, line 40)
- `candidates` (lexical_declaration, line 108)
- `runTransition` (lexical_declaration, line 49, exported)
- `runIndex` (lexical_declaration, line 165, exported)
- `shortestFree` (lexical_declaration, line 130)
- `ROW` (lexical_declaration, line 18)
- `SECTION` (lexical_declaration, line 20)
- `SECTION_WORD` (lexical_declaration, line 22)
- `ACTIVE_STATE` (lexical_declaration, line 24)
- `UPPER` (lexical_declaration, line 26)
- `LOWER` (lexical_declaration, line 28)
- `SEAT_STATES` (lexical_declaration, line 30, exported)
- `TransitionRequest` (interface_declaration, line 32, exported)
- `rowLetter` (lexical_declaration, line 44)
- `foreign` (lexical_declaration, line 50, exported)
- `cells` (lexical_declaration, line 67, exported)
- `current` (lexical_declaration, line 72, exported)
- `IndexRequest` (interface_declaration, line 98, exported)
- `IndexOutcome` (interface_declaration, line 103, exported)
- `out` (lexical_declaration, line 109)
- `lastRowIn` (lexical_declaration, line 140)
- `opened` (lexical_declaration, line 141)
- `index` (lexical_declaration, line 144)
- `line` (lexical_declaration, line 145)
- `trimmed` (lexical_declaration, line 146)
- `role` (lexical_declaration, line 166, exported)
- `source` (lexical_declaration, line 171, exported)
- `lines` (lexical_declaration, line 172, exported)
- `at` (lexical_declaration, line 174, exported)
- `letter` (lexical_declaration, line 179, exported)
- `written` (lexical_declaration, line 184, exported)
- `witness` (lexical_declaration, line 186, exported)

## Uses

- [tools/core/strings/index.strings.ts](https://banes-lab.com/source/coordination/tools/core/strings/index.strings.ts.md)

## Source

```typescript
import {
    INDEX_CONTENDED,
    LETTERS_EXHAUSTED,
    ROLE_MISSING,
    ROW_SECTION_MISSING,
    alreadyInState,
    foreignWithoutReason,
    malformedRow,
    rowAdded,
    stateChanged,
    unboundLetter,
    unknownState,
} from "../strings/index.strings.ts";

import { readFileSync, writeFileSync } from "node:fs";
import { indexedLetters } from "../inspectors/index.inspector.ts";

const ROW = "| ";

const SECTION = "═";

const SECTION_WORD = "INDEX";

const ACTIVE_STATE = "ACTIVE";

const UPPER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

const LOWER = "abcdefghijklmnopqrstuvwxyz";

export const SEAT_STATES: readonly string[] = ["ACTIVE", "INACTIVE", "INVOKED"];

export interface TransitionRequest {
    readonly absolute: string;
    readonly letter: string;
    readonly state: string;
    readonly by: string;
    readonly warrant: string | null;
}

const refused = function refused(message: string): IndexOutcome {
    return { code: 2, message };
};

const rowLetter = function rowLetter(line: string): string {
    const cells = line.split("|");
    return (cells[1] ?? "").trim();
};

export const runTransition = function runTransition(request: TransitionRequest): IndexOutcome {
    const foreign = request.letter !== request.by;
    if (foreign && (request.warrant === null || request.warrant.trim().length === 0)) {
        return refused(foreignWithoutReason(request.by, request.letter));
    }

    if (!SEAT_STATES.includes(request.state)) {
        return refused(unknownState(request.state, SEAT_STATES));
    }

    const source = readFileSync(request.absolute, "utf8");
    const lines = source.split("\n");

    const at = lines.findIndex((line) => line.startsWith(ROW) && rowLetter(line) === request.letter);
    if (at === -1) {
        return refused(unboundLetter(request.letter));
    }

    const cells = (lines[at] ?? "").split("|");
    if (cells.length < 5) {
        return refused(malformedRow(request.letter));
    }

    const current = (cells[3] ?? "").trim();
    if (current === request.state) {
        return { code: 0, message: alreadyInState(request.letter, request.state) };
    }

    cells[3] = ` ${request.state} `;
    lines[at] = cells.join("|");

    if (foreign) {
        lines.splice(
            at + 1,
            0,
            `> ${request.letter} moved to ${request.state} by ${request.by} under ${String(request.warrant)}`,
        );
    }

    const witness = readFileSync(request.absolute, "utf8");
    if (witness !== source) {
        return refused(INDEX_CONTENDED);
    }

    writeFileSync(request.absolute, lines.join("\n"), "utf8");

    return { code: 0, message: stateChanged(request.letter, request.state, foreign ? request.by : null) };
};

export interface IndexRequest {
    readonly absolute: string;
    readonly role: string;
}

export interface IndexOutcome {
    readonly code: number;
    readonly message: string;
}

const candidates = function candidates(): string[] {
    const out: string[] = [];

    for (const first of UPPER) {
        out.push(first);
    }
    for (const first of UPPER) {
        for (const second of LOWER) {
            out.push(`${first}${second}`);
        }
    }
    for (const first of UPPER) {
        for (const second of UPPER) {
            for (const third of LOWER) {
                out.push(`${first}${second}${third}`);
            }
        }
    }

    return out;
};

const shortestFree = function shortestFree(taken: ReadonlySet<string>): string | null {
    for (const candidate of candidates()) {
        if (!taken.has(candidate)) {
            return candidate;
        }
    }

    return null;
};

const lastRowIn = function lastRowIn(lines: readonly string[]): number {
    let opened = false;
    let at = -1;

    for (let index = 0; index < lines.length; index += 1) {
        const line = lines[index] ?? "";
        const trimmed = line.trim();

        if (!opened) {
            if (trimmed.startsWith(SECTION) && trimmed.includes(SECTION_WORD)) {
                opened = true;
            }
            continue;
        }
        if (trimmed.startsWith(SECTION)) {
            break;
        }
        if (line.startsWith(ROW)) {
            at = index;
        }
    }

    return at;
};

export const runIndex = function runIndex(request: IndexRequest): IndexOutcome {
    const role = request.role.trim();
    if (role.length === 0) {
        return refused(ROLE_MISSING);
    }

    const source = readFileSync(request.absolute, "utf8");
    const lines = source.split("\n");

    const at = lastRowIn(lines);
    if (at === -1) {
        return refused(ROW_SECTION_MISSING);
    }

    const letter = shortestFree(indexedLetters(source).letters);
    if (letter === null) {
        return refused(LETTERS_EXHAUSTED);
    }

    const written = [...lines.slice(0, at + 1), `| ${letter} | ${role} | ${ACTIVE_STATE} |`, ...lines.slice(at + 1)];

    const witness = readFileSync(request.absolute, "utf8");
    if (witness !== source) {
        return refused(INDEX_CONTENDED);
    }

    writeFileSync(request.absolute, written.join("\n"), "utf8");

    return { code: 0, message: rowAdded(letter, role) };
};
```
