55 lines
2.9 KiB
JavaScript
55 lines
2.9 KiB
JavaScript
// Benchmark — ADR-131 §8 / ADR-126 §1.1.
|
||
// HOMECORE exists partly because HA's frontend is a ~5 MB Lit bundle
|
||
// (ADR-126 §1.1). This benchmark enforces a hard bundle budget and
|
||
// measures cold render throughput for all 10 panels.
|
||
// Run: node tests/benchmark.mjs
|
||
import { install } from './dom-shim.mjs';
|
||
install();
|
||
import { readFileSync, readdirSync, statSync } from 'node:fs';
|
||
import { resolve } from 'node:path';
|
||
|
||
const ROOT = resolve(import.meta.dirname, '..');
|
||
const BUDGET_BYTES = 250 * 1024; // 250 KB total — vs HA's ~5 MB (20× smaller)
|
||
|
||
function walk(dir) {
|
||
let total = 0; const rows = [];
|
||
for (const name of readdirSync(dir)) {
|
||
if (name === 'tests' || name === 'node_modules') continue;
|
||
const p = resolve(dir, name); const s = statSync(p);
|
||
if (s.isDirectory()) { const sub = walk(p); total += sub.total; rows.push(...sub.rows); }
|
||
else if (/\.(js|css|html|json)$/.test(name)) { total += s.size; rows.push([p.replace(ROOT + '/', ''), s.size]); }
|
||
}
|
||
return { total, rows };
|
||
}
|
||
|
||
const { total, rows } = walk(ROOT);
|
||
rows.sort((a, b) => b[1] - a[1]);
|
||
console.log('── Bundle size (uncompressed) ──');
|
||
for (const [f, sz] of rows.slice(0, 8)) console.log(` ${(sz / 1024).toFixed(1).padStart(7)} KB ${f}`);
|
||
console.log(` ${'-'.repeat(40)}`);
|
||
console.log(` ${(total / 1024).toFixed(1).padStart(7)} KB TOTAL across ${rows.length} files`);
|
||
console.log(` budget ${(BUDGET_BYTES / 1024).toFixed(0)} KB · HA baseline ~5120 KB · ratio ${(5120 * 1024 / total).toFixed(1)}× smaller`);
|
||
|
||
// ── render throughput ───────────────────────────────────────────────
|
||
const { api } = await import('../js/api.js');
|
||
const ctx = { api, navigate() {}, params: { id: 'seed-livingroom-a1' }, onEvent() { return () => {}; }, onWs(fn) { fn({ state: 'open', lagged: false }); return () => {}; } };
|
||
const PANELS = ['dashboard', 'fleet', 'seed-detail', 'entities', 'rooms', 'cogs', 'calibration', 'events', 'audit', 'settings'];
|
||
const mods = {};
|
||
for (const p of PANELS) mods[p] = (await import(`../js/panels/${p}.js`)).default;
|
||
|
||
console.log('\n── Cold render throughput (avg of 50 renders each) ──');
|
||
let worst = 0;
|
||
for (const p of PANELS) {
|
||
const N = 50; const t0 = performance.now();
|
||
for (let i = 0; i < N; i++) { const root = document.createElement('div'); const c = await mods[p].render(root, ctx); if (typeof c === 'function') c(); }
|
||
const ms = (performance.now() - t0) / N;
|
||
worst = Math.max(worst, ms);
|
||
console.log(` ${ms.toFixed(3).padStart(7)} ms/render ${p}`);
|
||
}
|
||
|
||
console.log('');
|
||
let exit = 0;
|
||
if (total > BUDGET_BYTES) { console.error(`FAIL — bundle ${(total / 1024).toFixed(1)} KB exceeds ${(BUDGET_BYTES / 1024).toFixed(0)} KB budget`); exit = 1; }
|
||
else console.log(`OK — bundle within budget; slowest panel ${worst.toFixed(2)} ms/render`);
|
||
process.exit(exit);
|