health
import { summarizeHealth, writeHealthSummary, readHealthSummary, healthIssueCount,} from "louise-toolkit/health";The site-health co-pilot’s data layer: one owner-facing snapshot composed from primitives the toolkit already has. Binding: a KV namespace. No required peers.
Why it’s persisted rather than computed
Section titled “Why it’s persisted rather than computed”The three inputs have very different costs, and the split follows from that:
- Broken links come from a crawl—network, seconds, driven by a Cron Trigger. Far too slow for a dashboard request.
- Missing alt and SEO gaps are cheap
COUNTs the site computes at scan time.
So a scheduled scan assembles everything and writes it; the dashboard only ever reads. That is why the module is a summarize/write/read triple rather than a “get health” call.
Assembling and storing
Section titled “Assembling and storing”function summarizeHealth(input: HealthInput): HealthSummary;function writeHealthSummary(kv, summary, opts?: { key?; ttlSeconds? }): Promise<void>;function readHealthSummary(kv, key?): Promise<HealthSummary | null>;// in a scheduled handlerconst broken = await checkLinks({ base, paths });await writeHealthSummary( env.HEALTH_KV, summarizeHealth({ brokenLinks: broken, missingAlt, seoGaps }),);Counts are guarded to non-negative integers, so a bad input can’t skew the traffic
light. now is injectable for deterministic tests.
brokenLinkDetails is capped at MAX_BROKEN_LINK_DETAILS (50)—the counts
stay exact, the details are a sample for a list view, so one badly broken deploy
can’t bloat the stored blob.
Omit ttlSeconds to keep the summary until the next scan overwrites it. A stale
snapshot is more useful than none, and checkedAt tells the dashboard how old it
is.
readHealthSummary returns null for both “nothing stored” and “stored blob is
unparseable”—a corrupt value degrades to no-data rather than throwing inside a
dashboard request.
Folding in Core Web Vitals
Section titled “Folding in Core Web Vitals”HealthSummary.cwv holds a CwvSummary once a scan adds
one. Absent means “not measured yet” and the panel says so—distinct from
measured-and-poor, which is a real result.
Reading it
Section titled “Reading it”function healthIssueCount(summary: HealthSummary): number;The “N things need attention” number: broken links + missing alt + SEO gaps. CWV is deliberately not in it—a slow LCP is not a countable defect the way a 404 is, and adding it would make the number jump for something you can’t fix by editing one page.
HealthSummary is shape-compatible with overview.health (the extra detail field
is ignored there), so the overview route can return a stored summary directly
rather than re-mapping it.
HealthSummary, HealthInput, HealthKV. Constants: HEALTH_KV_KEY
("louise:health:summary"), MAX_BROKEN_LINK_DETAILS.
HealthKV is structural—get/put—so a real KVNamespace satisfies it
without this module importing Workers types.