# core/converters/report.converter.ts

> 56 lines of code and 11 definitions.

Tree: GovLab Pipeline
Language: typescript
Layer: processing
Canonical: https://banes-lab.com/anatomy/pipeline#file-pipeline-core-converters-report-converter-ts
Source text: https://banes-lab.com/source/pipeline/core/converters/report.converter.ts.txt

Listed in [core/converters](https://banes-lab.com/api/source/pipeline/core/converters.md), after [core/converters/invocation.converter.ts](https://banes-lab.com/source/pipeline/core/converters/invocation.converter.ts.md) and before [core/converters/stage.converter.ts](https://banes-lab.com/source/pipeline/core/converters/stage.converter.ts.md).

## Definitions

- `rowOf` (lexical_declaration, line 7, exported)
- `buildArtifact` (lexical_declaration, line 48, exported)
- `outputOf` (lexical_declaration, line 11, exported)
- `tallyByStage` (lexical_declaration, line 21, exported)
- `stageArtifacts` (lexical_declaration, line 34)
- `rowsOf` (lexical_declaration, line 15, exported)
- `tallies` (lexical_declaration, line 22, exported)
- `entry` (lexical_declaration, line 24, exported)
- `order` (lexical_declaration, line 35)
- `stageRows` (lexical_declaration, line 37)
- `failed` (lexical_declaration, line 49, exported)

## Contained in

- [core/converters](https://banes-lab.com/anatomy/pipeline/folder-pipeline-core-converters.md)

## Uses

- [core/selectors/violation.selector.ts](https://banes-lab.com/source/pipeline/core/selectors/violation.selector.ts.md)

## Used by

- [core/coordinators/report.coordinator.ts](https://banes-lab.com/source/pipeline/core/coordinators/report.coordinator.ts.md)
- [core/coordinators/stage.coordinator.ts](https://banes-lab.com/source/pipeline/core/coordinators/stage.coordinator.ts.md)
- [core/reporters/stage.reporter.ts](https://banes-lab.com/source/pipeline/core/reporters/stage.reporter.ts.md)

## Linked from

- [core/converters](https://banes-lab.com/anatomy/pipeline/folder-pipeline-core-converters.md)
- [core/coordinators](https://banes-lab.com/anatomy/pipeline/folder-pipeline-core-coordinators.md)
- [core/reporters](https://banes-lab.com/anatomy/pipeline/folder-pipeline-core-reporters.md)
- [core/selectors](https://banes-lab.com/anatomy/pipeline/folder-pipeline-core-selectors.md)

## Source

```typescript
import type { ReportArtifact, ReportRow, StageArtifact } from "#types/stage.types";
import type { StageTally, StepOutput } from "#types/report.types";
import type { CapturedOutcome } from "#types/shell.types";
import { FALLBACK_EXIT } from "#configuration/constants/shell.constants";
import { lastCount } from "#core/selectors/violation.selector";

export const rowOf = function rowOf(stage: string, label: string, outcome: CapturedOutcome): ReportRow {
    return { code: outcome.code, count: lastCount(outcome.out), label, out: outcome.out, stage };
};

export const outputOf = function outputOf(row: ReportRow): StepOutput {
    return { label: row.label, ok: row.code === 0, out: row.out, stage: row.stage };
};

export const rowsOf = function rowsOf(outputs: readonly StepOutput[]): ReportRow[] {
    return outputs.map((entry) =>
        rowOf(entry.stage, entry.label, { code: entry.ok ? 0 : FALLBACK_EXIT, out: entry.out }),
    );
};

export const tallyByStage = function tallyByStage(rows: readonly ReportRow[]): Map<string, StageTally> {
    const tallies = new Map<string, StageTally>();
    for (const row of rows) {
        const entry = tallies.get(row.stage) ?? { failed: 0, passed: 0, violations: 0 };
        tallies.set(row.stage, {
            failed: entry.failed + (row.code === 0 ? 0 : 1),
            passed: entry.passed + (row.code === 0 ? 1 : 0),
            violations: entry.violations + (row.count ?? 0),
        });
    }
    return tallies;
};

const stageArtifacts = function stageArtifacts(rows: readonly ReportRow[]): StageArtifact[] {
    const order = [...new Set(rows.map((row) => row.stage))];
    return order.map((stage) => {
        const stageRows = rows.filter((row) => row.stage === stage);
        return {
            failed: stageRows.filter((row) => row.code !== 0).length,
            passed: stageRows.filter((row) => row.code === 0).length,
            stage,
            steps: stageRows.map((row) => ({ label: row.label, ok: row.code === 0, violations: row.count })),
            violations: stageRows.reduce((sum, row) => sum + (row.count ?? 0), 0),
        };
    });
};

export const buildArtifact = function buildArtifact(rows: readonly ReportRow[], label: string): ReportArtifact {
    const failed = rows.filter((row) => row.code !== 0).length;
    return {
        generatedAt: new Date().toISOString(),
        label,
        ok: failed === 0,
        stages: stageArtifacts(rows),
        totals: {
            failed,
            passed: rows.length - failed,
            steps: rows.length,
            violations: rows.reduce((sum, row) => sum + (row.count ?? 0), 0),
        },
    };
};
```
