domain/converters/tension.converter.ts
domain/converters/tension.converter.ts is a file in Bane's Lab Site. 149 lines of code and 16 definitions.
import {
CONTRACT_LABEL,
FIELD_SEPARATOR,
FLOW_ARROW,
LAYER_LABEL,
LIST_SEPARATOR,
NONE_LABEL,
RULE_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,
SCOPE_A_LABEL,
SCOPE_B_LABEL,
TOPOLOGY_CAPTION,
TOPOLOGY_INTRO,
TOPOLOGY_SECTION_TITLE,
VERSUS_LABEL,
} from "#configuration/strings/tension.strings";
import { EDGE_LABEL_OPEN, EDGE_SOLID } from "#configuration/constants/diagram.constants";
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, fields, glossary, labeled, linkList, linkOf, linked, row } from "#domain/converters/ontology.converter";
import { diagram, edgeLine, node } from "#domain/converters/ontology.graph.converter";
import { LAYER_SECTION_ICON } from "#configuration/icons/ontology.icons";
import { SPACE } from "#configuration/constants/document.constants";
const HEADER = "flowchart TB";
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,
EDGE_LABEL_OPEN + edge.kind.label + EDGE_SOLID,
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) => linkOf(edge.kind) + FLOW_ARROW + pick(edge)).join(LIST_SEPARATOR);
};
const layerSubsection = function layerSubsection(layer: LayerNodeView): Subsection {
return {
blocks: [
chips([linked(CONTRACT_LABEL, layer.contract)]),
glossary([
row(MEMBER_CATEGORIES_LABEL, linkList(layer.members)),
row(
OUTGOING_LABEL,
edgeText(layer.outgoing, (edge) => linkOf(edge.to)),
),
row(
INCOMING_LABEL,
edgeText(layer.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(layerSubsection),
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 scopeText = function scopeText(side: TensionView["a"], scope: TensionView["scopeA"]): string {
const layer = labeled(LAYER_LABEL, linkOf(scope)) ?? "";
return linkOf(side) + FIELD_SEPARATOR + layer;
};
const tensionSubsection = function tensionSubsection(tension: TensionView): Subsection {
return {
blocks: [
chips([linked(MECHANISM_LABEL, tension.mechanism), tension.explicit ? EXPLICIT_LABEL : DERIVED_LABEL]),
glossary([
row(SCOPE_A_LABEL, scopeText(tension.a, tension.scopeA)),
row(SCOPE_B_LABEL, scopeText(tension.b, 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)];
};