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 { mkdir, writeFile } from 'node:fs/promises';
import { blockPoint } from './direct-building.mjs';
import { fitFrame } from './browser-support.mjs';
const reload = async p => {
await p.page.reload(); await p.page.locator('#stage iframe').waitFor();
p.frame = await (await p.page.locator('#stage iframe').elementHandle()).contentFrame();
await p.frame.locator('#app[data-ready=true]').waitFor();
};
const synced = (p,n) => p.frame.waitForFunction(n=>document.querySelector('canvas').dataset.confirmedEdits===String(n)&&document.querySelector('#app').dataset.pending==='0',n,{timeout:30000});
const join = async (p,code) => {
await p.frame.locator('#join').click(); await p.frame.locator('#code').fill(code);
await p.frame.locator('#confirmJoin').click();
try {await p.frame.locator('#share').waitFor({timeout:30000});}
catch(error){console.log('Join diagnostic',await p.frame.locator('body').innerText(),(await p.page.locator('body').innerText()).slice(-2000));throw error;}
};
export default async ({players,connectIdentity,approveBackendAccount,check}) => {
const [a,b]=players;
const approvalWait=Number(process.env.NAPCRAFT_APPROVAL_WAIT_MS ?? 20000);
const signingWait=Number(process.env.NAPCRAFT_SIGN_WAIT_MS ?? 25000);
await mkdir('.napplet-space/promotion',{recursive:true});
await connectIdentity(a);
await a.page.evaluate((signingWait)=>{
const original=window.nostr.signEvent.bind(window.nostr);
window.signingProbe={started:0,finished:0};
window.nostr.signEvent=async event=>{
window.signingProbe.started=Date.now();
await new Promise(r=>setTimeout(r,signingWait));
const result=await original(event);window.signingProbe.finished=Date.now();return result;
};
},signingWait);
await a.frame.evaluate(()=>{
const original=window.napplet.cvm.registry.call.bind(window.napplet.cvm.registry);
window.creationCalls=[];let lose=true;
window.napplet.cvm.registry.call=async(family,tool,args,options)=>{
const result=await original(family,tool,args,options);
if(args.operation==='createWorld') {
window.creationCalls.push({args:structuredClone(args),options,result});
if(lose&&!result.isError) {lose=false;throw Error('Test lost acknowledgement after world creation');}
}
return result;
};
});
await a.frame.locator('#create').click(); await a.frame.locator('#name').fill('Patient builders');
await a.frame.locator('#confirmCreate').click();
await a.page.locator('#prompt[open] #confirm').waitFor();
const start=Date.now();
// Real elapsed user think-time at the actual host consent prompt, followed by
// a slow NIP-07 approval. Both exceed the old 15-second UI deadline.
await a.page.waitForTimeout(approvalWait);
check(`host consent remains open for ${approvalWait} ms`,await a.frame.locator('#confirmCreate').isDisabled() && (await a.frame.locator('#error').innerText())==='');
await approveBackendAccount(a,'worlds');
await a.frame.locator('#retrySave').waitFor({timeout:90000});
const signing=await a.page.evaluate(()=>window.signingProbe);
check(`extension signing was allowed ${signingWait} ms`,signing.finished-signing.started>=signingWait);
const first=await a.frame.evaluate(()=>window.creationCalls[0]);
check('account-bound invocation uses two-minute UI timeout',first.options.timeoutMs===120000);
check('world committed despite delayed approval and signing',!first.result.isError&&Boolean(first.result.structuredContent.instance));
const pending=await a.frame.evaluate(async()=>JSON.parse(await window.napplet.storage.getItem('napcraft-pending-v1')));
check('lost creation reply retains original intent',JSON.stringify(pending)===JSON.stringify(first.args));
check('recovery explains safe retry instead of another create',(await a.frame.locator('#panel').innerText()).includes('exact same saved request'));
await a.frame.locator('#retrySave').click();await a.frame.locator('#share').waitFor({timeout:30000});
const calls=await a.frame.evaluate(()=>window.creationCalls);
check('retry returns the same world without a duplicate request',calls.length===2&&JSON.stringify(calls[0].args)===JSON.stringify(calls[1].args)&&calls[0].result.structuredContent.instance===calls[1].result.structuredContent.instance);
await a.frame.locator('#share').click();const code=await a.frame.locator('#shareCode').inputValue();await a.frame.locator('#close').click();
await join(b,code);
await Promise.all(players.map(p=>p.frame.waitForFunction(()=>document.querySelector('canvas').dataset.peerCount==='1')));
check('independent second browser joins and sees the first',true);
await a.frame.locator('canvas').click({position:await blockPoint(a.frame,2,8)});
await Promise.all(players.map(p=>synced(p,1)));
await b.frame.locator('canvas').click({position:await blockPoint(b.frame,4,8)});
await Promise.all(players.map(p=>synced(p,2)));
check('both players edits reach each other',true);
await Promise.all(players.map(reload));
for(const p of players) {await join(p,code);await synced(p,2);}
check('saved world and edits survive both browser reloads',true);
// An old failed creation from before the host fix. Never send the expired
// request, change its expiry, or quietly mint another world.
const legacy={...first.args,expiresAt:Math.floor(Date.now()/1000)-86400};
await b.frame.evaluate(value=>window.napplet.storage.setItem('napcraft-pending-v1',value),JSON.stringify(legacy));
await reload(b);
await b.frame.evaluate(()=>{
const original=window.napplet.cvm.registry.call.bind(window.napplet.cvm.registry);window.newCreations=0;
window.napplet.cvm.registry.call=(f,t,a,o)=>{if(a.operation==='createWorld')window.newCreations++;return original(f,t,a,o);};
});
await b.frame.locator('#recover').click();
check('expired creation has an explanation instead of a retry loop',await b.frame.locator('#retrySave').count()===0&&(await b.frame.locator('#panel').innerText()).includes('might already exist'));
await fitFrame(b.page,390,844);
await b.page.locator('#stage iframe').screenshot({path:'.napplet-space/promotion/recovery-phone.png'});
await b.frame.locator('#keepSave').click();
const kept=await b.frame.evaluate(async()=>JSON.parse(await window.napplet.storage.getItem('napcraft-kept-saves-v1')));
check('setting aside preserves the exact expired request durably',JSON.stringify(kept[0].intent)===JSON.stringify(legacy));
await b.frame.locator('#practice').click();await b.frame.locator('#worldMenu').click();
check('kept recovery record does not prevent practice',await b.frame.locator('#kept').isVisible());
await join(b,code);await synced(b,2);
check('can join saved worlds while retaining an unresolved creation',true);
await b.frame.locator('#worldMenu').click();await b.frame.locator('#create').click();
await b.frame.locator('#differentWorld').click();await b.frame.locator('#confirmCreate').click();
check('a separate world requires explicit acknowledgement',await b.frame.locator('#separateWorld').isVisible()&&(await b.frame.locator('#error').innerText()).includes('Confirm')&&await b.frame.evaluate(()=>window.newCreations)===0);
await reload(b);await b.frame.locator('#kept').click();await b.frame.locator('#records button').first().click();
const restored=JSON.parse(await b.frame.locator('#recoveryDetails').inputValue());
check('recovery record survives reload without altered payload',JSON.stringify(restored.intent)===JSON.stringify(legacy));
await b.frame.evaluate(()=>{
const original=window.napplet.cvm.registry.call.bind(window.napplet.cvm.registry);
window.recoveryReads=[];let conflicts=2;
window.napplet.cvm.registry.call=(f,t,a,o)=>{
if(a.operation==='readWorld'){
window.recoveryReads.push(a.requestId);
if(conflicts-->0)return Promise.resolve({isError:true,structuredContent:{ok:false,error:{code:'CONFLICT',message:'Test concurrent query'}}});
}
return original(f,t,a,o);
};
});
await b.frame.locator('#recoveredCode').fill(code);await b.frame.locator('#checkCode').click();
try { await b.frame.locator('#useRecovered').waitFor(); }
catch(error) {console.log('Recovery diagnostic',await b.frame.locator('#panel').innerText());throw error;}
check('recovered code asks the player to recognize the world', (await b.frame.locator('#panel').innerText()).includes('Patient builders'));
check('recovery retries query conflicts with fresh read IDs',await b.frame.evaluate(()=>window.recoveryReads.length===3&&new Set(window.recoveryReads).size===3));
await b.frame.locator('#useRecovered').click();await synced(b,2);
const linked=await b.frame.evaluate(async()=>JSON.parse(await window.napplet.storage.getItem('napcraft-kept-saves-v1')));
check('recognizing the saved world retains original request and links its code',linked[0].code===code&&JSON.stringify(linked[0].intent)===JSON.stringify(legacy));
await writeFile('.napplet-space/consent-recovery.json',JSON.stringify({passed:true,hostApprovalWaitMs:approvalWait,signingWaitMs:signing.finished-signing.started,elapsedCreationMs:Date.now()-start,instance:first.result.structuredContent.instance,edits:2},null,2));
};
