// kb-headers.mjs — Parse optional authority headers from KB reference files. // Zero dependencies. Scans only the top of the file (header region) where the // **Source:** / **Primary source:** line lives, mirroring report-changes' // parseLastUpdated convention. const HEADER_REGION_BYTES = 500; const SOURCE_PATTERNS = [ /\*\*Source:\*\*\s*(\S+)/i, /\*\*Primary source:\*\*\s*(\S+)/i, ]; // Spor 3 Port 1 frontmatter contract: these live in the SAME top-500-byte bold-label // region as **Source:**, so build-registry and the create-guard read one header format. // `Verified:` and `Verified by:` are distinct labels — the date pattern below is // anchored on the colon right after "Verified" so it never swallows the "by:" line. const TYPE_PATTERN = /\*\*Type:\*\*\s*(\S+)/i; const VERIFIED_PATTERN = /\*\*Verified:\*\*\s*(\S+)/i; const VERIFIED_BY_PATTERN = /\*\*Verified by:\*\*\s*(\S+)/i; /** Scan only the header region for the first capture of `pattern`, trimmed. */ function scanHeader(content, pattern) { if (!content || typeof content !== 'string') return null; const match = content.slice(0, HEADER_REGION_BYTES).match(pattern); return match ? match[1].trim() : null; } /** * Extract the declared authority source URL from a KB reference file's header. * @param {string} content — full file content (only the first 500 bytes are scanned) * @returns {string|null} the source URL, or null if no header is present */ export function parseSourceHeader(content) { if (!content || typeof content !== 'string') return null; const head = content.slice(0, HEADER_REGION_BYTES); for (const pattern of SOURCE_PATTERNS) { const match = head.match(pattern); if (match) return match[1].trim(); } return null; } /** * Extract the Port 1 `type` (reference|template|methodology|regulatory), lowercased. * @param {string} content — full file content (only the first 500 bytes are scanned) * @returns {string|null} the declared type, or null if absent */ export function parseTypeHeader(content) { const t = scanHeader(content, TYPE_PATTERN); return t === null ? null : t.toLowerCase(); } /** * Extract the Port 1 `verified` date (when the file was last confirmed against source). * Anchored on `**Verified:**` so it never matches the `**Verified by:**` line. * @param {string} content — full file content (only the first 500 bytes are scanned) * @returns {string|null} the verified date (YYYY-MM or YYYY-MM-DD), or null if absent */ export function parseVerifiedHeader(content) { return scanHeader(content, VERIFIED_PATTERN); } /** * Extract the Port 1 `verified_by` field (`judge-vN` / `human`). * @param {string} content — full file content (only the first 500 bytes are scanned) * @returns {string|null} the verifier, or null if absent */ export function parseVerifiedByHeader(content) { return scanHeader(content, VERIFIED_BY_PATTERN); }