SummerProject2026/dashboard/js/data.js

605 lines
20 KiB
JavaScript

const SCHEMA_VERSION = "dashboard_data_v1";
const APPROVED_PARTITIONS = Object.freeze(["train", "tune", "calibrate"]);
const APPROVED_TARGET = "first-attempt next-episode binary non-pass rate";
const REQUIRED_CALIBRATION_MODELS = Object.freeze([
"hist_gradient_boosting_platt",
"logistic_platt",
]);
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",
"credential",
"email",
"inspector",
"internal",
"ip",
"owner",
"password",
"pid",
"plate",
"prediction",
"probability",
"raw",
"secret",
"session",
"station",
"technician",
"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");
if (!manifest.development_preview || manifest.population_estimate_allowed) {
throw new DataContractError(
"The dashboard accepts development-sample, non-population assets only.",
);
}
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 (manifest.definitions.target !== APPROVED_TARGET) {
throw new DataContractError("data_manifest.definitions.target is outside the approved scope.");
}
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.`);
}
}
for (const model of REQUIRED_CALIBRATION_MODELS) {
const matches = validated.diagnostics.rows.filter(
(row) => row.model === model && row.partition === "calibrate",
);
if (matches.length !== 1) {
throw new DataContractError(
`model_diagnostics must contain exactly one approved calibration row for ${model}.`,
);
}
}
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 };