75 lines
2.9 KiB
JavaScript
75 lines
2.9 KiB
JavaScript
// innboks-frontmatter.mjs
|
|
// Step 5: deterministisk frontmatter-projeksjon for et konsept (fra splitConcepts).
|
|
// Regel-basert (INGEN LLM): type/tags utledes fra title+sti og snappes mot det
|
|
// lukkede vokabularet (okf-vocab); resource = original relativ sti (kanonisk navn);
|
|
// description = foerste ikke-tomme avsnitt (whitespace kollapset, trunkert);
|
|
// timestamp = ISO-8601 av original-fil-mtime (IKKE veggklokke -> idempotens by
|
|
// construction per maskin); kilde:innboks provenans-markoer (extension key for
|
|
// retrieval-rangering). Beregner ogsaa konseptets mal-sti
|
|
// (concept.destRel = routeLevel(type)/slug.md) FOER relasjons-steget (Step 6),
|
|
// jf. Pass-2 ordering-fiks (Revisions #21). Serialiserer via writeFrontmatter
|
|
// (additiv array-gren fra Step 3). Zero npm dependencies.
|
|
|
|
import { writeFrontmatter } from './frontmatter.mjs';
|
|
import { snapType, snapTags, routeLevel, TYPE_VOCAB, TAGS_VOCAB } from './okf-vocab.mjs';
|
|
|
|
const DESCRIPTION_MAX = 240;
|
|
|
|
// Vokab sortert lengst-foerst saa "Overordnede OKR" matcher foer "OKR".
|
|
const TYPE_BY_LENGTH = [...TYPE_VOCAB].sort((a, b) => b.length - a.length);
|
|
|
|
// Regel-utledning: foerste vokab-term som forekommer i title+sti -> kanonisk type.
|
|
function deriveType(title, sourcePath) {
|
|
const hay = `${title} ${sourcePath}`.toLowerCase();
|
|
for (const t of TYPE_BY_LENGTH) {
|
|
if (hay.includes(t.toLowerCase())) return snapType(t);
|
|
}
|
|
return snapType('');
|
|
}
|
|
|
|
// Tags: vokab-termer som forekommer i title (vokab-rekkefolge bevart), saa snappet.
|
|
function deriveTags(title) {
|
|
const hay = String(title).toLowerCase();
|
|
return snapTags(TAGS_VOCAB.filter((t) => hay.includes(t.toLowerCase())));
|
|
}
|
|
|
|
// Foerste ikke-tomme avsnitt, whitespace kollapset, trunkert deterministisk.
|
|
function deriveDescription(body) {
|
|
const para = String(body)
|
|
.split(/\n\s*\n/)
|
|
.map((p) => p.replace(/\s+/g, ' ').trim())
|
|
.find((p) => p !== '');
|
|
if (!para) return '';
|
|
return para.length > DESCRIPTION_MAX ? `${para.slice(0, DESCRIPTION_MAX).trimEnd()}...` : para;
|
|
}
|
|
|
|
// concept (fra splitConcepts) -> beriket konsept med OKF-frontmatter + destRel.
|
|
export function projectFrontmatter(concept, { sourcePath, sourceMtime } = {}) {
|
|
const resource = String(sourcePath ?? '');
|
|
const type = deriveType(concept.title, resource);
|
|
const description = deriveDescription(concept.body);
|
|
const tags = deriveTags(concept.title);
|
|
const timestamp = new Date(sourceMtime).toISOString();
|
|
const destRel = `${routeLevel(type)}/${concept.slug}.md`;
|
|
|
|
// Kanonisk noekkel-rekkefolge; description/tags utelates naar tomme.
|
|
const fields = { type, resource, title: concept.title };
|
|
if (description) fields.description = description;
|
|
if (tags.length > 0) fields.tags = tags;
|
|
fields.timestamp = timestamp;
|
|
fields.kilde = 'innboks';
|
|
|
|
const frontmatter = writeFrontmatter(fields);
|
|
|
|
return {
|
|
...concept,
|
|
type,
|
|
resource,
|
|
description,
|
|
tags,
|
|
timestamp,
|
|
kilde: 'innboks',
|
|
destRel,
|
|
frontmatter,
|
|
};
|
|
}
|