@buoy-gg/mcp 7.0.67 → 7.0.68
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -1
- package/dist/camera-KL6KTSFE.mjs +1 -0
- package/dist/chunk-D4P6PPYP.mjs +422 -0
- package/dist/chunk-DVRR7W22.mjs +146 -0
- package/dist/chunk-FOMLVT7K.mjs +1 -0
- package/dist/{chunk-LDULNQCZ.mjs → chunk-KXEKQVCL.mjs} +145 -139
- package/dist/chunk-OP23W722.mjs +3 -0
- package/dist/chunk-OUCB2Y5J.mjs +5 -0
- package/dist/{chunk-7XCEVM3W.mjs → chunk-PDYZSHOS.mjs} +1 -1
- package/dist/chunk-SPQBWBV6.mjs +4 -0
- package/dist/cli.js +583 -154
- package/dist/cli.mjs +14 -11
- package/dist/flowRun-F3PQIRPB.mjs +2 -0
- package/dist/freeView-3LHV3RBG.mjs +1 -0
- package/dist/index.d.mts +7 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +193 -151
- package/dist/index.mjs +1 -1
- package/dist/policy-D342AXEF.mjs +1 -0
- package/dist/simFarm-V7TBSSV2.mjs +1 -0
- package/dist/src-N7WTCDAL.mjs +1 -0
- package/dist/{webBench-QCHYXYCA.mjs → webBench-BEMJWKB3.mjs} +1 -1
- package/package.json +4 -4
- package/skill/buoy-optimize/MEASURING.md +3 -1
- package/skill/buoy-optimize/SKILL.md +10 -0
- package/dist/camera-F5Q7WMHF.mjs +0 -1
- package/dist/chunk-2MSKDRYW.mjs +0 -2
- package/dist/chunk-AMU2EOGE.mjs +0 -6
- package/dist/chunk-OPHPYPJD.mjs +0 -146
- package/dist/chunk-VKWX4XAY.mjs +0 -5
- package/dist/flowRun-WBOCHITQ.mjs +0 -2
- package/dist/simFarm-OFT42BGC.mjs +0 -1
- package/dist/src-DPWEFWQY.mjs +0 -1
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import{createRequire as et}from"module";const xt=et(import.meta.url);import{createRequire as tt}from"module";import{execFileSync as ke}from"child_process";import{join as fe}from"path";import{closeSync as ve,linkSync as xe,mkdirSync as nt,openSync as Ee,readFileSync as st,renameSync as rt,statSync as Te,unlinkSync as Z,writeFileSync as Pe,writeSync as at}from"fs";import{tmpdir as ot}from"os";import{pathToFileURL as it}from"url";var lt=`(() => {
|
|
2
|
+
const supported = (t) => {
|
|
3
|
+
try { return (PerformanceObserver.supportedEntryTypes || []).includes(t); } catch { return false; }
|
|
4
|
+
};
|
|
5
|
+
const frames = [], longTasks = [], loaf = [], shifts = [], events = [], observers = [];
|
|
6
|
+
const watch = (type, cb, extra) => {
|
|
7
|
+
if (!supported(type)) return false;
|
|
8
|
+
const o = new PerformanceObserver((l) => l.getEntries().forEach(cb));
|
|
9
|
+
o.observe(Object.assign({ type, buffered: false }, extra || {}));
|
|
10
|
+
observers.push([o, cb]);
|
|
11
|
+
return true;
|
|
12
|
+
};
|
|
13
|
+
const hasLong = watch("longtask", (e) => longTasks.push({ start: e.startTime, end: e.startTime + e.duration }));
|
|
14
|
+
const hasLoaf = watch("long-animation-frame", (e) => loaf.push({
|
|
15
|
+
startTime: e.startTime,
|
|
16
|
+
duration: e.duration,
|
|
17
|
+
blockingDuration: e.blockingDuration || 0,
|
|
18
|
+
styleAndLayoutStart: e.styleAndLayoutStart || 0,
|
|
19
|
+
scripts: (e.scripts || []).map((s) => {
|
|
20
|
+
const file = s.sourceURL ? s.sourceURL.split("?")[0].split("/").pop() : "";
|
|
21
|
+
const where = s.sourceFunctionName ? s.sourceFunctionName + (file ? " @ " + file : "") : file;
|
|
22
|
+
// Invokers like "FrameRequestCallback" say how a script ran, not which
|
|
23
|
+
// one; add the function and file when the browser gives them.
|
|
24
|
+
const name = s.invoker ? (where ? s.invoker + " \u2192 " + where : s.invoker) : where || "(anonymous)";
|
|
25
|
+
return { duration: s.duration, name, forced: s.forcedStyleAndLayoutDuration || 0 };
|
|
26
|
+
}),
|
|
27
|
+
}));
|
|
28
|
+
// 16 ms is Event Timing's smallest threshold; the default (104) hides most taps.
|
|
29
|
+
const hasEvents = watch("event", (e) => {
|
|
30
|
+
if (e.interactionId) events.push({
|
|
31
|
+
name: e.name, startTime: e.startTime, duration: e.duration,
|
|
32
|
+
processingStart: e.processingStart, processingEnd: e.processingEnd, interactionId: e.interactionId,
|
|
33
|
+
});
|
|
34
|
+
}, { durationThreshold: 16 });
|
|
35
|
+
// Click proof: which step selectors each real click landed inside, read
|
|
36
|
+
// at the moment of the click (capture phase, before the page's handlers).
|
|
37
|
+
const clickTargets = Array.isArray(window.__buoyBenchClickTargets) ? window.__buoyBenchClickTargets : [];
|
|
38
|
+
const clicks = [], keys = [];
|
|
39
|
+
// Only real (trusted) input counts as proof; a click the page fakes in
|
|
40
|
+
// script doesn't. Input after the run's end doesn't count either.
|
|
41
|
+
const onClick = (e) => {
|
|
42
|
+
if (!e.isTrusted || ended) return;
|
|
43
|
+
const t = e.target;
|
|
44
|
+
const hit = clickTargets.filter((sel) => { try { return t && t.closest && t.closest(sel); } catch { return false; } });
|
|
45
|
+
clicks.push({ t: performance.now(), hit });
|
|
46
|
+
};
|
|
47
|
+
const onKey = (e) => {
|
|
48
|
+
if (!e.isTrusted || ended) return;
|
|
49
|
+
keys.push({ t: performance.now(), key: e.key, ctrl: e.ctrlKey, shift: e.shiftKey, alt: e.altKey, meta: e.metaKey });
|
|
50
|
+
};
|
|
51
|
+
// Expect steps, judged inside end() so no page work can slip in between
|
|
52
|
+
// the end of the run and the check.
|
|
53
|
+
const expects = Array.isArray(window.__buoyBenchExpects) ? window.__buoyBenchExpects : [];
|
|
54
|
+
let expectResults = null;
|
|
55
|
+
document.addEventListener("click", onClick, true);
|
|
56
|
+
document.addEventListener("keydown", onKey, true);
|
|
57
|
+
const hasShift = watch("layout-shift", (e) => shifts.push({ startTime: e.startTime, value: e.value, hadRecentInput: e.hadRecentInput }));
|
|
58
|
+
let hidden = document.visibilityState === "hidden";
|
|
59
|
+
const onVis = () => { if (document.visibilityState === "hidden") hidden = true; };
|
|
60
|
+
document.addEventListener("visibilitychange", onVis);
|
|
61
|
+
// Latched: a resize or zoom that's undone before stop still changed the work.
|
|
62
|
+
let resized = false;
|
|
63
|
+
const onResize = () => { resized = true; };
|
|
64
|
+
window.addEventListener("resize", onResize);
|
|
65
|
+
// A pixel-ratio change (moving to another screen) may not fire resize.
|
|
66
|
+
let dprQuery = null;
|
|
67
|
+
try {
|
|
68
|
+
dprQuery = matchMedia("(resolution: " + window.devicePixelRatio + "dppx)");
|
|
69
|
+
dprQuery.addEventListener("change", onResize);
|
|
70
|
+
} catch {}
|
|
71
|
+
let raf = 0;
|
|
72
|
+
const tick = (t) => { frames.push(t); raf = requestAnimationFrame(tick); };
|
|
73
|
+
raf = requestAnimationFrame(tick);
|
|
74
|
+
const start = performance.now();
|
|
75
|
+
// Trace markers: the driver counts Chrome's frames between these two.
|
|
76
|
+
try { performance.mark("__buoy_bench_start"); } catch {}
|
|
77
|
+
const domStart = document.getElementsByTagName("*").length;
|
|
78
|
+
let ended = 0;
|
|
79
|
+
window.__buoyBench = {
|
|
80
|
+
// Two steps so the driver can read Chrome's counters between them:
|
|
81
|
+
// end() freezes the end time at once; collect() then waits up to two
|
|
82
|
+
// frames (capped at 300 ms) so an input's entry, which only arrives after
|
|
83
|
+
// its paint, isn't lost. Entries after the end are filtered out later.
|
|
84
|
+
end: () => {
|
|
85
|
+
if (ended) return ended;
|
|
86
|
+
ended = performance.now();
|
|
87
|
+
try { performance.mark("__buoy_bench_end"); } catch {}
|
|
88
|
+
cancelAnimationFrame(raf);
|
|
89
|
+
expectResults = expects.map((x) => {
|
|
90
|
+
const n = document.querySelector(x.selector);
|
|
91
|
+
const full = n ? (n.textContent || "") : null;
|
|
92
|
+
// Match against the full text here; only a short snippet goes back.
|
|
93
|
+
return { selector: x.selector, text: x.text == null ? null : x.text, found: !!n, matched: full != null && (x.text == null || full.includes(x.text)), content: full == null ? null : full.slice(0, 200) };
|
|
94
|
+
});
|
|
95
|
+
return ended;
|
|
96
|
+
},
|
|
97
|
+
collect: () => new Promise((resolve) => {
|
|
98
|
+
const end = window.__buoyBench.end();
|
|
99
|
+
let done = false;
|
|
100
|
+
const finish = () => {
|
|
101
|
+
if (done) return;
|
|
102
|
+
done = true;
|
|
103
|
+
document.removeEventListener("visibilitychange", onVis);
|
|
104
|
+
window.removeEventListener("resize", onResize);
|
|
105
|
+
document.removeEventListener("click", onClick, true);
|
|
106
|
+
document.removeEventListener("keydown", onKey, true);
|
|
107
|
+
try { dprQuery && dprQuery.removeEventListener("change", onResize); } catch {}
|
|
108
|
+
for (const [o, cb] of observers) { o.takeRecords().forEach(cb); o.disconnect(); }
|
|
109
|
+
resolve({
|
|
110
|
+
start, end, frames,
|
|
111
|
+
longTasks: hasLong ? longTasks : null,
|
|
112
|
+
loaf: hasLoaf ? loaf : null,
|
|
113
|
+
shifts: hasShift ? shifts : null,
|
|
114
|
+
events: hasEvents ? events : null,
|
|
115
|
+
clicks,
|
|
116
|
+
keys,
|
|
117
|
+
expectResults,
|
|
118
|
+
domStart,
|
|
119
|
+
domEnd: document.getElementsByTagName("*").length,
|
|
120
|
+
hidden,
|
|
121
|
+
resized,
|
|
122
|
+
});
|
|
123
|
+
};
|
|
124
|
+
requestAnimationFrame(() => requestAnimationFrame(finish));
|
|
125
|
+
setTimeout(finish, 300);
|
|
126
|
+
}),
|
|
127
|
+
};
|
|
128
|
+
return true;
|
|
129
|
+
})()`;var ct=`(() => (window.__buoyBench ? window.__buoyBench.end() : null))()`;var ut=`(() => (window.__buoyBench ? window.__buoyBench.collect() : null))()`;var dt=`new Promise((resolve) => {
|
|
130
|
+
let n = 0, t0 = 0;
|
|
131
|
+
const tick = (t) => {
|
|
132
|
+
if (!t0) t0 = t;
|
|
133
|
+
n++;
|
|
134
|
+
if (t - t0 >= 1000) resolve(((n - 1) / (t - t0)) * 1000);
|
|
135
|
+
else requestAnimationFrame(tick);
|
|
136
|
+
};
|
|
137
|
+
requestAnimationFrame(tick);
|
|
138
|
+
})`;function Re(e,r,t=[]){const a=e.median;const s=[];const n=[];const i=(u,l,d)=>{if(l==null)return;if(d==null)n.push(u);else if(d>l)s.push(`${u} ${d} > ${l}`)};i("droppedPct",r.droppedPct,e.droppedMin!=null?a?.droppedPct:null);const o=e.runs.filter(u=>!u.failed&&u.fps!=null);const c=t.some(u=>"click"in u||"press"in u||"type"in u);const p=o.map(u=>u.interactions==null||!u.inputCaptureComplete?null:u.worstInteractionMs??16);i("inputMs",r.inputMs,c&&p.length&&p.every(u=>u!=null)?Math.max(...p):null);const f=o.map(u=>Math.max(u.frameGapMaxMs??0,u.edgeSilenceMs??0));i("freezeMs",r.freezeMs,f.length?Math.max(...f):null);i("mainThreadPct",r.mainThreadPct,a?.mainThreadPct);i("jsHeapMb",r.jsHeapMb,a?.heapMb);return{meets:Boolean(a)&&s.length===0&&n.length===0,over:s,unknown:n}}var x=e=>Math.round(e*10)/10;var _e=`(() => {
|
|
139
|
+
try {
|
|
140
|
+
const gl = document.createElement("canvas").getContext("webgl2") || document.createElement("canvas").getContext("webgl");
|
|
141
|
+
if (!gl) return null;
|
|
142
|
+
const ext = gl.getExtension("WEBGL_debug_renderer_info");
|
|
143
|
+
return String(gl.getParameter(ext ? ext.UNMASKED_RENDERER_WEBGL : gl.RENDERER));
|
|
144
|
+
} catch { return null; }
|
|
145
|
+
})()`;function he(e){return/swiftshader|llvmpipe|softpipe|software|microsoft basic render/i.test(e)}function Ce(e,r){if(e.length===0)return null;const t=[...e].sort((s,n)=>s-n);const a=Math.ceil(r/100*t.length)-1;return t[Math.min(t.length-1,Math.max(0,a))]}var ft=[30,60,75,90,120,144,165,240];function Ae(e){for(const r of ft)if(Math.abs(e-r)/r<=.08)return r;return Math.max(1,Math.round(e))}function pe(e,r){let t=r>>>0;const a=()=>{t=t+1831565813>>>0;let n=t;n=Math.imul(n^n>>>15,n|1);n^=n+Math.imul(n^n>>>7,n|61);return((n^n>>>14)>>>0)/4294967296};const s=[...e];for(let n=s.length-1;n>0;n--){const i=Math.floor(a()*(n+1));[s[n],s[i]]=[s[i],s[n]]}return s}function Le(e,r,t){const a=new URL(e);const s=r.path??t;if(s)a.pathname=s.startsWith("/")?s:`${a.pathname.replace(/\/$/,"")}/${s}`;for(const[n,i]of Object.entries(r.params??{}))a.searchParams.set(n,i);return a.toString()}var ht=["STATE_PRESENTED_ALL","STATE_PRESENTED_PARTIAL","STATE_DROPPED"];function Fe(e){const r=d=>e.find(M=>M.name===d&&M.ts!=null);const t=r("__buoy_bench_start");const a=r("__buoy_bench_end");if(!t||!a||a.ts<=t.ts)return null;if(t.pid!==a.pid)return null;const s=t.pid;const n={STATE_PRESENTED_ALL:0,STATE_PRESENTED_PARTIAL:1,STATE_DROPPED:2};const i=new Map;for(const d of e){if(d.name!=="PipelineReporter"||d.ph!=="b")continue;if(s!=null&&d.pid!==s)continue;if(d.ts==null||d.ts<t.ts||d.ts>a.ts)continue;const M=d.args?.frame_reporter;if(!M||M.frame_sequence==null)continue;const S=M.state;if(!ht.includes(S))continue;const T=`${M.layer_tree_host_id??""}:${M.frame_source??""}:${M.frame_sequence}`;const b=Boolean(M.affects_smoothness)&&S!=="STATE_PRESENTED_ALL";const A=i.get(T);if(!A)i.set(T,{state:S,fault:b});else{if(n[S]>n[A.state])A.state=S;A.fault=A.fault||b}}let o=0;let c=0;let p=0;let f=0;for(const d of i.values()){if(d.state==="STATE_PRESENTED_ALL")o++;else if(d.state==="STATE_PRESENTED_PARTIAL")c++;else p++;if(d.fault)f++}const u=o+c+p;if(u===0)return null;const l=(a.ts-t.ts)/1e6;return{full:o,partial:c,dropped:p,faults:f,shownFps:x((o+c)/l),droppedPct:x(f/u*100)}}function Ne(e,r){const t=e.split("+");const a=t[t.length-1];const s=new Set(t.slice(0,-1).map(o=>o.toLowerCase()));if(r.key!==a)return false;const n=a.toLowerCase();const i={ctrl:s.has("control")||n==="control",alt:s.has("alt")||n==="alt",meta:s.has("meta")||s.has("command")||n==="meta",shift:s.has("shift")||n==="shift"};if(Boolean(r.ctrl)!==i.ctrl||Boolean(r.alt)!==i.alt||Boolean(r.meta)!==i.meta)return false;if(!i.shift&&r.shift&&a.length!==1)return false;if(i.shift&&!r.shift)return false;return true}function De(e){const r=[];for(const t of e){const a=t.startTime+t.duration;const s=r.find(i=>i.interactionId===t.interactionId&&Math.abs(i.startTime+i.duration-a)<=8);if(!s){r.push({...t});continue}const n=s.startTime+s.duration;s.startTime=Math.min(s.startTime,t.startTime);s.processingStart=Math.min(s.processingStart,t.processingStart);s.processingEnd=Math.max(s.processingEnd,t.processingEnd);s.duration=Math.max(n,a)-s.startTime;if(t.processingEnd-t.processingStart>0)s.name=t.name}return r}function Be(e,r){const{start:t,end:a}=e;const s=Math.max(1,a-t);const n=[];for(let m=1;m<e.frames.length;m++)n.push(e.frames[m]-e.frames[m-1]);const i=1e3/r;const o=Ce(n,95);let c=null;if(e.longTasks){let m=0;for(const k of e.longTasks){const q=Math.min(k.end,a)-Math.max(k.start,t);if(q>0)m+=q}c=x(Math.min(100,m/s*100))}let p=null;let f=null;let u=null;let l=[];let d=0;if(e.loaf){let m=0;let k=0;let q=0;const ne=new Map;for(const E of e.loaf){if(E.startTime>=t&&E.startTime<=a&&E.startTime+E.duration>a)d++;if(E.startTime<t||E.startTime+E.duration>a)continue;m+=E.blockingDuration;if(E.styleAndLayoutStart>E.startTime)k+=Math.max(0,E.startTime+E.duration-E.styleAndLayoutStart);for(const L of E.scripts){q+=L.forced;const W=ne.get(L.name)??{ms:0,count:0};W.ms+=L.duration;W.count++;ne.set(L.name,W)}}p=x(m);f=x(k);u=x(q);l=[...ne.entries()].map(([E,L])=>({name:E,ms:x(L.ms),count:L.count})).sort((E,L)=>L.ms-E.ms).slice(0,5)}let M=null;if(e.shifts){let m=0;for(const k of e.shifts)if(!k.hadRecentInput&&k.startTime>=t&&k.startTime<=a)m+=k.value;M=Math.round(m*1e3)/1e3}let S=null;let T=null;if(e.events){const m=De(e.events.filter(k=>k.startTime>=t&&k.startTime<=a));S=new Set(m.map(k=>k.interactionId)).size;for(const k of m)if(!T||k.duration>T.duration)T=k}const b=T;const A=e.frames;const te=[...(e.clicks??[]).map(m=>m.t),...(e.keys??[]).map(m=>m.t)];const I=te.length?Math.max(...te):null;const re=I==null||A.filter(m=>m>I).length>=2;const oe=A.length===0?s:Math.max(0,A[0]-t,a-A[A.length-1]);return{edgeSilenceMs:x(oe),frameGapCount:n.length,inputCaptureComplete:re,resized:Boolean(e.resized),interactions:S,worstInteractionMs:b?x(b.duration):null,worstInputDelayMs:b?x(Math.max(0,b.processingStart-b.startTime)):null,worstProcessingMs:b?x(Math.max(0,b.processingEnd-b.processingStart)):null,worstPresentationMs:b?x(Math.max(0,b.startTime+b.duration-b.processingEnd)):null,fps:x(e.frames.length/s*1e3),frameGapP95Ms:o==null?null:x(o),frameGapMaxMs:n.length?x(Math.max(...n)):null,longFrames:n.filter(m=>m>i*1.5).length,longTaskPct:c,loafBlockingMs:p,styleLayoutMs:f,forcedLayoutMs:u,topScripts:l,loafCutOff:d,layoutShift:M,domNodesStart:e.domStart,domNodesEnd:e.domEnd}}function Ie(e,r,t){const a=o=>o in e&&o in r?r[o]-e[o]:null;const s=o=>{const c=a(o);return c==null?null:x(c*1e3)};const n=s("TaskDuration");const i=s("Timestamp")??t;return{mainThreadPct:n==null?null:x(n/Math.max(1,i)*100),scriptMs:s("ScriptDuration"),styleMs:s("RecalcStyleDuration"),layoutMs:s("LayoutDuration"),layoutCount:a("LayoutCount"),styleCount:a("RecalcStyleCount"),heapMb:"JSHeapUsedSize"in r?x(r.JSHeapUsedSize/(1024*1024)):null}}function ze(e,r){const t=[];if(e.droppedPct!=null&&e.droppedPct>=5)t.push(`${e.droppedPct}% of frames dropped or partial`);if(e.fps!=null&&e.fps<r*.95)t.push("Low main-thread frame rate");if(e.frameGapCount&&e.longFrames/e.frameGapCount>=.02)t.push("Frequent long frames");const a=Math.max(e.frameGapMaxMs??0,e.edgeSilenceMs??0);if(a>=100)t.push(`Freeze of ${Math.round(a)} ms`);if(e.mainThreadPct!=null&&e.mainThreadPct>=80)t.push("Main thread near full");if(e.longTaskPct!=null&&e.longTaskPct>=20)t.push("Long tasks block the main thread");if(e.forcedLayoutMs!=null&&e.forcedLayoutMs>=50)t.push(`Forced layout in scripts (${Math.round(e.forcedLayoutMs)} ms)`);if(e.layoutShift!=null&&e.layoutShift>=.1)t.push(`Layout shifts (${e.layoutShift})`);if(e.worstInteractionMs!=null&&e.worstInteractionMs>=200){t.push(`Slow response to input (${Math.round(e.worstInteractionMs)} ms)`)}return t}function me(e,r){const t=e.filter(c=>!c.failed&&c.fps!=null);const a=[...t].sort((c,p)=>(c.fps??0)-(p.fps??0));const s=t.filter(c=>c.droppedPct!=null).length;const n=t.length>0&&s===t.length;const i=n?[...t].sort((c,p)=>c.droppedPct-p.droppedPct):a;const o=i.length?i[Math.floor((i.length-1)/2)]:null;return{caseName:e[0]?.caseName??"",cpu:e[0]?.cpu??1,validRuns:t.length,plannedRuns:e.length,isBaseline:false,deltaDroppedPct:null,tracedRuns:s,...(()=>{const c=n?t.map(f=>f.droppedPct):[];const p=t.map(f=>f.worstInteractionMs).filter(f=>f!=null);return{droppedMin:c.length?Math.min(...c):null,droppedMax:c.length?Math.max(...c):null,inputMin:p.length?Math.min(...p):null,inputMax:p.length?Math.max(...p):null}})(),runs:e,median:o,fpsMin:a.length?a[0].fps:null,fpsMax:a.length?a[a.length-1].fps:null,flags:o?ze(n?o:{...o,droppedPct:null},r):[]}}var Oe="Playwright isn't installed in this project. Install it, then run again:\n npm i -D playwright && npx playwright install chromium";async function je(e){const r=tt(fe(e,"noop.js"));for(const t of["playwright","playwright-core"]){let a;try{a=r.resolve(t)}catch{continue}const s=await import(it(a).href);const n=s.chromium??s.default?.chromium;if(n)return n}throw new Error(`${Oe}
|
|
146
|
+
(Searched from ${e}. Pass projectRoot to look elsewhere.)`)}async function G(e,r,t){let a;const s=new Promise(n=>{a=setTimeout(()=>n("timeout"),r)});try{const n=await Promise.race([e,s]);if(n==="timeout"){e.catch(()=>{});return await t()}return n}finally{clearTimeout(a)}}function pt(e){try{return ke("git",["rev-parse","HEAD"],{cwd:e,stdio:["ignore","pipe","ignore"],timeout:3e3}).toString().trim()}catch{return null}}function mt(e){try{const r=ke("git",["status","--porcelain"],{cwd:e,stdio:["ignore","pipe","ignore"],timeout:5e3});return r.toString().trim().length>0}catch{return null}}async function U(e,r){if(!e)return true;return G(e.close().then(()=>true,()=>false),r,async()=>false)}function Ge(e){if(!e.cases?.length)throw new Error("Give at least one case.");const r=new Set;for(const i of e.cases){if(!i.name?.trim())throw new Error("Every case needs a name.");if(r.has(i.name))throw new Error(`Two cases are named "${i.name}". Give each case its own name.`);r.add(i.name)}const t=(i,o,c,p)=>{if(i==null)return;if(!Number.isInteger(i)||i<c||i>p)throw new Error(`${o} must be a whole number from ${c} to ${p}.`)};const a=(i,o,c)=>{if(i==null)return;if(!Number.isFinite(i)||i<c)throw new Error(`${o} must be a number of at least ${c}.`)};const s=(i,o,c,p)=>{if(i==null)return;if(!Number.isFinite(i)||i<c||i>p)throw new Error(`${o} must be from ${c} to ${p}.`)};t(e.runsPerCase,"runsPerCase",1,10);s(e.durationMs,"durationMs",500,12e4);s(e.settleMs,"settleMs",0,6e4);s(e.refreshHz,"refreshHz",1,500);const n=e.cpuSlowdown??[];for(const i of n)s(i,"each cpuSlowdown rate",1,20);if(new Set(n).size!==n.length)throw new Error("cpuSlowdown has the same rate twice.");if(e.viewport){t(e.viewport.width,"viewport.width",200,8e3);t(e.viewport.height,"viewport.height",200,8e3);s(e.viewport.deviceScaleFactor,"viewport.deviceScaleFactor",.5,4)}t(e.extraRoundsWhenClose,"extraRoundsWhenClose",0,10);s(e.maxBatchMs,"maxBatchMs",1e4,6*60*6e4);for(const[i,o]of Object.entries(e.budgets??{}))s(o,`budgets.${i}`,0,1e9);for(const i of e.steps??[]){if("wait"in i)s(i.wait,"a wait step",0,6e4);if("scroll"in i)s(i.scroll,"a scroll step",-1e5,1e5)}new URL(e.url)}function ae(e){return e.replace(/\u001b\[[0-9;]*m/g,"").split("\n")[0].trim().slice(0,160)}var wt=["disabled-by-default-devtools.timeline.frame","blink.user_timing"];var gt=["devtools.timeline","disabled-by-default-devtools.timeline","disabled-by-default-devtools.timeline.frame","blink.user_timing","loading","latencyInfo","v8.execute"];var we=15e7;var ge=e=>new Promise(r=>setTimeout(r,e));async function Ue(e,r){const t=new Map;for(const a of r){if("expect"in a)continue;const s="click"in a?a.click:"type"in a?a.type.selector:null;if(!s||t.has(s))continue;const n=await e.$(s);const i=n?await n.boundingBox():null;if(!i)throw new Error(`step target ${s} isn't on the page (or isn't visible)`);t.set(s,{x:i.x+i.width/2,y:i.y+i.height/2})}return t}async function yt(e,r,t){for(const a of r){if("click"in a){const s=t.get(a.click);await e.mouse.click(s.x,s.y)}else if("press"in a)await e.keyboard.press(a.press);else if("type"in a){const s=t.get(a.type.selector);await e.mouse.click(s.x,s.y);await e.keyboard.type(a.type.text)}else if("scroll"in a)await e.mouse.wheel(0,a.scroll);else if("wait"in a)await ge(a.wait)}}function He(e,r,t=[]){const a=[...r].sort((f,u)=>f.t-u.t);const s=[...t].sort((f,u)=>f.t-u.t);let n=0;let i=0;let o=-Infinity;const c=f=>{while(n<a.length){const u=a[n++];if(u.t>=o&&u.hit.includes(f)){o=u.t;return true}}return false};const p=f=>{while(i<s.length){const u=s[i++];if(u.t>=o&&Ne(f,u)){o=u.t;return true}}return false};for(const[f,u]of e.entries()){if("click"in u&&!c(u.click)){return`step ${f+1} (click ${u.click}) has no real click on its target, in order (moved, covered, or ignored)`}if("type"in u){if(!c(u.type.selector))return`step ${f+1} (type into ${u.type.selector}) has no real click on its target`;for(const l of u.type.text){if(!p(l))return`step ${f+1} (type "${u.type.text}") didn't deliver the key "${l}"`}}if("press"in u&&!p(u.press))return`step ${f+1} (press ${u.press}) didn't deliver that key`}return null}async function Mt(e,r){for(const t of r){if(!("expect"in t))continue;const{selector:a,text:s}=t.expect;const n=await e.evaluate(`(() => { const n = document.querySelector(${JSON.stringify(a)}); if (!n) return false; ${s==null?"return true;":`return (n.textContent || "").includes(${JSON.stringify(s)});`} })()`);if(n)return s==null?a:`${a} contains "${s}"`}return null}async function bt(e,r){for(const[t,a]of r){const s=await e.$(t);const n=s?await s.boundingBox():null;if(!n)return`step target ${t} was gone after the run`;const i=n.x+n.width/2;const o=n.y+n.height/2;if(Math.abs(i-a.x)>4||Math.abs(o-a.y)>4){return`step target ${t} moved during the run, so a click may have missed it`}}return null}function qe(e){for(const r of e){if(!r.found)return`expected ${r.selector} at the end of the run, but it isn't there`;const t=r.matched??(r.text==null||(r.content??"").includes(r.text));if(!t){return`expected ${r.selector} to contain "${r.text}" at the end of the run, got "${(r.content??"").slice(0,60)}"`}}return null}async function We(e){const{metrics:r}=await e.send("Performance.getMetrics");return Object.fromEntries(r.map(t=>[t.name,t.value]))}async function $t(e,r=()=>{}){Ge(e);const t=await Je(e.lockWaitMs??20*6e4,r);const a={browserLeftRunning:false};try{return await St(e,r,a)}finally{t(a.browserLeftRunning)}}async function Je(e,r=()=>{},t=2e3){const a=Date.now()+Math.max(0,e);let s=false;for(;;){try{return Ke()}catch(n){const i=n?.message??String(n);const o=/Another web benchmark|already running/.test(i);if(!o||Date.now()+t>a)throw n;if(!s){r("another web benchmark is running; waiting for it to finish");s=true}await new Promise(c=>setTimeout(c,t))}}}var ee=fe(ot(),"buoy-web-bench.lock");var ye=false;function Ke(){if(ye){throw new Error("A web benchmark is already running in this process (or left a stuck browser). Wait, or restart the MCP server.")}const e=`${process.pid}:${Date.now()}:${Math.random().toString(36).slice(2)}`;const r=JSON.stringify({pid:process.pid,token:e,startedAt:new Date().toISOString()});const t=()=>{const n=`${ee}.${e.replace(/[^\w]/g,"")}.tmp`;try{Pe(n,r);xe(n,ee);return true}catch{return false}finally{try{Z(n)}catch{}}};const a=()=>{try{return JSON.parse(st(ee,"utf8"))}catch{return null}};const s=n=>{if(!n)return false;try{process.kill(n,0);return true}catch{return false}};if(!t()){const n=a();if(!n)throw new Error("Another web benchmark is starting. Wait for it to finish.");if(s(n.pid)){throw new Error(`Another web benchmark is running (pid ${n.pid}, since ${n.startedAt}). Two at once would skew both; wait for it to finish.`)}const i=`${ee}.cleanup`;let o=null;try{if(Date.now()-Te(i).mtimeMs>3e4){const c=`${i}.${e.replace(/[^\w]/g,"")}.old`;rt(i,c);if(Date.now()-Te(c).mtimeMs>3e4)Z(c);else{try{xe(c,i)}catch{}Z(c)}}}catch{}try{o=Ee(i,"wx")}catch{throw new Error("Another web benchmark is clearing an old lock. Try again in a moment.")}try{const c=a();if(c?.token===n.token)Z(ee);if(!t())throw new Error("Another web benchmark just started. Wait for it to finish.")}finally{ve(o);try{Z(i)}catch{}}}ye=true;return(n=false)=>{if(n)return;ye=false;if(a()?.token===e){try{Z(ee)}catch{}}}}async function St(e,r,t){const a=e.runsPerCase??3;const s=Math.max(500,e.durationMs??5e3);const n=Math.max(0,e.settleMs??1e3);const i=e.frameTrace!==false;const o=(e.cpuSlowdown?.length?e.cpuSlowdown:[1]).map(h=>Math.max(1,h));let c=e.refreshHz??60;const p={width:e.viewport?.width??1280,height:e.viewport?.height??800,deviceScaleFactor:e.viewport?.deviceScaleFactor??1};const f=[];const u=[];let l=null;if(a<2)f.push("runsPerCase=1 gives no spread; use 3 or more before choosing.");const d=await je(e.projectRoot??process.cwd());const M=await d.launch({headless:!e.headed,args:["--enable-gpu"]});const S=[];const T=[];const b=Date.now();const A=new Date(b).toISOString();const te=e.maxBatchMs??30*6e4;let I=false;let re=false;const oe={commit:pt(e.projectRoot??process.cwd()),dirty:mt(e.projectRoot??process.cwd())};try{const h={viewport:{width:p.width,height:p.height},deviceScaleFactor:p.deviceScaleFactor};const $=await G(M.newContext(h),3e4,async()=>{t.browserLeftRunning=true;throw new Error("Chromium didn't open a page in 30 s. Restart the MCP server and try again.")});const R=3e4+15e3+n+s+6e4;const X=async(g,w,v,_)=>{const C={};return G(F(C,g,w,v,_),R,async()=>{C.cancelled=true;if(!C.ctx)I=true;else if(!await U(C.ctx,1e4))I=true;return{...J(g.name,w,v),failed:`run took over ${Math.round(R/1e3)} s (the page may be stuck in a loop)`}})};const J=(g,w,v)=>({caseName:g,cpu:w,round:v,fps:null,frameGapP95Ms:null,frameGapMaxMs:null,longFrames:0,longTaskPct:null,loafBlockingMs:null,styleLayoutMs:null,forcedLayoutMs:null,topScripts:[],layoutShift:null,domNodesStart:null,domNodesEnd:null,mainThreadPct:null,scriptMs:null,styleMs:null,layoutMs:null,layoutCount:null,styleCount:null,heapMb:null,pageErrors:[]});const F=async(g,w,v,_,C)=>{const K=await M.newContext(h);g.ctx=K;if(g.cancelled){await U(K,1e4);throw new Error("run was cancelled during setup")}const y=await K.newPage();const z=[];y.on("pageerror",P=>z.push(String(P?.message??P)));const N={...J(w.name,v,_),pageErrors:z};try{const P=await K.newCDPSession(y);await P.send("Performance.enable",{timeDomain:"timeTicks"});if(v>1)await P.send("Emulation.setCPUThrottlingRate",{rate:v});const ie=await y.goto(Le(e.url,w,e.path),{waitUntil:"load",timeout:3e4});if(ie&&ie.status()>=400)return{...N,failed:`HTTP ${ie.status()}`};if(e.readySelector)await y.waitForSelector(e.readySelector,{state:"attached",timeout:15e3});await ge(n);const be=C||i;if(be){await P.send("Tracing.start",{transferMode:"ReturnAsStream",categories:(C?gt:wt).join(",")})}const le=e.steps?.length?await Ue(y,e.steps):new Map;if(e.steps?.length){const Q=await Mt(y,e.steps);if(Q)return{...N,failed:`expect ${Q} already held before the steps, so it can't prove they worked`};const V=[...le.keys()];const j=e.steps.flatMap(Y=>"expect"in Y?[Y.expect]:[]);await y.evaluate(`window.__buoyBenchClickTargets = ${JSON.stringify(V)}; window.__buoyBenchExpects = ${JSON.stringify(j)}; true`)}const ce=await We(P);await y.evaluate(lt);const $e=Date.now();if(e.steps?.length)await yt(y,e.steps,le);const Se=s-(Date.now()-$e);if(Se>0)await ge(Se);await y.evaluate(ct);const ue=await We(P);const Ye=Date.now()-$e;const O=await y.evaluate(ut);let D=null;if(be){const Q=new Promise(B=>P.on("Tracing.tracingComplete",se=>B(se.stream)));await P.send("Tracing.end");const V=await Q;const j=C?Ee(C,"w"):null;const Y=[];let de=0;try{for(;;){const B=await P.send("IO.read",{handle:V});const se=B.base64Encoded?Buffer.from(B.data,"base64").toString("utf8"):B.data;de+=se.length;if(j!=null)at(j,se);if(de<=we)Y.push(se);else Y.length=0;if(B.eof)break}}finally{await P.send("IO.close",{handle:V}).catch(()=>{});if(j!=null)ve(j)}if(C)u.push(C);if(de>we){f.push(`Trace for ${w.name} was over ${Math.round(we/1e6)} MB, so its frames weren't counted. A page that busy shows its problem in the table already.`)}else{try{const B=JSON.parse(Y.join(""));D=Fe(Array.isArray(B)?B:B.traceEvents??[])}catch{D=null}}}if(!O)return{...N,failed:"probe returned nothing (page navigated away?)"};if(O.hidden)return{...N,failed:"page was hidden during the run"};if(O.resized)return{...N,failed:"the window size or zoom changed during the run"};if(z.length&&!e.allowPageErrors){return{...N,failed:`page error: ${ae(z[0])}`}}if(e.steps?.length){const Q=He(e.steps,O.clicks??[],O.keys??[]);if(Q)return{...N,failed:Q};const V=qe(O.expectResults??[]);if(V)return{...N,failed:V};const j=await bt(y,le);if(j)return{...N,failed:j}}const Ze="Timestamp"in ce&&"Timestamp"in ue?x((ue.Timestamp-ce.Timestamp)*1e3):null;return{...N,...Be(O,c),...Ie(ce,ue,Ye),probeSpanMs:x(O.end-O.start),cdpSpanMs:Ze,shownFps:D?.shownFps??null,droppedFrames:D?.faults??null,frameCounts:D?{full:D.full,partial:D.partial,dropped:D.dropped,faults:D.faults}:null,droppedPct:D?.droppedPct??null}}catch(P){return{...N,failed:ae(P?.message??String(P))}}finally{await U(y,5e3);if(!await U(K,1e4))I=true}};{const g=await G($.newPage(),15e3,async()=>{throw new Error("Chromium didn't open the GPU check page in 15 s.")});try{l=await G(g.evaluate(_e),5e3,async()=>null)??null}catch{l=null}finally{await U(g,5e3)}if(l)r(`GPU ${l}`);if(l&&he(l)){f.push(`Chrome is drawing with a software GPU (${l}). Canvas, WebGL and Skia pages run several times slower than on a real screen, so their numbers mean little. Try headed: true, or a machine with a GPU.`)}}if(e.refreshHz==null){const g=await G($.newPage(),15e3,async()=>{throw new Error("Chromium didn't open the calibration page in 15 s.")});try{await G(g.goto("about:blank"),15e3,async()=>null);const w=await G(g.evaluate(dt),5e3,async()=>{throw new Error("idle rate check timed out")});c=Ae(w);r(`idle frame rate ${w.toFixed(1)} \u2192 ${c} Hz`)}catch{f.push("Couldn't measure the idle frame rate; frame budgets assume 60 Hz.")}finally{await U(g,5e3)}}r("warmup load");await X(e.cases[0],1,-1);const H=async(g,w,v,_)=>{if(I){S.push({...J(g.name,w,v),failed:"an earlier run left the browser stuck; batch stopped"});return}if(Date.now()-b>te){S.push({...J(g.name,w,v),failed:"batch time limit reached; run skipped"});return}r(_);T.push({cpu:w,round:v,caseName:g.name});S.push(await X(g,w,v))};for(const g of o){for(let w=0;w<a;w++){const v=pe(e.cases,2654435769^w*7919+g);for(const _ of v)await H(_,g,w,`cpu ${g}x \xB7 round ${w+1}/${a} \xB7 ${_.name}`)}for(let w=0;w<(e.extraRoundsWhenClose??0);w++){const v=e.cases.map(y=>me(S.filter(z=>z.cpu===g&&z.caseName===y.name),c));for(const y of v)y.control=e.cases.find(z=>z.name===y.caseName)?.control===true;const _=new Set([...Me(v).flat(),...v.filter(y=>!y.control&&y.validRuns<2&&y.plannedRuns>0).map(y=>y.caseName)]);if(_.size===0)break;const C=a+w;const K=pe(e.cases.filter(y=>_.has(y.name)),20973^C*7919+g);for(const y of K)await H(y,g,C,`cpu ${g}x \xB7 extra round ${w+1} (close call) \xB7 ${y.name}`)}}if(e.traceDir){nt(e.traceDir,{recursive:true});for(const[g,w]of e.cases.entries()){const v=fe(e.traceDir,`${String(g+1).padStart(2,"0")}-${w.name.replace(/[^\w.-]+/g,"_")}.json`);if(I||Date.now()-b>te){f.push(`Trace run for ${w.name} skipped (batch time limit or a stuck browser).`);continue}r(`trace \xB7 ${w.name}`);const _=await X(w,o[0],-1,v);if(_.failed)f.push(`Trace run for ${w.name} failed: ${_.failed}`)}}await U($,1e4)}finally{const h=await U(M,15e3);t.browserLeftRunning=!h;if(!h){re=true;f.push("Chromium didn't close in 15 s and may still be running. The benchmark lock stays held; restart the MCP server before the next batch.")}}const m=[];for(const h of o){for(const $ of e.cases){m.push(me(S.filter(R=>R.cpu===h&&R.caseName===$.name),c))}}for(const h of m)h.control=e.cases.find($=>$.name===h.caseName)?.control===true;for(const h of o){const $=m.filter(F=>F.cpu===h);const R=$.find(F=>!F.control);if(!R)continue;R.isBaseline=true;const X=F=>F.droppedMin!=null?F.median?.droppedPct??null:null;const J=X(R);for(const F of $.filter(H=>H!==R&&!H.control)){const H=X(F);F.deltaDroppedPct=J!=null&&H!=null?x(H-J):null}}if(e.allowPageErrors&&S.some(h=>h.pageErrors.length)){f.push("Some runs threw page errors (kept because allowPageErrors is on).")}if(I)f.push("A browser context wouldn't close, so the batch stopped early. Restart and run again.");if(e.budgets)for(const h of m)h.budget=Re(h,e.budgets,e.steps??[]);const k=m.filter(h=>h.median).length;const q=m.every(h=>h.validRuns===h.plannedRuns);const ne=k===0?"failed":k<m.length||!q?"partial":"ok";const E=m.filter(h=>h.median&&h.validRuns>0&&h.validRuns<h.plannedRuns);for(const h of E){const $=Array.from(new Set(h.runs.filter(R=>R.failed).map(R=>R.failed)));f.push(`${h.caseName} (CPU ${h.cpu}x): ${h.plannedRuns-h.validRuns} of ${h.plannedRuns} runs failed: ${$.join("; ")}`)}for(const h of m){const $=h.median;if($?.probeSpanMs&&$.cdpSpanMs&&Math.abs($.cdpSpanMs-$.probeSpanMs)/$.probeSpanMs>.15){f.push(`${h.caseName} (CPU ${h.cpu}x): Chrome's counters cover ${Math.round($.cdpSpanMs)} ms but frames cover ${Math.round($.probeSpanMs)} ms, so its cost columns include extra time.`)}if($?.probeSpanMs!=null&&$.probeSpanMs<s*.8){f.push(`${h.caseName} (CPU ${h.cpu}x) recorded ${Math.round($.probeSpanMs)} ms, not the ${s} ms asked for.`)}}const L={status:ne,hasSteps:Boolean(e.steps?.length),browserLeftRunning:re,replay:{startedAt:A,...oe,runOrder:T},url:e.url,browser:`Chromium ${M.version()}${e.headed?"":" (headless)"}`,gpu:l,viewport:p,refreshHz:c,durationMs:s,runsPerCase:a,results:m,traces:u,warnings:f};const W=e.redact?e.redact(L):L;if(e.outFile){try{const{redact:h,...$}=e;Pe(e.outFile,JSON.stringify({options:$,result:W},null,2))}catch(h){W.warnings.push(`Couldn't write outFile ${e.outFile}: ${ae(h?.message??String(h))}`)}}return W}var Qe=1.5;var Ve=2;function Me(e){const r=e.filter(n=>!n.control&&n.median&&n.validRuns>1);const t=r.filter(n=>n.droppedMin!=null);const a=[];if(t.length>=2&&t.length===r.length){const n=[...t].sort((i,o)=>i.median.droppedPct-o.median.droppedPct);for(let i=1;i<n.length;i++){const[o,c]=[n[i-1],n[i]];const p=c.median.droppedPct-o.median.droppedPct<=Qe;if(c.droppedMin<=o.droppedMax||p)a.push([o.caseName,c.caseName])}return a}if(t.length>0)return a;const s=r.filter(n=>n.median.fps!=null).sort((n,i)=>i.median.fps-n.median.fps);for(let n=1;n<s.length;n++){const[i,o]=[s[n-1],s[n]];const c=i.median.fps-o.median.fps<=Ve;if((o.fpsMax??0)>=(i.fpsMin??0)||c)a.push([i.caseName,o.caseName])}return a}function Xe(e){const r=e.filter(o=>!o.control&&o.median);const t=[];const a=r.filter(o=>o.validRuns<2);if(a.length)t.push(`**Too few runs to rank:** ${a.map(o=>o.caseName).join(", ")} (need 2 or more valid runs).`);const s=r.filter(o=>o.validRuns>1);const n=s.filter(o=>o.droppedMin!=null);if(n.length>0&&n.length<s.length){t.push(`**Not enough traced runs to compare smoothness:** ${s.filter(o=>!n.includes(o)).map(o=>`${o.caseName} (${o.tracedRuns}/${o.validRuns} traced)`).join(", ")}.`);return t.join("\n")}const i=new Map(s.map(o=>[o.caseName,o]));for(const[o,c]of Me(e)){const p=i.get(o);const f=i.get(c);t.push(n.length?`**Too close to call:** ${o} (${p.droppedMin}\u2013${p.droppedMax}% dropped) and ${c} (${f.droppedMin}\u2013${f.droppedMax}%) overlap or sit within ${Qe} pp. No clear winner between them; more runs may separate them.`:`**Too close to call:** ${o} and ${c} overlap or sit within ${Ve} FPS. No clear winner between them; more runs may separate them.`)}return t.length?t.join("\n"):null}function kt(e){const r=(o,c=1)=>o==null?"\u2014":o.toFixed(c);const t=[];t.push(`### Web benchmark \xB7 ${e.url}`);t.push(`${e.browser}${e.gpu?` \xB7 GPU: ${e.gpu}${he(e.gpu)?" \u26A0\uFE0F software":""}`:""} \xB7 ${e.viewport.width}\xD7${e.viewport.height} @${e.viewport.deviceScaleFactor}x \xB7 ${e.runsPerCase} runs${e.results.some(o=>o.plannedRuns>e.runsPerCase)?" (more for close calls)":""} \xD7 ${e.durationMs/1e3}s per case, fresh browser context each run, case order shuffled each round.`);const a=e.results.map(o=>o.median?.probeSpanMs).filter(o=>o!=null);const s=e.results.map(o=>o.median?.cdpSpanMs).filter(o=>o!=null);const n=o=>`${(Math.min(...o)/1e3).toFixed(2)}\u2013${(Math.max(...o)/1e3).toFixed(2)} s`;if(a.length){t.push(`Recorded: frames over ${n(a)}${s.length?`; cost columns (Chrome's counters) over ${n(s)}`:""}, not the requested time.`)}if(e.replay?.dirty)t.push(`Note: the project had uncommitted changes, so commit ${e.replay.commit?.slice(0,10)??"?"} alone doesn't describe the code that ran.`);t.push("");t.push(`**How to read this.** Dropped % is the smoothness number: frames Chrome dropped or only partly produced that it says hurt smoothness, as a share of the frames it wanted, from its own verdicts in a trace. Rank on it first, and only across variants doing the same work. Shown FPS counts frames Chrome produced (headless Chrome displays nothing), so an animation that changes less often needs fewer; don't compare it across variants that update at different rates. Main-thread FPS and frame gaps come from requestAnimationFrame. Main thread % is Chrome's task time for the run. Desktop numbers, even with CPU slowdown, don't stand in for a real phone's GPU, memory or heat.`);const i=Array.from(new Set(e.results.map(o=>o.cpu)));for(const o of i){t.push("");t.push(o===1?`#### No CPU slowdown`:`#### CPU slowed ${o}x`);t.push("");t.push(`| Case | Runs | Shown FPS | Dropped % | Main-thread FPS (range) | p95 gap ms | Worst gap ms | Main thread % | Script ms | Style ms | Layout ms | Layouts | DOM nodes | JS heap MB | Flags |`);t.push(`|------|-----:|----------:|----------:|------------------------:|-----------:|-------------:|--------------:|----------:|---------:|----------:|--------:|----------:|--------:|-------|`);for(const l of e.results.filter(d=>d.cpu===o)){const d=l.median;if(!d){const T=l.runs.find(b=>b.failed)?.failed??"no valid runs";t.push(`| ${l.caseName} | 0/${l.plannedRuns} | \u2014 | \u2014 | \u2014 | \u2014 | \u2014 | \u2014 | \u2014 | \u2014 | \u2014 | \u2014 | \u2014 | \u2014 | \u274C FAILED (${T}) |`);continue}const M=l.fpsMin!=null&&l.fpsMax!=null&&l.validRuns>1?` (${r(l.fpsMin)}\u2013${r(l.fpsMax)})`:"";const S=l.flags.length?`\u26A0\uFE0F ${l.flags.join("; ")}`:"no flags";t.push(`| ${l.isBaseline?`${l.caseName} (baseline)`:l.control?`${l.caseName} (control)`:l.caseName} | ${l.validRuns}/${l.plannedRuns} | ${r(d.shownFps)} | ${l.droppedMin==null&&l.tracedRuns>0?`\u2014 (${l.tracedRuns}/${l.validRuns} traced)`:r(l.droppedMin==null?null:d.droppedPct)}${l.droppedMin!=null&&l.validRuns>1?` [${l.droppedMin}\u2013${l.droppedMax}]`:""}${l.deltaDroppedPct!=null?` (${l.deltaDroppedPct>0?"+":""}${l.deltaDroppedPct} pp)`:""} | ${r(d.fps)}${M} | ${r(d.frameGapP95Ms)} | ${r(d.frameGapMaxMs)} | ${r(d.mainThreadPct)} | ${r(d.scriptMs,0)} | ${r(d.styleMs,0)} | ${r(d.layoutMs,0)} | ${r(d.layoutCount,0)} | ${r(d.domNodesEnd,0)} | ${r(d.heapMb)} | ${S} |`)}const c=Xe(e.results.filter(l=>l.cpu===o));if(c){t.push("");t.push(c)}const p=e.results.filter(l=>l.cpu===o&&l.budget);if(p.length){t.push("");t.push(`Budget:`);for(const l of p){const d=l.budget;const M=[...d.over,...d.unknown.map(T=>`${T} not measured`)].join("; ");const S=l.control?`${l.caseName} (control)`:l.caseName;t.push(`- **${S}**: ${d.meets?"meets every limit":`misses (${M||"no result"})`}`)}}const f=e.results.filter(l=>l.cpu===o&&l.median?.interactions!=null&&(e.hasSteps||l.median.interactions>0)).map(l=>{const d=l.median;const M=l.inputMin!=null&&l.inputMax!=null?` \xB7 slowest across all runs ${l.inputMax} ms (range ${l.inputMin}\u2013${l.inputMax})`:"";const S=l.runs.filter(b=>!b.failed&&b.fps!=null&&b.inputCaptureComplete===false).length;const T=S?` \xB7 \u26A0\uFE0F timing incomplete in ${S} run(s): the last input's paint came after the run ended, so its time is unknown (leave more time after the last step)`:"";if(!d.interactions)return`- **${l.caseName}**: middle run had no input entry (inputs under 16 ms aren't reported)${M}${T}`;return`- **${l.caseName}**: middle run: ${d.interactions} reported (16 ms or more), slowest ${d.worstInteractionMs} ms (waiting ${d.worstInputDelayMs} \xB7 handlers ${d.worstProcessingMs} \xB7 to paint ${d.worstPresentationMs})${M}${T}`});if(f.length){t.push("");t.push(`Input response (scripted steps, Event Timing). Inputs under 16 ms aren't reported. Under 200 ms is good.`);t.push(...f)}const u=e.results.filter(l=>l.cpu===o&&l.median&&l.median.topScripts.length).map(l=>`- **${l.caseName}**: ${l.median.topScripts.slice(0,3).map(d=>`${d.name} ${d.ms} ms`).join(", ")}`);if(u.length){t.push("");t.push(`Scripts in slow frames (Long Animation Frames; frames that ran past the end of a run are left out):`);t.push(...u)}}if(e.traces.length){t.push("");t.push(`Chrome traces (open in DevTools \u2192 Performance \u2192 Load profile): ${e.traces.join(", ")}`)}if(e.warnings.length){t.push("");t.push(...e.warnings.map(o=>`\u26A0\uFE0F ${o}`))}return t.join("\n")}export{Re as checkBudgets,_e as GPU_RENDERER_JS,he as isSoftwareGpu,Ce as percentile,Ae as snapRefreshRate,pe as shuffled,Le as caseUrl,Fe as summarizeFrameTrace,Ne as keyMatches,De as mergeInteractions,Be as summarizeProbe,Ie as cdpDeltas,ze as webBenchFlags,me as aggregateCase,Oe as PLAYWRIGHT_MISSING,je as loadChromium,G as withDeadline,U as closeWithin,Ge as validateWebBenchOptions,ae as oneLine,Ue as locateStepTargets,He as missedInput,qe as expectFailureFrom,$t as runWebBench,Je as waitForHostLock,Ke as takeHostLock,Me as tiedPairs,Xe as closeCall,kt as formatWebBenchMarkdown};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createRequire as m}from"module";const P=m(import.meta.url);import{AsyncLocalStorage as d}from"async_hooks";var r={list_devices:null,list_tool_actions:null,camera_diagnose:null,describe_screen:e=>e.includeBuoy?"includeBuoy":null,tap_element:e=>e.includeBuoy?"includeBuoy":null,native_tap:e=>e.allowBuoy?"allowBuoy":null,native_type:null,native_swipe:e=>e.allowBuoy?"allowBuoy":null,native_pinch:e=>e.allowBuoy?"allowBuoy":null,native_inspect:null,run_flow:null,screenshot_component:null,screen_recording:null,device_control:null,reload_app:null,navigate:null,get_routes:null,get_console:null,get_network_requests:e=>e.includeBodies?"includeBodies":null,get_triage:null,run_benchmark_batch:null,wait_for_automation:null,get_automation_status:null,get_batch_report:null,compare_reports:null,get_benchmark_settings:null,set_benchmark_settings:null,get_js_thread_top:null,run_web_benchmark:e=>e.traceDir?"traceDir":null,find_web_ceiling:null,screenshot_web_variants:null,start_optimize_session:null,sim_farm_status:null,sim_farm_setup:null,sim_farm_acquire:null,sim_farm_release:null,sim_farm_action:null,sim_farm_lock:null,sim_farm_report:null,sim_farm_extend:null,sim_farm_finish:null};var u={"highlight-updates":["describeScreen","tapElement","hitTest","readMaps","setSilentTracking"],"route-events":["@snapshot","getCurrentRoute","navigate"],app:["@snapshot","ping","reloadApp"],console:["@snapshot"],network:["@snapshot"],storage:["async.getAllKeys","secure.keys"],"perf-monitor":["@snapshot","getAutomationConfig","setAutomationConfig","acknowledgeAutomation","setEnabled","refreshIndex","startAutomation","loadReport"],"js-top":["sample"]};function f(e,t){return Object.prototype.hasOwnProperty.call(u,e)&&u[e].includes(t)}var i=()=>new Date;function h(e){i=e??(()=>new Date)}function l(e=i()){const t=e.getDay();return t===0||t===6}function g(e,t){const n=l();if(!Object.prototype.hasOwnProperty.call(r,e))return{tool:e,level:"pro",weekend:n};const s=r[e];const a=s?s(t&&typeof t==="object"?t:{}):null;return a?{tool:e,level:"pro",proOption:a,weekend:n}:{tool:e,level:"free",weekend:n}}function c(){return o.getStore()?.weekend??l()}function p(e){if(!e)return false;if(e.isPro===true)return true;return e.tier==="free"&&c()}function y(e){return Object.prototype.hasOwnProperty.call(r,e)}function w(){return Object.keys(r).sort()}var o=new d;function b(e,t){return o.run(Object.freeze({...e}),t)}function v(e){return o.exit(e)}function k(){return o.getStore()??{tool:"",level:"pro"}}function B(e,t){if(!e)return false;if(p(e))return true;return t==="free"&&e.tier==="free"}var _="Free covers reading the screen, taps, swipes and typing, run_flow, screenshots, screen recording, device_control, reload_app, navigate, get_routes, get_console, the request list and get_triage. It also covers Buoy Optimize and the Sim Farm: benchmark batches, reports, web benchmarks and JS thread totals, without the render names, script names and JS origins that say why. Every Saturday and Sunday a free account gets everything Pro does.";function A(e,t){const n=e.proOption?`\`${e.tool}\` with \`${e.proOption}\` needs Buoy Pro.`:e.tool?`\`${e.tool}\` needs Buoy Pro.`:"This needs Buoy Pro.";return`${n}${t} ${_} Tell the user Pro unlocks the rest (https://buoy.gg/pricing). To add a license, enter it in the app, or pass \`licenseKey\` to \`<FloatingDevTools />\` for an app with no on-device UI. No setting, argument or other tool turns Pro on, so don't try to work around it.`}export{f as isFreeAppCall,h as setClockForTests,l as isWeekendPass,g as accessFor,c as weekendNow,p as hasProAccess,y as isFreeTool,w as freeToolNames,b as runWithAccess,v as outsideCalls,k as currentAccess,B as meetsAccess,_ as FREE_SUMMARY,A as proRequiredMessage};
|