'use strict'; const assert=require('assert/strict'); const fs=require('fs'); const os=require('os'); const path=require('path'); const dataRoot=fs.mkdtempSync(path.join(os.tmpdir(),'link-field-recovery-')),dataDir=path.join(dataRoot,'world'); process.env.LINK_FIELD_TEST_DATA_ROOT=dataRoot; const BuildMeta=require('../build-meta'); const server=require('../server'); const worldFile=path.join(dataDir,'shared-world.json'),commitFile=path.join(dataDir,'shared-world.commit.json'),boardsDir=path.join(dataDir,'shared-world.boards'); const playerId='aaaaaaaaaaaaaaaaaaaaaaaa',playerFile=path.join(dataDir,`${playerId}.json`); const player=earnedScore=>({playerId,name:'Player',tokenHash:'0'.repeat(64),purchases:[],generationBonuses:[],earnedScore,economyRevision:earnedScore,createdAt:1,updatedAt:1}); const world=revision=>({revision,rowRevision:revision,boardVersions:{},changes:[],clearEvents:[],expansionGrants:{},global:{nextId:1,worldGeneration:BuildMeta.WORLD_GENERATION},createdAt:1,updatedAt:1}); (async()=>{ fs.mkdirSync(boardsDir,{recursive:true}); fs.writeFileSync(worldFile,JSON.stringify(world(0))); fs.writeFileSync(playerFile,JSON.stringify(player(0))); fs.writeFileSync(path.join(boardsDir,'B0.1.json'),JSON.stringify({meta:{id:'B0'},state:{solved:true}})); const nextWorld=world(1);nextWorld.boardVersions.B0=1; fs.writeFileSync(commitFile,JSON.stringify({revision:1,world:nextWorld,nextPlayer:player(100),previousPlayer:player(0),changedBoardIds:['B0']})); assert.equal(await server.recoverPendingWorldCommit(),true); assert.equal(JSON.parse(fs.readFileSync(worldFile,'utf8')).revision,1); assert.equal(JSON.parse(fs.readFileSync(playerFile,'utf8')).earnedScore,100); assert.equal(fs.existsSync(commitFile),false); fs.writeFileSync(path.join(boardsDir,'B0.0.json'),'{}'); assert.equal(await server.collectRetiredBoardVersions(),1); assert.equal(fs.existsSync(path.join(boardsDir,'B0.1.json')),true); assert.equal(fs.existsSync(path.join(boardsDir,'B0.0.json')),false); const failedWorld=world(2);failedWorld.boardVersions={B0:1,B1:2}; fs.writeFileSync(playerFile,JSON.stringify(player(200))); fs.writeFileSync(commitFile,JSON.stringify({revision:2,world:failedWorld,nextPlayer:player(200),previousPlayer:player(100),changedBoardIds:['B1']})); assert.equal(await server.recoverPendingWorldCommit(),false); assert.equal(JSON.parse(fs.readFileSync(worldFile,'utf8')).revision,1); assert.equal(JSON.parse(fs.readFileSync(playerFile,'utf8')).earnedScore,100); assert.equal(fs.existsSync(commitFile),false); const poisoned=JSON.parse(fs.readFileSync(worldFile,'utf8'));poisoned.global.worldGeneration='foreign-generation';fs.writeFileSync(worldFile,JSON.stringify(poisoned));fs.writeFileSync(path.join(boardsDir,'B9.99.json'),'{}');assert.equal(await server.collectRetiredBoardVersions(),0);assert.equal(fs.existsSync(path.join(boardsDir,'B9.99.json')),true,'Generation mismatch cleanup deleted an unreferenced shard'); console.log('Shared-world commit recovery and retired-version collection passed'); })().finally(()=>fs.rmSync(dataRoot,{recursive:true,force:true})).catch(error=>{console.error(error);process.exitCode=1});