# tools/core/readers/venue.reader.ts

> 43 lines of code and 17 definitions.

Tree: Coordination tree
Language: typescript
Layer: processing
Canonical: https://banes-lab.com/anatomy/coordination#file-coordination-tools-core-readers-venue-reader-ts
Source text: https://banes-lab.com/source/coordination/tools/core/readers/venue.reader.ts.txt

Listed in [tools/core/readers](https://banes-lab.com/api/source/coordination/tools/core/readers.md), after [tools/core/readers/token.reader.ts](https://banes-lab.com/source/coordination/tools/core/readers/token.reader.ts.md).

## Definitions

- `textIfPresent` (lexical_declaration, line 13, exported)
- `isSignOffLetter` (lexical_declaration, line 27)
- `surfaceText` (lexical_declaration, line 17, exported)
- `signOffEntry` (lexical_declaration, line 32)
- `venueRecords` (lexical_declaration, line 45, exported)
- `AGENT_LABEL` (lexical_declaration, line 5)
- `SIGN_OFF_COLON_LIMIT` (lexical_declaration, line 7)
- `SIGN_OFF_LETTER_LIMIT` (lexical_declaration, line 9)
- `SIGN_OFF_BANNER` (lexical_declaration, line 11, exported)
- `signOffSection` (lexical_declaration, line 21, exported)
- `lines` (lexical_declaration, line 22, exported)
- `opens` (lexical_declaration, line 23, exported)
- `first` (lexical_declaration, line 28)
- `colon` (lexical_declaration, line 33)
- `letter` (lexical_declaration, line 34)
- `signOffLines` (lexical_declaration, line 38, exported)
- `entries` (lexical_declaration, line 39, exported)

## Uses

- [tools/core/analyzers/board.analyzer.ts](https://banes-lab.com/source/coordination/tools/core/analyzers/board.analyzer.ts.md)

## Source

```typescript
import { existsSync, readFileSync } from "node:fs";
import { boardRecords } from "../analyzers/board.analyzer.ts";
import { resolve } from "node:path";

const AGENT_LABEL = "Agent ";

const SIGN_OFF_COLON_LIMIT = 4;

const SIGN_OFF_LETTER_LIMIT = 2;

export const SIGN_OFF_BANNER = "═══════════════════ SIGN-OFF";

export const textIfPresent = function textIfPresent(absolute: string): string {
    return existsSync(absolute) ? readFileSync(absolute, "utf8") : "";
};

export const surfaceText = function surfaceText(repoRoot: string, relative: string): string {
    return textIfPresent(resolve(repoRoot, relative));
};

export const signOffSection = function signOffSection(venue: string): string[] {
    const lines = venue.split("\n");
    const opens = lines.findIndex((line) => line.startsWith(SIGN_OFF_BANNER));
    return opens === -1 ? [] : lines.slice(opens + 1);
};

const isSignOffLetter = function isSignOffLetter(letter: string): boolean {
    const first = letter.charAt(0);
    return letter.length > 0 && letter.length <= SIGN_OFF_LETTER_LIMIT && first >= "A" && first <= "Z";
};

const signOffEntry = function signOffEntry(line: string): [string, string] | null {
    const colon = line.indexOf(":");
    const letter = colon <= 0 || colon > SIGN_OFF_COLON_LIMIT ? "" : line.slice(0, colon).trim();
    return isSignOffLetter(letter) ? [letter, line.slice(colon + 1).trim()] : null;
};

export const signOffLines = function signOffLines(venue: string): Map<string, string> {
    const entries = signOffSection(venue)
        .map(signOffEntry)
        .filter((entry): entry is [string, string] => entry !== null);
    return new Map(entries.toReversed());
};

export const venueRecords = function venueRecords(venue: string): Map<string, ReadonlyMap<string, string>> {
    return new Map(
        boardRecords(venue)
            .filter((record) => record.kind === "agent")
            .map((record): [string, ReadonlyMap<string, string>] => [
                record.label.slice(AGENT_LABEL.length).trim(),
                record.fields,
            ]),
    );
};
```
