# tools/core/generators/venue.generator.ts

> 85 lines of code and 27 definitions.

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

Listed in [tools/core/generators](https://banes-lab.com/api/source/coordination/tools/core/generators.md), after [tools/core/generators/fixture.generator.ts](https://banes-lab.com/source/coordination/tools/core/generators/fixture.generator.ts.md).

## Definitions

- `deferringLabel` (lexical_declaration, line 46, exported)
- `inheritedBlock` (lexical_declaration, line 30)
- `unclaimedBlock` (lexical_declaration, line 36)
- `venueEntries` (lexical_declaration, line 50)
- `blockFor` (lexical_declaration, line 40, exported)
- `siblingVenues` (lexical_declaration, line 78, exported)
- `DEFERRED_HEADING` (lexical_declaration, line 8)
- `DIGITS` (lexical_declaration, line 10)
- `RECORD_OPEN` (lexical_declaration, line 12)
- `RECORD_CLOSE_MARKER` (lexical_declaration, line 14)
- `LETTER_SLOT` (lexical_declaration, line 16)
- `STATE_SLOT` (lexical_declaration, line 18)
- `PLACEHOLDER_OPEN` (lexical_declaration, line 20)
- `ordinalOf` (lexical_declaration, line 22, exported)
- `ordinal` (lexical_declaration, line 31)
- `carried` (lexical_declaration, line 32)
- `deferringVenues` (lexical_declaration, line 61, exported)
- `found` (lexical_declaration, line 66, exported)
- `recordSpecimen` (lexical_declaration, line 82, exported)
- `lines` (lexical_declaration, line 83, exported)
- `opens` (lexical_declaration, line 84, exported)
- `open` (lexical_declaration, line 89, exported)
- `indent` (lexical_declaration, line 90, exported)
- `closes` (lexical_declaration, line 92, exported)
- `seatRecord` (lexical_declaration, line 100, exported)
- `named` (lexical_declaration, line 102, exported)
- `cut` (lexical_declaration, line 103, exported)

## Uses

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

## Used by

- [tools/core/runners/delivery.runner.ts](https://banes-lab.com/source/coordination/tools/core/runners/delivery.runner.ts.md)
- [tools/core/runners/venue.runner.ts](https://banes-lab.com/source/coordination/tools/core/runners/venue.runner.ts.md)

## Source

```typescript
import { BLOCKING_SUFFIX, INHERITED_BANNER, RECORD_ABSENT, VENUE_ARCHIVE } from "../constants/blocking.constants.ts";
import { NO_VENUE_DEFERRING, inheritedSection, unclaimedSection } from "../strings/venue.strings.ts";
import { basename, resolve } from "node:path";
import { existsSync, readFileSync, readdirSync } from "node:fs";
import type { Deferring } from "../types/venue.types.ts";
import { clauseLines } from "../analyzers/converge.analyzer.ts";

const DEFERRED_HEADING = "## DEFERRED";

const DIGITS = new Set("0123456789");

const RECORD_OPEN = "┌─── AGENT ";

const RECORD_CLOSE_MARKER = "└─── END AGENT ";

const LETTER_SLOT = "<letter>";

const STATE_SLOT = "<ACTIVE | INACTIVE>";

const PLACEHOLDER_OPEN = "<";

export const ordinalOf = function ordinalOf(name: string): string {
    return (
        basename(name)
            .split(".")
            .find((part) => part.length > 0 && DIGITS.has(part.charAt(0))) ?? ""
    );
};

const inheritedBlock = function inheritedBlock(predecessorName: string, clauses: readonly string[]): string {
    const ordinal = ordinalOf(predecessorName);
    const carried = clauses.map((clause) => `- ${clause}`).join("\n");
    return inheritedSection(INHERITED_BANNER, ordinal.length === 0 ? predecessorName : ordinal, carried);
};

const unclaimedBlock = function unclaimedBlock(invariant: string): string {
    return unclaimedSection(INHERITED_BANNER, invariant, RECORD_ABSENT);
};

export const blockFor = function blockFor(invariant: string, deferring: Deferring): string {
    return deferring.clauses.length === 0
        ? unclaimedBlock(invariant)
        : inheritedBlock(deferring.names.join(", "), deferring.clauses);
};

export const deferringLabel = function deferringLabel(deferring: Deferring): string {
    return deferring.names.length === 0 ? NO_VENUE_DEFERRING : deferring.names.join(", ");
};

const venueEntries = function venueEntries(roots: readonly string[]): { root: string; entry: string }[] {
    return roots
        .filter((root) => existsSync(root))
        .flatMap((root) =>
            readdirSync(root)
                .toSorted((left, right) => left.localeCompare(right))
                .filter((entry) => entry.endsWith(BLOCKING_SUFFIX))
                .map((entry) => ({ entry, root })),
        );
};

export const deferringVenues = function deferringVenues(
    directory: string,
    repoRoot: string,
    invariant: string,
): Deferring {
    const found = venueEntries([directory, resolve(repoRoot, VENUE_ARCHIVE)])
        .map(({ entry, root }) => ({
            carried: clauseLines(readFileSync(resolve(root, entry), "utf8"), DEFERRED_HEADING)
                .filter((line) => line.receiver === invariant)
                .map((line) => line.clause),
            entry,
        }))
        .filter((venue) => venue.carried.length > 0);

    return { clauses: found.flatMap((venue) => venue.carried), names: found.map((venue) => venue.entry) };
};

export const siblingVenues = function siblingVenues(directory: string, repoRoot: string): string[] {
    return venueEntries([directory, resolve(repoRoot, VENUE_ARCHIVE)]).map(({ entry }) => entry);
};

export const recordSpecimen = function recordSpecimen(template: string): string[] {
    const lines = template.split("\n");
    const opens = lines.findIndex((line) => line.trimStart().startsWith(RECORD_OPEN) && line.includes(LETTER_SLOT));
    if (opens === -1) {
        return [];
    }

    const open = lines[opens] ?? "";
    const indent = open.slice(0, open.length - open.trimStart().length);

    const closes = lines.findIndex((line, index) => index >= opens && line.trimStart().startsWith(RECORD_CLOSE_MARKER));
    if (closes === -1) {
        return [];
    }

    return lines.slice(opens, closes + 1).map((line) => (line.startsWith(indent) ? line.slice(indent.length) : line));
};

export const seatRecord = function seatRecord(specimen: readonly string[], letter: string): string[] {
    return specimen.map((line) => {
        const named = line.split(LETTER_SLOT).join(letter).split(STATE_SLOT).join("ACTIVE");
        const cut = named.indexOf(PLACEHOLDER_OPEN);
        return cut === -1 ? named : named.slice(0, cut).trimEnd();
    });
};
```
