# core/persistence/report.persistence.ts

> 69 lines of code and 14 definitions.

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

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

## Definitions

- `writeHexMaster` (lexical_declaration, line 69, exported)
- `persistArtifacts` (lexical_declaration, line 22, exported)
- `writeSvgCollection` (lexical_declaration, line 40, exported)
- `staleFiles` (lexical_declaration, line 18, exported)
- `isArtifact` (lexical_declaration, line 14)
- `HEX_DIR` (lexical_declaration, line 12, exported)
- `hexDir` (lexical_declaration, line 26, exported)
- `slugOf` (lexical_declaration, line 36)
- `svgDir` (lexical_declaration, line 45, exported)
- `ran` (lexical_declaration, line 46, exported)
- `written` (lexical_declaration, line 47, exported)
- `pruned` (lexical_declaration, line 56, exported)
- `slug` (lexical_declaration, line 57, exported)
- `found` (lexical_declaration, line 70, exported)

## Contained in

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

## Uses

- [configuration/strings/report.strings.ts](https://banes-lab.com/source/patterns/configuration/strings/report.strings.ts.md)
- [core/formatters/report.formatter.ts](https://banes-lab.com/source/patterns/core/formatters/report.formatter.ts.md)

## Used by

- [core/coordinators/report.coordinator.ts](https://banes-lab.com/source/patterns/core/coordinators/report.coordinator.ts.md)
- [runtime/entrypoints/report.entrypoint.ts](https://banes-lab.com/source/patterns/runtime/entrypoints/report.entrypoint.ts.md)

## Linked from

- [configuration/strings](https://banes-lab.com/anatomy/patterns/folder-patterns-configuration-strings.md)
- [core/coordinators](https://banes-lab.com/anatomy/patterns/folder-patterns-core-coordinators.md)
- [core/formatters](https://banes-lab.com/anatomy/patterns/folder-patterns-core-formatters.md)
- [core/persistence](https://banes-lab.com/anatomy/patterns/folder-patterns-core-persistence.md)
- [runtime/entrypoints](https://banes-lab.com/anatomy/patterns/folder-patterns-runtime-entrypoints.md)

## Source

```typescript
import { ARTIFACT_SUFFIXES, SLUG_JOIN, SVG_SUFFIX } from "#configuration/constants/report.constants";
import type { ModuleFindings, ModuleSvgs, ModuleTitle } from "#types/report.types";
import { dirname, join } from "node:path";
import { existsSync, mkdirSync, readdirSync, unlinkSync } from "node:fs";
import { masterWritten, svgCollection } from "#configuration/strings/report.strings";
import { buildMasterFindings } from "#core/formatters/report.formatter";
import { loadModuleFindings } from "#core/loaders/finding.loader";
import process from "node:process";
import { relativePath } from "@ssot/paths";
import { writeVerbatim } from "@govlab/canonical-write";

export const HEX_DIR = relativePath("moduleInfo.root");

const isArtifact = function isArtifact(name: string): boolean {
    return ARTIFACT_SUFFIXES.some((suffix) => name.endsWith(suffix));
};

export const staleFiles = function staleFiles(hexDir: string, keep: ReadonlySet<string>): string[] {
    return (existsSync(hexDir) ? readdirSync(hexDir) : []).filter((name) => isArtifact(name) && !keep.has(name));
};

export const persistArtifacts = function persistArtifacts(
    moduleDir: string,
    artifacts: ReadonlyMap<string, string>,
): void {
    const hexDir = join(moduleDir, HEX_DIR);
    mkdirSync(hexDir, { recursive: true });
    for (const name of staleFiles(hexDir, new Set(artifacts.keys()))) {
        unlinkSync(join(hexDir, name));
    }
    for (const [name, content] of artifacts) {
        writeVerbatim(join(hexDir, name), `${content}\n`);
    }
};

const slugOf = function slugOf(fileName: string): string {
    return fileName.slice(0, Math.max(0, fileName.indexOf(SLUG_JOIN)));
};

export const writeSvgCollection = function writeSvgCollection(
    rootDir: string,
    units: readonly ModuleSvgs[],
    known: ReadonlySet<string>,
): void {
    const svgDir = join(rootDir, relativePath("moduleInfo.svg"));
    const ran = new Set(units.map((unit) => unit.slug));
    const written = new Map(
        units.flatMap((unit) =>
            [...unit.svgs].map(([page, markup]): [string, string] => [
                `${unit.slug}${SLUG_JOIN}${page}${SVG_SUFFIX}`,
                markup,
            ]),
        ),
    );
    mkdirSync(svgDir, { recursive: true });
    const pruned = readdirSync(svgDir).filter((name) => {
        const slug = slugOf(name);
        return name.endsWith(SVG_SUFFIX) && !written.has(name) && (ran.has(slug) || !known.has(slug));
    });
    for (const name of pruned) {
        unlinkSync(join(svgDir, name));
    }
    for (const [name, markup] of written) {
        writeVerbatim(join(svgDir, name), `${markup}\n`);
    }
    process.stdout.write(svgCollection(written.size, pruned.length, svgDir));
};

export const writeHexMaster = function writeHexMaster(masterPath: string, units: readonly ModuleTitle[]): void {
    const found = units
        .map((unit) => loadModuleFindings(unit.dir, unit.title))
        .filter((entry): entry is ModuleFindings => entry !== null);
    mkdirSync(dirname(masterPath), { recursive: true });
    writeVerbatim(masterPath, `${buildMasterFindings(found)}\n`);
    process.stdout.write(masterWritten(masterPath));
};
```
