2026-07-30 15:13:53 +09:00
'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 temporaryRoot = fs . mkdtempSync ( path . join ( os . tmpdir ( ) , 'bend-field-browser-benchmark-' ) ) ;
const profilePath = path . join ( temporaryRoot , 'edge-profile' ) ;
const worldDbName = 'bend-field:v30:v47-field-reset-20260728-interaction-fix: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 ( ) < deadline ) {
try { const value = await check ( ) ; if ( value ) return value } catch ( error ) { lastError = error }
await sleep ( interval ) ;
}
throw lastError || new Error ( ` Timed out waiting for ${ label } ` ) ;
}
async function endpoint ( ) {
return waitFor ( async ( ) => {
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 < chunkCount ; chunk ++ ) {
const rowOffset = Math . floor ( chunk / columns ) * 5 , columnOffset = ( chunk % columns ) * 5 , gateOffset = g . length ;
for ( const gate of source . g ) g . push ( [ gate [ 0 ] + rowOffset , gate [ 1 ] + columnOffset , gate [ 2 ] ] ) ;
for ( const number of source . n || [ ] ) n . push ( [ number [ 0 ] + rowOffset , number [ 1 ] + columnOffset , number [ 2 ] ] ) ;
for ( const cell of source . valid || [ ] ) valid . push ( [ cell [ 0 ] + rowOffset , cell [ 1 ] + columnOffset ] ) ;
for ( const path of source . solution || [ ] ) solution . push ( { ... structuredClone ( path ) , startGate : path . startGate + gateOffset , endGate : path . endGate + gateOffset , cells : path . cells . map ( cell => [ 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 ; index < count ; index ++ ) {
const id = 'B' + index , x = profile . mode === 'line' ? index : ( index % columns ) * spacingX , y = profile . mode === 'line' ? 0 : Math . floor ( index / columns ) * spacingY ,
metaRev = 100000 + index , stateRev = 200000 + index , author = 'benchmark' , puzzle = profile . mode === 'complex' ? structuredClone ( complexPuzzle ) : structuredClone ( starterPuzzle ) ,
chunks = profile . mode === 'complex' ? structuredClone ( complexChunks ) : structuredClone ( starter . index . chunks ) , state = structuredClone ( starterState ) ;
state . paths = profile . mode === 'line' && index > 0 ? [ { 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(x<view.left||x>view.right||y<view.top||y>view.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 ] , x : worldX , y : worldY , createdAt : reactionNow , expiresAt : reactionNow + Math . max ( 3000 , $ { 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 === 10 ) queueWorldSignal ( { sessionId : 'benchmark-cross-tab' , commitId : 'benchmark:' + Date . now ( ) , worldEpoch : data . worldEpoch , stateIds : [ 'B1' ] } ) ; if ( index < $ { steps } ) requestAnimationFrame ( tick ) ; else { dispatch ( 'pointerup' , index , 0 ) ; setTimeout ( ( ) => Promise . resolve ( syncQueue ) . finally ( ( ) => { 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'))" ) , 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 ( ` (()=>{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 . 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 < steps ; index ++ ) {
pendingMoves . push ( client . send ( 'Input.dispatchMouseEvent' , { type : 'mouseMoved' , x : center . x + ( index % 2 ? 3 : - 3 ) , y : center . y + ( ( index % 3 ) - 1 ) * 2 , button : 'left' , buttons : 1 } ) ) ;
await sleep ( sampleDelay ) ;
}
await Promise . all ( pendingMoves ) ;
return { moves : steps , reason : 'complete' , snapshot : await client . evaluate ( 'BEND_PERF.snapshot()' ) } ;
} finally {
await client . send ( 'Input.dispatchMouseEvent' , { type : 'mouseReleased' , x : center . x , y : center . y , button : 'left' , buttons : 0 , clickCount : 1 } ) . catch ( ( ) => { } ) ;
await sleep ( 120 ) ;
await client . evaluate ( ` (()=>{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 ( ) ;
2026-07-31 12:54:46 +09:00
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 ) } ;
2026-07-30 15:13:53 +09:00
return { left : rect . left + 40 , top : rect . top + 40 , width : Math . max ( 120 , rect . width - 80 ) , height : Math . max ( 120 , rect . height - 80 ) } ;
} ) ( ) ` );
try {
2026-07-31 12:54:46 +09:00
const dispatches = [ ] ;
2026-07-30 15:13:53 +09:00
for ( let index = 0 ; index < steps ; index ++ ) {
2026-07-31 12:54:46 +09:00
dispatches . push ( client . send ( 'Input.dispatchMouseEvent' , { type : 'mouseMoved' , x : setup . left + ( index * 7 ) % setup . width , y : setup . top + ( index * 3 ) % setup . height , button : 'none' , buttons : 0 } ) ) ;
2026-07-30 15:13:53 +09:00
await sleep ( 8 ) ;
}
2026-07-31 12:54:46 +09:00
await Promise . all ( dispatches ) ;
await sleep ( 120 ) ; const measured = await client . evaluate ( ` (()=>{
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 >= 30 && gap . p50 <= 20 , ` DOM cursor cadence missed acceptance: ${ JSON . stringify ( { gap , diagnostic : measured . diagnostic } )} ` ) ;
assert ( age . count >= 25 && age . p95 < 30 , ` DOM cursor input age missed acceptance: ${ JSON . stringify ( age ) } ` ) ;
2026-07-30 15:13:53 +09:00
return snapshot ;
2026-07-31 12:54:46 +09:00
} 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" ) }
2026-07-30 15:13:53 +09:00
}
async function zoom ( client , deltaY , repetitions ) {
const center = await viewportCenter ( client ) ;
for ( let index = 0 ; index < repetitions ; index ++ ) {
await client . send ( 'Input.dispatchMouseEvent' , { type : 'mouseWheel' , x : center . x , y : center . y , deltaX : 0 , deltaY } ) ;
await sleep ( 22 ) ;
}
await sleep ( 300 ) ;
}
function timing ( snapshot , name ) { return snapshot . timings ? . [ name ] || { count : 0 , p50 : 0 , p95 : 0 , max : 0 } }
async function measureGameplaySimplificationBudgets ( client ) {
const result = await client . evaluate ( ` (async()=>{
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 ; index < count ; index ++ ) { const started = performance . now ( ) ; operation ( index ) ; rows . push ( performance . now ( ) - started ) } return { p95 : percentile ( rows , . 95 ) , max : Math . max ( ... rows ) } } ;
const snap = sample ( index => directionFromDelta ( 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 ,
2026-07-31 12:54:46 +09:00
controls , overviewBelow , overviewAbove , northHudGap ,
2026-07-30 15:13:53 +09:00
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' ) ,
2026-07-31 12:54:46 +09:00
dragLimit = cpuRate === 1 ? 8 : 16 , functionalDragLimit = cpuRate === 1 ? 16 : 24 , minimapLimit = cpuRate === 1 ? 20 : 33 , lodLimit = cpuRate === 1 ? 40 : 100 ;
2026-07-30 15:13:53 +09:00
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 >= 5 && ( 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 ` ) ;
2026-07-31 12:54:46 +09:00
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 ` ) ;
2026-07-30 15:13:53 +09:00
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 ) ;
2026-07-31 12:54:46 +09:00
assert ( dragGap . p50 <= 18 && dragGap . p95 <= 28 , ` ${ profile } pickup visual median/p95 gap ${ dragGap . p50 . toFixed ( 2 ) } / ${ dragGap . p95 . toFixed ( 2 ) } ms exceeded the capped 60 Hz cadence budget; active timings ${ JSON . stringify ( cadenceHot ) } ` ) ;
2026-07-30 15:13:53 +09:00
assert ( cameraGap . p50 <= 20 , ` ${ profile } camera median gap ${ cameraGap . p50 . toFixed ( 2 ) } ms exceeded 20 ms ` ) ;
assert ( dragAge . p95 < 30 , ` ${ profile } pickup input age ${ dragAge . p95 . toFixed ( 2 ) } ms exceeded 30 ms ` ) ;
assert ( cameraAge . p95 < 25 , ` ${ profile } camera input age ${ cameraAge . p95 . toFixed ( 2 ) } ms exceeded 25 ms ` ) ;
}
2026-07-31 12:54:46 +09:00
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 ) } ) ` ) ;
2026-07-30 15:13:53 +09:00
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' ) ;
2026-07-31 12:54:46 +09:00
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' } ` ) ;
2026-07-30 15:13:53 +09:00
}
2026-07-31 12:54:46 +09:00
for ( const name of [ 'minimapDrawsDuringInteraction' , 'lodPassesDuringInteraction' , 'persistenceDuringInteraction' , 'worldRefreshesDuringInteraction' ] )
2026-07-30 15:13:53 +09:00
assert ( ( snapshot . counters [ name ] || 0 ) === 0 && ( cadenceSnapshot . counters [ name ] || 0 ) === 0 , ` ${ profile } / ${ cpuRate } x ran ${ name } during an active gesture ` ) ;
assert ( ( snapshot . counters . worldRefreshesDeferredDuringInteraction || 0 ) >= 1 , ` ${ profile } / ${ cpuRate } x did not defer the injected cross-tab refresh until gesture settlement ` ) ;
assert ( snapshot . gauges . renderedBoards >= snapshot . gauges . visibleUnsolvedBoards , ` ${ profile } / ${ cpuRate } x omitted a visible unsolved board from detailed rendering ` ) ;
assert ( ( cadenceSnapshot . rates . dragFramesPerSecond || 0 ) <= 65 , ` ${ profile } / ${ cpuRate } x pickup presentation exceeded the 60 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 ` } ) }
2026-07-31 12:54:46 +09:00
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 ) } ` ) }
2026-07-30 15:13:53 +09:00
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 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 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()' ) ;
const result = { profile : profile . name , boards : profile . boards , cpuRate , snapshot , pickupCadence , pickupProbe , overviewPathsObserved , overviewBuildBaseline , pickupProbeLongTasks , pickupLongTasks , panLongTasks , claimApproved , claimDenied , edgePan , pinch } ;
validateMeasurement ( result ) ;
return result ;
}
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 } ) ;
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)||''})" ) ;
2026-07-31 12:54:46 +09:00
assert ( state . ready === 'true' && state . version === 'v47.83' && state . boards === 1 && state . origin && state . worldGeneration === 'v47-field-reset-20260728-interaction-fix' , ` Real-browser startup state is incomplete: ${ JSON . stringify ( state ) } ` ) ;
2026-07-30 15:13:53 +09:00
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 ) ;
assert ( displayCadence . count >= 60 && displayCadence . p50 <= 20 , ` Headless display baseline is not 60 Hz: ${ JSON . stringify ( displayCadence ) } ` ) ;
const gameplayBudgets = await measureGameplaySimplificationBudgets ( client ) ;
const cursorModes = await measureCursorModes ( client ) ;
2026-07-31 12:54:46 +09:00
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 ) } ` ) ;
2026-07-30 15:13:53 +09:00
const cursorCadence = await measureCursorCadence ( client ) ;
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 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 ) ;
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 } ` ) ;
}
console . log ( ` BROWSER_BENCHMARK_JSON= ${ JSON . stringify ( results . map ( result => ( {
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
} ) ) ) } ` );
console . log ( 'Real-browser performance benchmark passed' ) ;
} 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 ) } ) ;