initial code

This commit is contained in:
2026-07-15 17:55:53 -06:00
parent 0952a7ffce
commit 05729fc6de
53 changed files with 12965 additions and 1 deletions
+67
View File
@@ -0,0 +1,67 @@
# Utah Vehicle Health dashboard
This directory is a dependency-free static prototype. It uses semantic HTML,
CSS, vanilla ES modules, and inline SVG generated from approved aggregate JSON.
It has no database client, server credential, analytics SDK, or external CDN.
## Run locally from the repository root
```bash
node dashboard/server.mjs
```
Open `http://127.0.0.1:4173`. Do not open `index.html` directly with a `file:`
URL; browsers will block the JSON module requests. To use another port:
```bash
PORT=8080 node dashboard/server.mjs
```
Run the dependency-free contract checks with:
```bash
node --test dashboard/tests/contract.test.mjs
```
## Public-data boundary
The browser requires and validates these files beneath `public/data/`:
- `data_manifest.json`
- `overview_period_county.json`
- `age_risk_curve.json`
- `cohort_scorecard.json`
- `model_diagnostics.json`
- `coverage_quality.json`
- `filter_catalog.json`
- `sha256_manifest.json`
If any file is missing, malformed, has inconsistent publication flags, or
contains a denied identifier-like field, the dashboard shows an unavailable
state and no estimates. Regenerate assets with the repository's private local
pipeline; never hand-edit public JSON to bypass suppression.
The next-test estimator is intentionally disabled. It must remain disabled
until a separately reviewed and suppressed `prediction_lookup` contract is
approved. Do not add a connection from this site to `countydata`.
## Import into Bolt
The recommended publication boundary is a separate static Bolt project (and,
ideally, a separate deployment repository) containing only the contents of
`dashboard/`. Upload or copy this directory's contents so `index.html` is at the
new project root. There is no install or build step. If Bolt asks for a preview
command, use `node server.mjs` with `HOST=0.0.0.0`; Bolt supplies `PORT`.
Before publishing, confirm that `/public/data/data_manifest.json` resolves,
the development-preview banner remains visible, and the contract test passes.
The generated JSON bundle must also complete its privacy review.
Importing the whole source repository is a discouraged fallback because Bolt
would receive SQL, database tooling, and other files that are not needed by the
public site. If that has already happened, set the project/working root to
`dashboard`, never serve the repository root, and create a dashboard-only
project before production publication. Do not copy private `data/`,
`artifacts/`, `models/`, `.env`, or database tooling into the public project.
No publishing action is performed by this repository.
+379
View File
@@ -0,0 +1,379 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Utah Vehicle Health — an accessible, aggregate view of emissions-inspection outcomes.">
<meta name="referrer" content="no-referrer">
<title>Overview · Utah Vehicle Health</title>
<link rel="stylesheet" href="./styles.css">
<script type="module" src="./js/app.js"></script>
</head>
<body>
<a class="skip-link" href="#main-content">Skip to dashboard content</a>
<div id="preview-banner" class="preview-banner" role="status" hidden>
<span class="preview-banner__icon" aria-hidden="true">◆</span>
<div>
<strong id="preview-title">Development preview</strong>
<span id="preview-copy">Sample results are not population estimates and must not be used for individual decisions.</span>
</div>
</div>
<header class="site-header">
<div class="site-header__inner">
<a class="brand" href="#overview" aria-label="Utah Vehicle Health overview">
<svg class="brand__mark" viewBox="0 0 56 56" aria-hidden="true">
<path d="M9 43 23 17l7 12 6-9 12 23H9Z" fill="currentColor"></path>
<path d="m18 43 9-16 6 10 4-6 6 12H18Z" fill="var(--sand-100)"></path>
</svg>
<span>
<span class="brand__name">Utah Vehicle Health</span>
<span class="brand__tagline">Next-episode inspection insights</span>
</span>
</a>
<div class="header-status" id="header-status" aria-live="polite">
<span class="status-dot" aria-hidden="true"></span>
<span>Loading approved aggregates…</span>
</div>
</div>
<nav class="primary-nav" aria-label="Dashboard views">
<div class="primary-nav__inner">
<a href="#overview" data-route="overview" aria-current="page">Overview</a>
<a href="#reliability" data-route="reliability">Reliability explorer</a>
<a href="#estimator" data-route="estimator">Next-test estimator</a>
<a href="#methods" data-route="methods">Data &amp; methods</a>
</div>
</nav>
</header>
<main id="main-content" tabindex="-1">
<section class="view" id="view-overview" data-view="overview" aria-labelledby="overview-heading">
<div class="page-shell">
<header class="page-heading page-heading--split">
<div>
<p class="eyebrow">Inspection outcomes over time</p>
<h1 id="overview-heading">A clearer view of the next inspection</h1>
<p class="lede">Explore aggregate first-attempt outcomes for returning vehicles in participating Utah county feeds. Non-pass combines fail, reject, and abort.</p>
</div>
<div class="data-stamp" aria-label="Dataset status">
<span>Data through</span>
<strong id="data-cutoff">Unavailable</strong>
<small id="model-version">Waiting for manifest</small>
</div>
</header>
<div class="unavailable-state" data-unavailable hidden role="status">
<div class="unavailable-state__icon" aria-hidden="true">!</div>
<div>
<h2>Dashboard data is unavailable</h2>
<p data-unavailable-message>The approved aggregate files could not be validated. No estimates are being shown.</p>
</div>
</div>
<section class="kpi-grid" aria-label="Overview statistics" data-requires-data>
<article class="kpi-card">
<span class="kpi-card__label">Published support</span>
<strong id="kpi-eligible">—</strong>
<small>Rounded total; suppressed cells omitted</small>
</article>
<article class="kpi-card kpi-card--pass">
<span class="kpi-card__label">Pass rate</span>
<strong id="kpi-pass">—</strong>
<small>Recognized first-attempt outcomes</small>
</article>
<article class="kpi-card kpi-card--nonpass">
<span class="kpi-card__label">Non-pass rate</span>
<strong id="kpi-nonpass">—</strong>
<small>Fail + reject + abort</small>
</article>
<article class="kpi-card">
<span class="kpi-card__label">Covered counties</span>
<strong id="kpi-counties">—</strong>
<small>Not statewide coverage</small>
</article>
</section>
<div class="dashboard-grid dashboard-grid--wide" data-requires-data>
<article class="panel panel--wide">
<header class="panel__header">
<div>
<p class="eyebrow">Quarterly outcomes</p>
<h2>First-attempt non-pass trend</h2>
</div>
<div class="chart-legend" aria-label="Outcome legend">
<span><i class="legend-swatch legend-swatch--fail"></i>Non-pass (fail + reject + abort)</span>
</div>
</header>
<div id="outcome-trend-chart" class="chart chart--large" role="img" aria-label="Quarterly inspection outcome rates"></div>
<p class="chart-note" id="outcome-trend-note">Partial periods and source changes are called out when present.</p>
</article>
<article class="panel">
<header class="panel__header">
<div>
<p class="eyebrow">Feed availability</p>
<h2>County coverage</h2>
</div>
</header>
<div id="utah-coverage-map" class="utah-map" role="img" aria-label="Utah county-feed coverage"></div>
<div id="county-coverage-list" class="county-list" aria-label="County availability list"></div>
<p class="chart-note">Teal counties appear in the approved inspection feeds. Gray counties are unavailable, not zero.</p>
</article>
<article class="panel">
<header class="panel__header">
<div>
<p class="eyebrow">Age pattern</p>
<h2>Non-pass risk by vehicle age</h2>
</div>
</header>
<div id="age-risk-chart" class="chart" role="img" aria-label="Published non-pass risk by vehicle-age band"></div>
<p class="chart-note">The development bundle does not include confidence intervals; published aggregate rates are shown as points.</p>
</article>
</div>
<aside class="callout" data-requires-data>
<span class="callout__icon" aria-hidden="true">i</span>
<p><strong>What this measures:</strong> emissions-inspection outcomes for covered feeds. It is not a diagnosis of mechanical condition, safety, roadworthiness, or legal compliance.</p>
</aside>
</div>
</section>
<section class="view" id="view-reliability" data-view="reliability" aria-labelledby="reliability-heading" hidden>
<div class="page-shell">
<header class="page-heading">
<p class="eyebrow">Supported aggregate cohorts</p>
<h1 id="reliability-heading">Reliability explorer</h1>
<p class="lede">Compare published observed next-episode non-pass rates. Every result is a supported aggregate cohort—not an individual prediction.</p>
</header>
<div class="unavailable-state" data-unavailable hidden role="status">
<div class="unavailable-state__icon" aria-hidden="true">!</div>
<div><h2>Explorer unavailable</h2><p data-unavailable-message>Approved scorecards could not be validated.</p></div>
</div>
<div class="explorer-layout" data-requires-data>
<aside class="filter-panel" aria-labelledby="filter-heading">
<div class="filter-panel__heading">
<h2 id="filter-heading">Refine cohorts</h2>
<button class="text-button" id="reset-filters" type="button">Reset</button>
</div>
<form id="explorer-filters">
<label class="field">
<span>Make or model</span>
<input id="cohort-search" type="search" autocomplete="off" placeholder="Search supported cohorts">
</label>
<label class="field">
<span>County</span>
<select id="filter-county"><option value="">All covered counties</option></select>
</label>
<label class="field">
<span>Vehicle age</span>
<select id="filter-age"><option value="">All age bands</option></select>
</label>
<label class="field">
<span>Fuel</span>
<select id="filter-fuel"><option value="">All supported fuels</option></select>
</label>
<label class="field">
<span>Program</span>
<select id="filter-program"><option value="">All approved programs</option></select>
</label>
<label class="field">
<span>Period</span>
<select id="filter-period"><option value="">All published periods</option></select>
</label>
<fieldset class="segmented-control">
<legend>Risk view</legend>
<label><input type="radio" name="risk-view" value="observed" checked><span>Observed</span></label>
<label><input type="radio" name="risk-view" value="adjusted"><span>Model-adjusted</span></label>
</fieldset>
</form>
</aside>
<div class="explorer-results">
<div class="result-toolbar">
<p id="result-summary" aria-live="polite">Loading supported cohorts…</p>
<label class="field field--inline">
<span>Sort</span>
<select id="result-sort">
<option value="support">Largest support</option>
<option value="risk-desc">Highest non-pass risk</option>
<option value="risk-asc">Lowest non-pass risk</option>
<option value="name">Make and model</option>
</select>
</label>
</div>
<article class="panel">
<header class="panel__header">
<div><p class="eyebrow">Published aggregate comparison</p><h2>Supported cohort scorecard</h2></div>
</header>
<div id="cohort-dot-plot" class="chart chart--scorecard" role="img" aria-label="Ranked published cohort non-pass rates"></div>
</article>
<div id="cohort-cards" class="cohort-cards"></div>
<div class="empty-results" id="empty-results" hidden>
<h2>No supported cohorts match</h2>
<p>Try clearing one or more filters. Suppressed cohorts are never included in the browser bundle.</p>
</div>
</div>
</div>
</div>
</section>
<section class="view" id="view-estimator" data-view="estimator" aria-labelledby="estimator-heading" hidden>
<div class="page-shell page-shell--narrow">
<header class="page-heading">
<p class="eyebrow">Cohort estimate · not an individual diagnosis</p>
<h1 id="estimator-heading">Next-test risk estimator</h1>
<p class="lede">This planned tool will combine approved, coarsened attributes to return a calibrated next-episode non-pass probability.</p>
</header>
<div class="locked-panel" role="status" aria-labelledby="estimator-status-title">
<span class="locked-panel__icon" aria-hidden="true">◇</span>
<div>
<p class="eyebrow">Intentionally disabled</p>
<h2 id="estimator-status-title">An approved prediction lookup is not available</h2>
<p>The estimator will remain off until a privacy-reviewed, suppressed <code>prediction_lookup</code> is published. The current model diagnostics are not enough to serve individual or row-level estimates.</p>
</div>
</div>
<form class="estimator-form" aria-describedby="estimator-disabled-copy">
<fieldset disabled>
<legend>Coarsened cohort attributes</legend>
<div class="form-grid">
<label class="field"><span>County</span><select><option>Select a covered county</option></select></label>
<label class="field"><span>Supported make and model</span><select><option>Select a cohort</option></select></label>
<label class="field"><span>Vehicle-age band</span><select><option>Select an age band</option></select></label>
<label class="field"><span>Fuel</span><select><option>Select a supported fuel</option></select></label>
<label class="field"><span>Prior episode outcome</span><select><option>Pass / fail / reject / abort</option></select></label>
<label class="field"><span>Time since prior episode</span><select><option>Select a coarsened interval</option></select></label>
<label class="field"><span>Season</span><select><option>Select a season</option></select></label>
<label class="field"><span>Program category</span><select><option>Select an approved program</option></select></label>
</div>
<button class="button" type="button">Estimate cohort risk</button>
</fieldset>
</form>
<p id="estimator-disabled-copy" class="form-note">No VIN, plate, exact address, station, free text, or current-test diagnostic will ever be requested.</p>
<section class="method-preview" aria-labelledby="future-output-heading">
<div>
<p class="eyebrow">Future approved output</p>
<h2 id="future-output-heading">Designed for calibrated context</h2>
</div>
<ul class="check-list">
<li>Calibrated non-pass probability and uncertainty</li>
<li>Relevant aggregate baseline</li>
<li>High-level factor contributions</li>
<li>Support and coverage limitations</li>
</ul>
</section>
</div>
</section>
<section class="view" id="view-methods" data-view="methods" aria-labelledby="methods-heading" hidden>
<div class="page-shell">
<header class="page-heading">
<p class="eyebrow">Transparent by design</p>
<h1 id="methods-heading">Data &amp; methods</h1>
<p class="lede">How episodes, labels, temporal evaluation, coverage limits, and privacy controls shape every published result.</p>
</header>
<div class="unavailable-state" data-unavailable hidden role="status">
<div class="unavailable-state__icon" aria-hidden="true">!</div>
<div><h2>Live diagnostics unavailable</h2><p data-unavailable-message>Static methodology remains below; no performance values are being shown.</p></div>
</div>
<section class="method-grid" aria-label="Core definitions">
<article class="method-card">
<span class="method-card__number">01</span>
<h2>Prediction unit</h2>
<p>The first attempt of the next inspection episode. Attempts no more than 30 days apart form one episode.</p>
</article>
<article class="method-card">
<span class="method-card__number">02</span>
<h2>Binary target</h2>
<p>Pass versus non-pass. Non-pass combines fail, reject, and abort; blanks and unrecognized outcomes are not labels.</p>
</article>
<article class="method-card">
<span class="method-card__number">03</span>
<h2>Point-in-time features</h2>
<p>Every history window ends before the target episode. Current-test results, diagnostics, stations, and later attempts are excluded.</p>
</article>
</section>
<section class="panel" data-requires-data>
<header class="panel__header"><div><p class="eyebrow">Availability by source era</p><h2>Coverage timeline</h2></div></header>
<div id="coverage-heatmap" class="heatmap" role="img" aria-label="Inspection source availability by year"></div>
<p class="chart-note">The <code>slc</code> and <code>slco</code> labels are source eras for Salt Lake County, not separate physical counties.</p>
</section>
<section class="split-section" aria-labelledby="split-heading">
<div class="split-section__copy">
<p class="eyebrow">No random row split</p>
<h2 id="split-heading">Chronological evaluation</h2>
<p>Historical events establish prior context. The 2025 development-sample holdout was evaluated once after model choices were frozen, so it is no longer a pristine final test. A later complete-data run is confirmatory; the partial 2026 feed is monitoring only.</p>
</div>
<ol class="timeline">
<li><span>2010–15</span><strong>Context</strong><small>History only</small></li>
<li><span>2016–22</span><strong>Train</strong><small>Fit preprocessing + model</small></li>
<li><span>2023</span><strong>Tune</strong><small>Select regularization</small></li>
<li><span>2024</span><strong>Calibrate</strong><small>Scale probabilities</small></li>
<li class="timeline__holdout"><span>2025</span><strong>One-time holdout</strong><small>Already evaluated</small></li>
<li><span>2026</span><strong>Shadow</strong><small>Partial-period drift</small></li>
</ol>
</section>
<div class="dashboard-grid" data-requires-data>
<article class="panel">
<header class="panel__header"><div><p class="eyebrow" id="diagnostic-scope-label">Model diagnostics</p><h2>Published model diagnostics</h2></div></header>
<div class="diagnostic-kpis">
<div><span>PR-AUC</span><strong id="diagnostic-ap">—</strong></div>
<div><span>Brier score</span><strong id="diagnostic-brier">—</strong></div>
<div><span>Support</span><strong id="diagnostic-n">—</strong></div>
</div>
<div id="calibration-chart" class="chart" role="img" aria-label="Predicted versus observed non-pass risk calibration"></div>
</article>
<article class="panel">
<header class="panel__header"><div><p class="eyebrow">Interpret with care</p><h2>Known limitations</h2></div></header>
<ul class="limitation-list" id="manifest-limitations">
<li>Participating inspection feeds do not cover all 29 counties.</li>
<li>Feed and program changes can resemble real-world trends.</li>
<li>DMV history has a 2021 gap and ends in March 2024.</li>
<li>Reject and abort can reflect process or readiness issues.</li>
<li>Association is not causation or a mechanical diagnosis.</li>
</ul>
</article>
</div>
<section class="privacy-panel">
<div><p class="eyebrow">Publication boundary</p><h2>Only approved aggregates reach this site</h2></div>
<div class="privacy-flow" aria-label="Private-to-public data flow">
<span class="privacy-flow__private">Private source<br><small>Read-only local pipeline</small></span>
<span aria-hidden="true">→</span>
<span>Suppression<br><small>Minimum support + complements</small></span>
<span aria-hidden="true">→</span>
<span class="privacy-flow__public">Public assets<br><small>Aggregate JSON only</small></span>
</div>
<p>Direct identifiers, private vehicle tokens, plates, ZIPs, stations, raw records, and row-level predictions are outside the public data contract.</p>
</section>
</div>
</section>
</main>
<footer class="site-footer">
<div class="page-shell site-footer__inner">
<div><strong>Utah Vehicle Health</strong><p>Aggregate emissions-inspection research.</p></div>
<p>Not a diagnosis, certification, safety assessment, or guarantee of an inspection outcome.</p>
</div>
</footer>
<div id="route-announcer" class="sr-only" aria-live="polite"></div>
<noscript><div class="noscript">JavaScript is required to validate and display the approved aggregate data files. No estimates are shown without validation.</div></noscript>
</body>
</html>
+352
View File
@@ -0,0 +1,352 @@
import { loadDashboardData } from "./data.js";
import {
aggregateOverview,
compactNumber,
percent,
renderAgeRisk,
renderCohortDotPlot,
renderCoverageHeatmap,
renderNoCalibration,
renderOutcomeTrend,
renderUtahCoverage,
} from "./charts.js";
const ROUTES = Object.freeze({
overview: "Overview",
reliability: "Reliability explorer",
estimator: "Next-test estimator",
methods: "Data & methods",
});
const UTAH_COUNTIES = [
"Beaver",
"Box Elder",
"Cache",
"Carbon",
"Daggett",
"Davis",
"Duchesne",
"Emery",
"Garfield",
"Grand",
"Iron",
"Juab",
"Kane",
"Millard",
"Morgan",
"Piute",
"Rich",
"Salt Lake",
"San Juan",
"Sanpete",
"Sevier",
"Summit",
"Tooele",
"Uintah",
"Utah",
"Wasatch",
"Washington",
"Wayne",
"Weber",
];
let dashboardData = null;
let visibleScorecards = [];
function byId(id) {
return document.getElementById(id);
}
function setText(id, value) {
const element = byId(id);
if (element) element.textContent = value;
}
function normalizeCounty(value) {
return String(value).trim().toLowerCase().replaceAll("_", " ").replace(/\s+/g, " ");
}
function displayCategory(value) {
return String(value)
.replaceAll("_", " ")
.toLowerCase()
.replace(/\b\w/g, (letter) => letter.toUpperCase());
}
function routeFromHash() {
const candidate = window.location.hash.replace(/^#/, "").toLowerCase();
return Object.hasOwn(ROUTES, candidate) ? candidate : "overview";
}
function showRoute({ announce = true, focus = false } = {}) {
const route = routeFromHash();
for (const view of document.querySelectorAll("[data-view]")) {
view.hidden = view.dataset.view !== route;
}
for (const link of document.querySelectorAll("[data-route]")) {
if (link.dataset.route === route) link.setAttribute("aria-current", "page");
else link.removeAttribute("aria-current");
}
document.title = `${ROUTES[route]} · Utah Vehicle Health`;
if (announce) setText("route-announcer", `${ROUTES[route]} view`);
if (focus) {
const heading = document.querySelector(`[data-view="${route}"] h1`);
if (heading) {
heading.setAttribute("tabindex", "-1");
heading.focus({ preventScroll: true });
heading.scrollIntoView({ behavior: "smooth", block: "start" });
}
}
}
function initializeRouting() {
showRoute({ announce: false, focus: false });
window.addEventListener("hashchange", () => showRoute({ focus: true }));
}
function setHeaderStatus(state, message) {
const status = byId("header-status");
if (!status) return;
status.dataset.state = state;
const text = status.querySelector("span:last-child");
if (text) text.textContent = message;
}
function setPreviewBanner(manifest) {
const preview = manifest.development_preview || !manifest.population_estimate_allowed;
const banner = byId("preview-banner");
banner.hidden = !preview;
if (!preview) return;
setText("preview-title", "Development preview");
setText(
"preview-copy",
"These suppressed sample aggregates are not population estimates and must not be used for individual decisions.",
);
}
function setUnavailable(error) {
const safeMessage =
error instanceof Error && error.message
? `${error.message} No estimates are being shown.`
: "The approved aggregate files could not be validated. No estimates are being shown.";
for (const state of document.querySelectorAll("[data-unavailable]")) {
state.hidden = false;
const copy = state.querySelector("[data-unavailable-message]");
if (copy) copy.textContent = safeMessage;
}
for (const region of document.querySelectorAll("[data-requires-data]")) {
region.hidden = true;
}
const banner = byId("preview-banner");
banner.hidden = false;
setText("preview-title", "Data unavailable");
setText("preview-copy", "This static shell could not validate every required public aggregate. No fallback or estimated values are displayed.");
setHeaderStatus("error", "Approved data unavailable");
}
function populateSelect(id, values, formatter = displayCategory) {
const select = byId(id);
if (!select) return;
for (const value of values) {
const option = document.createElement("option");
option.value = String(value);
option.textContent = formatter(value);
select.append(option);
}
}
function disableUnsupportedFilter(id, explanation) {
const select = byId(id);
if (!select) return;
select.disabled = true;
select.title = explanation;
select.options[0].textContent = explanation;
}
function renderCountyList(coveredCounties) {
const container = byId("county-coverage-list");
container.replaceChildren();
const normalized = new Set(coveredCounties.map(normalizeCounty));
for (const county of UTAH_COUNTIES) {
const chip = document.createElement("span");
chip.className = "county-chip";
chip.dataset.covered = String(normalized.has(normalizeCounty(county)));
chip.textContent = county;
container.append(chip);
}
}
function renderOverview(data) {
const rows = data.overview.rows;
const periods = aggregateOverview(rows);
const totalSupport = rows.reduce((sum, row) => sum + row.support_rounded, 0);
const weightedNonpass = rows.reduce(
(sum, row) => sum + row.support_rounded * row.nonpass_rate,
0,
);
const nonpassRate = totalSupport > 0 ? weightedNonpass / totalSupport : 0;
const counties = [...new Set(rows.map((row) => row.public_county))].sort();
const latest = periods[periods.length - 1];
setText("kpi-eligible", `≈${compactNumber(totalSupport)}`);
setText("kpi-pass", `≈${percent(1 - nonpassRate)}`);
setText("kpi-nonpass", `≈${percent(nonpassRate)}`);
setText("kpi-counties", String(counties.length));
setText("data-cutoff", latest ? `${latest.year} Q${latest.quarter}` : "Unavailable");
setText(
"model-version",
`Release ${data.manifest.release_id.slice(0, 8)} · ${data.manifest.model_versions.join(" / ")}`,
);
renderOutcomeTrend(byId("outcome-trend-chart"), rows);
setText(
"outcome-trend-note",
"The current approved bundle publishes binary non-pass rates only. Four-class outcome mix and blank rates are not inferred or displayed.",
);
renderAgeRisk(byId("age-risk-chart"), data.ageRisk.rows);
renderUtahCoverage(byId("utah-coverage-map"), counties);
renderCountyList(counties);
}
function scorecardLabel(row) {
return `${row.prior_make} ${row.prior_model}`.trim();
}
function sortedScorecards(rows, mode) {
const sorted = [...rows];
if (mode === "risk-desc") sorted.sort((a, b) => b.nonpass_rate - a.nonpass_rate);
else if (mode === "risk-asc") sorted.sort((a, b) => a.nonpass_rate - b.nonpass_rate);
else if (mode === "name") sorted.sort((a, b) => scorecardLabel(a).localeCompare(scorecardLabel(b)));
else sorted.sort((a, b) => b.support_rounded - a.support_rounded);
return sorted;
}
function renderScorecardCards(rows) {
const container = byId("cohort-cards");
container.replaceChildren();
for (const row of rows.slice(0, 18)) {
const article = document.createElement("article");
article.className = "cohort-card";
const heading = document.createElement("h3");
heading.textContent = scorecardLabel(row);
const meta = document.createElement("div");
meta.className = "cohort-card__meta";
meta.textContent = `${compactNumber(row.support_rounded)} rounded support`;
const bar = document.createElement("div");
bar.className = "risk-bar";
bar.setAttribute("aria-hidden", "true");
const fill = document.createElement("span");
fill.style.width = `${Math.min(100, row.nonpass_rate * 100)}%`;
bar.append(fill);
const value = document.createElement("div");
value.className = "cohort-card__value";
const strong = document.createElement("strong");
strong.textContent = percent(row.nonpass_rate);
const small = document.createElement("small");
small.textContent = "Observed non-pass";
value.append(strong, small);
article.append(heading, meta, bar, value);
container.append(article);
}
}
function updateExplorer() {
if (!dashboardData) return;
const query = byId("cohort-search").value.trim().toLowerCase();
const sortMode = byId("result-sort").value;
const matches = dashboardData.scorecards.rows.filter((row) =>
scorecardLabel(row).toLowerCase().includes(query),
);
visibleScorecards = sortedScorecards(matches, sortMode);
setText(
"result-summary",
`${visibleScorecards.length} supported cohort${visibleScorecards.length === 1 ? "" : "s"}; showing up to 18 cards and 12 chart rows.`,
);
byId("empty-results").hidden = visibleScorecards.length > 0;
renderCohortDotPlot(byId("cohort-dot-plot"), visibleScorecards);
renderScorecardCards(visibleScorecards);
}
function initializeExplorer(data) {
populateSelect("filter-county", data.filters.public_counties);
populateSelect("filter-age", data.filters.age_bands, (value) => String(value));
populateSelect(
"filter-period",
data.filters.periods.map((period) => period.year),
(value) => String(value),
);
// Current scorecards are make/model aggregates only. These planned controls
// remain visible but disabled so the UI never implies unsupported slicing.
disableUnsupportedFilter("filter-county", "Unavailable at current scorecard grain");
disableUnsupportedFilter("filter-age", "Unavailable at current scorecard grain");
disableUnsupportedFilter("filter-fuel", "Fuel not published in this bundle");
disableUnsupportedFilter("filter-program", "Program not published in this bundle");
disableUnsupportedFilter("filter-period", "Period not published in this scorecard");
const adjusted = document.querySelector('input[name="risk-view"][value="adjusted"]');
adjusted.disabled = true;
adjusted.parentElement.title = "Model-adjusted cohort scorecards are not published.";
byId("cohort-search").addEventListener("input", updateExplorer);
byId("result-sort").addEventListener("change", updateExplorer);
byId("reset-filters").addEventListener("click", () => {
byId("explorer-filters").reset();
byId("cohort-search").value = "";
byId("result-sort").value = "support";
updateExplorer();
});
updateExplorer();
}
function chooseDiagnostic(rows) {
const partitionRank = { calibrate: 3, tune: 2, train: 1 };
return [...rows].sort((left, right) => {
const preferredLeft = left.model === "logistic_platt" ? 1 : 0;
const preferredRight = right.model === "logistic_platt" ? 1 : 0;
return (
preferredRight - preferredLeft ||
(partitionRank[right.partition] ?? 0) - (partitionRank[left.partition] ?? 0)
);
})[0];
}
function renderMethods(data) {
renderCoverageHeatmap(byId("coverage-heatmap"), data.coverage.rows);
const diagnostic = chooseDiagnostic(data.diagnostics.rows);
setText(
"diagnostic-scope-label",
diagnostic
? `${diagnostic.partition === "calibrate" ? "Calibration cohort" : "Development diagnostics"} · ${diagnostic.model}`
: "Development diagnostics · model unavailable",
);
setText("diagnostic-ap", diagnostic ? diagnostic.average_precision.toFixed(3) : "—");
setText("diagnostic-brier", diagnostic ? diagnostic.brier.toFixed(3) : "—");
setText("diagnostic-n", "Not published");
renderNoCalibration(byId("calibration-chart"));
}
function renderDashboard(data) {
dashboardData = data;
setPreviewBanner(data.manifest);
renderOverview(data);
initializeExplorer(data);
renderMethods(data);
setHeaderStatus(
"ready",
data.manifest.development_preview ? "Validated development aggregates" : "Validated public aggregates",
);
}
async function initialize() {
initializeRouting();
try {
const data = await loadDashboardData();
renderDashboard(data);
} catch (error) {
console.error("Dashboard data contract rejected the public assets.", error);
setUnavailable(error);
}
}
initialize();
export { displayCategory, normalizeCounty, routeFromHash };
+281
View File
@@ -0,0 +1,281 @@
const SVG_NS = "http://www.w3.org/2000/svg";
function escapeText(value) {
return String(value)
.replaceAll("&", "&amp;")
.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;")
.replaceAll('"', "&quot;")
.replaceAll("'", "&#039;");
}
function percent(value, digits = 1) {
return new Intl.NumberFormat("en-US", {
style: "percent",
minimumFractionDigits: digits,
maximumFractionDigits: digits,
}).format(value);
}
function compactNumber(value) {
return new Intl.NumberFormat("en-US", {
notation: value >= 10_000 ? "compact" : "standard",
maximumFractionDigits: 1,
}).format(value);
}
function emptyChart(container, message) {
container.innerHTML = `<div class="chart-empty"><p>${escapeText(message)}</p></div>`;
container.setAttribute("aria-label", message);
}
function aggregateOverview(rows) {
const groups = new Map();
for (const row of rows) {
const key = `${row.year}-Q${row.quarter}`;
const current = groups.get(key) ?? {
label: `${row.year} Q${row.quarter}`,
year: row.year,
quarter: row.quarter,
support: 0,
weightedRisk: 0,
};
current.support += row.support_rounded;
current.weightedRisk += row.support_rounded * row.nonpass_rate;
groups.set(key, current);
}
return [...groups.values()]
.map((group) => ({
...group,
risk: group.support > 0 ? group.weightedRisk / group.support : 0,
}))
.sort((left, right) => left.year - right.year || left.quarter - right.quarter);
}
export function renderOutcomeTrend(container, rows) {
const points = aggregateOverview(rows);
if (points.length < 2) {
emptyChart(container, "Not enough approved periods to draw a trend.");
return;
}
const width = 920;
const height = 330;
const margin = { top: 24, right: 22, bottom: 54, left: 58 };
const plotWidth = width - margin.left - margin.right;
const plotHeight = height - margin.top - margin.bottom;
const maxRisk = Math.max(0.05, ...points.map((point) => point.risk));
const yMax = Math.min(1, Math.ceil(maxRisk * 20) / 20);
const x = (index) => margin.left + (index / (points.length - 1)) * plotWidth;
const y = (value) => margin.top + plotHeight - (value / yMax) * plotHeight;
const path = points
.map((point, index) => `${index === 0 ? "M" : "L"}${x(index).toFixed(1)},${y(point.risk).toFixed(1)}`)
.join(" ");
const yTicks = Array.from({ length: 5 }, (_, index) => (index / 4) * yMax);
const tickEvery = Math.max(1, Math.ceil(points.length / 8));
const labelled = points.filter(
(_point, index) => index % tickEvery === 0 || index === points.length - 1,
);
const description = points
.map((point) => `${point.label}: ${percent(point.risk)}`)
.join("; ");
container.innerHTML = `
<svg viewBox="0 0 ${width} ${height}" aria-hidden="true" focusable="false">
${yTicks
.map(
(tick) => `
<line class="grid-line" x1="${margin.left}" x2="${width - margin.right}" y1="${y(tick)}" y2="${y(tick)}"></line>
<text class="axis-label" x="${margin.left - 10}" y="${y(tick) + 4}" text-anchor="end">${percent(tick, 0)}</text>`,
)
.join("")}
<line class="axis-line" x1="${margin.left}" x2="${width - margin.right}" y1="${height - margin.bottom}" y2="${height - margin.bottom}"></line>
<path class="chart-line" d="${path}"></path>
${points
.map(
(point, index) => `
<circle class="chart-dot" cx="${x(index)}" cy="${y(point.risk)}" r="4">
<title>${escapeText(point.label)}: ${percent(point.risk)} non-pass (${compactNumber(point.support)} rounded support)</title>
</circle>`,
)
.join("")}
${labelled
.map((point) => {
const index = points.indexOf(point);
return `<text class="axis-label" x="${x(index)}" y="${height - 24}" text-anchor="middle">${escapeText(point.label.replace(" ", "\u00a0"))}</text>`;
})
.join("")}
<text class="axis-label" transform="translate(16 ${height / 2}) rotate(-90)" text-anchor="middle">Non-pass rate</text>
</svg>`;
container.setAttribute(
"aria-label",
`Quarterly aggregate non-pass trend. ${description}`,
);
}
export function renderAgeRisk(container, rows) {
if (!rows.length) {
emptyChart(container, "No supported vehicle-age bands are available.");
return;
}
const points = [...rows];
const width = 600;
const height = 300;
const margin = { top: 24, right: 20, bottom: 58, left: 52 };
const plotWidth = width - margin.left - margin.right;
const plotHeight = height - margin.top - margin.bottom;
const maxRisk = Math.max(0.05, ...points.map((point) => point.nonpass_rate));
const yMax = Math.min(1, Math.ceil(maxRisk * 20) / 20);
const denominator = Math.max(1, points.length - 1);
const x = (index) => margin.left + (index / denominator) * plotWidth;
const y = (value) => margin.top + plotHeight - (value / yMax) * plotHeight;
const path = points
.map((point, index) => `${index === 0 ? "M" : "L"}${x(index)},${y(point.nonpass_rate)}`)
.join(" ");
container.innerHTML = `
<svg viewBox="0 0 ${width} ${height}" aria-hidden="true" focusable="false">
${[0, 0.25, 0.5, 0.75, 1]
.map((ratio) => {
const tick = ratio * yMax;
return `<line class="grid-line" x1="${margin.left}" x2="${width - margin.right}" y1="${y(tick)}" y2="${y(tick)}"></line>
<text class="axis-label" x="${margin.left - 8}" y="${y(tick) + 4}" text-anchor="end">${percent(tick, 0)}</text>`;
})
.join("")}
<path class="chart-line" d="${path}"></path>
${points
.map(
(point, index) => `
<circle class="chart-dot" cx="${x(index)}" cy="${y(point.nonpass_rate)}" r="5">
<title>${escapeText(point.age_band)}: ${percent(point.nonpass_rate)} (${compactNumber(point.support_rounded)} rounded support)</title>
</circle>
<text class="axis-label" x="${x(index)}" y="${height - 25}" text-anchor="middle">${escapeText(point.age_band)}</text>`,
)
.join("")}
</svg>`;
container.setAttribute(
"aria-label",
`Non-pass risk by vehicle-age band. ${points
.map((point) => `${point.age_band}: ${percent(point.nonpass_rate)}`)
.join("; ")}`,
);
}
export function renderCohortDotPlot(container, rows) {
const points = rows.slice(0, 12);
if (!points.length) {
emptyChart(container, "No supported cohorts match the current filters.");
return;
}
const width = 820;
const rowHeight = 34;
const margin = { top: 24, right: 60, bottom: 42, left: 220 };
const height = margin.top + margin.bottom + points.length * rowHeight;
const plotWidth = width - margin.left - margin.right;
const maxRisk = Math.max(0.05, ...points.map((point) => point.nonpass_rate));
const xMax = Math.min(1, Math.ceil(maxRisk * 20) / 20);
const x = (value) => margin.left + (value / xMax) * plotWidth;
container.innerHTML = `
<svg viewBox="0 0 ${width} ${height}" aria-hidden="true" focusable="false">
${[0, 0.25, 0.5, 0.75, 1]
.map((ratio) => {
const value = ratio * xMax;
return `<line class="grid-line" x1="${x(value)}" x2="${x(value)}" y1="${margin.top - 8}" y2="${height - margin.bottom + 5}"></line>
<text class="axis-label" x="${x(value)}" y="${height - 15}" text-anchor="middle">${percent(value, 0)}</text>`;
})
.join("")}
${points
.map((point, index) => {
const y = margin.top + index * rowHeight + rowHeight / 2;
const label = `${point.prior_make} ${point.prior_model}`;
return `
<text class="axis-label" x="${margin.left - 12}" y="${y + 4}" text-anchor="end">${escapeText(label)}</text>
<line x1="${margin.left}" x2="${x(point.nonpass_rate)}" y1="${y}" y2="${y}" stroke="var(--red-100)" stroke-width="8" stroke-linecap="round"></line>
<circle class="chart-dot" cx="${x(point.nonpass_rate)}" cy="${y}" r="5"><title>${escapeText(label)}: ${percent(point.nonpass_rate)}, ${compactNumber(point.support_rounded)} rounded support</title></circle>
<text class="axis-label" x="${x(point.nonpass_rate) + 10}" y="${y + 4}">${percent(point.nonpass_rate)}</text>`;
})
.join("")}
</svg>`;
container.setAttribute(
"aria-label",
`Ranked supported cohort non-pass risk. ${points
.map((point) => `${point.prior_make} ${point.prior_model}: ${percent(point.nonpass_rate)}`)
.join("; ")}`,
);
}
const COUNTY_POINTS = {
cache: [154, 40],
weber: [137, 86],
davis: [126, 108],
salt_lake: [133, 133],
"salt lake": [133, 133],
utah: [140, 170],
};
export function renderUtahCoverage(container, coveredCounties) {
const normalized = new Set(coveredCounties.map((county) => county.toLowerCase()));
const points = [...normalized]
.map((county) => ({ county, coordinates: COUNTY_POINTS[county] }))
.filter((item) => item.coordinates);
container.innerHTML = `
<svg viewBox="0 0 270 300" aria-hidden="true" focusable="false">
<path d="M79 18h101v54l18 18v183H52V116l27-27V18Z" fill="var(--gray-100)" stroke="var(--gray-300)" stroke-width="3"></path>
<path d="M80 20h98v53l17 18v179H55V117l25-27V20Z" fill="none" stroke="var(--sand-200)" stroke-width="1.5" stroke-dasharray="4 5"></path>
${points
.map(
({ county, coordinates }) => `
<circle cx="${coordinates[0]}" cy="${coordinates[1]}" r="8" fill="var(--teal-700)" stroke="var(--paper)" stroke-width="3"><title>${escapeText(county.replace("_", " "))} feed available</title></circle>`,
)
.join("")}
<text x="135" y="288" text-anchor="middle" class="axis-label">Participating feeds highlighted</text>
</svg>`;
container.setAttribute(
"aria-label",
coveredCounties.length
? `Utah feed coverage includes ${coveredCounties.join(", ")}. Other counties are unavailable.`
: "No county feed coverage is available.",
);
}
export function renderCoverageHeatmap(container, rows) {
if (!rows.length) {
emptyChart(container, "No source-era coverage rows are available.");
return;
}
const years = [...new Set(rows.map((row) => row.year))].sort((a, b) => a - b);
const eras = [...new Set(rows.map((row) => row.source_era))].sort();
const lookup = new Map(rows.map((row) => [`${row.source_era}-${row.year}`, row]));
const maxSupport = Math.max(1, ...rows.map((row) => row.support_rounded));
const columns = `90px repeat(${years.length}, minmax(34px, 1fr))`;
container.innerHTML = `
<div class="heatmap-grid" style="grid-template-columns:${columns}">
<span></span>${years.map((year) => `<span class="heatmap-label">${year}</span>`).join("")}
${eras
.map(
(era) => `<span class="heatmap-label">${escapeText(era)}</span>${years
.map((year) => {
const row = lookup.get(`${era}-${year}`);
if (!row) return `<span class="heatmap-cell" title="${escapeText(era)} ${year}: unavailable"></span>`;
const alpha = 0.15 + 0.75 * Math.sqrt(row.support_rounded / maxSupport);
return `<span class="heatmap-cell" style="background:rgb(25 116 119 / ${alpha.toFixed(2)})" title="${escapeText(era)} ${year}: ${compactNumber(row.support_rounded)} rounded support; ${percent(row.labeled_rate)} labeled"></span>`;
})
.join("")}`,
)
.join("")}
</div>`;
container.setAttribute(
"aria-label",
`Source-era coverage from ${years[0]} through ${years[years.length - 1]} for ${eras.join(", ")}.`,
);
}
export function renderNoCalibration(container) {
emptyChart(
container,
"Calibration-bin data is not published in this development bundle. No calibration curve is shown.",
);
}
export { aggregateOverview, compactNumber, percent };
+574
View File
@@ -0,0 +1,574 @@
const SCHEMA_VERSION = "dashboard_data_v1";
const APPROVED_PARTITIONS = Object.freeze(["train", "tune", "calibrate"]);
const ENVELOPE_KEYS = Object.freeze([
"development_preview",
"population_estimate_allowed",
"schema_version",
]);
export const REQUIRED_ASSETS = Object.freeze({
manifest: "data_manifest.json",
overview: "overview_period_county.json",
ageRisk: "age_risk_curve.json",
scorecards: "cohort_scorecard.json",
diagnostics: "model_diagnostics.json",
coverage: "coverage_quality.json",
filters: "filter_catalog.json",
shaManifest: "sha256_manifest.json",
});
const SENSITIVE_KEY_PARTS = new Set([
"address",
"certificate",
"email",
"internal",
"ip",
"owner",
"pid",
"plate",
"raw",
"session",
"station",
"token",
"user",
"vin",
"zip",
]);
const ASSET_VALIDATORS = {
overview: (value) => validateRows(value, validateOverviewRow, "overview_period_county"),
ageRisk: (value) => validateRows(value, validateAgeRiskRow, "age_risk_curve"),
scorecards: (value) => validateRows(value, validateScorecardRow, "cohort_scorecard"),
diagnostics: (value) => validateRows(value, validateDiagnosticRow, "model_diagnostics"),
coverage: (value) => validateRows(value, validateCoverageRow, "coverage_quality"),
filters: validateFilterCatalog,
shaManifest: validateShaManifest,
};
export class DataContractError extends Error {
constructor(message) {
super(message);
this.name = "DataContractError";
}
}
function isPlainObject(value) {
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
const prototype = Object.getPrototypeOf(value);
return prototype === Object.prototype || prototype === null;
}
function requirePlainObject(value, label) {
if (!isPlainObject(value)) {
throw new DataContractError(`${label} must be a JSON object.`);
}
}
function requireExactKeys(value, expectedKeys, label) {
const observed = Object.keys(value).sort();
const expected = [...expectedKeys].sort();
if (JSON.stringify(observed) !== JSON.stringify(expected)) {
throw new DataContractError(`${label} does not match the approved field schema.`);
}
}
function requireExactStringSet(value, expectedValues, label) {
validateStringArray(value, label);
const observed = [...value].sort();
const expected = [...expectedValues].sort();
if (JSON.stringify(observed) !== JSON.stringify(expected)) {
throw new DataContractError(`${label} does not match the approved values.`);
}
}
function requireBoolean(value, label) {
if (typeof value !== "boolean") {
throw new DataContractError(`${label} must be true or false.`);
}
}
function requireString(value, label) {
if (typeof value !== "string" || value.trim() === "") {
throw new DataContractError(`${label} must be a non-empty string.`);
}
}
function requireSafeModelVersion(value, label) {
requireString(value, label);
if (!/^[A-Za-z0-9._-]{1,64}$/.test(value)) {
throw new DataContractError(`${label} is not an approved model version.`);
}
}
function requireNumber(value, label, { min = -Infinity, max = Infinity } = {}) {
if (typeof value !== "number" || !Number.isFinite(value) || value < min || value > max) {
throw new DataContractError(`${label} must be a finite number in the approved range.`);
}
}
function requireInteger(value, label, { min = -Infinity, max = Infinity } = {}) {
requireNumber(value, label, { min, max });
if (!Number.isInteger(value)) {
throw new DataContractError(`${label} must be an integer.`);
}
}
function validateEnvelope(value, label) {
requirePlainObject(value, label);
if (value.schema_version !== SCHEMA_VERSION) {
throw new DataContractError(`${label} uses an unsupported schema version.`);
}
requireBoolean(value.development_preview, `${label}.development_preview`);
requireBoolean(
value.population_estimate_allowed,
`${label}.population_estimate_allowed`,
);
rejectSensitiveKeys(value, label);
}
function validateRows(value, rowValidator, label) {
validateEnvelope(value, label);
requireExactKeys(value, [...ENVELOPE_KEYS, "rows"], label);
if (!Array.isArray(value.rows)) {
throw new DataContractError(`${label}.rows must be an array.`);
}
value.rows.forEach((row, index) => {
requirePlainObject(row, `${label}.rows[${index}]`);
rowValidator(row, `${label}.rows[${index}]`);
});
return value;
}
function validateOverviewRow(row, label) {
requireExactKeys(
row,
["year", "quarter", "public_county", "support_rounded", "nonpass_rate"],
label,
);
requireInteger(row.year, `${label}.year`, { min: 2010, max: 2100 });
requireInteger(row.quarter, `${label}.quarter`, { min: 1, max: 4 });
requireString(row.public_county, `${label}.public_county`);
requireInteger(row.support_rounded, `${label}.support_rounded`, { min: 0 });
requireNumber(row.nonpass_rate, `${label}.nonpass_rate`, { min: 0, max: 1 });
}
function validateAgeRiskRow(row, label) {
requireExactKeys(row, ["age_band", "support_rounded", "nonpass_rate"], label);
requireString(row.age_band, `${label}.age_band`);
requireInteger(row.support_rounded, `${label}.support_rounded`, { min: 0 });
requireNumber(row.nonpass_rate, `${label}.nonpass_rate`, { min: 0, max: 1 });
}
function validateScorecardRow(row, label) {
requireExactKeys(
row,
["prior_make", "prior_model", "support_rounded", "nonpass_rate"],
label,
);
requireString(row.prior_make, `${label}.prior_make`);
requireString(row.prior_model, `${label}.prior_model`);
requireInteger(row.support_rounded, `${label}.support_rounded`, { min: 0 });
requireNumber(row.nonpass_rate, `${label}.nonpass_rate`, { min: 0, max: 1 });
}
function validateDiagnosticRow(row, label) {
requireExactKeys(
row,
[
"model",
"partition",
"average_precision",
"brier",
"log_loss",
"roc_auc",
"top_10_capture",
],
label,
);
requireString(row.model, `${label}.model`);
requireString(row.partition, `${label}.partition`);
if (!APPROVED_PARTITIONS.includes(row.partition)) {
throw new DataContractError(`${label}.partition is not approved for this public schema.`);
}
for (const metric of [
"average_precision",
"brier",
"log_loss",
"roc_auc",
"top_10_capture",
]) {
requireNumber(row[metric], `${label}.${metric}`, { min: 0 });
}
if (row.average_precision > 1 || row.brier > 1 || row.roc_auc > 1 || row.top_10_capture > 1) {
throw new DataContractError(`${label} contains a probability metric above 1.`);
}
}
function validateCoverageRow(row, label) {
requireExactKeys(
row,
[
"year",
"source_era",
"support_rounded",
"labeled_rate",
"overall_result_share",
"utah_obd_proxy_share",
],
label,
);
requireInteger(row.year, `${label}.year`, { min: 2010, max: 2100 });
requireString(row.source_era, `${label}.source_era`);
requireInteger(row.support_rounded, `${label}.support_rounded`, { min: 0 });
for (const metric of ["labeled_rate", "overall_result_share", "utah_obd_proxy_share"]) {
requireNumber(row[metric], `${label}.${metric}`, { min: 0, max: 1 });
}
}
function validateStringArray(value, label) {
if (!Array.isArray(value)) throw new DataContractError(`${label} must be an array.`);
value.forEach((item, index) => requireString(item, `${label}[${index}]`));
}
function validateFilterCatalog(value) {
const label = "filter_catalog";
validateEnvelope(value, label);
requireExactKeys(
value,
[
...ENVELOPE_KEYS,
"public_counties",
"age_bands",
"prior_make_models",
"periods",
"models",
"partitions",
],
label,
);
validateStringArray(value.public_counties, `${label}.public_counties`);
validateStringArray(value.age_bands, `${label}.age_bands`);
validateStringArray(value.models, `${label}.models`);
requireExactStringSet(value.partitions, APPROVED_PARTITIONS, `${label}.partitions`);
if (!Array.isArray(value.prior_make_models)) {
throw new DataContractError(`${label}.prior_make_models must be an array.`);
}
value.prior_make_models.forEach((row, index) => {
requirePlainObject(row, `${label}.prior_make_models[${index}]`);
requireExactKeys(
row,
["prior_make", "prior_model"],
`${label}.prior_make_models[${index}]`,
);
requireString(row.prior_make, `${label}.prior_make_models[${index}].prior_make`);
requireString(row.prior_model, `${label}.prior_make_models[${index}].prior_model`);
});
if (!Array.isArray(value.periods)) {
throw new DataContractError(`${label}.periods must be an array.`);
}
value.periods.forEach((period, index) => {
requirePlainObject(period, `${label}.periods[${index}]`);
requireExactKeys(period, ["year", "quarters"], `${label}.periods[${index}]`);
requireInteger(period.year, `${label}.periods[${index}].year`, {
min: 2010,
max: 2100,
});
if (!Array.isArray(period.quarters) || period.quarters.length === 0) {
throw new DataContractError(`${label}.periods[${index}].quarters must be non-empty.`);
}
period.quarters.forEach((quarter, quarterIndex) =>
requireInteger(quarter, `${label}.periods[${index}].quarters[${quarterIndex}]`, {
min: 1,
max: 4,
}),
);
});
return value;
}
function validateShaManifest(value) {
const label = "sha256_manifest";
validateEnvelope(value, label);
requireExactKeys(value, [...ENVELOPE_KEYS, "files"], label);
if (!Array.isArray(value.files)) {
throw new DataContractError(`${label}.files must be an array.`);
}
const expectedNames = Object.entries(REQUIRED_ASSETS)
.filter(([name]) => name !== "shaManifest")
.map(([, filename]) => filename)
.sort();
const observedNames = [];
for (const [index, file] of value.files.entries()) {
requirePlainObject(file, `${label}.files[${index}]`);
requireExactKeys(file, ["name", "sha256"], `${label}.files[${index}]`);
requireString(file.name, `${label}.files[${index}].name`);
requireString(file.sha256, `${label}.files[${index}].sha256`);
if (!/^[0-9a-f]{64}$/.test(file.sha256)) {
throw new DataContractError(`${label}.files[${index}].sha256 is invalid.`);
}
observedNames.push(file.name);
}
observedNames.sort();
if (JSON.stringify(observedNames) !== JSON.stringify(expectedNames)) {
throw new DataContractError(`${label} does not cover the exact approved asset set.`);
}
return value;
}
function rejectSensitiveKeys(value, label, seen = new WeakSet()) {
if (value === null || typeof value !== "object") return;
if (seen.has(value)) return;
seen.add(value);
if (Array.isArray(value)) {
value.forEach((item, index) => rejectSensitiveKeys(item, `${label}[${index}]`, seen));
return;
}
for (const [key, child] of Object.entries(value)) {
const normalizedParts = key.toLowerCase().split(/[^a-z0-9]+/).filter(Boolean);
if (normalizedParts.some((part) => SENSITIVE_KEY_PARTS.has(part))) {
throw new DataContractError(`${label} contains a field outside the public data contract.`);
}
rejectSensitiveKeys(child, `${label}.${key}`, seen);
}
}
export function validateAssetSet(rawAssets) {
requirePlainObject(rawAssets, "asset set");
requireExactKeys(rawAssets, Object.keys(REQUIRED_ASSETS), "asset set");
const manifest = rawAssets.manifest;
validateEnvelope(manifest, "data_manifest");
requireExactKeys(
manifest,
[
...ENVELOPE_KEYS,
"assets",
"data_scope",
"definitions",
"model_versions",
"release_id",
],
"data_manifest",
);
if (Object.hasOwn(manifest, "rows")) {
throw new DataContractError("data_manifest must not contain row data.");
}
requirePlainObject(manifest.data_scope, "data_manifest.data_scope");
requirePlainObject(manifest.definitions, "data_manifest.definitions");
requireString(manifest.release_id, "data_manifest.release_id");
if (!/^[0-9a-f]{64}$/.test(manifest.release_id)) {
throw new DataContractError("data_manifest.release_id is invalid.");
}
if (!Array.isArray(manifest.model_versions) || manifest.model_versions.length === 0) {
throw new DataContractError("data_manifest.model_versions must be a non-empty array.");
}
manifest.model_versions.forEach((version, index) =>
requireSafeModelVersion(version, `data_manifest.model_versions[${index}]`),
);
if (
JSON.stringify(manifest.model_versions) !==
JSON.stringify([...new Set(manifest.model_versions)].sort())
) {
throw new DataContractError("data_manifest.model_versions must be sorted and unique.");
}
requireExactKeys(
manifest.data_scope,
["first_year", "last_year", "model_names", "partitions"],
"data_manifest.data_scope",
);
requireInteger(manifest.data_scope.first_year, "data_manifest.data_scope.first_year", {
min: 2010,
max: 2100,
});
requireInteger(manifest.data_scope.last_year, "data_manifest.data_scope.last_year", {
min: manifest.data_scope.first_year,
max: 2100,
});
validateStringArray(manifest.data_scope.model_names, "data_manifest.data_scope.model_names");
requireExactStringSet(
manifest.data_scope.partitions,
APPROVED_PARTITIONS,
"data_manifest.data_scope.partitions",
);
requireExactKeys(
manifest.definitions,
[
"episode_gap_days",
"locked_test_metrics_published",
"support_rounding",
"suppression_min_nonpass",
"suppression_min_pass",
"suppression_min_support",
"suppression_min_distinct_vehicles",
"suppression_min_distinct_pass_vehicles",
"suppression_min_distinct_nonpass_vehicles",
"target",
],
"data_manifest.definitions",
);
requireInteger(manifest.definitions.episode_gap_days, "data_manifest.definitions.episode_gap_days", {
min: 1,
});
requireBoolean(
manifest.definitions.locked_test_metrics_published,
"data_manifest.definitions.locked_test_metrics_published",
);
if (manifest.definitions.locked_test_metrics_published !== false) {
throw new DataContractError("Locked-test metrics are not approved for this public preview.");
}
for (const key of [
"support_rounding",
"suppression_min_nonpass",
"suppression_min_pass",
"suppression_min_support",
"suppression_min_distinct_vehicles",
"suppression_min_distinct_pass_vehicles",
"suppression_min_distinct_nonpass_vehicles",
]) {
requireInteger(manifest.definitions[key], `data_manifest.definitions.${key}`, { min: 1 });
}
requireString(manifest.definitions.target, "data_manifest.definitions.target");
if (!Array.isArray(manifest.assets)) {
throw new DataContractError("data_manifest.assets must be an array.");
}
validateStringArray(manifest.assets, "data_manifest.assets");
const expectedManifestAssets = Object.entries(REQUIRED_ASSETS)
.filter(([name]) => !["manifest", "shaManifest"].includes(name))
.map(([, filename]) => filename)
.sort();
const declaredAssets = [...manifest.assets].sort();
if (JSON.stringify(declaredAssets) !== JSON.stringify(expectedManifestAssets)) {
throw new DataContractError("data_manifest.assets does not match the approved asset set.");
}
const validated = { manifest };
for (const [name, validator] of Object.entries(ASSET_VALIDATORS)) {
if (!Object.hasOwn(rawAssets, name)) {
throw new DataContractError(`A required approved aggregate is missing: ${name}.`);
}
validated[name] = validator(rawAssets[name]);
if (
validated[name].development_preview !== manifest.development_preview ||
validated[name].population_estimate_allowed !== manifest.population_estimate_allowed
) {
throw new DataContractError(`${name} publication flags disagree with data_manifest.`);
}
}
requireExactStringSet(
validated.filters.models,
manifest.data_scope.model_names,
"filter_catalog.models",
);
for (const [index, row] of validated.diagnostics.rows.entries()) {
if (!validated.filters.models.includes(row.model)) {
throw new DataContractError(`model_diagnostics.rows[${index}].model is not cataloged.`);
}
}
const minimumSupport = manifest.definitions.suppression_min_support;
const supportRounding = manifest.definitions.support_rounding;
for (const name of ["overview", "ageRisk", "scorecards", "coverage"]) {
for (const [index, row] of validated[name].rows.entries()) {
if (
row.support_rounded < minimumSupport ||
row.support_rounded % supportRounding !== 0
) {
throw new DataContractError(
`${name}.rows[${index}].support_rounded violates the publication thresholds.`,
);
}
}
}
return Object.freeze(validated);
}
async function fetchBytes(path, fetchImplementation) {
let response;
try {
response = await fetchImplementation(path, {
cache: "no-store",
credentials: "same-origin",
});
} catch {
throw new DataContractError("Approved dashboard assets could not be reached.");
}
if (!response.ok) {
throw new DataContractError("One or more approved dashboard assets are unavailable.");
}
try {
return await response.arrayBuffer();
} catch {
throw new DataContractError("An approved dashboard asset could not be read safely.");
}
}
function parseJsonBytes(bytes, label) {
if (typeof TextDecoder !== "function") {
throw new DataContractError("This browser cannot decode dashboard assets safely.");
}
try {
return JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
} catch {
throw new DataContractError(`${label} is not valid UTF-8 JSON.`);
}
}
async function sha256Hex(bytes, cryptoImplementation) {
if (!cryptoImplementation?.subtle || typeof cryptoImplementation.subtle.digest !== "function") {
throw new DataContractError("This browser cannot verify dashboard asset integrity.");
}
let digest;
try {
digest = await cryptoImplementation.subtle.digest("SHA-256", bytes);
} catch {
throw new DataContractError("Dashboard asset integrity verification failed.");
}
return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("");
}
export async function loadDashboardData({
basePath = "./public/data/",
fetchImplementation = globalThis.fetch,
cryptoImplementation = globalThis.crypto,
} = {}) {
if (typeof fetchImplementation !== "function") {
throw new DataContractError("This browser cannot load dashboard assets safely.");
}
const baseUrl = new URL(basePath, globalThis.location?.href ?? "http://local/");
const shaBytes = await fetchBytes(
new URL(REQUIRED_ASSETS.shaManifest, baseUrl),
fetchImplementation,
);
const shaManifest = validateShaManifest(
parseJsonBytes(shaBytes, "The checksum manifest"),
);
const expectedDigests = new Map(
shaManifest.files.map((entry) => [entry.name, entry.sha256]),
);
const assetEntries = Object.entries(REQUIRED_ASSETS).filter(
([name]) => name !== "shaManifest",
);
const rawEntries = await Promise.all(
assetEntries.map(async ([name, filename]) => [
name,
filename,
await fetchBytes(new URL(filename, baseUrl), fetchImplementation),
]),
);
await Promise.all(
rawEntries.map(async ([, filename, bytes]) => {
const observed = await sha256Hex(bytes, cryptoImplementation);
if (observed !== expectedDigests.get(filename)) {
throw new DataContractError(`Integrity verification failed for ${filename}.`);
}
}),
);
const parsedEntries = rawEntries.map(([name, filename, bytes]) => [
name,
parseJsonBytes(bytes, filename),
]);
parsedEntries.push(["shaManifest", shaManifest]);
return validateAssetSet(Object.fromEntries(parsedEntries));
}
export { SCHEMA_VERSION };
+14
View File
@@ -0,0 +1,14 @@
{
"name": "utah-vehicle-health-dashboard",
"version": "0.1.0",
"private": true,
"type": "module",
"description": "Dependency-free static dashboard for approved Utah Vehicle Health aggregates.",
"scripts": {
"start": "node server.mjs",
"test": "node --test tests/contract.test.mjs"
},
"engines": {
"node": ">=20"
}
}
@@ -0,0 +1 @@
{"development_preview":true,"population_estimate_allowed":false,"rows":[{"age_band":"0-3","nonpass_rate":0.058,"support_rounded":500},{"age_band":"4-7","nonpass_rate":0.06,"support_rounded":5900},{"age_band":"8-11","nonpass_rate":0.077,"support_rounded":9800},{"age_band":"12-15","nonpass_rate":0.109,"support_rounded":9700},{"age_band":"16-20","nonpass_rate":0.157,"support_rounded":8600},{"age_band":"21+","nonpass_rate":0.221,"support_rounded":4700}],"schema_version":"dashboard_data_v1"}
@@ -0,0 +1 @@
{"development_preview":true,"population_estimate_allowed":false,"rows":[{"nonpass_rate":0.116,"prior_make":"FORD","prior_model":"F150","support_rounded":1100},{"nonpass_rate":0.09,"prior_make":"HONDA","prior_model":"ACCORD","support_rounded":400},{"nonpass_rate":0.073,"prior_make":"HONDA","prior_model":"CIVIC","support_rounded":300},{"nonpass_rate":0.086,"prior_make":"TOYOTA","prior_model":"CAMRY","support_rounded":500}],"schema_version":"dashboard_data_v1"}
@@ -0,0 +1 @@
{"development_preview":true,"population_estimate_allowed":false,"rows":[{"labeled_rate":1.0,"overall_result_share":1.0,"source_era":"slc","support_rounded":3100,"utah_obd_proxy_share":0.0,"year":2016},{"labeled_rate":0.883,"overall_result_share":0.002,"source_era":"utah","support_rounded":600,"utah_obd_proxy_share":0.998,"year":2016},{"labeled_rate":1.0,"overall_result_share":1.0,"source_era":"weber","support_rounded":700,"utah_obd_proxy_share":0.0,"year":2016},{"labeled_rate":1.0,"overall_result_share":1.0,"source_era":"slc","support_rounded":3200,"utah_obd_proxy_share":0.0,"year":2017},{"labeled_rate":0.899,"overall_result_share":0.0,"source_era":"utah","support_rounded":900,"utah_obd_proxy_share":1.0,"year":2017},{"labeled_rate":1.0,"overall_result_share":1.0,"source_era":"weber","support_rounded":700,"utah_obd_proxy_share":0.0,"year":2017},{"labeled_rate":1.0,"overall_result_share":1.0,"source_era":"slc","support_rounded":3200,"utah_obd_proxy_share":0.0,"year":2018},{"labeled_rate":0.921,"overall_result_share":0.001,"source_era":"utah","support_rounded":1000,"utah_obd_proxy_share":0.999,"year":2018},{"labeled_rate":1.0,"overall_result_share":1.0,"source_era":"weber","support_rounded":800,"utah_obd_proxy_share":0.0,"year":2018},{"labeled_rate":1.0,"overall_result_share":1.0,"source_era":"slc","support_rounded":3300,"utah_obd_proxy_share":0.0,"year":2019},{"labeled_rate":0.93,"overall_result_share":0.001,"source_era":"utah","support_rounded":800,"utah_obd_proxy_share":0.999,"year":2019},{"labeled_rate":1.0,"overall_result_share":1.0,"source_era":"weber","support_rounded":800,"utah_obd_proxy_share":0.0,"year":2019},{"labeled_rate":1.0,"overall_result_share":1.0,"source_era":"slc","support_rounded":3200,"utah_obd_proxy_share":0.0,"year":2020},{"labeled_rate":1.0,"overall_result_share":1.0,"source_era":"weber","support_rounded":800,"utah_obd_proxy_share":0.0,"year":2020},{"labeled_rate":1.0,"overall_result_share":1.0,"source_era":"slc","support_rounded":2200,"utah_obd_proxy_share":0.0,"year":2021},{"labeled_rate":0.931,"overall_result_share":0.0,"source_era":"utah","support_rounded":1100,"utah_obd_proxy_share":1.0,"year":2021},{"labeled_rate":1.0,"overall_result_share":1.0,"source_era":"weber","support_rounded":800,"utah_obd_proxy_share":0.0,"year":2021},{"labeled_rate":1.0,"overall_result_share":1.0,"source_era":"slc","support_rounded":3000,"utah_obd_proxy_share":0.0,"year":2022},{"labeled_rate":0.927,"overall_result_share":0.001,"source_era":"utah","support_rounded":1100,"utah_obd_proxy_share":0.999,"year":2022},{"labeled_rate":1.0,"overall_result_share":1.0,"source_era":"weber","support_rounded":800,"utah_obd_proxy_share":0.0,"year":2022},{"labeled_rate":1.0,"overall_result_share":1.0,"source_era":"slc","support_rounded":3000,"utah_obd_proxy_share":0.0,"year":2023},{"labeled_rate":0.93,"overall_result_share":0.0,"source_era":"utah","support_rounded":1200,"utah_obd_proxy_share":1.0,"year":2023},{"labeled_rate":1.0,"overall_result_share":1.0,"source_era":"weber","support_rounded":800,"utah_obd_proxy_share":0.0,"year":2023},{"labeled_rate":1.0,"overall_result_share":1.0,"source_era":"slc","support_rounded":500,"utah_obd_proxy_share":0.0,"year":2024},{"labeled_rate":1.0,"overall_result_share":1.0,"source_era":"slco","support_rounded":300,"utah_obd_proxy_share":0.0,"year":2024},{"labeled_rate":0.929,"overall_result_share":0.0,"source_era":"utah","support_rounded":1100,"utah_obd_proxy_share":1.0,"year":2024},{"labeled_rate":1.0,"overall_result_share":1.0,"source_era":"weber","support_rounded":800,"utah_obd_proxy_share":0.0,"year":2024}],"schema_version":"dashboard_data_v1"}
+1
View File
@@ -0,0 +1 @@
{"assets":["age_risk_curve.json","cohort_scorecard.json","coverage_quality.json","filter_catalog.json","model_diagnostics.json","overview_period_county.json"],"data_scope":{"first_year":2016,"last_year":2024,"model_names":["hist_gradient_boosting_platt","hist_gradient_boosting_raw","logistic_platt","logistic_raw","previous_episode_literal","training_prevalence"],"partitions":["train","tune","calibrate"]},"definitions":{"episode_gap_days":30,"locked_test_metrics_published":false,"support_rounding":100,"suppression_min_distinct_nonpass_vehicles":10,"suppression_min_distinct_pass_vehicles":10,"suppression_min_distinct_vehicles":100,"suppression_min_nonpass":10,"suppression_min_pass":10,"suppression_min_support":100,"target":"first-attempt next-episode binary non-pass rate"},"development_preview":true,"model_versions":["baseline_v1","hist_gradient_boosting_v1"],"population_estimate_allowed":false,"release_id":"7c1af8d22d3841b84d2cf4cd5ba7bce6a43578c78bc3d32a25d200b4b1986704","schema_version":"dashboard_data_v1"}
@@ -0,0 +1 @@
{"age_bands":["0-3","4-7","8-11","12-15","16-20","21+"],"development_preview":true,"models":["hist_gradient_boosting_platt","hist_gradient_boosting_raw","logistic_platt","logistic_raw","previous_episode_literal","training_prevalence"],"partitions":["train","tune","calibrate"],"periods":[{"quarters":[1,2,3,4],"year":2016},{"quarters":[1,2,3,4],"year":2017},{"quarters":[1,2,3,4],"year":2018},{"quarters":[1,2,3,4],"year":2019},{"quarters":[1,2,3,4],"year":2020},{"quarters":[1,2,3,4],"year":2021},{"quarters":[1,2,3,4],"year":2022},{"quarters":[1,2,3,4],"year":2023},{"quarters":[1,2,3,4],"year":2024}],"population_estimate_allowed":false,"prior_make_models":[{"prior_make":"FORD","prior_model":"F150"},{"prior_make":"HONDA","prior_model":"ACCORD"},{"prior_make":"HONDA","prior_model":"CIVIC"},{"prior_make":"TOYOTA","prior_model":"CAMRY"}],"public_counties":["salt_lake","utah","weber"],"schema_version":"dashboard_data_v1"}
@@ -0,0 +1 @@
{"development_preview":true,"population_estimate_allowed":false,"rows":[{"average_precision":0.275,"brier":0.0928,"log_loss":0.3218,"model":"hist_gradient_boosting_platt","partition":"calibrate","roc_auc":0.7072,"top_10_capture":0.2744},{"average_precision":0.3966,"brier":0.0877,"log_loss":0.3011,"model":"hist_gradient_boosting_raw","partition":"train","roc_auc":0.7871,"top_10_capture":0.3658},{"average_precision":0.2743,"brier":0.1011,"log_loss":0.3431,"model":"hist_gradient_boosting_raw","partition":"tune","roc_auc":0.7147,"top_10_capture":0.2635},{"average_precision":0.275,"brier":0.0928,"log_loss":0.3218,"model":"hist_gradient_boosting_raw","partition":"calibrate","roc_auc":0.7072,"top_10_capture":0.2744},{"average_precision":0.2573,"brier":0.0936,"log_loss":0.3261,"model":"logistic_platt","partition":"calibrate","roc_auc":0.6905,"top_10_capture":0.2729},{"average_precision":0.2482,"brier":0.0963,"log_loss":0.3327,"model":"logistic_raw","partition":"train","roc_auc":0.6955,"top_10_capture":0.2593},{"average_precision":0.2821,"brier":0.1012,"log_loss":0.3448,"model":"logistic_raw","partition":"tune","roc_auc":0.7072,"top_10_capture":0.2527},{"average_precision":0.2573,"brier":0.0936,"log_loss":0.3261,"model":"logistic_raw","partition":"calibrate","roc_auc":0.6905,"top_10_capture":0.2729},{"average_precision":0.146,"brier":0.1695,"log_loss":5.8429,"model":"previous_episode_literal","partition":"train","roc_auc":0.5687,"top_10_capture":0.2128},{"average_precision":0.1658,"brier":0.1723,"log_loss":5.9436,"model":"previous_episode_literal","partition":"tune","roc_auc":0.5821,"top_10_capture":0.2291},{"average_precision":0.1476,"brier":0.1677,"log_loss":5.7909,"model":"previous_episode_literal","partition":"calibrate","roc_auc":0.5767,"top_10_capture":0.224},{"average_precision":0.116,"brier":0.1025,"log_loss":0.3588,"model":"training_prevalence","partition":"train","roc_auc":0.5,"top_10_capture":0.1},{"average_precision":0.1246,"brier":0.1092,"log_loss":0.3764,"model":"training_prevalence","partition":"tune","roc_auc":0.5,"top_10_capture":0.1},{"average_precision":0.1129,"brier":0.1002,"log_loss":0.3526,"model":"training_prevalence","partition":"calibrate","roc_auc":0.5,"top_10_capture":0.1}],"schema_version":"dashboard_data_v1"}
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
{"development_preview":true,"files":[{"name":"age_risk_curve.json","sha256":"5445d52365ad3494ba05f097e7f4f5e41fffeecfe50fbb2952d72044f496c925"},{"name":"cohort_scorecard.json","sha256":"2ae83d9d55aa1f2ccae6420c66350fb491405eedfa90374aa3df2782a4aa0bf1"},{"name":"coverage_quality.json","sha256":"35b7dbed50e1b8260aac18c269f5045010ea23649a649f984a4693f3f00a1e83"},{"name":"data_manifest.json","sha256":"b7196b04eb223683c928cfb5375ed618e1230fa03382bd8a889de24c589557a0"},{"name":"filter_catalog.json","sha256":"9e45d428cd38853002ebd0bda089eb46d0832cd1b7b6b05ac953bc8472b01eb5"},{"name":"model_diagnostics.json","sha256":"ebf99eb32436a5eabb7d754cd88109f2cc0981575dd28966757031ac7faafa57"},{"name":"overview_period_county.json","sha256":"fe2a4233563b47fa31fc87859b100983d3bfb574c5a7fbc7241b9cfb4661b038"}],"population_estimate_allowed":false,"schema_version":"dashboard_data_v1"}
+82
View File
@@ -0,0 +1,82 @@
import { createReadStream, realpathSync, statSync } from "node:fs";
import { createServer } from "node:http";
import path from "node:path";
import { fileURLToPath } from "node:url";
const ROOT = realpathSync(path.dirname(fileURLToPath(import.meta.url)));
const HOST = process.env.HOST || "127.0.0.1";
const PORT = Number.parseInt(process.env.PORT || "4173", 10);
const TYPES = new Map([
[".css", "text/css; charset=utf-8"],
[".html", "text/html; charset=utf-8"],
[".js", "text/javascript; charset=utf-8"],
[".json", "application/json; charset=utf-8"],
[".mjs", "text/javascript; charset=utf-8"],
[".svg", "image/svg+xml"],
]);
const PUBLIC_PATHS = new Set([
"/index.html",
"/styles.css",
"/js/app.js",
"/js/charts.js",
"/js/data.js",
"/public/data/age_risk_curve.json",
"/public/data/cohort_scorecard.json",
"/public/data/coverage_quality.json",
"/public/data/data_manifest.json",
"/public/data/filter_catalog.json",
"/public/data/model_diagnostics.json",
"/public/data/overview_period_county.json",
"/public/data/sha256_manifest.json",
]);
function safeFile(requestUrl) {
let pathname;
try {
pathname = decodeURIComponent(new URL(requestUrl, "http://local").pathname);
} catch {
return { file: null, status: 400 };
}
if (pathname === "/") pathname = "/index.html";
if (!PUBLIC_PATHS.has(pathname)) return { file: null, status: 404 };
try {
const candidate = realpathSync(path.join(ROOT, pathname.slice(1)));
if (!candidate.startsWith(`${ROOT}${path.sep}`) || !statSync(candidate).isFile()) {
return { file: null, status: 404 };
}
return { file: candidate, status: 200 };
} catch {
return { file: null, status: 404 };
}
}
const server = createServer((request, response) => {
if (!request.url || !["GET", "HEAD"].includes(request.method || "")) {
response.writeHead(405, { Allow: "GET, HEAD" });
response.end("Method not allowed");
return;
}
const resolved = safeFile(request.url);
if (!resolved.file) {
response.writeHead(resolved.status, { "Content-Type": "text/plain; charset=utf-8" });
response.end(resolved.status === 400 ? "Bad request" : "Not found");
return;
}
const file = resolved.file;
const extension = path.extname(file).toLowerCase();
response.writeHead(200, {
"Content-Type": TYPES.get(extension) || "application/octet-stream",
"Cache-Control": extension === ".json" ? "no-store" : "public, max-age=300",
"X-Content-Type-Options": "nosniff",
"Referrer-Policy": "no-referrer",
});
if (request.method === "HEAD") {
response.end();
return;
}
createReadStream(file).on("error", () => response.destroy()).pipe(response);
});
server.listen(PORT, HOST, () => {
process.stdout.write(`Utah Vehicle Health dashboard: http://${HOST}:${PORT}\n`);
});
+1358
View File
File diff suppressed because it is too large Load Diff
+196
View File
@@ -0,0 +1,196 @@
import assert from "node:assert/strict";
import { createHash, webcrypto } from "node:crypto";
import { readFileSync, readdirSync, statSync } from "node:fs";
import path from "node:path";
import test from "node:test";
import { fileURLToPath } from "node:url";
import {
DataContractError,
REQUIRED_ASSETS,
SCHEMA_VERSION,
loadDashboardData,
validateAssetSet,
} from "../js/data.js";
const DASHBOARD_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const PUBLIC_DATA = path.join(DASHBOARD_ROOT, "public", "data");
function json(filename) {
return JSON.parse(readFileSync(path.join(PUBLIC_DATA, filename), "utf8"));
}
function assetSet() {
return Object.fromEntries(
Object.entries(REQUIRED_ASSETS).map(([name, filename]) => [name, json(filename)]),
);
}
function publicDataFetch(overrides = new Map()) {
return async (requestUrl) => {
const filename = path.basename(new URL(String(requestUrl)).pathname);
const source = path.join(PUBLIC_DATA, filename);
let bytes;
try {
bytes = overrides.has(filename) ? overrides.get(filename) : readFileSync(source);
} catch {
return { ok: false, async arrayBuffer() { return new ArrayBuffer(0); } };
}
return {
ok: true,
async arrayBuffer() {
return Uint8Array.from(bytes).buffer;
},
};
};
}
function filesRecursively(directory) {
return readdirSync(directory).flatMap((name) => {
const target = path.join(directory, name);
return statSync(target).isDirectory() ? filesRecursively(target) : [target];
});
}
test("static shell exposes four semantic navigable views", () => {
const html = readFileSync(path.join(DASHBOARD_ROOT, "index.html"), "utf8");
assert.match(html, /<header\b/);
assert.match(html, /<nav\b[^>]*aria-label="Dashboard views"/);
assert.match(html, /<main\b/);
assert.match(html, /<footer\b/);
for (const route of ["overview", "reliability", "estimator", "methods"]) {
assert.match(html, new RegExp(`data-route="${route}"`));
assert.match(html, new RegExp(`data-view="${route}"`));
}
assert.match(html, /2025 development-sample holdout was evaluated once/i);
assert.match(html, /<strong>One-time holdout<\/strong><small>Already evaluated<\/small>/);
assert.doesNotMatch(html, /<strong>Locked test<\/strong>|<small>Final evaluation<\/small>/);
});
test("estimator remains disabled and contains no identifying input", () => {
const html = readFileSync(path.join(DASHBOARD_ROOT, "index.html"), "utf8");
const estimator = html.match(/<section class="view" id="view-estimator"[\s\S]*?<\/section>/)?.[0];
assert.ok(estimator, "estimator section is present");
assert.match(estimator, /<fieldset disabled>/);
const controlAttributes = [...estimator.matchAll(/<(?:input|select|textarea)\b([^>]*)>/g)].map(
(match) => match[1],
);
for (const attributes of controlAttributes) {
assert.doesNotMatch(
attributes,
/(?:name|id)\s*=\s*["'][^"']*(?:vin|plate|address|station|free.?text|zip)[^"']*["']/i,
);
}
});
test("all required generated assets satisfy the browser contract", () => {
const validated = validateAssetSet(assetSet());
assert.equal(validated.manifest.schema_version, SCHEMA_VERSION);
assert.equal(validated.manifest.development_preview, true);
assert.equal(validated.manifest.population_estimate_allowed, false);
assert.equal(validated.manifest.definitions.locked_test_metrics_published, false);
assert.match(validated.manifest.release_id, /^[0-9a-f]{64}$/);
assert.ok(validated.manifest.model_versions.length > 0);
assert.deepEqual(
validated.filters.partitions,
["train", "tune", "calibrate"],
);
assert.ok(validated.overview.rows.length > 0);
});
test("browser loader verifies every raw asset digest before rendering", async () => {
const validated = await loadDashboardData({
basePath: "http://dashboard.test/public/data/",
fetchImplementation: publicDataFetch(),
cryptoImplementation: webcrypto,
});
assert.equal(validated.manifest.schema_version, SCHEMA_VERSION);
const changedOverview = Buffer.concat([
readFileSync(path.join(PUBLIC_DATA, REQUIRED_ASSETS.overview)),
Buffer.from("\n"),
]);
await assert.rejects(
loadDashboardData({
basePath: "http://dashboard.test/public/data/",
fetchImplementation: publicDataFetch(
new Map([[REQUIRED_ASSETS.overview, changedOverview]]),
),
cryptoImplementation: webcrypto,
}),
(error) =>
error instanceof DataContractError &&
/Integrity verification failed for overview_period_county\.json/.test(error.message),
);
});
test("sha256 manifest covers and matches every approved data asset", () => {
const manifest = json(REQUIRED_ASSETS.shaManifest);
const expectedNames = Object.entries(REQUIRED_ASSETS)
.filter(([name]) => name !== "shaManifest")
.map(([, filename]) => filename)
.sort();
assert.deepEqual(
manifest.files.map((file) => file.name).sort(),
expectedNames,
);
for (const entry of manifest.files) {
const digest = createHash("sha256")
.update(readFileSync(path.join(PUBLIC_DATA, entry.name)))
.digest("hex");
assert.equal(digest, entry.sha256, `${entry.name} digest`);
}
});
test("contract fails closed for missing, inconsistent, or sensitive data", () => {
const missing = assetSet();
delete missing.overview;
assert.throws(() => validateAssetSet(missing), DataContractError);
const inconsistent = assetSet();
inconsistent.ageRisk.population_estimate_allowed = true;
assert.throws(() => validateAssetSet(inconsistent), DataContractError);
const sensitive = assetSet();
sensitive.scorecards.rows[0].vehicle_token = "not-public";
assert.throws(() => validateAssetSet(sensitive), DataContractError);
const extraRowField = assetSet();
extraRowField.overview.rows[0].note = "unapproved";
assert.throws(() => validateAssetSet(extraRowField), DataContractError);
const lockedMetrics = assetSet();
lockedMetrics.manifest.definitions.locked_test_metrics_published = true;
assert.throws(() => validateAssetSet(lockedMetrics), DataContractError);
const extraPartition = assetSet();
extraPartition.filters.partitions.push("locked_test");
assert.throws(() => validateAssetSet(extraPartition), DataContractError);
const unsortedVersions = assetSet();
unsortedVersions.manifest.model_versions = ["z_v1", "a_v1"];
assert.throws(() => validateAssetSet(unsortedVersions), DataContractError);
const belowSuppression = assetSet();
belowSuppression.overview.rows[0].support_rounded =
belowSuppression.manifest.definitions.suppression_min_support - 1;
assert.throws(() => validateAssetSet(belowSuppression), DataContractError);
const incorrectlyRounded = assetSet();
incorrectlyRounded.coverage.rows[0].support_rounded += 1;
assert.throws(() => validateAssetSet(incorrectlyRounded), DataContractError);
});
test("dashboard source has no external runtime dependency or credential marker", () => {
const sourceFiles = filesRecursively(DASHBOARD_ROOT).filter(
(filename) =>
!filename.includes(`${path.sep}public${path.sep}data${path.sep}`) &&
!filename.includes(`${path.sep}tests${path.sep}`) &&
/\.(?:html|css|js|mjs)$/.test(filename),
);
for (const filename of sourceFiles) {
const source = readFileSync(filename, "utf8");
assert.doesNotMatch(source, /(?:postgres(?:ql)?:\/\/|PGPASSWORD|PGHOST|countydata\.)/i);
assert.doesNotMatch(source, /<script[^>]+src=["']https?:|@import\s+url\(["']?https?:/i);
}
});