# core/coordinators/graph.coordinator.ts

> 166 lines of code and 31 definitions.

Tree: Bane's Lab Build Scripts
Language: typescript
Layer: application
Canonical: https://banes-lab.com/anatomy/build-scripts#file-build-scripts-core-coordinators-graph-coordinator-ts
Source text: https://banes-lab.com/source/build-scripts/core/coordinators/graph.coordinator.ts.txt

Listed in [core/coordinators](https://banes-lab.com/api/source/build-scripts/core/coordinators.md), after [core/coordinators/diagram.coordinator.ts](https://banes-lab.com/source/build-scripts/core/coordinators/diagram.coordinator.ts.md) and before [core/coordinators/icons.coordinator.ts](https://banes-lab.com/source/build-scripts/core/coordinators/icons.coordinator.ts.md).

## Definitions

- `deriveGraph` (lexical_declaration, line 132, exported)
- `buildGraph` (lexical_declaration, line 49, exported)
- `web` (lexical_declaration, line 55, exported)
- `tools` (lexical_declaration, line 56, exported)
- `{ collections }` (lexical_declaration, line 57, exported)
- `trees` (lexical_declaration, line 58, exported)
- `plans` (lexical_declaration, line 59, exported)
- `identities` (lexical_declaration, line 61, exported)
- `linker` (lexical_declaration, line 66, exported)
- `vocabulary` (lexical_declaration, line 67, exported)
- `ontology` (lexical_declaration, line 68, exported)
- `route` (lexical_declaration, line 69, exported)
- `roots` (lexical_declaration, line 70, exported)
- `context` (lexical_declaration, line 71, exported)
- `merged` (lexical_declaration, line 91, exported)
- `graph` (lexical_declaration, line 92, exported)
- `rules` (lexical_declaration, line 98, exported)
- `chunks` (lexical_declaration, line 106, exported)
- `sections` (lexical_declaration, line 107, exported)
- `teaching` (lexical_declaration, line 108, exported)
- `loaded` (lexical_declaration, line 134, exported)
- `discovery` (lexical_declaration, line 135, exported)
- `tabs` (lexical_declaration, line 136, exported)
- `blocks` (lexical_declaration, line 137, exported)
- `routes` (lexical_declaration, line 138, exported)
- `stops` (lexical_declaration, line 139, exported)
- `built` (lexical_declaration, line 140, exported)
- `{ chunks }` (lexical_declaration, line 141, exported)
- `report` (lexical_declaration, line 142, exported)
- `search` (lexical_declaration, line 146, exported)
- `records` (lexical_declaration, line 151, exported)

## Contained in

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

## Uses

- [configuration/strings/graph.strings.ts](https://banes-lab.com/source/build-scripts/configuration/strings/graph.strings.ts.md)
- [core/analyzers/graph.analyzer.ts](https://banes-lab.com/source/build-scripts/core/analyzers/graph.analyzer.ts.md)
- [core/converters/graph.converter.ts](https://banes-lab.com/source/build-scripts/core/converters/graph.converter.ts.md)
- [core/factories/server.factory.ts](https://banes-lab.com/source/build-scripts/core/factories/server.factory.ts.md)
- [core/formatters/search.formatter.ts](https://banes-lab.com/source/build-scripts/core/formatters/search.formatter.ts.md)
- [core/loaders/folder.loader.ts](https://banes-lab.com/source/build-scripts/core/loaders/folder.loader.ts.md)
- [core/persistence/graph.persistence.ts](https://banes-lab.com/source/build-scripts/core/persistence/graph.persistence.ts.md)

## Used by

- [core/steps/graph.step.ts](https://banes-lab.com/source/build-scripts/core/steps/graph.step.ts.md)

## Linked from

- [configuration/strings](https://banes-lab.com/anatomy/build-scripts/folder-build-scripts-configuration-strings.md)
- [core/analyzers](https://banes-lab.com/anatomy/build-scripts/folder-build-scripts-core-analyzers.md)
- [core/converters](https://banes-lab.com/anatomy/build-scripts/folder-build-scripts-core-converters.md)
- [core/factories](https://banes-lab.com/anatomy/build-scripts/folder-build-scripts-core-factories.md)
- [core/formatters](https://banes-lab.com/anatomy/build-scripts/folder-build-scripts-core-formatters.md)
- [core/loaders](https://banes-lab.com/anatomy/build-scripts/folder-build-scripts-core-loaders.md)
- [core/persistence](https://banes-lab.com/anatomy/build-scripts/folder-build-scripts-core-persistence.md)
- [core/steps](https://banes-lab.com/anatomy/build-scripts/folder-build-scripts-core-steps.md)

## Source

```typescript
import {
    GRAPH_FIELD_RULES,
    GRAPH_VOCABULARY,
    PRODUCER_SUFFIX,
    RECORDS_POPULATION,
    SITE_LAYER,
} from "#configuration/constants/graph.constants";
import type { Graph, GraphBuild, GraphContext } from "#types/graph.types";
import {
    ROLLED_UP_RELATIONS,
    TOOLTIP_RELATIONS,
    chunkKeyOf,
    isListingRef,
} from "@banes-lab/web/constants/graph.constants";
import { absolutePath, relativePath } from "@ssot/paths";
import { canonicalOf, sourceFiles, toolsOf } from "#core/converters/source.converter";
import { coverage, layerPopulation, presence, tooltipGaps, uncoveredSections } from "#core/analyzers/graph.analyzer";
import { danglingEdges, mergeGraphs } from "#core/converters/graph.converter";
import { discover, tabsFromDiscovery } from "#core/converters/site.converter";
import { graphLines, missingPopulation } from "#configuration/strings/graph.strings";
import { nodeOf, teachingNodes } from "@banes-lab/content/core/converters/section.converter.ts";
import { sectionNumbers, siteCodes } from "#core/converters/graph.code.converter";
import { writeGraphChunks, writeGraphReport } from "#core/persistence/graph.persistence";
import type { BuiltOntology } from "#types/ontology.types";
import type { Discovery } from "#types/site.types";
import type { ModuleImporter } from "#types/loader.types";
import type { RouteStop } from "#types/learning.types";
import { buildLearningMap } from "#core/coordinators/learning.coordinator";
import { buildTabs } from "#core/persistence/tab.persistence";
import { chunksOf } from "#core/converters/graph.segment.converter";
import { createLinker } from "#core/resolvers/link.resolver";
import { evidenceTargetOf } from "#core/resolvers/evidence.resolver";
import { importFolder } from "#core/loaders/folder.loader";
import { loadContentGraphs } from "@banes-lab/content/core/loaders/coverage.loader.ts";
import { loadFrom } from "#core/loaders/site.loader";
import { loadWebModules } from "#core/loaders/catalog.loader";
import { ontologyGraph } from "#core/converters/graph.ontology.converter";
import { recordIdentities } from "#core/converters/record.converter";
import { registeredProducers } from "#core/registries/producer.registry";
import { renderSearchAsset } from "#core/formatters/search.formatter";
import { routeEdges } from "#core/converters/graph.link.converter";
import { routeSections } from "#core/analyzers/learning.analyzer";
import { routeStops } from "#core/converters/learning.catalog.converter";
import { searchAssetOf } from "#core/loaders/search.loader";
import { sectionPlans } from "#core/converters/section.converter";
import { withModuleServer } from "#core/factories/server.factory";
import { writeCanonicalText } from "@govlab/canonical-write";

export const buildGraph = async function buildGraph(
    runner: ModuleImporter,
    discovery: Discovery,
    stops: readonly RouteStop[],
    built: BuiltOntology,
): Promise<GraphBuild> {
    const web = await loadWebModules(runner);
    const tools = toolsOf(web);
    const { collections } = built;
    const trees = web.anatomy.ANATOMY_TREES.map((tree) => ({ ...tree, label: web.source.treeLabelOf(tree.tab) }));
    const plans = sectionPlans(discovery, () => null);
    const records = recordIdentities(collections, web.ontology.anchorHrefOf);
    const identities = [
        ...plans.map((plan) => plan.identity),
        ...records,
        ...sourceFiles(trees, tools).map((file) => file.identity),
    ];
    const linker = createLinker(identities, discovery.site, canonicalOf(discovery.site, tools));
    const vocabulary = GRAPH_VOCABULARY;
    const ontology = ontologyGraph(collections, vocabulary, GRAPH_FIELD_RULES);
    const route = routeEdges(stops);
    const roots = trees.map((tree) => tree.snapshot.tree);
    const context: GraphContext = {
        built,
        codes: siteCodes(discovery.routes, plans, stops),
        discovery,
        evidenceSubjects: [...plans.map((plan) => plan.identity.ref), ...records.map((record) => record.ref)],
        evidenceTarget: evidenceTargetOf(roots, web.definition.definitionIndexOf(roots), web.source),
        ids: {
            ...tools,
            folderHref: web.source.folderHref,
            folderId: web.source.folderId,
            nodeHref: web.source.nodeHref,
        },
        numbers: sectionNumbers(plans, stops),
        ontology,
        route,
        scope: { linker, plans, web },
        trees,
        vocabulary,
    };
    await importFolder("app.graphProducers", PRODUCER_SUFFIX);
    const merged = mergeGraphs(registeredProducers().map((producer) => producer.produce(context)));
    const graph: Graph = {
        edges: merged.edges,
        nodes: merged.nodes.map((node) =>
            node.href === null ? { ...node, href: linker.byRef(node.ref)?.href ?? null } : node,
        ),
    };
    const rules = {
        contains: vocabulary.contains,
        keptRelations: TOOLTIP_RELATIONS,
        keyOf: chunkKeyOf,
        pairs: GRAPH_VOCABULARY.pairs,
        rollsUp: ROLLED_UP_RELATIONS,
        skipsSource: isListingRef,
    };
    const chunks = chunksOf(graph, rules);
    const sections = graph.nodes.filter((node) => node.layer === SITE_LAYER);
    const teaching = new Set(teachingNodes(await loadContentGraphs()));
    return {
        chunks,
        report: {
            ambiguous: ontology.ambiguous,
            dangling: danglingEdges(graph),
            duplicates: merged.duplicates.map(([kept, dropped]) => ({ dropped, kept })),
            fields: ontology.fields,
            graph,
            populations: [
                ...ontology.populations,
                layerPopulation(graph.nodes),
                presence("site numbers", sections, (node) => node.number !== null),
                presence("node links", graph.nodes, (node) => node.href !== null),
                ...coverage(graph),
            ],
            tooltipGaps: tooltipGaps(graph, chunks, rules),
            uncovered: uncoveredSections(plans, teaching, graph, nodeOf),
            undeclared: ontology.undeclared,
            unresolved: [...ontology.unresolved, ...route.unresolved],
        },
    };
};

export const deriveGraph = async function deriveGraph(root: string, ontology: BuiltOntology): Promise<string> {
    return withModuleServer(root, async (server) => {
        const loaded = await loadFrom(server);
        const discovery = await discover(loaded);
        const tabs = tabsFromDiscovery(discovery);
        const blocks = await buildLearningMap(loaded.links, tabs);
        const routes = await buildTabs(loaded.links, tabs);
        const stops = routeStops(blocks);
        const built = await buildGraph(loaded.runner, discovery, stops, ontology);
        const { chunks } = built;
        const report = {
            ...built.report,
            populations: [...built.report.populations, routeSections(await loadContentGraphs(), tabs)],
        };
        const search = await searchAssetOf(
            loaded.runner,
            stops.map((stop) => stop.path),
        );
        await writeCanonicalText(absolutePath("app.search"), renderSearchAsset(search));
        const records = report.populations.find((population) => population.name === RECORDS_POPULATION)?.whole;
        if (records === undefined) {
            throw new Error(missingPopulation(RECORDS_POPULATION));
        }
        writeGraphReport(report);
        await writeGraphChunks(chunks);
        return graphLines(
            { chunks: chunks.size, records, report, routes, search, stops: stops.length },
            {
                graph: relativePath("app.graph"),
                learning: relativePath("app.learning"),
                report: relativePath("govlabHost.reports.content.graph"),
                search: relativePath("app.search"),
                tabs: relativePath("app.tabs"),
            },
        );
    });
};
```
