import assert from 'node:assert/strict'; import { writeFile } from 'node:fs/promises'; import { performance } from 'node:perf_hooks'; // Isolated policy comparison; these illustrative metrics are not Studio rules. const separation = 10; function absolute(rows) { let end = -separation; return rows.map(r => { const y = Math.max(r.y, end + separation); end=y+r.height; return {...r,y}; }); } function gaps(rows) { let end=0; return rows.map((r,i) => { assert(r.gap >= (i?separation:0)); const y=end+r.gap; end=y+r.height; return {...r,y}; }); } function encodeGaps(rows) { return rows.map((r,i)=>({...r,gap:r.y-(i?rows[i-1].y+rows[i-1].height:0)})); } const positions = rows=>rows.map(r=>r.y); function moveAbsolute(rows,index,requested) { const current=absolute(rows); const floor=index?current[index-1].y+current[index-1].height+separation:0; const candidate=rows.map((r,i)=>({...r,y:i===index?Math.max(requested,floor):r.y})); return {candidate,preview:absolute(candidate)}; } function changeCount(before,after,key) {return before.reduce((n,r,i)=>n+(r[key]!==after[i][key]),0);} const initial=[{id:'A',y:100,height:20},{id:'B',y:200,height:20}]; const initialG=encodeGaps(initial); const cases=[]; function named(name,a,g,expectedA,expectedG) { assert.deepEqual(positions(a),expectedA,name+' absolute'); assert.deepEqual(positions(g),expectedG,name+' gaps'); cases.push({name,absolute:positions(a),gapsWithoutInverse:positions(g)}); } named('move A to50',absolute([{...initial[0],y:50},initial[1]]),gaps([{...initialG[0],gap:50},initialG[1]]),[50,200],[50,150]); named('move A to250',absolute([{...initial[0],y:250},initial[1]]),gaps([{...initialG[0],gap:250},initialG[1]]),[250,280],[250,350]); named('resize A to80',absolute([{...initial[0],height:80},initial[1]]),gaps([{...initialG[0],height:80},initialG[1]]),[100,200],[100,260]); named('resize A to120',absolute([{...initial[0],height:120},initial[1]]),gaps([{...initialG[0],height:120},initialG[1]]),[100,230],[100,300]); named('source reorder B A',absolute([initial[1],initial[0]]),gaps([initialG[1],initialG[0]]),[200,230],[80,200]); named('delete A',absolute([initial[1]]),gaps([initialG[1]]),[200],[80]); assert.deepEqual(positions(moveAbsolute(initial,1,110).preview),[100,130]); let feasible=0,moves=0,absoluteChanges=0,gapChanges=0; const started=performance.now(); for(const ha of [10,20,30])for(const hb of [10,20,30])for(const hc of [10,20,30]) for(let a=0;a<=40;a+=10)for(let b=a+ha+10;b<=90;b+=10)for(let c=b+hb+10;c<=140;c+=10) { const rows=[{id:'A',height:ha,y:a},{id:'B',height:hb,y:b},{id:'C',height:hc,y:c}]; assert.deepEqual(positions(absolute(rows)),[a,b,c]); assert.deepEqual(positions(gaps(encodeGaps(rows))),[a,b,c]); feasible++; for(let i=0;i<3;i++)for(const delta of [-50,-20,0,10,40]) { const requested=rows[i].y+delta, {candidate,preview}=moveAbsolute(rows,i,requested); const independent=[]; for(let j=0;j<3;j++) { const floor=j?independent[j-1]+rows[j-1].height+10:0; independent.push(Math.max(j===i?requested:rows[j].y,floor)); } assert.deepEqual(positions(preview),independent); assert.deepEqual(positions(absolute(preview)),independent); assert.deepEqual(positions(absolute(JSON.parse(JSON.stringify(candidate)))),independent); const encoded=encodeGaps(preview); assert.deepEqual(positions(gaps(encoded)),independent); assert.deepEqual(candidate.map(r=>r.id),['A','B','C']); for(let j=0;jr.y+r.height))+20; assert.equal(requiredFrameBottom,300); assert.equal(Math.max(300,requiredFrameBottom),300); assert.equal(Math.max(220,requiredFrameBottom),300); assert.deepEqual(children,[{y:160,height:20},{y:260,height:20}]); assert.equal(220-90,130); // delayed header top from moved create receive and header height const result={ scope:'Illustrative linear policy and anchor experiment; no production parser, REST, Scene, GUI or full fragment/lifecycle acceptance.', separation, cases, feasibleLayouts:feasible, moveCases:moves, changedAuthoredCoordinateRecords:{absolute:absoluteChanges,gapsWithInverse:gapChanges}, elapsedMs:performance.now()-started, assertions:'passed', interpretation:'Both policies express all enumerated feasible layouts. Absolute coordinates preserve unaffected positions with fewer authored coordinate changes under these fixtures. Choose no production schema from this alone; full scope/anchor/inverse and source lifecycle validation remain required.' }; await writeFile(new URL('./results.json',import.meta.url),JSON.stringify(result,null,2)+'\n',{flag:'wx'}); export default result;