# core/converters/reason.reference.converter.ts

> 141 lines of code and 29 definitions.

Tree: Bane's Lab Build Scripts
Language: typescript
Layer: processing
Canonical: https://banes-lab.com/anatomy/build-scripts#file-build-scripts-core-converters-reason-reference-converter-ts
Source text: https://banes-lab.com/source/build-scripts/core/converters/reason.reference.converter.ts.txt

Listed in [core/converters](https://banes-lab.com/api/source/build-scripts/core/converters.md), after [core/converters/reason.loop.converter.ts](https://banes-lab.com/source/build-scripts/core/converters/reason.loop.converter.ts.md) and before [core/converters/record.converter.ts](https://banes-lab.com/source/build-scripts/core/converters/record.converter.ts.md).

## Definitions

- `describedNode` (lexical_declaration, line 59)
- `describedLoop` (lexical_declaration, line 68)
- `reasonReferencesOf` (lexical_declaration, line 115, exported)
- `stageReferencesOf` (lexical_declaration, line 143, exported)
- `describedRecords` (lexical_declaration, line 82)
- `HYPHEN` (lexical_declaration, line 10)
- `MODEL_KIND` (lexical_declaration, line 11)
- `STAGES_RELATION` (lexical_declaration, line 12)
- `STAGE_KIND` (lexical_declaration, line 13)
- `Anchored` (interface_declaration, line 15)
- `kindOfAnchor` (lexical_declaration, line 20)
- `reasonCollections` (lexical_declaration, line 24)
- `inboundOf` (lexical_declaration, line 46)
- `targets` (lexical_declaration, line 49)
- `stages` (lexical_declaration, line 69)
- `nodes` (lexical_declaration, line 86)
- `loopRef` (lexical_declaration, line 87)
- `node` (lexical_declaration, line 90)
- `describeSubstrate` (lexical_declaration, line 99)
- `models` (lexical_declaration, line 107)
- `built` (lexical_declaration, line 116, exported)
- `outbound` (lexical_declaration, line 124, exported)
- `inbound` (lexical_declaration, line 125, exported)
- `substrate` (lexical_declaration, line 126, exported)
- `referencedBy` (lexical_declaration, line 129, exported)
- `index` (lexical_declaration, line 144, exported)
- `questions` (lexical_declaration, line 145, exported)
- `ref` (lexical_declaration, line 147, exported)
- `summary` (lexical_declaration, line 148, exported)

## Contained in

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

## Used by

- [core/converters/reference.converter.ts](https://banes-lab.com/source/build-scripts/core/converters/reference.converter.ts.md)

## Linked from

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

## Source

```typescript
import type { DerivationLoopView, ReasonNodeView } from "@banes-lab/web/types/loop.types.js";
import { REASON_FACE, REFERENCED_BY_RELATION, STAGE_FACE } from "@govlab/constants";
import type { ReferenceIndex, ReferenceRecord } from "@banes-lab/web/types/reference.types.js";
import { loopSummary, nodeSummary, substrateSummary } from "@banes-lab/web/strings/catalog.strings";
import { nameOf, recordOf, titleOf } from "#core/converters/reference.field.converter";
import type { EdgeRef } from "@banes-lab/web/types/link.types.js";
import type { ReasonView } from "@banes-lab/web/types/reason.types.js";
import { refOf } from "#core/resolvers/ontology.resolver";

const HYPHEN = "-";
const MODEL_KIND = "model";
const STAGES_RELATION = "stages";
const STAGE_KIND = "stage";

interface Anchored {
    readonly anchor: string;
    readonly id: string;
}

const kindOfAnchor = function kindOfAnchor(anchor: string, id: string): string {
    return anchor.slice(0, anchor.length - id.length - HYPHEN.length);
};

const reasonCollections = function reasonCollections(reason: ReasonView): readonly (readonly Anchored[])[] {
    return [
        reason.axes,
        reason.nodes,
        reason.substrate.nodes,
        reason.dimensions,
        reason.invariants,
        reason.layers,
        reason.lenses,
        reason.mathDomains,
        reason.mathTypes,
        reason.models,
        reason.modes,
        reason.patternTypes,
        reason.representations,
        reason.techniques,
        reason.testSurfaces,
        reason.universalAxes,
        [reason.derivationLoop],
    ];
};

const inboundOf = function inboundOf(records: ReadonlyMap<string, ReferenceRecord>): ReadonlyMap<string, EdgeRef[]> {
    const inbound = new Map<string, EdgeRef[]>();
    for (const [ref, record] of records) {
        const targets = record.relations.flatMap((relation) => relation.edges.map((edge) => edge.ref));
        for (const target of targets) {
            if (target !== null && records.has(target) && target !== ref) {
                inbound.set(target, [...(inbound.get(target) ?? []), { label: record.name, ref }]);
            }
        }
    }
    return inbound;
};

const describedNode = function describedNode(record: ReferenceRecord, node: ReasonNodeView): ReferenceRecord {
    return node.concept === null
        ? record
        : {
              ...record,
              summary: record.summary ?? nodeSummary(node.concept.label, node.axis.label, node.mathType.label),
          };
};

const describedLoop = function describedLoop(record: ReferenceRecord, loop: DerivationLoopView): ReferenceRecord {
    const stages = loop.stages.map((stage) => ({ label: titleOf(stage.id), ref: refOf(STAGE_FACE, stage.id) }));
    return {
        ...record,
        relations: [...record.relations, { edges: stages, relation: STAGES_RELATION }],
        summary:
            record.summary ??
            loopSummary(
                record.name,
                stages.map((stage) => stage.label),
            ),
    };
};

const describedRecords = function describedRecords(
    reason: ReasonView,
    built: ReadonlyMap<string, ReferenceRecord>,
): ReadonlyMap<string, ReferenceRecord> {
    const nodes = new Map(reason.nodes.map((node) => [refOf(REASON_FACE, node.anchor), node]));
    const loopRef = refOf(REASON_FACE, reason.derivationLoop.anchor);
    return new Map(
        [...built].map(([ref, record]) => {
            const node = nodes.get(ref);
            if (node !== undefined) {
                return [ref, describedNode(record, node)];
            }
            return [ref, ref === loopRef ? describedLoop(record, reason.derivationLoop) : record];
        }),
    );
};

const describeSubstrate = function describeSubstrate(
    reason: ReasonView,
    records: ReadonlyMap<string, ReferenceRecord>,
    inbound: ReadonlyMap<string, EdgeRef[]>,
): ReadonlyMap<string, string> {
    return new Map(
        reason.substrate.nodes.map((node) => {
            const ref = refOf(REASON_FACE, node.anchor);
            const models = (inbound.get(ref) ?? [])
                .filter((edge) => edge.ref !== null && records.get(edge.ref)?.kind === MODEL_KIND)
                .map((edge) => edge.label);
            return [ref, substrateSummary(node.layer.label, node.mathType.label, models)];
        }),
    );
};

export const reasonReferencesOf = function reasonReferencesOf(reason: ReasonView): ReferenceIndex {
    const built = new Map(
        reasonCollections(reason)
            .flat()
            .map((view) => [
                refOf(REASON_FACE, view.anchor),
                recordOf(view, kindOfAnchor(view.anchor, view.id), nameOf(view, view.id)),
            ]),
    );
    const outbound = describedRecords(reason, built);
    const inbound = inboundOf(outbound);
    const substrate = describeSubstrate(reason, outbound, inbound);
    const index: Record<string, ReferenceRecord> = {};
    for (const [ref, record] of outbound) {
        const referencedBy = inbound.get(ref) ?? [];
        const summary = record.summary ?? substrate.get(ref) ?? null;
        index[ref] =
            referencedBy.length === 0
                ? { ...record, summary }
                : {
                      ...record,
                      relations: [...record.relations, { edges: referencedBy, relation: REFERENCED_BY_RELATION }],
                      summary,
                  };
    }
    return index;
};

export const stageReferencesOf = function stageReferencesOf(reason: ReasonView): ReferenceIndex {
    const index: Record<string, ReferenceRecord> = {};
    const questions = new Map(reason.axes.map((axis) => [refOf(REASON_FACE, axis.anchor), axis.question]));
    for (const stage of reason.derivationLoop.stages) {
        const ref = refOf(STAGE_FACE, stage.id);
        const summary = stage.axis.ref === null ? null : (questions.get(stage.axis.ref) ?? null);
        index[ref] = { ...recordOf(stage, STAGE_KIND, titleOf(stage.id)), summary };
    }
    return index;
};
```
