# core/loaders/finding.loader.ts

> 53 lines of code and 9 definitions.

Tree: GovLab Patterns
Language: typescript
Layer: infrastructure
Canonical: https://banes-lab.com/anatomy/patterns#file-patterns-core-loaders-finding-loader-ts
Source text: https://banes-lab.com/source/patterns/core/loaders/finding.loader.ts.txt

Listed in [core/loaders](https://banes-lab.com/api/source/patterns/core/loaders.md), before [core/loaders/package.loader.ts](https://banes-lab.com/source/patterns/core/loaders/package.loader.ts.md).

## Definitions

- `stringOf` (lexical_declaration, line 8)
- `numberOf` (lexical_declaration, line 12)
- `stringsOf` (lexical_declaration, line 16)
- `findingsPathOf` (lexical_declaration, line 40, exported)
- `asCodeFinding` (lexical_declaration, line 20)
- `loadModuleFindings` (lexical_declaration, line 44, exported)
- `path` (lexical_declaration, line 45, exported)
- `parsed` (lexical_declaration, line 50, exported)
- `raw` (lexical_declaration, line 51, exported)

## Contained in

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

## Uses

- [core/predicates/record.predicate.ts](https://banes-lab.com/source/patterns/core/predicates/record.predicate.ts.md)

## Used by

- [core/caches/report.cache.ts](https://banes-lab.com/source/patterns/core/caches/report.cache.ts.md)

## Linked from

- [core/caches](https://banes-lab.com/anatomy/patterns/folder-patterns-core-caches.md)
- [core/loaders](https://banes-lab.com/anatomy/patterns/folder-patterns-core-loaders.md)
- [core/predicates](https://banes-lab.com/anatomy/patterns/folder-patterns-core-predicates.md)

## Source

```typescript
import { existsSync, readFileSync } from "node:fs";
import type { CodeFinding } from "#types/code.types";
import type { ModuleFindings } from "#types/report.types";
import { isRecord } from "#core/predicates/record.predicate";
import { join } from "node:path";
import { relativePath } from "@ssot/paths";

const stringOf = function stringOf(value: unknown): string {
    return typeof value === "string" ? value : "";
};

const numberOf = function numberOf(value: unknown): number {
    return typeof value === "number" ? value : 0;
};

const stringsOf = function stringsOf(value: unknown): string[] {
    return Array.isArray(value) ? value.filter((item): item is string => typeof item === "string") : [];
};

const asCodeFinding = function asCodeFinding(value: unknown): CodeFinding[] {
    if (!isRecord(value)) {
        return [];
    }
    return [
        {
            confidence: stringOf(value["confidence"]),
            detail: stringOf(value["detail"]),
            file: stringOf(value["file"]),
            kind: stringOf(value["kind"]),
            line: numberOf(value["line"]),
            members: stringsOf(value["members"]),
            name: stringOf(value["name"]),
            relevance: numberOf(value["relevance"]),
            remedy: stringOf(value["remedy"]),
            severity: stringOf(value["severity"]),
        },
    ];
};

export const findingsPathOf = function findingsPathOf(moduleDir: string): string {
    return join(moduleDir, relativePath("moduleInfo.findings"));
};

export const loadModuleFindings = function loadModuleFindings(moduleDir: string, title: string): ModuleFindings | null {
    const path = findingsPathOf(moduleDir);
    if (!existsSync(path)) {
        return null;
    }
    try {
        const parsed: unknown = JSON.parse(readFileSync(path, "utf8"));
        const raw: unknown[] = isRecord(parsed) && Array.isArray(parsed["findings"]) ? parsed["findings"] : [];
        return { findings: raw.flatMap(asCodeFinding), module: title };
    } catch (error) {
        if (!(error instanceof SyntaxError)) {
            throw error;
        }
        return null;
    }
};
```
