okr closed patch-lane #2 (commit 35a0bb8): scripts/okf-check.mjs now reads type: at root level only, so a type nested only in a sources[] entry no longer passes as typed. Both implementations now agree the fixture's concept is untyped, so the manifest's own note2 prediction ("flips to 'agree' only if okr scopes the checker's reader without touching inject") has come true. Renamed the fixture (manifest key + corpus directory) to match the red-*/green-* naming convention the parity unit test enforces, and updated okf-frontmatter.mjs's header comment to match. check-okf-parity.mjs: 10/10 fixtures pass. Full suite: 120/120.
46 lines
2.3 KiB
JavaScript
46 lines
2.3 KiB
JavaScript
// okf-frontmatter.mjs
|
|
// Minimal TOP-LEVEL frontmatter reader for the shared OKF conformance checker.
|
|
// Provenance: vendored from okr's lib/frontmatter.mjs at c06e4d7 so the catalog-hosted
|
|
// checker is self-contained — zero npm dependencies, no cross-repo import. The checker
|
|
// only reads (never writes), so only parseFrontmatter().get is vendored. okr has since
|
|
// added BOM/CRLF normalization here (lib/frontmatter.mjs:23); this copy has not, so the
|
|
// two have diverged and are no longer byte-identical (parity is the parity-gate's job,
|
|
// not this file's).
|
|
//
|
|
// SECOND DIVERGENCE, NOW CLOSED ON OKR'S CHECKER SIDE (2026-08-01, converged 2026-08-09):
|
|
// the key is anchored at column 0 here. okr's shared reader (lib/frontmatter.mjs) still
|
|
// keeps `^\s*key:` — that lookup remains load-bearing for their injector
|
|
// (lib/frontmatter.mjs:7-9 -> inject:69) — but okr's patch-lane #2 (commit 35a0bb8) scoped
|
|
// their CHECKER's caller (okf-check.mjs) to root-level fields only, so the two checkers now
|
|
// agree: §4.1's keys are top-level, and `sources[]` entries carry their own
|
|
// `resource`/`title`/`type`. Pinned as parity fixture `green-nested-key`.
|
|
|
|
const FM_RE = /^---\n([\s\S]*?)\n---/;
|
|
|
|
export function parseFrontmatter(content) {
|
|
const match = String(content).match(FM_RE);
|
|
const raw = match ? match[1] : null;
|
|
|
|
const get = (key) => {
|
|
if (raw === null) return null;
|
|
// Anchored at column 0: OKF's §4.1 keys are TOP-LEVEL keys. `sources[]` entries carry their
|
|
// own `resource`/`title`/`type` (§5:302), and §? :467-469 names them as distinct fields from
|
|
// the same-named top-level ones. A leading \s* matched those nested entries and reported the
|
|
// top-level key as present — a false negative, on `type` too (§4.1's only required field).
|
|
const m = raw.match(new RegExp(`^${key}:\\s*(.*)$`, 'm'));
|
|
if (!m) return null;
|
|
let v = m[1].trim();
|
|
if (v === '') return null;
|
|
const q = v[0];
|
|
if (q === '"' || q === "'") {
|
|
const end = v.indexOf(q, 1);
|
|
if (end !== -1) return v.slice(1, end); // internal '#' preserved
|
|
v = v.slice(1); // unterminated quote: fall back to the rest
|
|
} else {
|
|
v = v.replace(/\s+#.*$/, '').trim(); // unquoted: strip trailing comment
|
|
}
|
|
return v === '' ? null : v;
|
|
};
|
|
|
|
return { raw, get };
|
|
}
|