# core/validators/catalog.walk.validator.ts

> 42 lines of code and 12 definitions.

Tree: Bane's Lab Build Scripts
Language: typescript
Layer: processing
Canonical: https://banes-lab.com/anatomy/build-scripts#file-build-scripts-core-validators-catalog-walk-validator-ts
Source text: https://banes-lab.com/source/build-scripts/core/validators/catalog.walk.validator.ts.txt

Listed in [core/validators](https://banes-lab.com/api/source/build-scripts/core/validators.md), after [core/validators/catalog.validator.ts](https://banes-lab.com/source/build-scripts/core/validators/catalog.validator.ts.md) and before [core/validators/definition.validator.ts](https://banes-lab.com/source/build-scripts/core/validators/definition.validator.ts.md).

## Definitions

- `walkFindings` (lexical_declaration, line 36, exported)
- `jsonAt` (lexical_declaration, line 11)
- `relationNamesIn` (lexical_declaration, line 15)
- `RELATIONS_KEY` (lexical_declaration, line 8)
- `RELATION_KEY` (lexical_declaration, line 9)
- `groups` (lexical_declaration, line 16)
- `name` (lexical_declaration, line 21)
- `declaredRelations` (lexical_declaration, line 26)
- `queryFile` (lexical_declaration, line 30)
- `contract` (lexical_declaration, line 31)
- `listed` (lexical_declaration, line 32)
- `declared` (lexical_declaration, line 41, exported)

## Contained in

- [core/validators](https://banes-lab.com/anatomy/build-scripts/folder-build-scripts-core-validators.md)

## Used by

- [core/validators/catalog.validator.ts](https://banes-lab.com/source/build-scripts/core/validators/catalog.validator.ts.md)

## Linked from

- [core/validators](https://banes-lab.com/anatomy/build-scripts/folder-build-scripts-core-validators.md)

## Source

```typescript
import { fileOfAddress, queryIndex } from "#core/resolvers/catalog.resolver";
import type { Finding } from "#types/validation.types";
import { isRecord } from "#core/selectors/base.selector";
import { join } from "node:path";
import { readFileSync } from "node:fs";
import { undeclaredRelation } from "#configuration/strings/catalog.strings";

const RELATIONS_KEY = "relations";
const RELATION_KEY = "relation";

const jsonAt = function jsonAt(outDir: string, file: string): unknown {
    return JSON.parse(readFileSync(join(outDir, file), "utf8"));
};

const relationNamesIn = function relationNamesIn(value: unknown): readonly string[] {
    const groups = isRecord(value) ? value[RELATIONS_KEY] : undefined;
    if (!Array.isArray(groups)) {
        return [];
    }
    return groups.flatMap((group: unknown) => {
        const name = isRecord(group) ? group[RELATION_KEY] : undefined;
        return typeof name === "string" ? [name] : [];
    });
};

const declaredRelations = function declaredRelations(
    outDir: string,
    present: ReadonlySet<string>,
): ReadonlySet<string> {
    const queryFile = fileOfAddress(queryIndex().json);
    const contract: unknown = present.has(queryFile) ? jsonAt(outDir, queryFile) : null;
    const listed = isRecord(contract) ? contract[RELATIONS_KEY] : undefined;
    return new Set(Array.isArray(listed) ? listed.filter((name) => typeof name === "string") : []);
};

export const walkFindings = function walkFindings(
    outDir: string,
    files: readonly string[],
    present: ReadonlySet<string>,
): Finding[] {
    const declared = declaredRelations(outDir, present);
    return files.flatMap((file) =>
        [...new Set(relationNamesIn(jsonAt(outDir, file)))]
            .filter((name) => !declared.has(name))
            .map((name) => ({ file, message: undeclaredRelation(name) })),
    );
};
```
