# domain/converters/tension.converter.ts

> 161 lines of code and 19 definitions.

Tree: Site tree
Language: typescript
Layer: processing
Canonical: https://banes-lab.com/anatomy/tree#file-domain-converters-tension-converter-ts
Source text: https://banes-lab.com/assets/sources/source.2a3f5b8b133a5d37e371b324af225186f199c54ec6042d7c1444ac9de8c96c9a.generated.txt

## Definitions

- `edgeText` (lexical_declaration, line 66)
- `layerSubsection` (lexical_declaration, line 72)
- `topologySection` (lexical_declaration, line 95)
- `membershipSection` (lexical_declaration, line 106)
- `topologyDiagram` (lexical_declaration, line 58)
- `tensionTitle` (lexical_declaration, line 133)
- `tensionSection` (lexical_declaration, line 158)
- `tensionSections` (lexical_declaration, line 169, exported)
- `tensionSubsection` (lexical_declaration, line 137)
- `HEADER` (lexical_declaration, line 52)
- `LABEL_OPEN` (lexical_declaration, line 53)
- `LABEL_CLOSE` (lexical_declaration, line 54)
- `SPACE` (lexical_declaration, line 55)
- `LAYER_ANCHOR_REF` (lexical_declaration, line 56)
- `nodes` (lexical_declaration, line 59)
- `edges` (lexical_declaration, line 60)
- `outgoing` (lexical_declaration, line 73)
- `incoming` (lexical_declaration, line 74)
- `ordered` (lexical_declaration, line 159)

## Uses

- [domain/converters/learning.grid.converter.ts](https://banes-lab.com/source/tree/domain/converters/learning.grid.converter.ts.md)
- [domain/converters/ontology.converter.ts](https://banes-lab.com/source/tree/domain/converters/ontology.converter.ts.md)

## Source

```typescript
import {
    CONTRACT_LABEL,
    FIELD_SEPARATOR,
    FLOW_ARROW,
    LAYER_LABEL,
    LIST_SEPARATOR,
    NONE_LABEL,
} from "#configuration/strings/ontology.strings";
import {
    DERIVED_LABEL,
    EXPLICIT_LABEL,
    INCOMING_LABEL,
    MECHANISM_LABEL,
    MEMBERSHIP_INTRO,
    MEMBERSHIP_TITLE,
    MEMBER_CATEGORIES_LABEL,
    OUTGOING_LABEL,
    RESOLUTION_INTRO,
    RESOLUTION_TITLE,
    RULE_LABEL,
    SCOPE_A_LABEL,
    SCOPE_B_LABEL,
    TOPOLOGY_CAPTION,
    TOPOLOGY_INTRO,
    TOPOLOGY_SECTION_TITLE,
    VERSUS_LABEL,
} from "#configuration/strings/tension.strings";
import {
    LAYER_ANCHOR,
    MEMBERSHIP_SECTION_ID,
    RESOLUTION_SECTION_ID,
    TENSION_ANCHOR,
    TOPOLOGY_SECTION_ID,
} from "#core/ids/ontology.ids";
import type { LayerEdgeView, LayerNodeView, LayerView, TensionView } from "#types/ontology.types";
import type { Section, Subsection } from "#types/document.types";
import {
    chips,
    diagram,
    edgeLine,
    fields,
    glossary,
    labelled,
    linkList,
    linkOf,
    linked,
    node,
    row,
} from "#domain/converters/ontology.converter";
import { LAYER_SECTION_ICON } from "#configuration/icons/ontology.icons";

const HEADER = "flowchart TB";
const LABEL_OPEN = " -- ";
const LABEL_CLOSE = " --> ";
const SPACE = " ";
const LAYER_ANCHOR_REF = "layer:";

const topologyDiagram = function topologyDiagram(layers: LayerView): string {
    const nodes = layers.nodes.map((layer) => node(LAYER_ANCHOR_REF + layer.id, layer.label));
    const edges = layers.topology.map((edge) =>
        edgeLine(edge.from.ref ?? edge.from.label, LABEL_OPEN + edge.kind + LABEL_CLOSE, edge.to.ref ?? edge.to.label),
    );
    return diagram(HEADER, [...nodes, ...edges]);
};

const edgeText = function edgeText(edges: readonly LayerEdgeView[], pick: (edge: LayerEdgeView) => string): string {
    return edges.length === 0
        ? NONE_LABEL
        : edges.map((edge) => edge.kind + FLOW_ARROW + pick(edge)).join(LIST_SEPARATOR);
};

const layerSubsection = function layerSubsection(layer: LayerNodeView, topology: readonly LayerEdgeView[]): Subsection {
    const outgoing = topology.filter((edge) => edge.from.ref === LAYER_ANCHOR_REF + layer.id);
    const incoming = topology.filter((edge) => edge.to.ref === LAYER_ANCHOR_REF + layer.id);
    return {
        blocks: [
            chips([linked(CONTRACT_LABEL, layer.contract)]),
            glossary([
                row(MEMBER_CATEGORIES_LABEL, linkList(layer.members)),
                row(
                    OUTGOING_LABEL,
                    edgeText(outgoing, (edge) => linkOf(edge.to)),
                ),
                row(
                    INCOMING_LABEL,
                    edgeText(incoming, (edge) => linkOf(edge.from)),
                ),
            ]),
        ],
        id: LAYER_ANCHOR + layer.id,
        title: layer.label,
    };
};

const topologySection = function topologySection(layers: LayerView): Section {
    return {
        blocks: [{ caption: TOPOLOGY_CAPTION, kind: "mermaid", text: topologyDiagram(layers) }],
        icon: LAYER_SECTION_ICON,
        id: TOPOLOGY_SECTION_ID,
        intro: TOPOLOGY_INTRO,
        subsections: layers.nodes.map((layer) => layerSubsection(layer, layers.topology)),
        title: TOPOLOGY_SECTION_TITLE,
    };
};

const membershipSection = function membershipSection(layers: LayerView): Section {
    return {
        icon: LAYER_SECTION_ICON,
        id: MEMBERSHIP_SECTION_ID,
        intro: MEMBERSHIP_INTRO,
        subsections: [
            {
                blocks: [
                    glossary(
                        layers.membership.map((entry) =>
                            row(
                                entry.category.label,
                                fields([
                                    linkOf(entry.category),
                                    entry.layer === null ? NONE_LABEL : linkOf(entry.layer),
                                ]),
                            ),
                        ),
                    ),
                ],
                title: MEMBERSHIP_TITLE,
            },
        ],
        title: MEMBERSHIP_TITLE,
    };
};

const tensionTitle = function tensionTitle(tension: TensionView): string {
    return tension.a.label + SPACE + VERSUS_LABEL + SPACE + tension.b.label;
};

const tensionSubsection = function tensionSubsection(tension: TensionView): Subsection {
    return {
        blocks: [
            chips([labelled(MECHANISM_LABEL, tension.mechanism), tension.explicit ? EXPLICIT_LABEL : DERIVED_LABEL]),
            glossary([
                row(
                    SCOPE_A_LABEL,
                    linkOf(tension.a) + FIELD_SEPARATOR + (labelled(LAYER_LABEL, linkOf(tension.scopeA)) ?? ""),
                ),
                row(
                    SCOPE_B_LABEL,
                    linkOf(tension.b) + FIELD_SEPARATOR + (labelled(LAYER_LABEL, linkOf(tension.scopeB)) ?? ""),
                ),
                row(RULE_LABEL, tension.rule),
            ]),
        ],
        id: TENSION_ANCHOR + tension.id,
        title: tensionTitle(tension),
    };
};

const tensionSection = function tensionSection(layers: LayerView): Section {
    const ordered = [...layers.resolutions].sort((left, right) => Number(right.explicit) - Number(left.explicit));
    return {
        icon: LAYER_SECTION_ICON,
        id: RESOLUTION_SECTION_ID,
        intro: RESOLUTION_INTRO,
        subsections: ordered.map(tensionSubsection),
        title: RESOLUTION_TITLE,
    };
};

export const tensionSections = function tensionSections(layers: LayerView): readonly Section[] {
    return [topologySection(layers), membershipSection(layers), tensionSection(layers)];
};
```
