core/coordinators/catalog.coordinator.ts
core/coordinators/catalog.coordinator.ts is a file in Bane's Lab Build Scripts. 197 lines of code and 47 definitions.
import {
CHAPTER_PREFIX,
CHECK_RELATIONS,
GRAPH_VOCABULARY,
ONTOLOGY_LAYER,
} from "#configuration/constants/graph.constants";
import type { Catalog, Link, Placement, Relation } from "#types/catalog.types";
import { EVIDENCE_RELATION_ID, LINKS_TO_RELATION, reverseOf } from "@banes-lab/web/constants/graph.constants";
import { FILE_SEARCH, RECORD_SEARCH, SECTION_SEARCH } from "#configuration/constants/catalog.constants";
import { NUMBERS_TITLE, ROUTE_TITLE, unroutedTab } from "#configuration/strings/catalog.strings";
import { aliasPhrasesOf, vocabularyOf } from "#core/converters/vocabulary.converter";
import { canonicalOf, sourceFiles, sourceLeaves, toolsOf } from "#core/converters/source.converter";
import { closureLeaves, recordIdentities, recordLeaves, relationNamesOf } from "#core/converters/record.converter";
import { contactLeaf, contactOf } from "#core/converters/company.converter";
import { definitionPhrases, identityPhrases, resolveLeaves, resolveTable } from "#core/converters/identifier.converter";
import { edgeIndexOf, groundingSources, inboundRelations, outboundRelations } from "#core/converters/graph.converter";
import { facetGroups, kindFacetGroups } from "#core/converters/filter.converter";
import {
fileEntries,
queryLeaf,
recordEntries,
searchRulesOf,
searchSections,
sectionEntries,
} from "#core/converters/search.converter";
import {
navigationOf,
numberRows,
numbersLeaves,
routeLeaf,
routeStops,
} from "#core/converters/learning.catalog.converter";
import { sectionLeaves, sectionPlans } from "#core/converters/section.converter";
import type { BuiltOntology } from "#types/ontology.types";
import type { Discovery } from "#types/site.types";
import type { FolderRelations } from "#types/source.types";
import type { ModuleImporter } from "#types/loader.types";
import type { ReferenceRelation } from "@banes-lab/web/types/reference.types.js";
import type { SectionExporter } from "#types/section.types";
import { closeCatalog } from "#core/coordinators/catalog.index.coordinator";
import { createLinker } from "#core/resolvers/link.resolver";
import { createStore } from "#core/stores/catalog.store";
import { folderRelations } from "#core/converters/folder.converter";
import { guardPages } from "#core/guards/catalog.guard";
import { indexPlansOf } from "#core/converters/index.converter";
import { loadContentGraphs } from "@banes-lab/content/core/loaders/coverage.loader.ts";
import { loadWebModules } from "#core/loaders/catalog.loader";
import { nodeLinks } from "#core/converters/link.converter";
import { numbersIn } from "#core/converters/graph.code.converter";
import { placementsOf } from "#core/converters/location.converter";
import { readGraphReport } from "#core/loaders/graph.loader";
import { readJournal } from "#core/persistence/journal.persistence";
import { renderDataLeaf } from "#core/formatters/data.formatter";
import { searchLeaves } from "#core/converters/search.index.converter";
import { sourceTextOf } from "#core/loaders/source.loader";
export const buildCatalog = async function buildCatalog(
runner: ModuleImporter,
discovery: Discovery,
bodies: SectionExporter,
ontology: BuiltOntology,
): Promise<Catalog> {
guardPages(discovery);
const web = await loadWebModules(runner);
const { site } = discovery;
const { collections, context } = ontology;
const tools = toolsOf(web);
const summarize = (markdown: string): string | null =>
web.markup.summarizeMarkup(web.reference.plainMarkdown(markdown), "") || null;
const plans = sectionPlans(discovery, summarize);
const records = recordIdentities(collections, web.ontology.anchorHrefOf);
const trees = web.anatomy.ANATOMY_TREES.map((tree) => ({ ...tree, label: web.source.treeLabelOf(tree.tab) }));
const files = sourceFiles(trees, tools);
const identities = [...plans.map((plan) => plan.identity), ...records, ...files.map((file) => file.identity)];
const routes = new Set(discovery.routes.map((route) => route.path));
const linker = createLinker(identities, site, canonicalOf(site, tools), routes);
const store = createStore(site, renderDataLeaf);
const stops = routeStops(web.learning.LEARNING);
const groups = [...facetGroups(context), ...kindFacetGroups(collections)];
const indexes = indexPlansOf({
collections,
discovery,
files,
groups,
localPath: web.folder.localPath,
plans,
site,
});
const placements = placementsOf(
[...indexes.tabs, ...indexes.pages, ...indexes.collections, ...indexes.folders, ...indexes.trees],
identities,
site,
);
const placement = (ref: string): Placement | null => placements.get(ref) ?? null;
const folders = folderRelations(trees, { fileId: web.source.fileId, folderHref: web.source.folderHref }, linker);
const folder = (ref: string): FolderRelations | null => folders.get(ref) ?? null;
const { graph, report } = readGraphReport();
const edges = edgeIndexOf(graph);
const ontologyRefs = new Set(graph.nodes.filter((node) => node.layer === ONTOLOGY_LAYER).map((node) => node.ref));
const ontologyEdges = graph.edges.filter((edge) => ontologyRefs.has(edge.from) && ontologyRefs.has(edge.to));
const nodes = new Map(graph.nodes.map((node) => [node.ref, node]));
const evidence = (ref: string): readonly Link[] =>
nodeLinks(linker, nodes, edges.outgoing(ref, EVIDENCE_RELATION_ID));
const linkedBy = (ref: string): readonly Link[] =>
edges.incoming(ref, LINKS_TO_RELATION).map((edge) => linker.link(edge.label, edge.ref));
const links = (ref: string): readonly Link[] =>
edges.outgoing(ref, LINKS_TO_RELATION).map((edge) => linker.link(edge.label, edge.ref));
const sourcesOf = groundingSources(graph, edges, (ref) => linker.byRef(ref)?.href ?? null);
const groundsOf = (ref: string): readonly Link[] =>
sourcesOf(ref).map((source) => linker.link(source.label, source.ref));
store.add(
sectionLeaves(plans, {
bodies,
evidence: (href) => evidence(CHAPTER_PREFIX + href),
folder,
graphs: await loadContentGraphs(),
grounds: groundsOf,
linkedBy,
linker,
links,
navigation: navigationOf(stops, linker),
numbers: numbersIn(report),
placement,
}),
);
const checkEdges = graph.edges.filter(
(edge) => ontologyRefs.has(edge.from) && !ontologyRefs.has(edge.to) && CHECK_RELATIONS.has(edge.relation),
);
const inbound = inboundRelations({ edges: ontologyEdges, nodes: graph.nodes }, GRAPH_VOCABULARY.pairs);
const outbound = outboundRelations({ edges: [...ontologyEdges, ...checkEdges], nodes: graph.nodes });
const recordInbound = (ref: string): readonly ReferenceRelation[] => [
...(outbound.get(ref) ?? []),
...(inbound.get(ref) ?? []),
];
const tabLabels = new Map(discovery.routes.map((route) => [route.path, route.label]));
const tabLabel = (path: string): string => {
const label = tabLabels.get(path);
if (label === undefined) {
throw new Error(unroutedTab(path));
}
return label;
};
const recorded = recordLeaves(collections, {
context,
evidence,
inbound: recordInbound,
linkedBy,
linker,
placement,
tabLabel,
});
store.add(recorded);
store.add(closureLeaves(context, linker));
const grounds = async (ref: string): Promise<readonly Link[]> => groundsOf(ref);
const checks = (ref: string): readonly Relation[] =>
[...CHECK_RELATIONS].map((relation) => ({
links: edges.incoming(ref, relation).map((edge) => linker.link(edge.label, edge.ref)),
relation: reverseOf(relation) ?? relation,
}));
store.add(
await sourceLeaves(files, {
checks,
folder,
grounds,
linkedBy,
linker,
placement,
textOf: sourceTextOf,
tools,
}),
);
const aliases = aliasPhrasesOf(context);
const phrases = [...vocabularyOf(context), ...aliases, ...identityPhrases(identities), ...definitionPhrases(files)];
const words = (text: string): readonly string[] => web.matcher.markupWords(text);
const searched = [
{ entries: sectionEntries(await searchSections(web), linker), kind: SECTION_SEARCH },
{ entries: recordEntries(records, aliases, words), kind: RECORD_SEARCH },
{ entries: fileEntries(files, words), kind: FILE_SEARCH },
];
const queries = store.add([
routeLeaf(stops, linker, ROUTE_TITLE),
...numbersLeaves(numberRows(graph, discovery.routes, linker), site, NUMBERS_TITLE),
...searchLeaves(searched, searchRulesOf(web), linker),
queryLeaf(site, [
...GRAPH_VOCABULARY.pairs.flatMap((pair) => [pair.forward, pair.reverse]),
...relationNamesOf(collections, recordInbound),
]),
...resolveLeaves(resolveTable(phrases), linker),
contactLeaf(contactOf(web), site),
]);
const journal = closeCatalog(store, { discovery, indexes, journal: readJournal(), queries });
return {
files: store.files,
journal,
records: recorded.map((leaf) => leaf.page),
unresolved: linker.unresolved(),
};
};