# core/resolvers/ontology.resolver.ts

> 178 lines of code and 50 definitions.

Tree: Bane's Lab Build Scripts
Language: typescript
Layer: infrastructure
Canonical: https://banes-lab.com/anatomy/build-scripts#file-build-scripts-core-resolvers-ontology-resolver-ts
Source text: https://banes-lab.com/source/build-scripts/core/resolvers/ontology.resolver.ts.txt

Listed in [core/resolvers](https://banes-lab.com/api/source/build-scripts/core/resolvers.md), after [core/resolvers/link.resolver.ts](https://banes-lab.com/source/build-scripts/core/resolvers/link.resolver.ts.md) and before [core/resolvers/page.resolver.ts](https://banes-lab.com/source/build-scripts/core/resolvers/page.resolver.ts.md).

## Definitions

- `reasonAnchor` (lexical_declaration, line 41, exported)
- `refOf` (lexical_declaration, line 29, exported)
- `pagAnchor` (lexical_declaration, line 58, exported)
- `archRef` (lexical_declaration, line 86, exported)
- `vocabularyAnchor` (lexical_declaration, line 45, exported)
- `bare` (lexical_declaration, line 145, exported)
- `reasonAs` (lexical_declaration, line 102, exported)
- `lex` (lexical_declaration, line 89, exported)
- `titleCase` (lexical_declaration, line 74)
- `createResolver` (lexical_declaration, line 81, exported)
- `vocabulary` (lexical_declaration, line 49)
- `arch` (lexical_declaration, line 93, exported)
- `pagAnchorsOf` (lexical_declaration, line 62)
- `algo` (lexical_declaration, line 97, exported)
- `reason` (lexical_declaration, line 107, exported)
- `stage` (lexical_declaration, line 117, exported)
- `archExact` (lexical_declaration, line 120, exported)
- `pag` (lexical_declaration, line 125, exported)
- `target` (lexical_declaration, line 149, exported)
- `labels` (lexical_declaration, line 160, exported)
- `tension` (lexical_declaration, line 163, exported)
- `SPACE` (lexical_declaration, line 25)
- `HYPHEN` (lexical_declaration, line 26)
- `PAIR_JOINER` (lexical_declaration, line 27)
- `faceOf` (lexical_declaration, line 33, exported)
- `idOf` (lexical_declaration, line 37, exported)
- `held` (lexical_declaration, line 50)
- `known` (lexical_declaration, line 54)
- `archIndex` (lexical_declaration, line 82, exported)
- `categoryLabels` (lexical_declaration, line 83, exported)
- `reasonIndex` (lexical_declaration, line 84, exported)
- `stages` (lexical_declaration, line 85, exported)
- `term` (lexical_declaration, line 90, exported)
- `contract` (lexical_declaration, line 99, exported)
- `label` (lexical_declaration, line 104, exported)
- `typed` (lexical_declaration, line 108, exported)
- `kinded` (lexical_declaration, line 110, exported)
- `local` (lexical_declaration, line 113, exported)
- `resolution` (lexical_declaration, line 114, exported)
- `principle` (lexical_declaration, line 121, exported)
- `pagAnchors` (lexical_declaration, line 124, exported)
- `at` (lexical_declaration, line 126, exported)
- `id` (lexical_declaration, line 127, exported)
- `anchor` (lexical_declaration, line 128, exported)
- `byFace` (lexical_declaration, line 131, exported)
- `edgeSource` (lexical_declaration, line 139, exported)
- `found` (lexical_declaration, line 146, exported)
- `resolveIn` (lexical_declaration, line 153, exported)
- `resolved` (lexical_declaration, line 157, exported)
- `pair` (lexical_declaration, line 164, exported)

## Contained in

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

## Uses

- [configuration/strings/ontology.strings.ts](https://banes-lab.com/source/build-scripts/configuration/strings/ontology.strings.ts.md)

## Used by

- [core/converters/grammar.converter.ts](https://banes-lab.com/source/build-scripts/core/converters/grammar.converter.ts.md)
- [core/converters/ontology.reference.converter.ts](https://banes-lab.com/source/build-scripts/core/converters/ontology.reference.converter.ts.md)
- [core/converters/reason.catalog.converter.ts](https://banes-lab.com/source/build-scripts/core/converters/reason.catalog.converter.ts.md)
- [core/converters/reason.converter.ts](https://banes-lab.com/source/build-scripts/core/converters/reason.converter.ts.md)
- [core/converters/reason.loop.converter.ts](https://banes-lab.com/source/build-scripts/core/converters/reason.loop.converter.ts.md)
- [core/converters/vocabulary.converter.ts](https://banes-lab.com/source/build-scripts/core/converters/vocabulary.converter.ts.md)

## Linked from

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

## Source

```typescript
import {
    ALGO_FACE,
    ARCH_CATEGORY_FACE,
    ARCH_FACE,
    FACE_SEPARATOR,
    FORCE_FACE,
    KIND_FACE,
    LAYER_FACE,
    LEX_CATEGORY_FACE,
    LEX_FACE,
    PAG_FACE,
    REASON_FACE,
    STAGE_FACE,
    TENSION_FACE,
    VOCABULARY_FACE,
} from "@govlab/constants";
import { CANONICAL_KINDS, CLOSED_VOCABULARIES, type GovlabContext, type Principle, slugify } from "@govlab/context";
import { LOOP_KIND, NODE_KIND } from "#configuration/constants/ontology.constants";
import { archIndexOf, categoryLabelsOf } from "#core/converters/architecture.index.converter";
import type { EdgeRef } from "@banes-lab/web/types/link.types.js";
import type { Resolver } from "#types/ontology.types";
import { reasonIndexOf } from "#core/converters/reason.index.converter";
import { unregisteredVocabulary } from "#configuration/strings/ontology.strings";

const SPACE = " ";
const HYPHEN = "-";
const PAIR_JOINER = " / ";

export const refOf = function refOf(face: string, id: string): string {
    return face + FACE_SEPARATOR + id;
};

export const faceOf = function faceOf(ref: string): string {
    return ref.slice(0, ref.indexOf(FACE_SEPARATOR));
};

export const idOf = function idOf(ref: string): string {
    return ref.slice(ref.indexOf(FACE_SEPARATOR) + 1);
};

export const reasonAnchor = function reasonAnchor(kind: string, id: string): string {
    return kind + HYPHEN + id;
};

export const vocabularyAnchor = function vocabularyAnchor(id: string, value: string): string {
    return id + HYPHEN + value;
};

const vocabulary = function vocabulary(id: string, value: string): EdgeRef {
    const held = CLOSED_VOCABULARIES.find((entry) => entry.id === id);
    if (held === undefined) {
        throw new Error(unregisteredVocabulary(id));
    }
    const known = held.entries.some((entry) => entry.value === value);
    return { label: value, ref: known ? refOf(VOCABULARY_FACE, vocabularyAnchor(id, value)) : null };
};

export const pagAnchor = function pagAnchor(kind: string, id: string): string {
    return kind + HYPHEN + slugify(id);
};

const pagAnchorsOf = function pagAnchorsOf(context: GovlabContext): ReadonlySet<string> {
    return new Set(
        context.checkedRecords().flatMap((record) => {
            if (record.collection !== PAG_FACE) {
                return [];
            }
            const at = record.id.indexOf(FACE_SEPARATOR);
            return [pagAnchor(record.id.slice(0, at), record.id.slice(at + FACE_SEPARATOR.length))];
        }),
    );
};

const titleCase = function titleCase(slug: string): string {
    return slug
        .split(HYPHEN)
        .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
        .join(SPACE);
};

export const createResolver = function createResolver(context: GovlabContext): Resolver {
    const archIndex = archIndexOf(context);
    const categoryLabels = categoryLabelsOf(context);
    const reasonIndex = reasonIndexOf(context);
    const stages = new Set(context.reason.derivationLoop().stages.map((stage) => stage.id));
    const archRef = function archRef(principle: Principle): EdgeRef {
        return { label: principle.name, ref: refOf(ARCH_FACE, principle.id) };
    };
    const lex = function lex(label: string): EdgeRef {
        const term = context.lex.resolve(label);
        return { label: term?.name ?? label, ref: term === null ? null : refOf(LEX_FACE, term.id) };
    };
    const arch = function arch(label: string): EdgeRef {
        const principle = archIndex.get(slugify(label));
        return principle === undefined ? lex(label) : archRef(principle);
    };
    const algo = function algo(label: string): EdgeRef {
        const id = slugify(label);
        const contract = context.algo.get(id);
        return { label: contract?.title ?? label, ref: contract === null ? null : refOf(ALGO_FACE, id) };
    };
    const reasonAs = function reasonAs(kind: string, id: string): EdgeRef {
        const local = reasonIndex.get(kind)?.has(id) === true ? id : slugify(id);
        const label = reasonIndex.get(kind)?.get(local);
        return { label: label ?? id, ref: label === undefined ? null : refOf(REASON_FACE, reasonAnchor(kind, local)) };
    };
    const reason = function reason(id: string): EdgeRef {
        const typed = id.indexOf(FACE_SEPARATOR);
        if (typed !== -1) {
            const kinded = reasonAs(id.slice(0, typed), id.slice(typed + FACE_SEPARATOR.length));
            return { label: id, ref: kinded.ref };
        }
        const local = context.reason.resolve(id) === null ? slugify(id) : id;
        const resolution = context.reason.resolve(local);
        return resolution === null ? { label: id, ref: null } : reasonAs(resolution.kind, local);
    };
    const stage = function stage(id: string): EdgeRef {
        return { label: id, ref: stages.has(id) ? refOf(STAGE_FACE, id) : null };
    };
    const archExact = function archExact(id: string): EdgeRef {
        const principle = context.arch.get(id);
        return principle === null ? { label: id, ref: null } : archRef(principle);
    };
    const pagAnchors = pagAnchorsOf(context);
    const pag = function pag(local: string): EdgeRef {
        const at = local.indexOf(FACE_SEPARATOR);
        const id = local.slice(at + FACE_SEPARATOR.length);
        const anchor = pagAnchor(local.slice(0, at), id);
        return { label: id, ref: at !== -1 && pagAnchors.has(anchor) ? refOf(PAG_FACE, anchor) : null };
    };
    const byFace: ReadonlyMap<string, (local: string) => EdgeRef> = new Map([
        [ALGO_FACE, algo],
        [ARCH_FACE, archExact],
        [LEX_FACE, lex],
        [PAG_FACE, pag],
        [REASON_FACE, reason],
        [STAGE_FACE, stage],
    ]);
    const edgeSource = function edgeSource(id: string): EdgeRef {
        const found = [stage(id), reasonAs(NODE_KIND, id), reasonAs(LOOP_KIND, id)].find(
            (candidate) => candidate.ref !== null,
        );
        return found ?? { label: id, ref: null };
    };
    const bare = function bare(raw: string): EdgeRef {
        const found = [reason(raw), arch(raw), algo(raw)].find((candidate) => candidate.ref !== null);
        return found ?? { label: raw, ref: null };
    };
    const target = function target(raw: string): EdgeRef {
        if (!raw.includes(FACE_SEPARATOR)) {
            return bare(raw);
        }
        const resolveIn = byFace.get(faceOf(raw));
        if (resolveIn === undefined) {
            return { label: raw, ref: null };
        }
        const resolved = resolveIn(idOf(raw));
        return resolved.ref === null ? { label: raw, ref: null } : resolved;
    };
    const labels = function labels(phrases: readonly string[]): readonly EdgeRef[] {
        return phrases.map((phrase) => ({ label: phrase, ref: bare(phrase).ref }));
    };
    const tension = function tension(a: EdgeRef, b: EdgeRef): EdgeRef {
        const pair = [slugify(a.label), slugify(b.label)].sort((left, right) => left.localeCompare(right));
        return { label: a.label + PAIR_JOINER + b.label, ref: refOf(TENSION_FACE, pair.join(HYPHEN)) };
    };
    return {
        algo,
        arch,
        archCategory: (label) => ({ label, ref: refOf(ARCH_CATEGORY_FACE, slugify(label)) }),
        archId: archExact,
        edgeSource,
        force: (force, known) => ({ label: force, ref: known.has(force) ? refOf(FORCE_FACE, slugify(force)) : null }),
        kind: (kind) => ({ label: kind, ref: CANONICAL_KINDS.has(kind) ? refOf(KIND_FACE, kind) : null }),
        labels,
        layer: (id) => ({ label: context.algo.get(id)?.title ?? id, ref: refOf(LAYER_FACE, id) }),
        lexCategory: (slug) => ({
            label: categoryLabels.get(slug) ?? titleCase(slug),
            ref: refOf(LEX_CATEGORY_FACE, slug),
        }),
        pag,
        reason,
        reasonAs,
        stage,
        target,
        tension,
        vocabulary,
    };
};
```
