Something went wrong. Try again.
Monorepo for Aesthetic.Computer aesthetic.computer
Something went wrong. Try again.
14 kB ยท 312 lines
JavaScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313// Boot Performance Test โ real website boot, measured in a real browser.//// This measures the AESTHETIC.COMPUTER WEBSITE boot (the browser runtime that// boot.mjs bootstraps) โ NOT the AC Native OS / `ac-os` bare-metal boot. Those// are unrelated; don't conflate them.//// It loads the live site in headless Chrome, waits for the real boot to finish,// and reads boot.mjs's own telemetry (window._bootTimings, window.acAuthTiming)// plus JS coverage. It replaces the previous simulated test, which slept on// setTimeout and measured nothing.//// node tests/performance/boot-performance.test.mjs # prod// TEST_URL=https://localhost:8888 node .../boot-performance.test.mjs # local dev//// Single browser instance on purpose (8 GB machine โ no parallel Chromium).
import puppeteer from "puppeteer";import { writeFileSync, readFileSync, mkdirSync, existsSync } from "fs";
// Optional logged-in session: a JSON file of localStorage keyโvalue pairs// (the @@auth0spajs@@... entries from a real signed-in browser). When provided// via AC_SESSION_FILE, we inject them before navigation so boot takes the// authenticated path โ that's where the historical ~8.7s userExists fetch hid.// Capture from a logged-in tab's devtools console with:// copy(JSON.stringify(Object.fromEntries(Object.entries(localStorage))))function loadSession() { const f = process.env.AC_SESSION_FILE; if (!f || !existsSync(f)) return null; try { return JSON.parse(readFileSync(f, "utf8")); } catch { return null; }}
// Prefer a system Chrome (avoids puppeteer's version-pinned download). Override// with PUPPETEER_EXECUTABLE_PATH. Falls back to puppeteer's bundled binary.function chromePath() { const env = process.env.PUPPETEER_EXECUTABLE_PATH; if (env && existsSync(env)) return env; const mac = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"; return existsSync(mac) ? mac : undefined;}
const TEST_URL = process.env.TEST_URL || "https://aesthetic.computer";const BLOCK_WORKER_MANIFEST = process.env.AC_BLOCK_WORKER_MANIFEST === "1";const EXPECT_WORKER_FALLBACK = process.env.AC_EXPECT_WORKER_FALLBACK === "1";const EXPECT_STANDARD_WORKER = process.env.AC_EXPECT_STANDARD_WORKER === "1";const EXPECT_WORKER_BUNDLE = process.env.AC_EXPECT_WORKER_BUNDLE === "1";const OUT_DIR = "./tests/performance/reports";const CORE = ["boot.mjs", "bios.mjs", "disk.mjs", "kidlisp.mjs", "graph.mjs"];const BOOT_BUDGET = 6000; // ms, anon boot target โ warn (don't fail) over this
// boot.mjs's bootTelemetry POSTs {phase:"complete", data.elapsedTotal} to// /api/boot-log exactly once when boot is fully ready (bios โ acHIDE_BOOT_LOG).// Hook fetch before navigation so we catch that authoritative completion stamp,// rather than guessing from overlay/timeline state.async function installBootProbe(page) { await page.evaluateOnNewDocument(() => { window.__bootComplete = null; window.__permissionQueries = [];
// Permission lookups can cross into the host OS and quietly delay the // critical path. Record their real duration before boot.mjs runs. if (navigator.permissions?.query) { const originalQuery = navigator.permissions.query.bind(navigator.permissions); navigator.permissions.query = async (descriptor) => { const started = performance.now(); try { const result = await originalQuery(descriptor); window.__permissionQueries.push({ name: descriptor?.name || "unknown", ms: performance.now() - started, state: result?.state || null, }); return result; } catch (error) { window.__permissionQueries.push({ name: descriptor?.name || "unknown", ms: performance.now() - started, error: error?.message || String(error), }); throw error; } }; }
const origFetch = window.fetch; window.fetch = function (url, opts) { try { const u = typeof url === "string" ? url : url?.url; if (u && u.includes("/api/boot-log") && opts?.body) { const b = JSON.parse(opts.body); if (b.phase === "complete" && !window.__bootComplete) { window.__bootComplete = { at: performance.now(), elapsedTotal: b.data?.elapsedTotal ?? null }; } } } catch {} return origFetch.apply(this, arguments); }; });}
const NAVIGATION_CONTEXT_ERROR = /Execution context was destroyed|Cannot find context with specified id|Most likely because of a navigation/i;
async function evaluateThroughUrlRewrite(page, fn, ...args) { for (let attempt = 0; attempt < 10; attempt++) { try { return await page.evaluate(fn, ...args); } catch (error) { if (!NAVIGATION_CONTEXT_ERROR.test(error?.message || String(error))) throw error; await new Promise((resolve) => setTimeout(resolve, 100)); } } throw new Error("Page context did not settle after URL rewrite");}
// Wait for the authoritative complete stamp. Fall back to a settled timeline +// rendered canvas if the complete event never arrives (e.g. telemetry blocked).async function waitForBoot(page, maxMs = 60000) { const t0 = Date.now(); let last = -1, stable = 0; while (Date.now() - t0 < maxMs) { const s = await evaluateThroughUrlRewrite(page, () => { const c = document.querySelector("canvas"); return { complete: window.__bootComplete, n: (window._bootTimings || []).length, canvas: !!c && c.width > 0, }; }); if (s.complete) return { done: true, reason: "complete-event", elapsedTotal: s.complete.elapsedTotal, n: s.n, canvas: s.canvas }; if (s.n > 0 && s.n === last) stable++; else stable = 0; last = s.n; if (stable >= 10 && s.canvas) return { done: true, reason: "timeline-settled", n: s.n, canvas: s.canvas }; // ~3s quiet, no complete event await new Promise((r) => setTimeout(r, 300)); } return { done: false, ms: Date.now() - t0 };}
function coverageByFile(entries) { const byFile = {}; let blobTotal = 0, blobUsed = 0; for (const e of entries) { const total = e.text.length; const used = e.ranges.reduce((s, r) => s + (r.end - r.start), 0); if (e.url.startsWith("blob:")) { blobTotal += total; blobUsed += used; continue; } const name = (e.url.split("/").pop() || e.url).split("?")[0]; const f = (byFile[name] ||= { total: 0, used: 0 }); f.total += total; f.used += used; } return { byFile, blob: { total: blobTotal, used: blobUsed } };}
const kb = (n) => (n / 1024).toFixed(1) + "KB";const pct = (used, total) => (total ? ((used / total) * 100).toFixed(0) + "%" : "โ");
async function run() { console.log(`\n๐ Boot Performance โ website boot (not ac-native-os)`); console.log(` target: ${TEST_URL}\n`);
const browser = await puppeteer.launch({ headless: true, executablePath: chromePath(), args: ["--no-sandbox", "--disable-setuid-sandbox", "--ignore-certificate-errors"], });
try { const page = await browser.newPage(); if (BLOCK_WORKER_MANIFEST) { await page.setRequestInterception(true); page.on("request", async (request) => { if (request.url().endsWith("/aesthetic.computer/lib/disk-worker-manifest.json")) { await request.abort("failed"); } else { await request.continue(); } }); } await installBootProbe(page);
const session = loadSession(); if (session) { await page.evaluateOnNewDocument((entries) => { try { for (const [k, v] of Object.entries(entries)) localStorage.setItem(k, v); } catch {} }, session); console.log(` session: injecting ${Object.keys(session).length} localStorage keys (authenticated path)\n`); } else { console.log(` session: none โ measuring the anonymous boot path\n`); }
await page.coverage.startJSCoverage({ resetOnNavigation: false });
const wallStart = Date.now(); // domcontentloaded, not networkidle โ AC holds live sockets + an animation // loop, so the network never goes idle and networkidle2 would hang. await page.goto(TEST_URL, { waitUntil: "domcontentloaded", timeout: 30000 });
const booted = await waitForBoot(page); const wallMs = Date.now() - wallStart;
const data = await evaluateThroughUrlRewrite(page, () => { let auth = null; try { auth = window.acAuthTiming?.computeDurations?.() || null; } catch {} const nav = performance.getEntriesByType("navigation")[0] || {}; const paint = performance.getEntriesByType("paint"); return { timings: window._bootTimings || [], permissionQueries: window.__permissionQueries || [], workerBundle: window.acWORKER_BUNDLE || null, auth, ttfb: (nav.responseStart || 0) - (nav.requestStart || 0), fcp: paint.find((p) => p.name === "first-contentful-paint")?.startTime || 0, domComplete: nav.domComplete || 0, hasCanvas: !!document.querySelector("canvas"), }; });
const resources = await evaluateThroughUrlRewrite(page, () => performance.getEntriesByType("resource") .map((r) => ({ name: (r.name.split("/").pop() || r.name).split("?")[0], ms: r.duration, size: r.transferSize })) .sort((a, b) => b.ms - a.ms).slice(0, 12));
const cov = coverageByFile(await page.coverage.stopJSCoverage());
// boot timeline (elapsed ms is measured from bootStart inside boot.mjs). // Prefer the authoritative elapsedTotal from the "complete" telemetry event; // fall back to the last logged phase, then wall time. const timeline = data.timings; const bootMs = booted.elapsedTotal ?? (timeline.length ? Math.max(...timeline.map((t) => t.elapsed || 0)) : wallMs);
console.log(`โฑ๏ธ boot: ${bootMs}ms (telemetry) wall: ${wallMs}ms ttfb: ${data.ttfb.toFixed(0)}ms fcp: ${data.fcp.toFixed(0)}ms`); console.log(` complete: ${booted.done ? booted.reason : "โ NEVER (timeout)"} canvas: ${data.hasCanvas ? "yes" : "NO"}\n`); if (data.workerBundle?.requested) { console.log(`๐ฆ worker bundle: ${data.workerBundle.ready ? "ready" : "NOT READY"} file: ${data.workerBundle.filename || "none"} fallback: ${data.workerBundle.fallback || "none"}\n`); }
console.log(`๐ boot timeline (${timeline.length} phases):`); let prev = 0; for (const t of timeline) { const d = (t.elapsed || 0) - prev; prev = t.elapsed || 0; console.log(` +${String(t.elapsed).padStart(5)}ms (${d >= 0 ? "+" : ""}${d}ms) ${t.message}`); } console.log("");
if (data.auth && Object.keys(data.auth).length) { console.log("๐ auth0 durations:"); for (const [k, v] of Object.entries(data.auth)) console.log(` ${k}: ${Math.round(v)}ms`); console.log(""); }
if (data.permissionQueries.length) { console.log("๐ permission queries:"); for (const query of data.permissionQueries) { const result = query.error ? `error: ${query.error}` : query.state; console.log(` ${query.name}: ${query.ms.toFixed(0)}ms (${result})`); } console.log(""); }
console.log("๐ฆ slowest resources:"); resources.forEach((r, i) => console.log(` ${i + 1}. ${r.name} โ ${r.ms.toFixed(0)}ms ${r.size ? "(" + kb(r.size) + ")" : ""}`)); console.log("");
console.log("๐ JS coverage โ core files (used / total):"); for (const f of CORE) { const s = cov.byFile[f]; if (s) console.log(` ${f.padEnd(12)} ${pct(s.used, s.total).padStart(4)} (${kb(s.used)} / ${kb(s.total)})`); else console.log(` ${f.padEnd(12)} โ (loaded via WS blob โ see note)`); } if (cov.blob.total) { console.log(` blob (WS) ${pct(cov.blob.used, cov.blob.total).padStart(4)} (${kb(cov.blob.used)} / ${kb(cov.blob.total)})`); console.log(` โ ๏ธ modules loaded as blob: URLs can't be mapped to filenames โ for clean per-file`); console.log(` coverage, run against local dev or with the WS loader disabled.`); } console.log("");
// assertions: it must actually boot and show a surface. speed only warns. const workerBundleOk = EXPECT_STANDARD_WORKER ? data.workerBundle && !data.workerBundle.requested && !data.workerBundle.active : EXPECT_WORKER_FALLBACK ? data.workerBundle?.requested && Boolean(data.workerBundle.fallback) : EXPECT_WORKER_BUNDLE ? data.workerBundle?.requested && data.workerBundle.active && data.workerBundle.ready && !data.workerBundle.fallback : !data.workerBundle?.requested || (data.workerBundle.active && data.workerBundle.ready && !data.workerBundle.fallback); const ok = booted.done && data.hasCanvas && timeline.length > 0 && workerBundleOk; console.log(ok ? "โ
booted with a render surface" : "โ boot did not complete / no canvas"); if (bootMs > BOOT_BUDGET) console.log(`โ ๏ธ boot ${bootMs}ms over ${BOOT_BUDGET}ms budget (baseline note, not a failure)`);
// persist a dated baseline try { mkdirSync(OUT_DIR, { recursive: true }); const stamp = new Date().toISOString().replace(/[:.]/g, "-"); const path = `${OUT_DIR}/boot-${stamp}.json`; writeFileSync(path, JSON.stringify({ url: TEST_URL, bootMs, wallMs, ttfb: data.ttfb, fcp: data.fcp, complete: booted, timeline, auth: data.auth, permissionQueries: data.permissionQueries, workerBundle: data.workerBundle, resources, coverage: cov }, null, 2)); console.log(`\n๐ baseline โ ${path}`); } catch (e) { console.log(`\nโ ๏ธ could not write baseline: ${e.message}`); }
if (!ok) process.exit(1); } finally { await browser.close(); }}
if (import.meta.url === `file://${process.argv[1]}`) { run().catch((e) => { console.error("โ test failed:", e.message); process.exit(1); });}
export { run };