Files
BelgianBitcoinEmbassy/frontend/scripts/check-consistency.mjs
T
bbeandCursor 2ef68222bf feat: resolve live Nostr references in blog posts and add embeds
Support naddr/nevent/note slugs for unindexed posts, cache naddr lookups,
render Nostr embeds in markdown, and add a consistency check for events mirrors.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-29 03:57:32 +02:00

101 lines
2.9 KiB
JavaScript

#!/usr/bin/env node
/**
* Consistency check: confirms the number of upcoming meetups reported by the
* live /events page, the /events.md mirror, and the /llms.txt summary all match.
*
* Catches the exact "confidently wrong" drift this check exists to prevent:
* a mirror or summary going stale relative to the rendered page.
*
* Usage:
* node scripts/check-consistency.mjs [baseUrl]
* BASE_URL=https://belgianbitcoinembassy.org node scripts/check-consistency.mjs
*
* Exits 0 if all three agree, 1 on any mismatch or fetch failure.
*/
const baseUrl = (
process.argv[2] ||
process.env.BASE_URL ||
"http://localhost:3000"
).replace(/\/$/, "");
async function getText(path) {
const url = `${baseUrl}${path}`;
const res = await fetch(url, {
headers: { "User-Agent": "bbe-consistency-check" },
});
if (!res.ok) {
throw new Error(`GET ${url} -> ${res.status}`);
}
return res.text();
}
/** /llms.txt: "There are currently N upcoming meetup(s) scheduled." */
function parseLlmsTxt(text) {
const m = text.match(/currently\s+(\d+)\s+upcoming meetup/i);
if (!m) throw new Error("llms.txt: could not find upcoming-meetup count line");
return Number(m[1]);
}
/** /events.md: count "### " headings within the "## Upcoming" section. */
function parseEventsMd(text) {
const lines = text.split("\n");
let inUpcoming = false;
let count = 0;
let sawUpcoming = false;
for (const line of lines) {
if (/^##\s+Upcoming\b/.test(line)) {
inUpcoming = true;
sawUpcoming = true;
continue;
}
if (/^##\s+/.test(line) && inUpcoming) inUpcoming = false; // next section
if (inUpcoming && /^###\s+/.test(line)) count += 1;
}
if (!sawUpcoming) throw new Error("events.md: no '## Upcoming' section found");
return count;
}
/** /events: data-upcoming-count attribute rendered into the HTML. */
function parseEventsHtml(text) {
const m = text.match(/data-upcoming-count="(\d+)"/);
if (!m) throw new Error("events: data-upcoming-count attribute not found in HTML");
return Number(m[1]);
}
async function main() {
const [llmsTxt, eventsMd, eventsHtml] = await Promise.all([
getText("/llms.txt"),
getText("/events.md"),
getText("/events"),
]);
const counts = {
"/llms.txt": parseLlmsTxt(llmsTxt),
"/events.md": parseEventsMd(eventsMd),
"/events": parseEventsHtml(eventsHtml),
};
const values = Object.values(counts);
const allMatch = values.every((v) => v === values[0]);
console.log(`Consistency check against ${baseUrl}`);
for (const [name, value] of Object.entries(counts)) {
console.log(` ${name.padEnd(12)} upcoming = ${value}`);
}
if (!allMatch) {
console.error(
`\n✗ MISMATCH: upcoming-meetup counts disagree (${values.join(", ")}).`,
);
process.exit(1);
}
console.log(`\n✓ All sources agree: ${values[0]} upcoming meetup(s).`);
}
main().catch((err) => {
console.error(`✗ Consistency check failed: ${err.message}`);
process.exit(1);
});