# core/pipelines/catalog.pipeline.ts

> 68 lines of code and 14 definitions.

Tree: GovLab Quality
Language: typescript
Layer: processing
Canonical: https://banes-lab.com/anatomy/quality#file-quality-core-pipelines-catalog-pipeline-ts
Source text: https://banes-lab.com/source/quality/core/pipelines/catalog.pipeline.ts.txt

Listed in [core/pipelines](https://banes-lab.com/api/source/quality/core/pipelines.md).

## Definitions

- `orderSteps` (lexical_declaration, line 52, exported)
- `discoverSteps` (lexical_declaration, line 58, exported)
- `runSteps` (lexical_declaration, line 63, exported)
- `refuseDuplicateGivers` (lexical_declaration, line 9)
- `refuseMissing` (lexical_declaration, line 22)
- `orderFrom` (lexical_declaration, line 32)
- `STEP_SUFFIX` (lexical_declaration, line 7)
- `givers` (lexical_declaration, line 10)
- `first` (lexical_declaration, line 13)
- `missing` (lexical_declaration, line 25)
- `ready` (lexical_declaration, line 39)
- `state` (lexical_declaration, line 69, exported)
- `started` (lexical_declaration, line 70, exported)
- `given` (lexical_declaration, line 71, exported)

## Contained in

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

## Uses

- [configuration/strings/catalog.strings.ts](https://banes-lab.com/source/quality/configuration/strings/catalog.strings.ts.md)
- [core/loaders/folder.loader.ts](https://banes-lab.com/source/quality/core/loaders/folder.loader.ts.md)
- [core/registries/step.registry.ts](https://banes-lab.com/source/quality/core/registries/step.registry.ts.md)

## Used by

- [core/coordinators/catalog.coordinator.ts](https://banes-lab.com/source/quality/core/coordinators/catalog.coordinator.ts.md)

## Linked from

- [configuration/strings](https://banes-lab.com/anatomy/quality/folder-quality-configuration-strings.md)
- [core/coordinators](https://banes-lab.com/anatomy/quality/folder-quality-core-coordinators.md)
- [core/loaders](https://banes-lab.com/anatomy/quality/folder-quality-core-loaders.md)
- [core/pipelines](https://banes-lab.com/anatomy/quality/folder-quality-core-pipelines.md)
- [core/registries](https://banes-lab.com/anatomy/quality/folder-quality-core-registries.md)

## Source

```typescript
import type { CatalogKey, CatalogState, CatalogStep, CatalogWriter } from "#types/catalog.types";
import { duplicateGiver, missingNeed, stepLine, stuckSteps } from "#configuration/strings/catalog.strings";
import { importFolder } from "#core/loaders/folder.loader";
import { performance } from "node:perf_hooks";
import { registeredSteps } from "#core/registries/step.registry";

const STEP_SUFFIX = ".step.ts";

const refuseDuplicateGivers = function refuseDuplicateGivers(steps: readonly CatalogStep[]): void {
    const givers = new Map<CatalogKey, string>();
    for (const step of steps) {
        for (const key of step.gives) {
            const first = givers.get(key);
            if (first !== undefined) {
                throw new Error(duplicateGiver(key, first, step.name));
            }
            givers.set(key, step.name);
        }
    }
};

const refuseMissing = function refuseMissing(steps: readonly CatalogStep[]): void {
    const given = new Set(steps.flatMap((step) => step.gives));
    for (const step of steps) {
        const missing = step.needs.find((key) => !given.has(key));
        if (missing !== undefined) {
            throw new Error(missingNeed(step.name, missing));
        }
    }
};

const orderFrom = function orderFrom(
    pending: readonly CatalogStep[],
    available: ReadonlySet<CatalogKey>,
): CatalogStep[] {
    if (pending.length === 0) {
        return [];
    }
    const ready = pending.find((step) => step.needs.every((key) => available.has(key)));
    if (ready === undefined) {
        throw new Error(stuckSteps(pending.map((step) => step.name)));
    }
    return [
        ready,
        ...orderFrom(
            pending.filter((step) => step !== ready),
            new Set([...available, ...ready.gives]),
        ),
    ];
};

export const orderSteps = function orderSteps(steps: readonly CatalogStep[]): CatalogStep[] {
    refuseDuplicateGivers(steps);
    refuseMissing(steps);
    return orderFrom(steps, new Set());
};

export const discoverSteps = async function discoverSteps(): Promise<CatalogStep[]> {
    await importFolder("govlab.quality.steps", STEP_SUFFIX);
    return registeredSteps();
};

export const runSteps = async function runSteps(
    steps: readonly CatalogStep[],
    writer: CatalogWriter,
    write: (line: string) => void,
): Promise<CatalogState> {
    return orderSteps(steps).reduce(async (previous, step) => {
        const state = await previous;
        const started = performance.now();
        const given = await step.run(state, writer);
        write(stepLine(step.name, performance.now() - started));
        return { ...state, ...given };
    }, Promise.resolve<CatalogState>({}));
};
```
