SOURCE / PINNED RELEASE
Made of little things.
Napcraft
- Release
- ee47ac6a25df…
- Author-recorded commit
- 410dea87e109…
- License
- LICENSE
- Author’s source reference
- nostr://npub182jczunncwe0jn6frpqwq3e0qjws7yqqnc3auccqv9nte2dnd63scjm4rf/wss%3A%2F%2Fgit.napplet.soy%2F/n-52f9e22f5ce
Archive hash verified: 9e6e0cabb18d0d46…. The source-to-build association is the author’s claim; it has not been independently rebuilt.
import { instrument } from './edit-latency.mjs';
import {readFile,writeFile} from 'node:fs/promises';
const context = JSON.parse(await readFile('.napplet-space/soy-backend.json','utf8'));
const rendered = (p,n) => p.frame.waitForFunction(n => Number(document.querySelector('canvas').dataset.confirmedEdits) === n && document.querySelector('#app').dataset.pending === '0',n,{timeout:20000});
const key = async (p,code) => {
await p.frame.locator('canvas').focus(); await p.page.keyboard.press(code);
};
export default async ({players,connectIdentity,approveBackendAccount,check,diagnostics}) => {
const [a,b]=players;
await connectIdentity(a);
await a.frame.locator('#create').click();await a.frame.locator('#confirmCreate').click();
await approveBackendAccount(a,'worlds'); await a.frame.locator('#share').waitFor();
await a.frame.locator('#share').click();const code=await a.frame.locator('#shareCode').inputValue();await a.frame.locator('#close').click();
const [release,instance]=JSON.parse(Buffer.from(code.slice(4),'base64url'));
const target={module:{napplet:context.napplet,name:'worlds'},release,instance};
await b.frame.locator('#join').click();await b.frame.locator('#code').fill(code);await b.frame.locator('#confirmJoin').click();await b.frame.locator('#share').waitFor();
await Promise.all(players.map(p=>p.frame.waitForFunction(()=>document.querySelector('canvas').dataset.peerCount==='1')));
await Promise.all(players.map(p=>instrument(p.frame,150)));
const invoke=(p,operation,input)=>p.frame.evaluate(async ({target,operation,input})=>window.napplet.cvm.registry.call('soy.backends.v1','soy_backend_invoke',{target,operation,input,requestId:crypto.randomUUID(),expiresAt:Math.floor(Date.now()/1000)+240}),{target,operation,input});
for(let i=0;i<12;i++)await key(b,i%2?'x':'Space');
await b.frame.waitForFunction(()=>document.querySelector('canvas').dataset.edits==='12',null,{timeout:1000});
check('rapid inputs render while saves are in flight',Number(await b.frame.locator('#app').getAttribute('data-pending'))>0);
await Promise.all(players.map(p=>rendered(p,12)));
const batches=await b.frame.evaluate(()=>window.editProbe.calls.filter(c=>c.args.operation==='editBlocks'));
check('rapid edits are compact atomic batches',batches.length<12&&batches.some(c=>c.args.input.edits.length>1));
check('provider confirms every accepted burst edit',(await invoke(a,'readWorld',{})).structuredContent.result.edits===12);
// Hold a real background read at the SDK boundary. Input must still reach the canvas.
await a.frame.evaluate(()=>{window.editProbe.holdReads=true;document.dispatchEvent(new Event('visibilitychange'));});
await a.frame.waitForFunction(()=>typeof window.editProbe.releaseRead==='function');
await key(a,'Space');
await a.frame.waitForFunction(()=>document.querySelector('canvas').dataset.edits==='13');
check('background read never disables building',await a.frame.locator('#place').isEnabled());
await a.frame.evaluate(()=>{window.editProbe.holdReads=false;window.editProbe.releaseRead();});
await Promise.all(players.map(p=>rendered(p,13)));
await Promise.all(players.map(p=>key(p,'Space')));
await Promise.all(players.map(p=>rendered(p,14)));
const conflicts=await Promise.all(players.map(p=>p.frame.evaluate(()=>window.editProbe.calls.filter(c=>c.reply?.isError&&c.reply?.structuredContent?.error?.code==='CONFLICT').length)));
check('simultaneous same-cell edits converge with one winner',conflicts.reduce((x,y)=>x+y,0)>=1&&(await invoke(a,'readWorld',{})).structuredContent.result.edits===14);
// Failure before sending: queued predictions are visible, durable state stays unchanged.
await b.frame.evaluate(()=>{window.editProbe.offline=true;});
await key(b,'x');await key(b,'Space');await key(b,'x');
await b.frame.waitForFunction(()=>document.querySelector('#app').dataset.sync==='retry');
await b.page.locator('#stage iframe').screenshot({path:'.napplet-space/promotion/shared-edit-pending.png'});
check('offline queue stays explicitly unconfirmed',await b.frame.locator('#app').getAttribute('data-pending')==='3');
check('offline inputs do not masquerade as durable commits',(await invoke(a,'readWorld',{})).structuredContent.result.edits===14);
await b.frame.evaluate(()=>{window.editProbe.offline=false;window.dispatchEvent(new Event('online'));});
await Promise.all(players.map(p=>rendered(p,17)));
const retryCalls=await b.frame.evaluate(()=>window.editProbe.calls.filter(c=>c.args.operation==='editBlocks'&&c.error==='offline').map(c=>c.args));
const all=await b.frame.evaluate(()=>window.editProbe.calls.filter(c=>c.args.operation==='editBlocks'&&c.end).map(c=>c.args));
check('reconnect retries the original request unchanged',retryCalls.some(c=>all.some(a=>JSON.stringify(a)===JSON.stringify(c))));
// Commit succeeded, response lost. Reload must recover its original receipt, not replay it.
await b.frame.evaluate(()=>{window.editProbe.loseNext=true;window.editProbe.offlineAfterLoss=true;});
await key(b,'Space');
await b.frame.waitForFunction(()=>document.querySelector('#app').dataset.sync==='retry');
const lost=await b.frame.evaluate(()=>window.editProbe.calls.findLast(c=>c.error==='lost response').args);
check('lost acknowledgement leaves a recoverable pending intent',Boolean(await b.frame.evaluate(async()=>JSON.parse(await window.napplet.storage.getItem('napcraft-edit-journal-v1'))?.active?.intent?.requestId)));
await b.page.reload();
b.frame=await(await b.page.locator('#stage iframe').elementHandle()).contentFrame();
await b.frame.locator('#app[data-ready=true]').waitFor();
await instrument(b.frame,150);
await b.frame.locator('#recover').click();await b.frame.locator('#retrySave').click();
await rendered(b,18);
const recovered=await b.frame.evaluate(()=>window.editProbe.calls.find(c=>['editBlock','editBlocks'].includes(c.args.operation)).args);
check('reload retries identical ID, expiry and payload',JSON.stringify(recovered)===JSON.stringify(lost));
check('lost acknowledgement is committed exactly once',(await invoke(a,'readWorld',{})).structuredContent.result.edits===18);
// Close peer transport: durable polling must still deliver changes.
await b.frame.waitForFunction(()=>document.querySelector('canvas').dataset.peerCount==='1');
const diag=await diagnostics();
await b.frame.evaluate(id=>window.napplet.webrtc.close(id,'Test peer outage'),diag[1][0].session);
await key(b,'x');
await Promise.all(players.map(p=>rendered(p,19)));
check('peer outage retains persistence and polling fallback',true);
const unauthorized=await invoke(b,'setLocked',{locked:true});
check('guest still cannot change owner permissions',unauthorized.isError===true);
await a.frame.locator('#share').click();await a.frame.locator('#lock').click();
await b.frame.waitForFunction(()=>document.querySelector('#saveStatus').textContent.includes('paused'));
const denied=await invoke(b,'editBlocks',{after:19,edits:[{x:9,y:4,z:10,expected:0,block:5}]});
check('paused world rejects shared edit batches',denied.isError===true);
await a.frame.locator('#share').click();await a.frame.locator('#lock').click();
await b.frame.waitForFunction(()=>!document.querySelector('#saveStatus').textContent.includes('paused'));
await Promise.all(players.map(p=>p.page.reload()));
b.frame=await(await b.page.locator('#stage iframe').elementHandle()).contentFrame();
await b.frame.locator('#app[data-ready=true]').waitFor();
await b.frame.locator('#join').click();await b.frame.locator('#code').fill(code);await b.frame.locator('#confirmJoin').click();
await rendered(b,19);
check('world survives all players leaving after recovery and conflicts',true);
// Independent columns: instance-wide contention must not discard a still-valid edit.
a.frame=await(await a.page.locator('#stage iframe').elementHandle()).contentFrame();
await a.frame.locator('#app[data-ready=true]').waitFor();
await a.frame.locator('#join').click();await a.frame.locator('#code').fill(code);await a.frame.locator('#confirmJoin').click();
await rendered(a,19);
const aim=async(p,x,z)=>{
const point=await p.frame.evaluate(({x,z})=>{
const canvas=document.querySelector('canvas'),w=canvas.clientWidth,h=canvas.clientHeight;
const unit=Math.max(10,Math.min(w<600?w/26:w/43,h/29,33));
return {x:w/2+(x-z)*unit,y:h*.48+(x+z+1-17)*unit*.51-3*unit*.94};
},{x,z});
await p.frame.locator('canvas').click({position:point,modifiers:['Shift']});
await p.page.waitForTimeout(30);
const selected=await p.frame.locator('canvas').getAttribute('data-selected');
check('aimed independent column '+x+','+z,selected===`${x},${z}`);
};
await aim(a,2,8);await aim(b,3,8);
await Promise.all(players.map(p=>key(p,'Space')));
await Promise.all(players.map(p=>rendered(p,21)));
check('simultaneous independent columns both persist after revalidation',true);
// Simulate a fresh transport restoring host storage from a previous guest tab.
// The provider's receipt key includes the principal: never blindly replay after ABA.
await instrument(b.frame,150);
await aim(b,4,8);
await b.frame.evaluate(()=>{window.editProbe.loseNext=true;window.editProbe.offlineAfterLoss=true;});
await key(b,'Space');
await b.frame.waitForFunction(()=>document.querySelector('#app').dataset.sync==='retry');
const oldJournal=await b.frame.evaluate(()=>window.napplet.storage.getItem('napcraft-edit-journal-v1'));
let undone;
for(let i=0;i<3;i++) {
undone=await invoke(a,'editBlock',{x:4,y:3,z:8,expected:4,block:0});
if(!undone.isError)break;
await a.page.waitForTimeout(300);
}
check('another player can undo a committed edit whose acknowledgement was lost',!undone.isError);
await a.frame.evaluate(value=>window.napplet.storage.setItem('napcraft-edit-journal-v1',value),oldJournal);
await a.page.reload();
a.frame=await(await a.page.locator('#stage iframe').elementHandle()).contentFrame();
await a.frame.locator('#app[data-ready=true]').waitFor();await instrument(a.frame,150);
await a.frame.locator('#recover').click();await a.frame.locator('#retrySave').click();await a.frame.locator('#share').waitFor();
await rendered(a,23);
check('changed transport refreshes without replaying an already-undone edit',
await a.frame.evaluate(()=>!window.editProbe.calls.some(c=>['editBlock','editBlocks'].includes(c.args.operation))));
const legacy=JSON.parse(oldJournal).active.intent;
legacy.operation='editBlock';legacy.input={x:4,y:3,z:8,expected:0,block:4};
await a.frame.evaluate(value=>window.napplet.storage.setItem('napcraft-pending-v1',value),JSON.stringify(legacy));
await a.page.reload();a.frame=await(await a.page.locator('#stage iframe').elementHandle()).contentFrame();
await a.frame.locator('#app[data-ready=true]').waitFor();await instrument(a.frame,150);
await a.frame.locator('#recover').click();await a.frame.locator('#retrySave').click();await a.frame.locator('#share').waitFor();await rendered(a,23);
check('legacy pending save without a transport identity is refreshed, never replayed',
await a.frame.evaluate(()=>!window.editProbe.calls.some(c=>['editBlock','editBlocks'].includes(c.args.operation))));
await writeFile('.napplet-space/edit-correctness.json',JSON.stringify({passed:true,edits:23,code},null,2));
};
