# tools/core/validators/blocking.validator.ts

> 68 lines of code and 19 definitions.

Tree: Coordination tree
Language: typescript
Layer: processing
Canonical: https://banes-lab.com/anatomy/coordination#file-coordination-tools-core-validators-blocking-validator-ts
Source text: https://banes-lab.com/assets/sources/source.fb24d32f72e8446c2043360ad3207cb6d2d8babf491b6d981900c16687ab7667.generated.txt

## Definitions

- `isAddress` (lexical_declaration, line 13)
- `positionAuthors` (lexical_declaration, line 37)
- `addressAt` (lexical_declaration, line 21)
- `isDigit` (lexical_declaration, line 9)
- `startsAddress` (lexical_declaration, line 46)
- `addressesIn` (lexical_declaration, line 50)
- `headOf` (lexical_declaration, line 29)
- `unreadRoster` (lexical_declaration, line 64)
- `unreadAuthors` (lexical_declaration, line 78, exported)
- `unreadWhilePositionsStand` (lexical_declaration, line 70, exported)
- `MENTION_STATE` (lexical_declaration, line 4)
- `byAddress` (lexical_declaration, line 17)
- `cursor` (lexical_declaration, line 22)
- `end` (lexical_declaration, line 30)
- `out` (lexical_declaration, line 51)
- `mentioned` (lexical_declaration, line 52)
- `index` (lexical_declaration, line 54)
- `lines` (lexical_declaration, line 79, exported)
- `unread` (lexical_declaration, line 80, exported)

## Uses

- [tools/core/resolvers/sweep.resolver.ts](https://banes-lab.com/source/coordination/tools/core/resolvers/sweep.resolver.ts.md)

## Used by

- [tools/rules/blocking.rule.ts](https://banes-lab.com/source/coordination/tools/rules/blocking.rule.ts.md)

## Source

```typescript
import { POSITION_MARKER, UNREAD_MARKER } from "../constants/blocking.constants.ts";
import { itemSpans } from "../resolvers/sweep.resolver.ts";

const MENTION_STATE = new Map([
    ["<", true],
    [">", false],
]);

const isDigit = function isDigit(char: string): boolean {
    return char >= "0" && char <= "9";
};

const isAddress = function isAddress(char: string): boolean {
    return (char >= "A" && char <= "Z") || isDigit(char);
};

const byAddress = function byAddress(left: string, right: string): number {
    return left.localeCompare(right, "en");
};

const addressAt = function addressAt(line: string, from: number): string {
    let cursor = from;
    while (cursor < line.length && isAddress(line.charAt(cursor))) {
        cursor += 1;
    }
    return line.slice(from, cursor);
};

const headOf = function headOf(address: string): string {
    let end = 0;
    while (end < address.length && !isDigit(address.charAt(end))) {
        end += 1;
    }
    return address.slice(0, end);
};

const positionAuthors = function positionAuthors(lines: readonly string[]): Set<string> {
    return new Set(
        lines
            .filter((line) => line.startsWith(POSITION_MARKER))
            .map((line) => headOf(addressAt(line, POSITION_MARKER.length)))
            .filter((head) => head.length > 0),
    );
};

const startsAddress = function startsAddress(tail: string, index: number, mentioned: boolean): boolean {
    return !mentioned && isAddress(tail.charAt(index)) && (index === 0 || !isAddress(tail.charAt(index - 1)));
};

const addressesIn = function addressesIn(tail: string): string[] {
    const out: string[] = [];
    let mentioned = false;

    for (let index = 0; index < tail.length; index += 1) {
        mentioned = MENTION_STATE.get(tail.charAt(index)) ?? mentioned;
        if (startsAddress(tail, index, mentioned)) {
            out.push(addressAt(tail, index));
        }
    }

    return out;
};

const unreadRoster = function unreadRoster(lines: readonly string[]): Set<string> {
    return new Set(
        lines.filter((line) => line.startsWith(UNREAD_MARKER)).flatMap((line) => addressesIn(line.slice(UNREAD_MARKER.length))),
    );
};

export const unreadWhilePositionsStand = function unreadWhilePositionsStand(source: string): string[] {
    const lines = source.split("\n");
    if (positionAuthors(lines).size === 0 && itemSpans(source).length === 0) {
        return [];
    }
    return [...unreadRoster(lines)].toSorted(byAddress);
};

export const unreadAuthors = function unreadAuthors(source: string): string[] {
    const lines = source.split("\n");
    const unread = unreadRoster(lines);
    return [...positionAuthors(lines)].filter((author) => unread.has(author)).toSorted(byAddress);
};
```
