# core/converters/graph.concern.converter.ts

> 49 lines of code and 10 definitions.

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

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

## Definitions

- `edgesOf` (lexical_declaration, line 31)
- `concernEdges` (lexical_declaration, line 44, exported)
- `concernLayerEdges` (lexical_declaration, line 48, exported)
- `concernScopeOf` (lexical_declaration, line 9, exported)
- `filesOf` (lexical_declaration, line 18)
- `termsByTag` (lexical_declaration, line 22)
- `tag` (lexical_declaration, line 25)
- `byTag` (lexical_declaration, line 36)
- `term` (lexical_declaration, line 38)
- `to` (lexical_declaration, line 39)

## Contained in

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

## Linked from

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

## Source

```typescript
import type { AnatomyFile, AnatomyFolder } from "@banes-lab/web/types/anatomy.types.js";
import type { ConcernScope, ConcernSource } from "#types/graph.types";
import { FACE_SEPARATOR, LEX_CATEGORY_FACE, LEX_FACE } from "@govlab/constants";
import { ANATOMY_PREFIX } from "#configuration/constants/graph.constants";
import type { GraphEdge } from "@banes-lab/web/types/graph.types.js";
import type { Term } from "@govlab/context";
import { tagOfTerm } from "@govlab/context/core/selectors/lexicon.selector.ts";

export const concernScopeOf = function concernScopeOf(context: ConcernSource): ConcernScope {
    return {
        fileRef: (path) => ANATOMY_PREFIX + context.ids.fileId(path),
        records: new Set(context.ontology.nodes.map((node) => node.ref)),
        roots: context.trees.map((tree) => tree.snapshot.tree),
        terms: context.built.context.lex.all(),
    };
};

const filesOf = function filesOf(folder: AnatomyFolder): readonly AnatomyFile[] {
    return [...folder.files, ...folder.folders.flatMap(filesOf)];
};

const termsByTag = function termsByTag(terms: readonly Term[]): ReadonlyMap<string, Term> {
    return new Map(
        terms.flatMap((term) => {
            const tag = tagOfTerm(term);
            return tag === null ? [] : [[tag, term] as const];
        }),
    );
};

const edgesOf = function edgesOf(
    scope: ConcernScope,
    relation: string,
    targetOf: (file: AnatomyFile, term: Term) => string | null,
): readonly GraphEdge[] {
    const byTag = termsByTag(scope.terms);
    return scope.roots.flatMap(filesOf).flatMap((file): readonly GraphEdge[] => {
        const term = file.slots === null ? undefined : byTag.get(file.slots.concern);
        const to = term === undefined ? null : targetOf(file, term);
        return to === null || !scope.records.has(to) ? [] : [{ from: scope.fileRef(file.path), relation, to }];
    });
};

export const concernEdges = function concernEdges(scope: ConcernScope, relation: string): readonly GraphEdge[] {
    return edgesOf(scope, relation, (file, term) => (file.slots === null ? null : LEX_FACE + FACE_SEPARATOR + term.id));
};

export const concernLayerEdges = function concernLayerEdges(
    scope: ConcernScope,
    relation: string,
): readonly GraphEdge[] {
    return edgesOf(scope, relation, (file, term) =>
        file.layer === null ? null : LEX_CATEGORY_FACE + FACE_SEPARATOR + term.category,
    );
};
```
