core/converters/vocabulary.converter.ts
core/converters/vocabulary.converter.ts is a file in Bane's Lab Build Scripts. 186 lines of code and 37 definitions.
import { ALGO_FACE, ARCH_FACE, LAYER_FACE, LEX_FACE, PAG_FACE, REASON_FACE } from "@govlab/constants";
import {
AXIS_KIND,
DIMENSION_KIND,
FAILURE_SHAPE_KIND,
INVARIANT_KIND,
LENS_KIND,
MATH_DOMAIN_KIND,
MATH_TYPE_KIND,
MODEL_KIND,
MODE_KIND,
NODE_KIND,
PATTERN_TYPE_KIND,
REASON_LAYER_KIND,
REPRESENTATION_KIND,
SUBSTRATE_NODE_KIND,
TECHNIQUE_KIND,
TEST_SURFACE_KIND,
UNIVERSAL_AXIS_KIND,
} from "#configuration/constants/ontology.constants";
import { type GovlabContext, PAG_KINDS, type Principle, type Term, keywordIdOf } from "@govlab/context";
import { pagAnchor, reasonAnchor, refOf } from "#core/resolvers/ontology.resolver";
import type { Phrase } from "#types/search.types";
import type { VocabularyEntry } from "@banes-lab/web/types/vocabulary.types.js";
const PAREN_OPEN = "(";
const PAREN_CLOSE = ")";
const SPACE = " ";
const CODE_LIMIT = 6;
const UPPER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const KEY_SAFE = "abcdefghijklmnopqrstuvwxyz0123456789";
const isCode = function isCode(candidate: string): boolean {
if (candidate.length === 0 || candidate.length > CODE_LIMIT) {
return false;
}
let capitals = 0;
for (const char of candidate) {
if (UPPER.includes(char)) {
capitals += 1;
}
}
return capitals >= 2;
};
const splitName = function splitName(name: string): { readonly code: string | null; readonly plain: string } {
const open = name.indexOf(PAREN_OPEN);
const close = name.indexOf(PAREN_CLOSE, open);
if (open === -1 || close === -1) {
return { code: null, plain: name.trim() };
}
const inner = name.slice(open + 1, close).trim();
const plain = (name.slice(0, open) + name.slice(close + 1)).trim();
return { code: isCode(inner) ? inner : null, plain };
};
export const keyOf = function keyOf(phrase: string): string {
let key = "";
for (const char of phrase.toLowerCase()) {
key += KEY_SAFE.includes(char) ? char : SPACE;
}
return key
.split(SPACE)
.filter((word) => word.length > 0)
.join(SPACE);
};
const layerLabels = function layerLabels(context: GovlabContext): ReadonlyMap<string, string> {
return new Map(context.layers().map((layer) => [layer.id, layer.label]));
};
const principleEntries = function principleEntries(
principle: Principle,
layer: string | null,
): readonly VocabularyEntry[] {
const { code, plain } = splitName(principle.name);
const ref = refOf(ARCH_FACE, principle.id);
const base = { code, kind: principle.type, layer, prose: true, ref };
const phrases = [plain, ...(code === null ? [] : [code]), ...(principle.aliases ?? [])];
return phrases.map((phrase) => ({ ...base, phrase }));
};
const termEntries = function termEntries(term: Term, layer: string | null): readonly VocabularyEntry[] {
const { code, plain } = splitName(term.name);
const base = { code, kind: term.kind, layer, prose: false, ref: refOf(LEX_FACE, term.id) };
return [plain, ...term.aliases].map((phrase) => ({ ...base, phrase }));
};
const reasonEntry = function reasonEntry(kind: string, id: string, name: string): VocabularyEntry {
return {
code: null,
kind,
layer: null,
phrase: name,
prose: name.trim().includes(SPACE),
ref: refOf(REASON_FACE, reasonAnchor(kind, id)),
};
};
const reasonEntries = function reasonEntries(context: GovlabContext): readonly VocabularyEntry[] {
return [
...context.reason.invariants().map((invariant) => reasonEntry(INVARIANT_KIND, invariant.id, invariant.name)),
...context.reason.nodes().map((node) => reasonEntry(NODE_KIND, node.id, node.name)),
];
};
type Aliased = readonly { readonly aliases?: string[]; readonly id: string }[];
const phrasesOf = function phrasesOf(ref: string, aliases: readonly string[] | undefined): readonly Phrase[] {
return (aliases ?? []).map((phrase) => ({ phrase, ref }));
};
const reasonPhrases = function reasonPhrases({ reason }: GovlabContext): readonly Phrase[] {
const kinds: readonly (readonly [string, Aliased])[] = [
[NODE_KIND, reason.nodes()],
[AXIS_KIND, reason.axes()],
[REASON_LAYER_KIND, reason.layers()],
[MATH_TYPE_KIND, reason.mathTypes()],
[SUBSTRATE_NODE_KIND, reason.substrate().nodes],
[DIMENSION_KIND, reason.dimensions()],
[LENS_KIND, reason.lenses()],
[MODE_KIND, reason.modes()],
[REPRESENTATION_KIND, reason.representations()],
[MATH_DOMAIN_KIND, reason.mathDomains()],
[PATTERN_TYPE_KIND, reason.patternTypes()],
[MODEL_KIND, reason.models()],
[UNIVERSAL_AXIS_KIND, reason.universalAxes()],
[TEST_SURFACE_KIND, reason.testSurfaces()],
[TECHNIQUE_KIND, reason.techniques()],
[INVARIANT_KIND, reason.invariants()],
[FAILURE_SHAPE_KIND, reason.failureShapes()],
];
return kinds.flatMap(([kind, records]) =>
records.flatMap((record) => phrasesOf(refOf(REASON_FACE, reasonAnchor(kind, record.id)), record.aliases)),
);
};
const pagRef = function pagRef(kind: string, key: string): string {
return refOf(PAG_FACE, pagAnchor(kind, key));
};
const pagPhrases = function pagPhrases({ pag }: GovlabContext): readonly Phrase[] {
return [
...pag
.keywords()
.flatMap((record) => phrasesOf(pagRef(PAG_KINDS.keyword, keywordIdOf(record)), record.aliases)),
...pag.productions().flatMap((record) => phrasesOf(pagRef(PAG_KINDS.production, record.lhs), record.aliases)),
...pag
.documentTypes()
.flatMap((record) => phrasesOf(pagRef(PAG_KINDS.documentType, record.type), record.aliases)),
...pag.templates().flatMap((record) => phrasesOf(pagRef(PAG_KINDS.template, record.type), record.aliases)),
];
};
export const aliasPhrasesOf = function aliasPhrasesOf(context: GovlabContext): readonly Phrase[] {
return [
...context.arch.all().flatMap((record) => phrasesOf(refOf(ARCH_FACE, record.id), record.aliases)),
...context.lex.all().flatMap((record) => phrasesOf(refOf(LEX_FACE, record.id), record.aliases)),
...context.algo.all().flatMap((record) => phrasesOf(refOf(ALGO_FACE, record.id), record.aliases)),
...pagPhrases(context),
...reasonPhrases(context),
];
};
export const vocabularyOf = function vocabularyOf(context: GovlabContext): readonly VocabularyEntry[] {
const labels = layerLabels(context);
const held = new Map<string, VocabularyEntry>();
const admit = function admit(entry: VocabularyEntry): void {
const key = keyOf(entry.phrase);
if (key.length > 0 && !held.has(key)) {
held.set(key, entry);
}
};
const layerOf = function layerOf(id: string): string | null {
const layerId = context.layerOf(id);
return layerId === null ? null : (labels.get(layerId) ?? layerId);
};
for (const principle of context.arch.all()) {
for (const entry of principleEntries(principle, layerOf(principle.id))) {
admit(entry);
}
}
for (const layer of context.layers()) {
admit({
code: null,
kind: LAYER_FACE,
layer: layer.label,
phrase: layer.label,
prose: true,
ref: refOf(LAYER_FACE, layer.id),
});
}
for (const term of context.lex.all()) {
for (const entry of termEntries(term, layerOf(term.id))) {
admit(entry);
}
}
for (const entry of reasonEntries(context)) {
admit(entry);
}
return [...held.values()];
};