'use strict'; const fs=require('fs'); const os=require('os'); const path=require('path'); const http=require('http'); const {spawn,spawnSync}=require('child_process'); const {assert,root}=require('./helpers/app-source'); const edgePath=process.env.BEND_FIELD_EDGE_PATH||'C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe'; const sleep=milliseconds=>new Promise(resolve=>setTimeout(resolve,milliseconds)); let serverPort=0; const debuggingPort=20000+Math.floor(Math.random()*1000),benchmarkHost=process.env.BEND_FIELD_BENCHMARK_HOST||'localhost'; const startupOnly=process.env.BEND_FIELD_STARTUP_ONLY==='1'; const effectVisualFixture=JSON.parse(fs.readFileSync(path.join(root,'test','fixtures','effect-visual-checkpoints.json'),'utf8')); const benchmarkOutputPath=process.env.BEND_FIELD_BENCHMARK_OUTPUT||path.join(root,'test-results','browser-performance-benchmark.json'); function writeBenchmarkReport(report){fs.mkdirSync(path.dirname(benchmarkOutputPath),{recursive:true});fs.writeFileSync(benchmarkOutputPath,`${JSON.stringify(report,null,2)}\n`)} const temporaryRoot=fs.mkdtempSync(path.join(os.tmpdir(),'bend-field-browser-benchmark-')); const profilePath=path.join(temporaryRoot,'edge-profile'); const worldDbName='bend-field:v30:linkfield-single-world-20260801:world'; const allProfiles=[ {name:'small',boards:16,mode:'grid'}, {name:'medium',boards:128,mode:'grid'}, {name:'large',boards:512,mode:'grid'}, {name:'complex',boards:16,mode:'complex',chunks:10}, {name:'long-line',boards:256,mode:'line'} ]; const requestedProfile=process.env.BEND_FIELD_BENCHMARK_PROFILE||'',profiles=requestedProfile?allProfiles.filter(profile=>profile.name===requestedProfile):allProfiles; if(requestedProfile&&!profiles.length)throw new Error(`Unknown benchmark profile: ${requestedProfile}`); function stopBrowserTree(child){ if(!child?.pid)return; if(process.platform==='win32'){ const taskkill=path.join(process.env.SystemRoot||'C:\\Windows','System32','taskkill.exe'), result=spawnSync(taskkill,['/pid',String(child.pid),'/T','/F'],{stdio:'ignore',windowsHide:true}); if(result.status!==0&&!child.killed)try{child.kill()}catch(_){} }else if(!child.killed)child.kill('SIGTERM'); } class CdpClient{ constructor(url){this.url=url;this.sequence=0;this.pending=new Map();this.events=[];this.socket=null} async connect(){ this.socket=new WebSocket(this.url); await new Promise((resolve,reject)=>{ const timer=setTimeout(()=>reject(new Error('CDP WebSocket connection timed out')),10000); this.socket.addEventListener('open',()=>{clearTimeout(timer);resolve()},{once:true}); this.socket.addEventListener('error',event=>{clearTimeout(timer);reject(event.error||new Error('CDP WebSocket failed'))},{once:true}); }); this.socket.addEventListener('message',event=>{ const message=JSON.parse(event.data); if(!message.id){this.events.push(message);if(this.events.length>200)this.events.shift();return} const pending=this.pending.get(message.id);if(!pending)return;this.pending.delete(message.id); if(message.error)pending.reject(new Error(`${pending.method}: ${message.error.message}`)); else pending.resolve(message.result); }); } send(method,params={}){ const id=++this.sequence; return new Promise((resolve,reject)=>{ this.pending.set(id,{resolve,reject,method}); this.socket.send(JSON.stringify({id,method,params})); }); } async evaluate(expression){ const response=await this.send('Runtime.evaluate',{expression,awaitPromise:true,returnByValue:true,userGesture:true}); if(response.exceptionDetails)throw new Error(response.exceptionDetails.exception?.description||response.exceptionDetails.text||'Browser evaluation failed'); return response.result.value; } close(){try{this.socket?.close()}catch(_){}} } async function waitFor(check,{timeout=20000,interval=100,label='condition'}={}){ const deadline=Date.now()+timeout;let lastError=null; while(Date.now(){ const response=await fetch(`http://127.0.0.1:${debuggingPort}/json/list`,{signal:AbortSignal.timeout(1500)}); if(!response.ok)return null; const targets=await response.json(); return targets.find(target=>target.type==='page'&&target.webSocketDebuggerUrl); },{label:'Edge DevTools endpoint'}); } async function startStaticServer(){ const types={'.html':'text/html; charset=utf-8','.js':'text/javascript; charset=utf-8','.css':'text/css; charset=utf-8','.svg':'image/svg+xml','.ttf':'font/ttf','.ico':'image/x-icon'}; const server=http.createServer((request,response)=>{ const pathname=new URL(request.url,'http://localhost').pathname; const relative=pathname==='/'?'index.html':decodeURIComponent(pathname.slice(1)); const file=path.resolve(root,relative); if(file!==root&&!file.startsWith(root+path.sep)){response.writeHead(403);response.end();return} fs.readFile(file,(error,body)=>{ if(error){response.writeHead(error.code==='ENOENT'?404:500);response.end();return} response.writeHead(200,{'content-type':types[path.extname(file)]||'application/octet-stream','cache-control':'no-store'}); response.end(body); }); }); await new Promise((resolve,reject)=>{ server.once('error',reject);server.listen(0,'127.0.0.1',()=>{server.off('error',reject);resolve()}); }); serverPort=server.address().port; return server; } async function ready(client){ try{ await waitFor(()=>client.evaluate("document.body?.dataset?.ready==='true'&&Boolean(globalThis.BEND_PERF)"), {timeout:30000,label:'game startup'}); }catch(error){ const state=await client.evaluate("({href:location.href,ready:document.body?.dataset?.ready,hasPerf:Boolean(globalThis.BEND_PERF),status:document.querySelector('#statusMessage')?.textContent||'',text:document.body?.innerText?.slice(0,500)||''})").catch(()=>null); const exception=client.events.findLast?.(event=>event.method==='Runtime.exceptionThrown')?.params?.exceptionDetails; const consoleError=client.events.findLast?.(event=>event.method==='Runtime.consoleAPICalled'&&event.params?.type==='error')?.params; const consoleText=consoleError?.args?.map(argument=>argument.description||argument.value||'').join(' ')||''; throw new Error(`${error.message}: ${JSON.stringify(state)} ${exception?.exception?.description||exception?.text||consoleText}`); } await client.evaluate("document.querySelector('#normalPlayBtn')?.click();true"); await sleep(350); } async function readStarterRows(client){ const expression=`(async()=>{ const db=await new Promise((resolve,reject)=>{const request=indexedDB.open(${JSON.stringify(worldDbName)});request.onsuccess=()=>resolve(request.result);request.onerror=()=>reject(request.error)}); const read=request=>new Promise((resolve,reject)=>{request.onsuccess=()=>resolve(request.result);request.onerror=()=>reject(request.error)}); let tx=db.transaction('control','readonly'),control=await read(tx.objectStore('control').get('active')); await new Promise((resolve,reject)=>{tx.oncomplete=resolve;tx.onerror=()=>reject(tx.error);tx.onabort=()=>reject(tx.error)}); const epoch=control?.activeEpoch;tx=db.transaction(['worlds','boardIndex','boardPuzzles','boardStates'],'readonly'); const [world,index,puzzle,state]=await Promise.all([ read(tx.objectStore('worlds').get(epoch)),read(tx.objectStore('boardIndex').get([epoch,'B0'])), read(tx.objectStore('boardPuzzles').get([epoch,'B0'])),read(tx.objectStore('boardStates').get([epoch,'B0'])) ]); await new Promise((resolve,reject)=>{tx.oncomplete=resolve;tx.onerror=()=>reject(tx.error);tx.onabort=()=>reject(tx.error)}); db.close();return{control,world,index,puzzle,state}; })()`; return waitFor(async()=>{ const rows=await client.evaluate(expression); return rows?.control?.activeFormat===2&&rows?.world?.global&&rows?.index&&rows?.puzzle?.puzzle&&rows?.state?.value?rows:null; },{timeout:20000,label:'starter IndexedDB rows'}); } async function populateWorld(client,starter,profile){ const expression=`(async()=>{ const starter=${JSON.stringify(starter)},profile=${JSON.stringify(profile)},count=profile.boards; const makeComplexPuzzle=(source,chunkCount)=>{ const puzzle=structuredClone(source),columns=5,g=[],n=[],valid=[],solution=[]; for(let chunk=0;chunk[cell[0]+rowOffset,cell[1]+columnOffset])}); } puzzle.g=g;puzzle.n=n;puzzle.valid=valid;puzzle.solution=solution;puzzle.bounds={w:Math.min(columns,chunkCount)*5,h:Math.ceil(chunkCount/columns)*5};puzzle.level=10;puzzle.difficulty=10;puzzle.maxTurns=Math.max(...solution.map(path=>path.cells.length));puzzle.totalTurns=solution.reduce((sum,path)=>sum+path.cells.length,0);return puzzle; }; const starterPuzzle=starter.puzzle.puzzle,starterState=starter.state.value, complexPuzzle=profile.mode==='complex'?makeComplexPuzzle(starterPuzzle,profile.chunks||10):null, complexChunks=profile.mode==='complex'?Array.from({length:profile.chunks||10},(_,index)=>[index%5,Math.floor(index/5)]):null; const db=await new Promise((resolve,reject)=>{const request=indexedDB.open(${JSON.stringify(worldDbName)});request.onsuccess=()=>resolve(request.result);request.onerror=()=>reject(request.error)}); const tx=db.transaction(['control','worlds','boardIndex','boardPuzzles','boardStates','outboxV2','recoveryV2','tombstonesV2'],'readwrite'), indexStore=tx.objectStore('boardIndex'),puzzleStore=tx.objectStore('boardPuzzles'),stateStore=tx.objectStore('boardStates'); for(const name of ['worlds','boardIndex','boardPuzzles','boardStates','outboxV2','recoveryV2','tombstonesV2'])tx.objectStore(name).clear(); const columns=Math.ceil(Math.sqrt(count)),spacingX=profile.mode==='complex'?7:2,spacingY=profile.mode==='complex'?4:2; for(let index=0;index0?[{startGate:0,endGate:3,openGate:null,cells:[[0,0],[0,1],[0,2],[0,3],[0,4]],colorIndex:0,startColorIndex:0,endColorIndex:0}]:[]; state.solved=false;state.specialProgress={crossings:[]};state.rev=stateRev;state.revAuthor=author; indexStore.put({epoch:starter.control.activeEpoch,id,number:index,x,y,chunks,level:profile.mode==='complex'?10:starter.index.level,targetLevel:profile.mode==='complex'?10:starter.index.targetLevel,seed:index+1,axis:starter.index.axis,entrySide:null,sealedSides:[],metaRev,stateRev,metaRevAuthor:author,stateRevAuthor:author,revAuthor:author,solved:false,expanded:false,scoreAwarded:0,hasProgress:state.paths.length>0,specialFlags:{crossing:false,warp:false,lock:false},shop:null}); puzzleStore.put({epoch:starter.control.activeEpoch,id,metaRev,revAuthor:author,generatorVersion:starter.puzzle.generatorVersion,puzzle}); stateStore.put({epoch:starter.control.activeEpoch,id,stateRev,revAuthor:author,value:state}); } const current=structuredClone(starter.world.global),epoch=starter.control.activeEpoch; current.worldEpoch=epoch;current.nextId=count;current.solved=0;current.score=0;current.bonusScore=0;current.bonusEvents={}; current.globalRev=300000+count;current.globalRevAuthor='benchmark';current.updatedAt=Date.now();current.clockFloor=Date.now(); const world={...starter.world,epoch,status:'active',global:current,boardCount:count,solvedCount:0,score:0,bounds:{minX:0,minY:0,maxX:profile.mode==='line'?count:columns*spacingX,maxY:profile.mode==='line'?1:Math.ceil(count/columns)*spacingY},source:{kind:'benchmark'},progress:null}; tx.objectStore('worlds').put(world);tx.objectStore('control').put({key:'active',activeFormat:2,activeEpoch:epoch,activationVerified:true}); await new Promise((resolve,reject)=>{tx.oncomplete=resolve;tx.onerror=()=>reject(tx.error);tx.onabort=()=>reject(tx.error)}); db.close();localStorage.clear();sessionStorage.clear();return{count,solution:profile.mode==='complex'?complexPuzzle.solution:starterPuzzle.solution}; })()`; const result=await client.evaluate(expression);assert(result?.count===profile.boards,'Synthetic benchmark world was not written completely');return result.solution; } async function reload(client){ await client.evaluate("document.body.dataset.ready='reloading';true"); await client.send('Page.reload',{ignoreCache:true}); await ready(client); await client.evaluate("centerMeta(data.metas.B0);ensureBoards();renderAll();true"); await waitFor(()=>client.evaluate("Boolean(document.querySelector('.board-card[data-id=\"B0\"]:not(.board-static)'))"), {timeout:15000,label:'interactive origin board'}); await sleep(500); } async function waitForPersistenceIdle(client){ await client.evaluate("(async()=>{if(hasPendingPersistence())await persistNow({skipCloud:true});return true})()"); await waitFor(()=>client.evaluate("!hasPendingPersistence()&&!saveTimer"),{timeout:20000,label:'benchmark persistence idle'}); await client.evaluate("persistQueue.then(()=>true)"); } async function prepareInteractionMeasurement(client){ await waitForPersistenceIdle(client); await client.evaluate("globalThis.gc?.();true"); await sleep(120); await client.evaluate('BEND_PERF.reset();true'); } async function restoreOriginState(client,state){ const restored=await client.evaluate(`(async()=>{ const board=rendered.get('B0'),snapshot=${JSON.stringify(state)},nextFrame=()=>new Promise(resolve=>requestAnimationFrame(()=>resolve())), applySnapshot=()=>{ if(board){cancelBoardDragFrame(board);clearDragRender(board);board.drawing=null;board.armedGate=null;if(board.pendingClaimPointer)clearPendingClaimPointer(board,board.pendingClaimPointer.pointerId)} realtimeHeldPointers.clear();const current=metaState('B0');replaceObjectContents(current,deepClone(snapshot));normalizedStateObjects.add(current);return current; }; applySnapshot();refreshInteractionState();await nextFrame();await new Promise(resolve=>setTimeout(resolve,80)); const settledPathCount=metaState('B0').paths.length;applySnapshot();changed('B0'); if(board)renderBoardNow(board);refreshInteractionState();centerMeta(data.metas.B0,{select:false});await nextFrame();await persistNow({skipCloud:true}); return{pathCount:metaState('B0').paths.length,settledPathCount,solved:metaState('B0').solved,drawing:Boolean(board?.drawing),pending:Boolean(board?.pendingClaimPointer)}; })()`); assert(restored.pathCount===(state.paths||[]).length&&restored.solved===Boolean(state.solved)&&!restored.drawing&&!restored.pending,`Canonical origin restore failed: ${JSON.stringify(restored)}`); } async function pointFor(client,selector){ const point=await client.evaluate(`(()=>{const element=document.querySelector(${JSON.stringify(selector)});if(!element)return null;const rect=element.getBoundingClientRect();return{x:rect.left+rect.width/2,y:rect.top+rect.height/2}})()`); if(!point)throw new Error(`Missing benchmark target: ${selector}`); return point; } async function pointForCell(client,boardId,row,column){ const point=await client.evaluate(`(()=>{const board=rendered.get(${JSON.stringify(boardId)});if(!board)return null;const rect=board.svg.getBoundingClientRect(),vb=board.svg.viewBox.baseVal,x=PAD+(${column}+.5)*CELL,y=PAD+(${row}+.5)*CELL;return{x:rect.left+x*rect.width/vb.width,y:rect.top+y*rect.height/vb.height}})()`); if(!point)throw new Error(`Missing benchmark board cell: ${boardId} ${row},${column}`); return point; } async function pointsForCells(client,boardId,cells){ const points=await client.evaluate(`(()=>{const board=rendered.get(${JSON.stringify(boardId)}),cells=${JSON.stringify(cells)};if(!board)return null;const rect=board.svg.getBoundingClientRect(),vb=board.svg.viewBox.baseVal;return cells.map(([row,column])=>{const x=PAD+(column+.5)*CELL,y=PAD+(row+.5)*CELL;return{x:rect.left+x*rect.width/vb.width,y:rect.top+y*rect.height/vb.height}})})()`); if(!points)throw new Error(`Missing benchmark board cells: ${boardId}`); return points; } async function safePanPoint(client,selector){ const point=await client.evaluate(`(()=>{const card=document.querySelector(${JSON.stringify(selector)}),view=document.querySelector('#viewport')?.getBoundingClientRect(),rect=card?.getBoundingClientRect();if(!card||!view||!rect)return null;for(const fy of[.18,.32,.5,.68,.82])for(const fx of[.18,.32,.5,.68,.82]){const x=rect.left+rect.width*fx,y=rect.top+rect.height*fy;if(xview.right||yview.bottom)continue;const target=document.elementFromPoint(x,y);if(target?.closest?.(${JSON.stringify(selector)})&&!target.closest?.('button,a,input,select,textarea,[role="button"]'))return{x,y}}return null})()`); return point||pointFor(client,selector); } async function solveOrigin(client,solution,cpuRate=1){ for(const pathRow of solution){ const start=await pointFor(client,`.board-card[data-id="B0"] .gate-hit[data-gate="${pathRow.startGate}"]`), end=await pointFor(client,`.board-card[data-id="B0"] .gate-hit[data-gate="${pathRow.endGate}"]`); const cellPoints=await pointsForCells(client,'B0',pathRow.cells); await client.send('Input.dispatchMouseEvent',{type:'mousePressed',x:start.x,y:start.y,button:'left',buttons:1,clickCount:1}); await sleep(20); for(const point of cellPoints){ await client.send('Input.dispatchMouseEvent',{type:'mouseMoved',x:point.x,y:point.y,button:'left',buttons:1}); await sleep(cpuRate===1?8:16); } await client.send('Input.dispatchMouseEvent',{type:'mouseMoved',x:end.x,y:end.y,button:'left',buttons:1}); await sleep(25*Math.max(1,cpuRate)); await client.send('Input.dispatchMouseEvent',{type:'mouseReleased',x:end.x,y:end.y,button:'left',buttons:0,clickCount:1}); await waitFor(()=>client.evaluate("!rendered.get('B0')?.drawing"),{timeout:3000*Math.max(1,cpuRate),label:`origin path ${pathRow.startGate}-${pathRow.endGate} release`}); await sleep(45*Math.max(1,cpuRate)); } } async function viewportCenter(client){ return client.evaluate("(()=>{const r=document.querySelector('#viewport').getBoundingClientRect();return{x:r.left+r.width/2,y:r.top+r.height/2}})()"); } async function pan(client,steps=24,{button='right',selector=null,startPoint=null}={}){ const center=await viewportCenter(client),start=startPoint||(selector?await safePanPoint(client,selector):{x:center.x+180,y:center.y+80}),buttons=button==='left'?1:2; await client.send('Input.dispatchMouseEvent',{type:'mousePressed',x:start.x,y:start.y,button,buttons,clickCount:1}); for(let index=1;index<=steps;index++){ await client.send('Input.dispatchMouseEvent',{ type:'mouseMoved',x:start.x-index*9,y:start.y-index*4,button:'none',buttons }); await sleep(12); } await client.send('Input.dispatchMouseEvent',{type:'mouseReleased',x:start.x-steps*9,y:start.y-steps*4,button,buttons:0,clickCount:1}); await sleep(250); return start; } async function panCadenceProbe(client,steps=24){ return client.evaluate(`new Promise(resolve=>{ const viewport=document.querySelector('#viewport'),rect=viewport.getBoundingClientRect(),pointerId=91,startX=rect.left+rect.width*.7,startY=rect.top+rect.height*.6; const [worldX,worldY]=worldUnitAtClient(startX,startY),reactionNow=Date.now(),remoteId='benchmark-remote'; applyRemotePlayer({presenceId:remoteId,playerId:'benchmark',name:'Remote',cursorStyle:'default',x:worldX,y:worldY,vx:.08,vy:.03,sentAt:reactionNow}); applyRealtimeReaction({id:'benchmark-reaction',emoji:REACTION_EMOJIS[0],style:'firework',x:worldX,y:worldY,createdAt:reactionNow,expiresAt:reactionNow+Math.max(3400,${steps}*25)}); const dispatch=(type,index,buttons)=>viewport.dispatchEvent(new PointerEvent(type,{bubbles:true,cancelable:true,pointerId,pointerType:'mouse',isPrimary:true,button:type==='pointerdown'||type==='pointerup'?2:-1,buttons,clientX:startX-index*7,clientY:startY-index*3})); dispatch('pointerdown',0,2);let index=0; const tick=()=>{index++;dispatch('pointermove',index,2);if(index<${steps})requestAnimationFrame(tick);else{dispatch('pointerup',index,0);setTimeout(()=>{remotePlayers.delete(remoteId);realtimeReactions.delete('benchmark-reaction');resolve(index)},80)}}; requestAnimationFrame(tick); })`); } async function pinchZoomProbe(client){ return client.evaluate(`new Promise(resolve=>{ const viewport=document.querySelector('#viewport'),rect=viewport.getBoundingClientRect(),cx=rect.left+rect.width/2,cy=rect.top+rect.height/2,before=cam.scale; const dispatch=(type,pointerId,x)=>viewport.dispatchEvent(new PointerEvent(type,{bubbles:true,cancelable:true,pointerId,pointerType:'touch',isPrimary:pointerId===111,button:type==='pointerdown'?0:-1,buttons:type==='pointerup'?0:1,clientX:x,clientY:cy})); dispatch('pointerdown',111,cx-45);dispatch('pointerdown',112,cx+45);let frame=0; const tick=()=>{frame++;dispatch('pointermove',111,cx-45-frame*5);dispatch('pointermove',112,cx+45+frame*5);if(frame<8)requestAnimationFrame(tick);else{dispatch('pointerup',111,cx-85);dispatch('pointerup',112,cx+85);setTimeout(()=>resolve({before,after:cam.scale,changed:Math.abs(cam.scale-before)>.01}),100)}}; requestAnimationFrame(tick); })`); } async function pickupEdgePanProbe(client,pathRow){ const originalState=await client.evaluate("deepClone(metaState('B0'))"); await client.evaluate("(()=>{const r=document.querySelector('#viewport').getBoundingClientRect(),p=worldUnitAtClient(r.left+r.width/2,r.top+r.height/2),now=trustedNow();applyRealtimeReaction({id:'benchmark-edge-pan-effect',emoji:REACTION_EMOJIS[0],style:'comet',x:p[0],y:p[1],createdAt:now,expiresAt:now+12000});return true})()"); const start=await pointFor(client,`.board-card[data-id="B0"] .gate-hit[data-gate="${pathRow.startGate}"]`), edge=await client.evaluate("(()=>{const r=document.querySelector('#viewport').getBoundingClientRect();return{x:r.right-2,y:r.top+r.height/2}})()"), before=await client.evaluate("({x:cam.x,y:cam.y,frames:BEND_PERF.snapshot().counters.dragFrames||0})"); await client.send('Input.dispatchMouseEvent',{type:'mousePressed',x:start.x,y:start.y,button:'left',buttons:1,clickCount:1}); try{ await waitFor(()=>client.evaluate("Boolean(rendered.get('B0')?.drawing)"),{timeout:5000,label:'pickup edge-pan activation'}); await client.send('Input.dispatchMouseEvent',{type:'mouseMoved',x:edge.x,y:edge.y,button:'left',buttons:1});await sleep(350); const after=await client.evaluate("({x:cam.x,y:cam.y,frames:BEND_PERF.snapshot().counters.dragFrames||0})"); return{cameraMoved:Math.hypot(after.x-before.x,after.y-before.y)>5,dragFrames:after.frames-before.frames}; }finally{ await client.send('Input.dispatchMouseEvent',{type:'mouseReleased',x:edge.x,y:edge.y,button:'left',buttons:0,clickCount:1}).catch(()=>{}); await client.evaluate(`(()=>{realtimeReactions.delete('benchmark-edge-pan-effect');data.states.B0=${JSON.stringify(originalState)};normalizedStateObjects.add(data.states.B0);markStateDirty('B0');renderBoardNow(rendered.get('B0'));return true})()`); await client.evaluate("(async()=>{await persistNow({skipCloud:true});return true})()"); } } async function pickupCadenceProbe(client,pathRow,steps=120,sampleDelay=8){ const start=await pointFor(client,`.board-card[data-id="B0"] .gate-hit[data-gate="${pathRow.startGate}"]`), center=await pointForCell(client,'B0',pathRow.cells[0][0],pathRow.cells[0][1]), originalState=await client.evaluate("deepClone(metaState('B0'))"); await client.evaluate(`(()=>{const p=worldUnitAtClient(${start.x},${start.y}),now=trustedNow();applyRealtimeReaction({id:'benchmark-pickup-effect',emoji:REACTION_EMOJIS[0],style:'firework',x:p[0],y:p[1],createdAt:now,expiresAt:now+${Math.max(12000,steps*sampleDelay*2)}});return true})()`); await client.send('Input.dispatchMouseEvent',{type:'mousePressed',x:start.x,y:start.y,button:'left',buttons:1,clickCount:1}); try{ await waitFor(()=>client.evaluate("Boolean(rendered.get('B0')?.drawing)"),{timeout:5000,label:'continuous pickup activation'}); const pendingMoves=[]; for(let index=0;index{}); await sleep(120); await client.evaluate(`(()=>{realtimeReactions.delete('benchmark-pickup-effect');data.states.B0=${JSON.stringify(originalState)};normalizedStateObjects.add(data.states.B0);markStateDirty('B0');renderBoardNow(rendered.get('B0'));return true})()`); await client.evaluate("(async()=>{await persistNow({skipCloud:true});return true})()"); } } async function claimLatencyProbe(client,pathRow,approve){ return client.evaluate(`(async()=>{ const board=rendered.get('B0'),state=metaState('B0'),originalState=deepClone(state),originalEnsure=ensureBoardClaimForInput,pointerId=${approve?93:94};let resolveClaim; const wait=milliseconds=>new Promise(resolve=>setTimeout(resolve,milliseconds)),nextFrame=()=>new Promise(resolve=>requestAnimationFrame(resolve)); const cleanup=()=>{cancelBoardDragFrame(board);clearDragRender(board);board.drawing=null;board.armedGate=null;clearPendingClaimPointer(board,pointerId,{release:false});realtimeHeldPointers.delete(pointerId);try{if(board.svg.hasPointerCapture?.(pointerId))board.svg.releasePointerCapture(pointerId)}catch(_){}data.states.B0=originalState;normalizedStateObjects.add(originalState);renderBoardNow(board);refreshInteractionState()}; try{ const cells=${JSON.stringify(pathRow.cells)},startGate=${pathRow.startGate},startColor=canonicalGateColorIndex(board.meta,startGate,null); state.paths=[{startGate,endGate:null,openGate:null,cells:cells.slice(0,2).map(cell=>[...cell]),colorIndex:startColor,startColorIndex:startColor,endColorIndex:null}];board.drawing=null;renderBoardNow(board); const hit=board.pathLayer.querySelector('.endpoint-hit[data-path-index="0"][data-endpoint-side="end"]'),rect=board.svg.getBoundingClientRect(),vb=board.svg.viewBox.baseVal, screenPoint=([row,column])=>({clientX:rect.left+(PAD+(column+.5)*CELL)*rect.width/vb.width,clientY:rect.top+(PAD+(row+.5)*CELL)*rect.height/vb.height}), down=screenPoint(cells[1]),latest=screenPoint(cells[Math.min(2,cells.length-1)]),before=JSON.stringify(state.paths),fullBefore=perfCounters.fullBoardRenders||0; ensureBoardClaimForInput=()=>new Promise(resolve=>{resolveClaim=resolve}); hit.dispatchEvent(new PointerEvent('pointerdown',{bubbles:true,cancelable:true,pointerId,pointerType:'mouse',isPrimary:true,button:0,buttons:1,...down})); await nextFrame();board.svg.dispatchEvent(new PointerEvent('pointermove',{bubbles:true,cancelable:true,pointerId,pointerType:'mouse',isPrimary:true,button:-1,buttons:1,...latest})); await wait(70);await nextFrame(); const pending=board.pendingClaimPointer,previewWithinFrame=Boolean(pending?.preview?.getAttribute?.('transform')),tracksLatest=Boolean(pending&&Math.abs(pending.clientX-latest.clientX)<.1&&Math.abs(pending.clientY-latest.clientY)<.1), modelUntouched=JSON.stringify(state.paths)===before&&!board.drawing; resolveClaim(${approve});await wait(20);await nextFrame();await nextFrame(); const approved=Boolean(board.drawing?.pointerId===pointerId),processed=board.lastProcessedPointerMove, noJump=${approve}?Boolean(processed&&Math.abs(processed.clientX-latest.clientX)<.1&&Math.abs(processed.clientY-latest.clientY)<.1):!board.drawing&&!board.pendingClaimPointer&&JSON.stringify(state.paths)===before, fullBoardRenders=(perfCounters.fullBoardRenders||0)-fullBefore; return{previewWithinFrame,tracksLatest,modelUntouched,approved,noJump,fullBoardRenders}; }finally{ensureBoardClaimForInput=originalEnsure;cleanup()} })()`); } async function measureCursorModes(client){ return client.evaluate(`(async()=>{ const emoji=CURSOR_ITEMS.find(item=>item.cursorEmoji&&!item.flagAsset),flag=CURSOR_ITEMS.find(item=>item.flagAsset),nextFrame=()=>new Promise(resolve=>requestAnimationFrame(resolve)); try{ localStorage.removeItem('bend-field-cursor-renderer');syncCursorAppearance('default');const defaultMode=document.body.dataset.cursorMode; syncCursorAppearance(emoji.cursorStyle);const emojiMode=document.body.dataset.cursorMode; syncCursorAppearance(flag.cursorStyle);const flagMode=document.body.dataset.cursorMode; localStorage.setItem('bend-field-cursor-renderer','dom');syncCursorAppearance(emoji.cursorStyle); const viewport=document.querySelector('#viewport'),cursorEvent='onpointerrawupdate'in window?'pointerrawupdate':'pointermove',first=new PointerEvent(cursorEvent,{bubbles:true,pointerId:95,pointerType:'mouse',clientX:120,clientY:130}); viewport.dispatchEvent(first);await nextFrame();const firstTransform=customEmojiCursor.style.transform,visibleBeforeDrag=customEmojiCursor.classList.contains('visible'); document.body.classList.add('is-drawing');viewport.dispatchEvent(new PointerEvent(cursorEvent,{bubbles:true,pointerId:95,pointerType:'mouse',clientX:160,clientY:170}));await nextFrame(); return{defaultMode,emojiMode,flagMode,domMode:document.body.dataset.cursorMode,visibleBeforeDrag,visibleDuringDrag:customEmojiCursor.classList.contains('visible'),movedDuringDrag:firstTransform!==customEmojiCursor.style.transform}; }finally{document.body.classList.remove('is-drawing');localStorage.removeItem('bend-field-cursor-renderer');syncCursorAppearance('default')} })()`); } async function measureDisplayCadence(client,frames=90){ return client.evaluate(`new Promise(resolve=>{ const gaps=[];let previous=0,count=0; const tick=timestamp=>{if(previous)gaps.push(timestamp-previous);previous=timestamp;if(++count<${frames})requestAnimationFrame(tick);else{const sorted=gaps.sort((a,b)=>a-b),pick=q=>sorted[Math.floor((sorted.length-1)*q)]||0;resolve({count:gaps.length,p50:pick(.5),p95:pick(.95),max:sorted[sorted.length-1]||0})}}; requestAnimationFrame(tick); })`); } async function measureCursorCadence(client,steps=180){ const setup=await client.evaluate(`(()=>{ const emoji=CURSOR_ITEMS.find(item=>item.cursorEmoji&&!item.flagAsset),rect=document.querySelector('#viewport').getBoundingClientRect(); localStorage.setItem('bend-field-cursor-renderer','dom');syncCursorAppearance(emoji.cursorStyle);BEND_PERF.reset();globalThis.__benchmarkCursorCommits=[]; globalThis.__benchmarkCommitCustomCursorFrame=commitCustomCursorFrame;commitCustomCursorFrame=(sample,timestamp)=>{globalThis.__benchmarkCursorCommits.push({timestamp,inputAt:sample.inputAt,revision:sample.revision});return globalThis.__benchmarkCommitCustomCursorFrame(sample,timestamp)}; return{left:rect.left+40,top:rect.top+40,width:Math.max(120,rect.width-80),height:Math.max(120,rect.height-80)}; })()`); try{ const dispatches=[]; for(let index=0;index{ const snapshot=BEND_PERF.snapshot(),commits=globalThis.__benchmarkCursorCommits||[],gaps=commits.slice(1).map((entry,index)=>entry.timestamp-commits[index].timestamp); return{snapshot,diagnostic:{interval:DRAG_FRAME_INTERVAL,tolerance:INTERACTION_FRAME_TOLERANCE_MS,commitCount:commits.length,gaps:gaps.slice(0,20),lastDraw:customCursorLastDraw,inputRevision:customCursorInputRevision,committedRevision:customCursorCommittedRevision}}; })()`),snapshot=measured.snapshot,gap=timing(snapshot,'cursorFrameGap'),age=timing(snapshot,'cursorInputAge'); assert(gap.count>=6&&gap.p50<=40,`DOM cursor cadence missed the capped 30 FPS acceptance: ${JSON.stringify({gap,diagnostic:measured.diagnostic})}`); assert(age.count>=8&&age.p95<45,`DOM cursor input age missed acceptance: ${JSON.stringify(age)}`); return snapshot; }finally{await client.evaluate("if(globalThis.__benchmarkCommitCustomCursorFrame)commitCustomCursorFrame=globalThis.__benchmarkCommitCustomCursorFrame;delete globalThis.__benchmarkCommitCustomCursorFrame;delete globalThis.__benchmarkCursorCommits;localStorage.removeItem('bend-field-cursor-renderer');syncCursorAppearance('default');true")} } async function measureEffectsAndCosmetics(client,{cpuRate=1,visual=true,inventory=true,aurora=true,memory=true,singles=true}={}){ await client.send('Emulation.setCPUThrottlingRate',{rate:cpuRate}); return client.evaluate(`(async()=>{ const options=${JSON.stringify({cpuRate,visual,inventory,aurora,memory,singles,visualCases:effectVisualFixture.cases,visualWidth:effectVisualFixture.width,visualHeight:effectVisualFixture.height,perChannelTolerance:effectVisualFixture.perChannelTolerance,maximumChangedPixelRatio:effectVisualFixture.maximumChangedPixelRatio})},wait=milliseconds=>new Promise(resolve=>setTimeout(resolve,milliseconds)), styles=['classic','giant','laser','orbit','firework','comet'],emoji=REACTION_EMOJIS[0], runSet=async(runStyles,label,duration=900)=>{ for(const style of new Set(runStyles))BEND_PERF.warmEffectCache(emoji,style);await new Promise(resolve=>requestAnimationFrame(resolve));await wait(80); BEND_PERF.reset();const rect=document.querySelector('#viewport').getBoundingClientRect(),center=worldUnitAtClient(rect.left+rect.width/2,rect.top+rect.height/2),now=trustedNow(); runStyles.forEach((style,index)=>{const effectDuration=reactionDurationForStyle(style),angle=index*Math.PI*2/Math.max(1,runStyles.length),radius=runStyles.length>1?1.35:0;applyRealtimeReaction({id:'effect-benchmark-'+label+'-'+index,emoji,style,x:center[0]+Math.cos(angle)*radius,y:center[1]+Math.sin(angle)*radius,createdAt:now-effectDuration*.22,expiresAt:now+effectDuration*.78})}); await wait(duration);const snapshot=BEND_PERF.snapshot();realtimeReactions.clear();reactionDirty=true;scheduleReactionRender(true);await wait(90);return snapshot; }, loadPixels=dataUrl=>new Promise((resolve,reject)=>{const image=new Image();image.onload=()=>{const canvas=document.createElement('canvas');canvas.width=image.naturalWidth;canvas.height=image.naturalHeight;const context=canvas.getContext('2d',{willReadFrequently:true});context.drawImage(image,0,0);resolve({width:canvas.width,height:canvas.height,data:context.getImageData(0,0,canvas.width,canvas.height).data})};image.onerror=reject;image.src=dataUrl}), compareSamples=async(directUrl,cachedUrl)=>{const[a,b]=await Promise.all([loadPixels(directUrl),loadPixels(cachedUrl)]);let changed=0,active=0,alphaDelta=0,minX=a.width,minY=a.height,maxX=-1,maxY=-1;for(let offset=0;offset0||b.data[offset+3]>0;if(visible){active++;minX=Math.min(minX,x);minY=Math.min(minY,y);maxX=Math.max(maxX,x);maxY=Math.max(maxY,y)}if(Math.abs(a.data[offset]-b.data[offset])>options.perChannelTolerance||Math.abs(a.data[offset+1]-b.data[offset+1])>options.perChannelTolerance||Math.abs(a.data[offset+2]-b.data[offset+2])>options.perChannelTolerance||Math.abs(a.data[offset+3]-b.data[offset+3])>options.perChannelTolerance)changed++;alphaDelta+=Math.abs(a.data[offset+3]-b.data[offset+3])}return{changed,total:a.width*a.height,ratio:changed/(a.width*a.height),active,alphaDelta,bounds:[minX,minY,maxX,maxY]}}, result={cpuRate:options.cpuRate,singles:{},visual:[],visualGate:{perChannelTolerance:options.perChannelTolerance,maximumChangedPixelRatio:options.maximumChangedPixelRatio,width:options.visualWidth,height:options.visualHeight},inventory:null,aurora:null,memory:null}; BEND_PERF.clearEffectCaches(); if(options.singles)for(const style of styles)result.singles[style]=await runSet([style],style,style==='classic'?620:1200); result.overlap4=await runSet(['giant','laser','orbit','firework'],'overlap4',1100); result.overlap8=await runSet(['giant','laser','orbit','firework','comet','laser','orbit','firework'],'overlap8',1100); if(options.cpuRate===1){ const board=rendered.get('B0'),setup={};BEND_PERF.reset();for(let index=0;index<40;index++){const style=styles[index%styles.length],duration=reactionDurationForStyle(style),id='effect-publish-'+index,now=trustedNow();applyRealtimeReaction({id,emoji,style,x:0,y:0,createdAt:now,expiresAt:now+duration});realtimeReactions.delete(id)}cancelReactionRenderScheduler(true);setup.reaction=BEND_PERF.snapshot(); if(board){cleanupGemEffects();BEND_PERF.reset();for(let index=0;index<30;index++){playGemCollectionAnimation(board,100000);cleanupGemEffects()}setup.gem=BEND_PERF.snapshot();skipCompletionVisuals();BEND_PERF.reset();for(let index=0;index<30;index++){completionEffect(board,1000);finishCompletionVisual(board.id,false)}setup.completion=BEND_PERF.snapshot()} result.setup=setup; } if(options.visual){ for(const{style,life}of options.visualCases){ const direct=BEND_PERF.renderReactionSample({style,emoji,life,width:options.visualWidth,height:options.visualHeight,glyphCache:false,pathCache:false}),cached=BEND_PERF.renderReactionSample({style,emoji,life,width:options.visualWidth,height:options.visualHeight,glyphCache:true,pathCache:true}); result.visual.push({style,life,...await compareSamples(direct.dataUrl,cached.dataUrl)}); } } if(options.inventory){ const previousCursor=data.cursorStyle,previousLineColor=data.lineColorStyle,previousInventoryEntries=inventoryEntries,syntheticEntries=STORE_ITEMS.map((item,index)=>({meta:null,st:null,store:null,purchase:{id:item.id,itemId:item.id,boughtAt:index+1,paidCost:0},personal:true}));inventoryEntries=itemId=>itemId?syntheticEntries.filter(entry=>entry.purchase.id===itemId):[...syntheticEntries];invalidateEconomyCaches();await wait(100);BEND_PERF.reset();const resourcesBefore=performance.getEntriesByType('resource').filter(entry=>entry.name.includes('/assets/flags/')).length; openInventory();await new Promise(resolve=>requestAnimationFrame(()=>requestAnimationFrame(resolve)));const renderSnapshot=BEND_PERF.snapshot();inventoryPanel.scrollTop=Math.min(480,Math.max(0,inventoryPanel.scrollHeight-inventoryPanel.clientHeight));const scrollBefore=inventoryPanel.scrollTop,auroraItem=STORE_ITEMS.find(item=>item.aurora===true),focusTarget=auroraItem?inventoryItemViews.get(auroraItem.id)?.use:null,nodesBefore=inventoryList.querySelectorAll('*').length;focusTarget?.focus({preventScroll:true});BEND_PERF.reset(); for(let index=0;index<40;index++){data.lineColorStyle=index%2?previousLineColor:auroraItem.id;patchInventoryItems([auroraItem.id,previousLineColor])}await new Promise(resolve=>requestAnimationFrame(resolve)); const patchSnapshot=BEND_PERF.snapshot(),resourcesAfter=performance.getEntriesByType('resource').filter(entry=>entry.name.includes('/assets/flags/')).length;result.inventory={renderSnapshot,patchSnapshot,scrollBefore,scrollAfter:inventoryPanel.scrollTop,focusRetained:!focusTarget||document.activeElement===focusTarget,resourceDelta:resourcesAfter-resourcesBefore,mounted:inventoryList.querySelectorAll('[data-item-id]').length,nodesBefore,nodesAfter:inventoryList.querySelectorAll('*').length}; closeInventory(false);inventoryEntries=previousInventoryEntries;invalidateEconomyCaches();data.cursorStyle=previousCursor;data.lineColorStyle=previousLineColor;syncCursorAppearance(previousCursor);syncCosmeticAppearance();renderInventoryPanel(); } if(options.aurora){ const svg=document.createElementNS('http://www.w3.org/2000/svg','svg'),fragment=document.createDocumentFragment();svg.setAttribute('aria-hidden','true');svg.style.cssText='position:absolute;width:1px;height:1px;overflow:visible;pointer-events:none'; for(let index=0;index<500;index++){const path=document.createElementNS('http://www.w3.org/2000/svg','path');path.setAttribute('class','path line-effect-aurora');path.setAttribute('d',\`M0 \${index%25} L100 \${index%25}\`);fragment.append(path)}svg.append(fragment);world.append(svg);const previousCount=auroraVisiblePathCount;auroraVisiblePathCount+=500;BEND_PERF.reset();updateAuroraAnimationState();await wait(4300);const active=BEND_PERF.snapshot();auroraVisiblePathCount=0;updateAuroraAnimationState();BEND_PERF.reset();await wait(2300);const inactive=BEND_PERF.snapshot(); Object.defineProperty(document,'visibilityState',{value:'hidden',configurable:true});auroraVisiblePathCount=500;document.dispatchEvent(new Event('visibilitychange'));BEND_PERF.reset();await wait(2300);const hidden=BEND_PERF.snapshot();delete document.visibilityState;auroraVisiblePathCount=previousCount;svg.remove();document.dispatchEvent(new Event('visibilitychange'));updateAuroraAnimationState();result.aurora={active,inactive,hidden}; } if(options.memory&&globalThis.gc&&performance.memory){ const board=rendered.get('B0');BEND_PERF.clearEffectCaches();cleanupGemEffects();skipCompletionVisuals();globalThis.gc();await wait(80);const before=performance.memory.usedJSHeapSize; for(let index=0;index<100;index++){const style=styles[index%styles.length],duration=reactionDurationForStyle(style),id='effect-memory-'+index,now=trustedNow();applyRealtimeReaction({id,emoji,style,x:0,y:0,createdAt:now-duration*.5,expiresAt:now+duration*.5});reactionDirty=true;drawReactionLayer();realtimeReactions.delete(id);cancelReactionRenderScheduler(true);if(board){playGemCollectionAnimation(board,100000);cleanupGemEffects();completionEffect(board,1000);finishCompletionVisual(board.id,false)}} reactionDirty=true;drawReactionLayer();cancelReactionRenderScheduler(true);cleanupGemEffects();skipCompletionVisuals();BEND_PERF.clearEffectCaches();globalThis.gc();await wait(120);globalThis.gc();result.memory={before,after:performance.memory.usedJSHeapSize,delta:performance.memory.usedJSHeapSize-before,active:realtimeReactions.size,gemBatches:activeGemBatches.size,completionVisuals:activeCompletionVisuals.size,effectNodes:document.querySelectorAll('.gem-particle,.completion-flash,.completion-burst').length,cacheEntries:(BEND_PERF.snapshot().gauges.reactionGlyphCacheEntries||0)+(BEND_PERF.snapshot().gauges.reactionStaticPathCacheEntries||0)}; } return result; })()`); } function validateEffectsAndCosmetics(result,{mobile=false}={}){ const cpuRate=result.cpuRate,normalSpeed=cpuRate===1,singleLimit=normalSpeed?6:40,fourLimit=normalSpeed?12:80,longTaskLimit=normalSpeed?50:500,minFrames=normalSpeed?8:5,minGaps=normalSpeed?7:4; for(const[style,snapshot]of Object.entries(result.singles||{})){ const draw=timing(snapshot,`reactionStyle.${style}`),gap=timing(snapshot,'reactionFrameGap'); assert(draw.count>=minFrames&&(!normalSpeed||draw.p95<=singleLimit&&draw.p99<=8),`${mobile?'mobile ':''}${style}/${cpuRate}x reaction work missed acceptance: ${JSON.stringify({draw,singleLimit,minFrames})}`); if(normalSpeed)assert(gap.count>=minGaps&&gap.p50<=45,`${style}/${cpuRate}x single-effect median cadence missed acceptance: ${JSON.stringify({gap,draw:timing(snapshot,'reactionFrame'),rates:snapshot.rates,counters:snapshot.counters})}`); assert(timing(snapshot,'reactionFrame').max=minFrames&&(!normalSpeed||four.p95<=fourLimit&&four.p99<=20)&&four.max=(normalSpeed?6:4)&&eight.max=8,`Eight-effect overload did not render every valid reaction: ${JSON.stringify({eight,peakVisible:result.overlap8.gauges.peakVisibleReactions,visible:result.overlap8.gauges.visibleReactions})}`); for(const snapshot of[result.overlap4,result.overlap8]){ assert((snapshot.rates.reactionDeadlineTimerCallbacksPerSecond||0)<=37.5,`Reaction deadline timer exceeded the 37.5 callbacks/s short-window envelope: ${snapshot.rates.reactionDeadlineTimerCallbacksPerSecond}`); assert((snapshot.rates.reactionDrawRafCallbacksPerSecond||0)<=37.5,`Reaction draw RAF exceeded the 37.5 callbacks/s short-window envelope: ${snapshot.rates.reactionDrawRafCallbacksPerSecond}`); } if(result.visual?.length){const worst=result.visual.reduce((a,b)=>a.ratio>b.ratio?a:b),limit=result.visualGate?.maximumChangedPixelRatio??.005;assert(worst.ratio<=limit,`Cached effect pixels changed beyond tolerance: ${JSON.stringify({worst,gate:result.visualGate})}`)} if(result.setup){assert(timing(result.setup.reaction,'reactionPublish').p95<=2,`Reaction publication exceeded 2 ms: ${JSON.stringify(timing(result.setup.reaction,'reactionPublish'))}`);assert(timing(result.setup.gem,'gemEffectSetup').p95<=3,`Gem setup exceeded 3 ms: ${JSON.stringify(timing(result.setup.gem,'gemEffectSetup'))}`);assert(timing(result.setup.completion,'completionEffectSetup').p95<=3,`Completion setup exceeded 3 ms: ${JSON.stringify(timing(result.setup.completion,'completionEffectSetup'))}`)} if(result.inventory){ const render=timing(result.inventory.renderSnapshot,'inventoryRender'),initialPatch=timing(result.inventory.renderSnapshot,'inventoryPatch'),initial=render.count?render:initialPatch,patch=timing(result.inventory.patchSnapshot,'inventoryPatch'); assert(initial.count&&initial.p95<=100,`Full cosmetic inventory render exceeded 100 ms: ${JSON.stringify({render,initialPatch})}`);assert(patch.count>=20&&patch.p95<=8,`Cosmetic equip patch exceeded 8 ms: ${JSON.stringify({patch})}`); assert(result.inventory.scrollAfter===result.inventory.scrollBefore,`Cosmetic equip moved inventory scroll from ${result.inventory.scrollBefore} to ${result.inventory.scrollAfter}`);assert(result.inventory.focusRetained,'Cosmetic equip moved keyboard focus');assert(result.inventory.nodesAfter===result.inventory.nodesBefore,'Cosmetic equip rebuilt inventory nodes'); assert((result.inventory.renderSnapshot.counters.longTasks||0)===0&&(result.inventory.patchSnapshot.counters.longTasks||0)===0,`Full cosmetic inventory produced a long task: ${JSON.stringify({render,patch,mounted:result.inventory.mounted})}`); } if(result.aurora){const active=timing(result.aurora.active,'auroraTick');assert(active.p95<=.25,`Aurora tick exceeded 0.25 ms: ${JSON.stringify(active)}`);const writes=result.aurora.active.counters.auroraColorWrites||0;assert(writes>=2&&writes<=3,`Aurora did not select one curated color every two seconds: ${writes}`);for(const[name,snapshot]of Object.entries({inactive:result.aurora.inactive,hidden:result.aurora.hidden}))assert(!(snapshot.counters.auroraColorWrites||0)&&!timing(snapshot,'auroraTick').count,`Aurora performed work while ${name}: ${JSON.stringify(snapshot)}`)} if(result.memory)assert(result.memory.active===0&&result.memory.gemBatches===0&&result.memory.completionVisuals===0&&result.memory.effectNodes===0&&result.memory.cacheEntries===0&&result.memory.delta<=2*1024*1024,`Effect cleanup retained too much state: ${JSON.stringify(result.memory)}`); } async function zoom(client,deltaY,repetitions){ const center=await viewportCenter(client); for(let index=0;index{ const percentile=(rows,p)=>{const ordered=[...rows].sort((a,b)=>a-b);return ordered[Math.min(ordered.length-1,Math.floor(ordered.length*p))]||0}; const sample=(operation,count=2000)=>{const rows=[];for(let index=0;indexdirectionFromDelta(CELL*(index%2?.46:.6),index%2?0:CELL*.55)); const pointerSamples=sample(()=>pointerEventSamples({pointerId:1,pointerType:'pen',getCoalescedEvents:()=>[{clientX:10,clientY:20},{clientX:20,clientY:20},{clientX:20,clientY:10}]})); const rect=getMinimapRect(),event={clientX:rect.left+rect.width*.5,clientY:rect.top+rect.height*.5}; const minimap=sample(()=>minimapWorldPoint(event)); const noise=sample(()=>paintNoiseBackground(false),80); const controls={ origin:Boolean(document.querySelector('#minimapOriginBtn')), random:Boolean(document.querySelector('#minimapRandomBtn')), unsolved:Boolean(document.querySelector('#minimapUnsolvedBtn')), current:Boolean(document.querySelector('#minimapCurrentBtn')) }; const originalScale=cam.scale;cam.scale=OVERVIEW_ZOOM_THRESHOLD-.001;const overviewBelow=inWorldOverview(); cam.scale=OVERVIEW_ZOOM_THRESHOLD+.001;const overviewAbove=inWorldOverview();cam.scale=originalScale;inWorldOverview(); const active=rendered.get(activeBoard);let northHudGap=null; if(active){positionBoardLabel(active);if(active.label.dataset.side==='N'){const placement=hudPlacementCandidates(active.meta)[0],edgeY=PAD+placement.dy*UNIT;northHudGap=edgeY-parseFloat(active.label.style.top)}} let heartbeats=0;const heartbeat=setInterval(()=>heartbeats++,0),workerStarted=performance.now(); const workerResult=await verifyPuzzleUniquenessAsync(data.metas.B0.puzzle,2000); const workerElapsed=performance.now()-workerStarted;clearInterval(heartbeat); return{ snap,pointerSamples,minimap,noise,workerElapsed,heartbeats,workerStatus:workerResult.status, controls,overviewBelow,overviewAbove,northHudGap, cellHitCount:document.querySelectorAll('.cell-hit').length, cellShapeCount:document.querySelectorAll('.board-card .cell-shape').length, renderedBoardCount:rendered.size, visibleDetailedCount:[...visibleMetaIds()].filter(id=>data.metas[id]?.puzzle&&rendered.has(id)).length, visiblePuzzleCount:[...visibleMetaIds()].filter(id=>data.metas[id]?.puzzle).length, fpsVisible:Boolean(document.querySelector('#fpsCounter')), difficultyItems:STORE_ITEMS.filter(item=>item.id==='level-min-10'||item.id==='level-max-10').length }; })()`); assert(result.snap.p95<1,`Pointer snap p95 ${result.snap.p95.toFixed(3)} ms exceeded 1 ms`); assert(result.pointerSamples.p95<1,`Pointer-sample p95 ${result.pointerSamples.p95.toFixed(3)} ms exceeded 1 ms`); assert(result.minimap.p95<1,`Minimap conversion p95 ${result.minimap.p95.toFixed(3)} ms exceeded 1 ms`); assert(result.noise.p95<10&&result.noise.max<20,`Noise update p95/max ${result.noise.p95.toFixed(3)}/${result.noise.max.toFixed(3)} ms exceeded the budget`); assert(result.controls.origin&&result.controls.random&&!result.controls.unsolved&&!result.controls.current,'Minimap teleport controls do not match origin + random'); assert(result.overviewBelow&&!result.overviewAbove,'World overview retained zoom hysteresis'); if(result.northHudGap!=null)assert(Math.abs(result.northHudGap-22)<.01,`Top puzzle HUD gap is ${result.northHudGap}, expected 22 world pixels`); assert(result.cellHitCount===0&&result.cellShapeCount>0,'Detailed boards still allocate one hit node per cell or lack compound cell paths'); assert(result.visibleDetailedCount===result.visiblePuzzleCount,'A visible valid board was hidden from detailed rendering'); assert(result.fpsVisible&&result.difficultyItems===0,'FPS display is missing or retired difficulty items remain'); assert(result.heartbeats>0&&['unique','multiple','unsolved','timeout','invalid'].includes(result.workerStatus),'Uniqueness verification did not yield to the main thread'); return result; } function validateMeasurement(result){ const {snapshot,cpuRate,profile}=result,cadenceSnapshot=result.pickupCadence||snapshot, drag=timing(snapshot,'processBoardDragFrame'), probeDrag=timing(cadenceSnapshot,'processBoardDragFrame'), camera=timing(snapshot,'commitCameraInteraction'), dragGap=timing(cadenceSnapshot,'pickupVisualFrameGap'),cameraGap=timing(snapshot,'cameraFrameGap'), dragAge=timing(cadenceSnapshot,'pickupVisualInputAge'),cameraAge=timing(snapshot,'cameraInputAge'), minimap=timing(snapshot,'drawMinimap'),ensure=timing(snapshot,'ensureBoards'),save=timing(snapshot,'persistDirtyToDb'), overview=timing(snapshot,'drawWorldOverview'),mirrorChunk=timing(snapshot,'mirrorChunkWrite'), dragLimit=cpuRate===1?8:16,functionalDragLimit=cpuRate===1?18:24,minimapLimit=cpuRate===1?20:33,lodLimit=cpuRate===1?40:100; assert(drag.count+probeDrag.count>=5,`${profile}/${cpuRate}x captured only ${drag.count} functional and ${probeDrag.count} continuous-input drag frames: ${JSON.stringify(result.pickupProbe)}`); assert(camera.count>=5,`${profile}/${cpuRate}x did not capture frame-coalesced camera work`); assert(minimap.count>=2,`${profile}/${cpuRate}x did not capture minimap work`); assert(ensure.count>=2,`${profile}/${cpuRate}x did not capture LOD work`); assert(save.count>=1,`${profile}/${cpuRate}x did not capture an autosave`); assert(overview.count>=1,`${profile}/${cpuRate}x did not capture overview rendering`); assert(cameraGap.count>=4&&(cpuRate!==1||dragGap.count>=4),`${profile}/${cpuRate}x did not capture enough real interaction cadence samples (pickup ${dragGap.count}, camera ${cameraGap.count})`); if(cpuRate===1){ const approved=result.claimApproved,denied=result.claimDenied; assert(approved?.previewWithinFrame&&approved.tracksLatest&&approved.modelUntouched&&approved.approved&&approved.noJump&&approved.fullBoardRenders===0,`${profile} claim approval preview/commit failed: ${JSON.stringify(approved)}`); assert(denied?.previewWithinFrame&&denied.tracksLatest&&denied.modelUntouched&&!denied.approved&&denied.noJump&&denied.fullBoardRenders===0,`${profile} claim denial rollback failed: ${JSON.stringify(denied)}`); assert(result.edgePan?.cameraMoved&&result.edgePan.dragFrames>=2,`${profile} pickup edge-pan did not retain continuous drag frames: ${JSON.stringify(result.edgePan)}`); } assert(result.pinch?.changed,`${profile}/${cpuRate}x pinch zoom did not change camera scale: ${JSON.stringify(result.pinch)}`); assert(result.overviewPathsObserved===0,`${profile}/${cpuRate}x far overview rendered route lines`); assert((snapshot.counters.overviewCacheBuilds||0)>result.overviewBuildBaseline,`${profile}/${cpuRate}x long overview pan did not rebuild the exhausted cache after settlement`); assert((snapshot.counters.overviewBuildsDuringInteraction||0)>=1&&(snapshot.counters.overviewBuildsDuringInteraction||0)<=60,`${profile}/${cpuRate}x long overview pan did not use a bounded in-gesture cache refresh`); if(cpuRate===1){ const cadenceHot=Object.entries(cadenceSnapshot.timings||{}).filter(([,value])=>value.max>1).sort((a,b)=>b[1].max-a[1].max).slice(0,12); assert(dragGap.p50<=40&&dragGap.p95<=50,`${profile} pickup visual median/p95 gap ${dragGap.p50.toFixed(2)}/${dragGap.p95.toFixed(2)} ms exceeded the capped 30 FPS cadence budget; active timings ${JSON.stringify(cadenceHot)}`); assert(cameraGap.p50<=40,`${profile} camera median gap ${cameraGap.p50.toFixed(2)} ms exceeded the capped 30 FPS cadence budget`); assert(dragAge.p95<45,`${profile} pickup input age ${dragAge.p95.toFixed(2)} ms exceeded 45 ms`); assert(cameraAge.p95<45,`${profile} camera input age ${cameraAge.p95.toFixed(2)} ms exceeded 45 ms`); } assert(drag.p95<=functionalDragLimit&&probeDrag.p95<=dragLimit,`${profile}/${cpuRate}x drag work exceeded acceptance (functional ${drag.p95.toFixed(2)}/${functionalDragLimit} ms, cadence ${probeDrag.p95.toFixed(2)}/${dragLimit} ms, model ${timing(cadenceSnapshot,'pickupModelWork').p95.toFixed(2)}, visual ${timing(cadenceSnapshot,'pickupVisualWork').p95.toFixed(2)}, render ${timing(cadenceSnapshot,'renderDragFrame').p95.toFixed(2)})`); assert(camera.p95<=dragLimit,`${profile}/${cpuRate}x camera p95 ${camera.p95.toFixed(2)} ms exceeded acceptance`); assert(minimap.p95<=minimapLimit,`${profile}/${cpuRate}x minimap p95 ${minimap.p95.toFixed(2)} ms exceeded acceptance`); assert(ensure.p95<=lodLimit,`${profile}/${cpuRate}x LOD p95 ${ensure.p95.toFixed(2)} ms exceeded acceptance`); if(mirrorChunk.count)assert(mirrorChunk.max<50,`${profile}/${cpuRate}x mirror chunk write ${mirrorChunk.max.toFixed(2)} ms became a long task`); if(cpuRate===1){ const modelWork=timing(snapshot,'pickupModelWork'),visualWork=timing(snapshot,'pickupVisualWork'),dragRender=timing(snapshot,'renderDragFrame'); assert(result.pickupProbeLongTasks===0&&result.pickupLongTasks===0&&result.panLongTasks===0,`${profile} recorded a 50 ms long task during continuous pickup (${result.pickupProbeLongTasks}), real pickup (${result.pickupLongTasks}), or the ten-second pan (${result.panLongTasks}); last ${snapshot.gauges.lastInteractionLongTaskMs||0} ms; phases preview ${timing(snapshot,'pickupPointerDownPreview').max.toFixed(1)}, commit ${timing(snapshot,'pickupPointerDownCommit').max.toFixed(1)}, finish ${timing(snapshot,'pickupPointerFinish').max.toFixed(1)}, drag ${drag.max.toFixed(1)}, model ${modelWork.max.toFixed(1)} [prepare ${timing(snapshot,'pickupModelPrepare').max.toFixed(1)}, topology ${timing(snapshot,'pickupModelTopology').max.toFixed(1)}, traversal ${timing(snapshot,'pickupModelTraversal').max.toFixed(1)}, cell ${timing(snapshot,'pickupModelCell').max.toFixed(1)}], visual ${visualWork.max.toFixed(1)}, render ${dragRender.max.toFixed(1)}; ${snapshot.gauges.lastInteractionLoafScripts||'no LoAF attribution'}`); } for(const name of ['minimapDrawsDuringInteraction','lodPassesDuringInteraction','persistenceDuringInteraction','worldRefreshesDuringInteraction']) assert((snapshot.counters[name]||0)===0&&(cadenceSnapshot.counters[name]||0)===0,`${profile}/${cpuRate}x ran ${name} during an active gesture`); assert(snapshot.gauges.renderedBoards>=snapshot.gauges.visibleUnsolvedBoards,`${profile}/${cpuRate}x omitted a visible unsolved board from detailed rendering`); assert((cadenceSnapshot.rates.dragFramesPerSecond||0)<=35,`${profile}/${cpuRate}x pickup presentation exceeded the 30 FPS ceiling (${(cadenceSnapshot.rates.dragFramesPerSecond||0).toFixed(1)} FPS)`); assert(snapshot.gauges.domNodes<18000,`${profile}/${cpuRate}x DOM size is not viewport-bounded`); } async function measureScenario(client,starter,profile,cpuRate){ await client.send('Emulation.setCPUThrottlingRate',{rate:1}); await client.evaluate("if(typeof lifecyclePersistenceSuppressed!=='undefined')lifecyclePersistenceSuppressed=true;true"); const solution=await populateWorld(client,starter,profile); await reload(client); const pristineOriginState=await client.evaluate("deepClone(metaState('B0'))"); await client.send('Emulation.setCPUThrottlingRate',{rate:cpuRate}); const claimApproved=cpuRate===1?await claimLatencyProbe(client,solution[0],true):null; if(cpuRate===1)await restoreOriginState(client,pristineOriginState); const claimDenied=cpuRate===1?await claimLatencyProbe(client,solution[0],false):null; if(cpuRate===1)await restoreOriginState(client,pristineOriginState); const edgePan=cpuRate===1?await pickupEdgePanProbe(client,solution[0]):null; if(cpuRate===1)await restoreOriginState(client,pristineOriginState); if(cpuRate===1)await sleep(300); await prepareInteractionMeasurement(client); const pickupProbe=await pickupCadenceProbe(client,solution[0],cpuRate===1?180:120,cpuRate===1?8:16); const pickupCadence=pickupProbe.snapshot;delete pickupProbe.snapshot; const pickupProbeLongTasks=pickupCadence?.counters.interactionLongTasks||0; await restoreOriginState(client,pristineOriginState); await sleep(300); await prepareInteractionMeasurement(client); const preSolveState=await client.evaluate("({pathCount:metaState('B0').paths.length,solved:metaState('B0').solved,drawing:Boolean(rendered.get('B0')?.drawing),pending:Boolean(rendered.get('B0')?.pendingClaimPointer)})"); assert(preSolveState.pathCount===0&&!preSolveState.solved&&!preSolveState.drawing&&!preSolveState.pending,`${profile.name}/${cpuRate}x pickup probes did not restore a pristine origin board: ${JSON.stringify(preSolveState)}`); await solveOrigin(client,solution,cpuRate); try{await waitFor(()=>client.evaluate("metaState('B0').solved===true"),{timeout:5000*Math.max(1,cpuRate),label:`${profile.name}/${cpuRate}x solved origin`})} catch(error){const diagnostic=await client.evaluate("(()=>{const state=metaState('B0'),board=rendered.get('B0');return{solved:state.solved,paths:state.paths.map(path=>({startGate:path.startGate,endGate:path.endGate,openGate:path.openGate,cells:path.cells})),drawing:board?.drawing?{pathIndex:board.drawing.pathIndex,catchupPending:board.drawing.catchupPending}:null,drag:board?.dragScheduler?.inspect?.()||null,logicalTrace:board?.logicalPointerCellTrace||[],flushTrace:board?.lastFlushPointerCells||[]}})()");throw new Error(`${error.message}: ${JSON.stringify(diagnostic)}`)} await sleep(850); const pickupLongTasks=await client.evaluate('BEND_PERF.snapshot().counters.interactionLongTasks||0'); await client.evaluate('finishCompletionVisual("B0",true);centerMeta(data.metas.B0);true');await sleep(350); const leftStart=await safePanPoint(client,'.board-card[data-id="B0"]'), leftProbe=await client.evaluate(`(()=>{const target=document.elementFromPoint(${leftStart.x},${leftStart.y});return{tag:target?.tagName||null,classes:target?.getAttribute?.('class')||null,board:target?.closest?.('.board-card')?.dataset?.id||null,allowed:leftFieldPanAllowed({button:0,target})}})()`); const beforeLeftPan=await client.evaluate('({x:cam.x,y:cam.y,solved:metaState("B0").solved})'); assert(beforeLeftPan.solved&&leftProbe.allowed,`${profile.name}/${cpuRate}x solved-board left drag is not eligible for panning: ${JSON.stringify({leftStart,leftProbe,beforeLeftPan})}`); await client.evaluate("(()=>{const r=document.querySelector('#viewport').getBoundingClientRect(),p=worldUnitAtClient(r.left+r.width/2,r.top+r.height/2),now=trustedNow();applyRealtimeReaction({id:'benchmark-overview-effect',emoji:REACTION_EMOJIS[0],style:'orbit',x:p[0],y:p[1],createdAt:now,expiresAt:now+20000});return true})()"); await zoom(client,180,15); const overviewPathsObserved=await client.evaluate('drawWorldOverview();BEND_PERF.snapshot().gauges.overviewPaths||0'); const overviewBuildBaseline=await client.evaluate('BEND_PERF.snapshot().counters.overviewCacheBuilds||0'); const panLongTaskBaseline=await client.evaluate('BEND_PERF.snapshot().counters.interactionLongTasks||0'); await pan(client); await panCadenceProbe(client,cpuRate===1?600:60); await waitFor(()=>client.evaluate(`(()=>{if(!inWorldOverview()||!overviewCache)return false;const[centerX,centerY]=cameraCenterInChunks();return!overviewDirty&&Math.abs(centerX-overviewCache.anchorX)*overviewCache.unit<=overviewCache.overscan*.82&&Math.abs(centerY-overviewCache.anchorY)*overviewCache.unit<=overviewCache.overscan*.82})()`),{timeout:10000,label:'settled overview cache rebuild'}); const pinch=await pinchZoomProbe(client); await sleep(80); const panLongTasks=(await client.evaluate('BEND_PERF.snapshot().counters.interactionLongTasks||0'))-panLongTaskBaseline;await client.evaluate("realtimeReactions.delete('benchmark-overview-effect');true"); await zoom(client,-180,15); await sleep(650); try{await waitFor(()=>client.evaluate("!hasPendingPersistence()"),{timeout:20000,label:'durable persistence drain'})} catch(error){ const diagnostic=await client.evaluate(`({globalDirty,dirtyMetaIds:[...dirtyMetaIds],dirtyStateIds:[...dirtyStateIds],deletedBoardIds:[...deletedBoardIds],cloudOutboxDeleteKeys:[...cloudOutboxDeleteKeys],saveTimer:Boolean(saveTimer),status:saveStatusEl?.dataset?.state||null,message:statusMessage?.textContent||null,storageFormat:activeStorageFormat,epoch:data?.worldEpoch})`); throw new Error(`${error.message}: ${JSON.stringify(diagnostic)}`); } await sleep(500); const snapshot=await client.evaluate('BEND_PERF.snapshot()'); return{profile:profile.name,boards:profile.boards,cpuRate,snapshot,pickupCadence,pickupProbe,overviewPathsObserved,overviewBuildBaseline,pickupProbeLongTasks,pickupLongTasks,panLongTasks,claimApproved,claimDenied,edgePan,pinch}; } function profileSummaryRow(result){ return{ profile:result.profile,boards:result.boards,cpuRate:result.cpuRate, dragP95:timing(result.snapshot,'processBoardDragFrame').p95, cameraP95:timing(result.snapshot,'commitCameraInteraction').p95, dragGapP95:timing(result.pickupCadence||result.snapshot,'pickupVisualFrameGap').p95, dragInputAgeP95:timing(result.pickupCadence||result.snapshot,'pickupVisualInputAge').p95, cameraGapP50:timing(result.snapshot,'cameraFrameGap').p50, cameraInputAgeP95:timing(result.snapshot,'cameraInputAge').p95, minimapP95:timing(result.snapshot,'drawMinimap').p95, lodP95:timing(result.snapshot,'ensureBoards').p95, saveP95:timing(result.snapshot,'persistDirtyToDb').p95, overviewP95:timing(result.snapshot,'drawWorldOverview').p95, renderedBoards:result.snapshot.gauges.renderedBoards, staticBoards:result.snapshot.gauges.staticBoards, domNodes:result.snapshot.gauges.domNodes, longTasks:result.snapshot.counters.longTasks||0 }; } async function main(runProfiles=profiles,cpuRates=[1,4]){ if(!fs.existsSync(edgePath))throw new Error(`Microsoft Edge was not found at ${edgePath}`); const server=await startStaticServer(); const edge=spawn(edgePath,[ '--headless','--no-first-run','--no-sandbox','--force-device-scale-factor=1','--js-flags=--expose-gc','--disable-dev-shm-usage','--disable-extensions','--disable-background-networking','--disable-background-timer-throttling','--disable-renderer-backgrounding','--disable-backgrounding-occluded-windows','--disable-crash-reporter','--disable-breakpad','--disable-component-update','--disable-features=OptimizationGuideModelDownloading,OnDeviceModelService,PromptApiForGeminiNano', `--remote-debugging-port=${debuggingPort}`,'--remote-allow-origins=*',`--user-data-dir=${profilePath}`, `--host-resolver-rules=MAP ${benchmarkHost} 127.0.0.1`,'--window-size=1440,1000','about:blank' ],{stdio:'ignore',windowsHide:true}); const report={capturedAt:new Date().toISOString(),browserPath:edgePath,benchmarkHost,status:'running',effects:{},profiles:[]}; let client=null; try{ const target=await endpoint();client=new CdpClient(target.webSocketDebuggerUrl);await client.connect(); await client.send('Page.enable');await client.send('Runtime.enable'); await client.send('Page.navigate',{url:`http://${benchmarkHost}:${serverPort}/`}); if(startupOnly){ await sleep(12000); const state=await client.evaluate("({ready:document.body?.dataset?.ready||null,version:document.querySelector('.brand small')?.textContent||null,boards:typeof data!=='undefined'?Object.keys(data.metas).length:null,origin:typeof data!=='undefined'&&Boolean(data.metas?.B0?.puzzle),worldGeneration:typeof data!=='undefined'?data.worldGeneration:null,turnFont:getComputedStyle(document.querySelector('.board-svg text')||document.body).fontFamily,status:document.querySelector('#statusMessage')?.textContent||'',text:document.body?.innerText?.slice(0,300)||''})"); assert(state.ready==='true'&&state.version==='v47.87'&&state.boards===1&&state.origin&&state.worldGeneration==='linkfield-single-world-20260801',`Real-browser startup state is incomplete: ${JSON.stringify(state)}`); assert(/DotGothic16|Press Start 2P|MS Gothic|monospace/i.test(state.turnFont),'Dot-styled game font is not active in the browser'); console.log(`Real-browser startup passed: ${JSON.stringify(state)}`);return; } await ready(client);await sleep(1000); const displayCadence=await measureDisplayCadence(client);report.displayCadence=displayCadence; assert(displayCadence.count>=60&&displayCadence.p50<=20,`Headless display baseline is not 60 Hz: ${JSON.stringify(displayCadence)}`); const gameplayBudgets=await measureGameplaySimplificationBudgets(client);report.gameplayBudgets=gameplayBudgets; const cursorModes=await measureCursorModes(client);report.cursorModes=cursorModes; assert(cursorModes.defaultMode==='default'&&cursorModes.emojiMode==='dom'&&cursorModes.flagMode==='dom'&&cursorModes.domMode==='dom'&&cursorModes.visibleBeforeDrag&&!cursorModes.visibleDuringDrag&&!cursorModes.movedDuringDrag,`Cursor mode runtime probe failed: ${JSON.stringify(cursorModes)}`); const cursorCadence=await measureCursorCadence(client);report.cursorCadence=cursorCadence; console.log(`Display cadence | median ${displayCadence.p50.toFixed(2)} ms | p95 ${displayCadence.p95.toFixed(2)} ms`); console.log(`Gameplay budgets | snap ${gameplayBudgets.snap.p95.toFixed(3)} ms | pointer samples ${gameplayBudgets.pointerSamples.p95.toFixed(3)} ms | minimap ${gameplayBudgets.minimap.p95.toFixed(3)} ms | noise max ${gameplayBudgets.noise.max.toFixed(3)} ms | worker ${gameplayBudgets.workerElapsed.toFixed(1)} ms`); console.log(`Cursor cadence | median gap ${timing(cursorCadence,'cursorFrameGap').p50.toFixed(2)} ms | input p95 ${timing(cursorCadence,'cursorInputAge').p95.toFixed(2)} ms`); const effects1x=await measureEffectsAndCosmetics(client,{cpuRate:1,visual:true,inventory:true,aurora:true,memory:true,singles:true});report.effects.desktop1x=effects1x;validateEffectsAndCosmetics(effects1x); const effects4x=await measureEffectsAndCosmetics(client,{cpuRate:4,visual:false,inventory:false,aurora:false,memory:false,singles:true});report.effects.desktop4x=effects4x;validateEffectsAndCosmetics(effects4x); await client.send('Emulation.setDeviceMetricsOverride',{width:390,height:844,deviceScaleFactor:1,mobile:true,screenWidth:390,screenHeight:844});await sleep(220); const effectsMobile=await measureEffectsAndCosmetics(client,{cpuRate:1,visual:false,inventory:false,aurora:false,memory:false,singles:false});report.effects.mobile=effectsMobile;validateEffectsAndCosmetics(effectsMobile,{mobile:true}); await client.send('Emulation.clearDeviceMetricsOverride');await client.send('Emulation.setCPUThrottlingRate',{rate:1});await sleep(220); console.log(`Effects | single firework ${timing(effects1x.singles.firework,'reactionStyle.firework').p95.toFixed(2)} ms | overlap4 ${timing(effects1x.overlap4,'reactionFrame').p95.toFixed(2)} ms | overlap8 ${timing(effects1x.overlap8,'reactionFrame').p95.toFixed(2)} ms | visual max ${(Math.max(...effects1x.visual.map(row=>row.ratio))*100).toFixed(3)}%`); console.log(`EFFECT_BENCHMARK_JSON=${JSON.stringify({desktop1x:{single:Object.fromEntries(Object.entries(effects1x.singles).map(([style,snapshot])=>[style,timing(snapshot,`reactionStyle.${style}`)])),overlap4:timing(effects1x.overlap4,'reactionFrame'),overlap8:timing(effects1x.overlap8,'reactionFrame'),inventory:effects1x.inventory,memory:effects1x.memory,aurora:timing(effects1x.aurora.active,'auroraTick'),setup:effects1x.setup},desktop4x:{overlap4:timing(effects4x.overlap4,'reactionFrame'),overlap8:timing(effects4x.overlap8,'reactionFrame')},mobile:{overlap4:timing(effectsMobile.overlap4,'reactionFrame'),overlap8:timing(effectsMobile.overlap8,'reactionFrame')}})}`); const starter=await readStarterRows(client),results=[]; for(const profile of runProfiles)for(const cpuRate of cpuRates){ const result=await measureScenario(client,starter,profile,cpuRate);results.push(result);report.profiles=results.map(profileSummaryRow);validateMeasurement(result); const drag=timing(result.snapshot,'processBoardDragFrame'),camera=timing(result.snapshot,'commitCameraInteraction'),minimap=timing(result.snapshot,'drawMinimap'), ensure=timing(result.snapshot,'ensureBoards'),save=timing(result.snapshot,'persistDirtyToDb'); console.log(`${profile.name.padEnd(6)} ${cpuRate}x CPU | drag p95 ${drag.p95.toFixed(2)} ms | camera ${camera.p95.toFixed(2)} ms | minimap ${minimap.p95.toFixed(2)} ms | LOD ${ensure.p95.toFixed(2)} ms | save ${save.p95.toFixed(2)} ms | DOM ${result.snapshot.gauges.domNodes}`); } const profileSummary=results.map(profileSummaryRow);report.profiles=profileSummary;console.log(`BROWSER_BENCHMARK_JSON=${JSON.stringify(profileSummary)}`); report.status='passed';writeBenchmarkReport(report);console.log(`Browser benchmark report: ${benchmarkOutputPath}`); console.log('Real-browser performance benchmark passed'); }catch(error){ report.status='failed';report.failure={name:error?.name||'Error',message:error?.message||String(error),stack:error?.stack||''}; try{writeBenchmarkReport(report);console.error(`Browser benchmark failure report: ${benchmarkOutputPath}`)}catch(reportError){console.error(`Browser benchmark report write failed: ${reportError.message}`)} throw error; }finally{ client?.close(); stopBrowserTree(edge); server.closeAllConnections?.();await new Promise(resolve=>server.close(()=>resolve())); for(let attempt=0;attempt<8;attempt++){ try{fs.rmSync(temporaryRoot,{recursive:true,force:true});break} catch(error){if(attempt===7)console.warn(`Benchmark cleanup deferred: ${error.message}`);await sleep(150)} } } } module.exports={main,allProfiles}; if(require.main===module)main().then(()=>process.exit(0)).catch(error=>{console.error(error);process.exit(1)});