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

> 32 lines of code and 17 definitions.

Tree: Coordination tree
Language: typescript
Canonical: https://banes-lab.com/anatomy/coordination#file-coordination-tools-core-readers-board-reader-ts
Source text: https://banes-lab.com/assets/sources/source.54ab0610c590e807bd4cb20048bb3c9cb34203aa73969c8e6ca806105b3345ba.generated.txt

## Definitions

- `firstFields` (lexical_declaration, line 29)
- `fieldsAfter` (lexical_declaration, line 23)
- `readBoardContract` (lexical_declaration, line 37, exported)
- `BoardContract` (interface_declaration, line 1, exported)
- `AGENT_ANCHOR` (lexical_declaration, line 6)
- `GATE_ANCHOR` (lexical_declaration, line 8)
- `PLACEHOLDER` (lexical_declaration, line 10)
- `EMPTY` (lexical_declaration, line 12)
- `fieldKey` (lexical_declaration, line 14)
- `line` (lexical_declaration, line 15)
- `colon` (lexical_declaration, line 16)
- `key` (lexical_declaration, line 17)
- `value` (lexical_declaration, line 18)
- `placeholder` (lexical_declaration, line 19)
- `keys` (lexical_declaration, line 24)
- `end` (lexical_declaration, line 25)
- `lines` (lexical_declaration, line 38, exported)

## Source

```typescript
export interface BoardContract {
    readonly agentFields: readonly string[];
    readonly gateFields: readonly string[];
}

const AGENT_ANCHOR = "Agent <";

const GATE_ANCHOR = "Gate ";

const PLACEHOLDER = "<";

const EMPTY = "—";

const fieldKey = function fieldKey(raw: string): string | null {
    const line = raw.trim();
    const colon = line.indexOf(":");
    const key = colon <= 0 ? "" : line.slice(0, colon).trim();
    const value = line.slice(colon + 1).trim();
    const placeholder = value.startsWith(PLACEHOLDER) || value.startsWith(EMPTY);
    return key.length === 0 || key.includes(" ") || !placeholder ? null : key;
};

const fieldsAfter = function fieldsAfter(lines: readonly string[], from: number): string[] {
    const keys = lines.slice(from).map(fieldKey);
    const end = keys.indexOf(null);
    return (end === -1 ? keys : keys.slice(0, end)).filter((key): key is string => key !== null);
};

const firstFields = function firstFields(lines: readonly string[], anchor: string): string[] {
    return (
        lines
            .map((line, index) => (line.trim().startsWith(anchor) ? fieldsAfter(lines, index + 1) : []))
            .find((fields) => fields.length > 0) ?? []
    );
};

export const readBoardContract = function readBoardContract(template: string): BoardContract {
    const lines = template.split("\n");
    return { agentFields: firstFields(lines, AGENT_ANCHOR), gateFields: firstFields(lines, GATE_ANCHOR) };
};
```
