# tools/core/transformers/agenda.transformer.ts

> 45 lines of code and 11 definitions.

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

Listed in [tools/core/transformers](https://banes-lab.com/api/source/coordination/tools/core/transformers.md), before [tools/core/transformers/board.transformer.ts](https://banes-lab.com/source/coordination/tools/core/transformers/board.transformer.ts.md).

## Definitions

- `healSchedule` (lexical_declaration, line 27, exported)
- `driftedRows` (lexical_declaration, line 42, exported)
- `ROW_MARKER` (lexical_declaration, line 6)
- `TableBounds` (interface_declaration, line 8)
- `scheduleBounds` (lexical_declaration, line 13, exported)
- `from` (lexical_declaration, line 14, exported)
- `to` (lexical_declaration, line 19, exported)
- `lines` (lexical_declaration, line 28, exported)
- `held` (lexical_declaration, line 34, exported)
- `bounds` (lexical_declaration, line 46, exported)
- `rendered` (lexical_declaration, line 51, exported)

## Uses

- [tools/core/normalizers/document.normalizer.ts](https://banes-lab.com/source/coordination/tools/core/normalizers/document.normalizer.ts.md)

## Source

```typescript
import { SCHEDULE_HEADER } from "../constants/agenda.constants.ts";
import type { ScheduleReading } from "../types/agenda.types.ts";
import { renderSchedule } from "../formatters/agenda.formatter.ts";
import { sameRow } from "../normalizers/document.normalizer.ts";

const ROW_MARKER = "|";

interface TableBounds {
    readonly from: number;
    readonly to: number;
}

export const scheduleBounds = function scheduleBounds(lines: readonly string[]): TableBounds | null {
    const from = lines.findIndex((line) => sameRow(line, SCHEDULE_HEADER));
    if (from === -1) {
        return null;
    }

    let to = from;
    while (to + 1 < lines.length && (lines[to + 1] ?? "").trim().startsWith(ROW_MARKER)) {
        to += 1;
    }

    return { from, to };
};

export const healSchedule = function healSchedule(source: string, rendered: readonly string[]): string | null {
    const lines = source.split("\n");
    const bounds = scheduleBounds(lines);
    if (bounds === null) {
        return null;
    }

    const held = lines.slice(bounds.from, bounds.to + 1);
    if (held.length === rendered.length && held.every((line, index) => sameRow(line, rendered[index] ?? ""))) {
        return null;
    }

    return [...lines.slice(0, bounds.from), ...rendered, ...lines.slice(bounds.to + 1)].join("\n");
};

export const driftedRows = function driftedRows(
    lines: readonly string[],
    readings: readonly ScheduleReading[],
): ScheduleReading[] {
    const bounds = scheduleBounds(lines);
    if (bounds === null) {
        return [];
    }

    const rendered = renderSchedule(readings);
    return readings.filter(
        (_reading, index) => !sameRow(lines[bounds.from + 2 + index] ?? "", rendered[index + 2] ?? ""),
    );
};
```
