# tools/core/readers/snapshot.reader.ts

> 53 lines of code and 14 definitions.

Tree: Coordination tree
Language: typescript
Layer: processing
Canonical: https://banes-lab.com/anatomy/coordination#file-coordination-tools-core-readers-snapshot-reader-ts
Source text: https://banes-lab.com/source/coordination/tools/core/readers/snapshot.reader.ts.txt

Listed in [tools/core/readers](https://banes-lab.com/api/source/coordination/tools/core/readers.md), after [tools/core/readers/rule.reader.ts](https://banes-lab.com/source/coordination/tools/core/readers/rule.reader.ts.md) and before [tools/core/readers/source.reader.ts](https://banes-lab.com/source/coordination/tools/core/readers/source.reader.ts.md).

## Definitions

- `strings` (lexical_declaration, line 13)
- `retainedFrom` (lexical_declaration, line 40, exported)
- `extentOf` (lexical_declaration, line 17)
- `parsedReport` (lexical_declaration, line 29)
- `SNAPSHOT_STEP` (lexical_declaration, line 9, exported)
- `REPORT` (lexical_declaration, line 11)
- `{ anchors, lifetime, mark }` (lexical_declaration, line 22)
- `path` (lexical_declaration, line 41, exported)
- `parsed` (lexical_declaration, line 42, exported)
- `derivations` (lexical_declaration, line 43, exported)
- `held` (lexical_declaration, line 44, exported)
- `{ anchorKinds, range, surfaces }` (lexical_declaration, line 49, exported)
- `out` (lexical_declaration, line 54, exported)
- `extent` (lexical_declaration, line 56, exported)

## Uses

- [tools/core/predicates/file.predicate.ts](https://banes-lab.com/source/coordination/tools/core/predicates/file.predicate.ts.md)
- [tools/core/predicates/schema.predicate.ts](https://banes-lab.com/source/coordination/tools/core/predicates/schema.predicate.ts.md)

## Source

```typescript
import type { Extent, Retained } from "../types/snapshot.types.ts";
import { existsSync, readFileSync } from "node:fs";
import { GENERATED_DIR } from "../constants/path.constants.ts";
import { isObject } from "../predicates/schema.predicate.ts";
import { isUnreadableJson } from "../predicates/file.predicate.ts";
import { resolve } from "node:path";
import { ruleReportName } from "../reporters/rule.reporter.ts";

export const SNAPSHOT_STEP = "snapshot";

const REPORT = ruleReportName(SNAPSHOT_STEP);

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

const extentOf = function extentOf(value: unknown): Extent | null {
    if (!isObject(value)) {
        return null;
    }

    const { anchors, lifetime, mark } = value;
    if (typeof lifetime !== "string" || !Array.isArray(anchors)) {
        return null;
    }
    return { anchors: strings(anchors), lifetime, mark: typeof mark === "string" ? mark : "" };
};

const parsedReport = function parsedReport(path: string): unknown {
    try {
        return JSON.parse(readFileSync(path, "utf8"));
    } catch (error) {
        if (isUnreadableJson(error)) {
            return null;
        }
        throw error;
    }
};

export const retainedFrom = function retainedFrom(repoRoot: string): Retained | null {
    const path = resolve(repoRoot, GENERATED_DIR, REPORT);
    const parsed = existsSync(path) ? parsedReport(path) : null;
    const derivations = isObject(parsed) ? parsed["derivations"] : null;
    const held = isObject(derivations) ? derivations["retainedExtent"] : null;
    if (!isObject(held)) {
        return null;
    }

    const { anchorKinds, range, surfaces } = held;
    if (typeof range !== "string" || !isObject(surfaces)) {
        return null;
    }

    const out: Record<string, Extent> = {};
    for (const [surface, value] of Object.entries(surfaces)) {
        const extent = extentOf(value);
        if (extent !== null) {
            out[surface] = extent;
        }
    }

    return { anchorKinds: strings(anchorKinds), range, surfaces: out };
};
```
