# core/validators/document.validator.ts

> 48 lines of code and 14 definitions.

Tree: GovLab Docs
Language: typescript
Layer: processing
Canonical: https://banes-lab.com/anatomy/docs#file-docs-core-validators-document-validator-ts
Source text: https://banes-lab.com/source/docs/core/validators/document.validator.ts.txt

Listed in [core/validators](https://banes-lab.com/api/source/docs/core/validators.md), after [core/validators/diagram.validator.ts](https://banes-lab.com/source/docs/core/validators/diagram.validator.ts.md) and before [core/validators/figure.validator.ts](https://banes-lab.com/source/docs/core/validators/figure.validator.ts.md).

## Definitions

- `validateSpine` (lexical_declaration, line 40, exported)
- `missingFields` (lexical_declaration, line 31)
- `TITLE_PREFIX` (lexical_declaration, line 6)
- `SETEXT_CHAR` (lexical_declaration, line 7)
- `isSetextUnderline` (lexical_declaration, line 9)
- `titleDefect` (lexical_declaration, line 18)
- `lines` (lexical_declaration, line 19)
- `at` (lexical_declaration, line 20)
- `first` (lexical_declaration, line 24)
- `setext` (lexical_declaration, line 25)
- `{ requireFrontmatter = true, requireTitle = true }` (lexical_declaration, line 45, exported)
- `frontmatter` (lexical_declaration, line 46, exported)
- `fieldDefects` (lexical_declaration, line 50, exported)
- `title` (lexical_declaration, line 51, exported)

## Contained in

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

## Uses

- [configuration/strings/document.strings.ts](https://banes-lab.com/source/docs/configuration/strings/document.strings.ts.md)

## Used by

- [core/analyzers/document.analyzer.ts](https://banes-lab.com/source/docs/core/analyzers/document.analyzer.ts.md)

## Linked from

- [configuration/strings](https://banes-lab.com/anatomy/docs/folder-docs-configuration-strings.md)
- [core/analyzers](https://banes-lab.com/anatomy/docs/folder-docs-core-analyzers.md)

## Source

```typescript
import { SPINE_DETAILS, missingFrontmatterField } from "#configuration/strings/document.strings";
import type { SpineDefect, SpineOptions } from "#types/document.types";
import { parseFrontmatter } from "#core/parsers/metadata.parser";
import { splitLines } from "#core/parsers/markdown.parser";

const TITLE_PREFIX = "# ";
const SETEXT_CHAR = "=";

const isSetextUnderline = function isSetextUnderline(line: string): boolean {
    for (const char of line) {
        if (char !== SETEXT_CHAR) {
            return false;
        }
    }
    return line.length > 0;
};

const titleDefect = function titleDefect(source: string, bodyStart: number): SpineDefect | null {
    const lines = splitLines(source);
    let at = bodyStart;
    while (at < lines.length && (lines[at] ?? "").trim() === "") {
        at += 1;
    }
    const first = lines[at] ?? "";
    const setext = first.trim().length > 0 && isSetextUnderline((lines[at + 1] ?? "").trim());
    return first.startsWith(TITLE_PREFIX) || setext
        ? null
        : { code: "no-title", detail: SPINE_DETAILS.noTitle, line: at + 1 };
};

const missingFields = function missingFields(
    fields: Record<string, string>,
    requiredKeys: readonly string[],
): SpineDefect[] {
    return requiredKeys
        .filter((key) => (fields[key] ?? "") === "")
        .map((key): SpineDefect => ({ code: "missing-field", detail: missingFrontmatterField(key), line: 1 }));
};

export const validateSpine = function validateSpine(
    source: string,
    requiredKeys: readonly string[],
    options: SpineOptions = {},
): SpineDefect[] {
    const { requireFrontmatter = true, requireTitle = true } = options;
    const frontmatter = parseFrontmatter(source);
    if (!frontmatter.present && requireFrontmatter) {
        return [{ code: "no-frontmatter", detail: SPINE_DETAILS.noFrontmatter, line: 1 }];
    }
    const fieldDefects = frontmatter.present ? missingFields(frontmatter.fields, requiredKeys) : [];
    const title = requireTitle ? titleDefect(source, frontmatter.bodyStart) : null;
    return title === null ? fieldDefects : [...fieldDefects, title];
};
```
