Back to Napcraft
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.

tests/public-consent.mjs
// Actual napplet.soy + public CVM/RTC. Two separate browser processes; disposable
// NIP-07 test signer, with real signatures and deliberately delayed approval.
import { chromium, executable, installTestIdentity } from './browser-support.mjs';
import { readFile, writeFile } from 'node:fs/promises';
import { execFileSync } from 'node:child_process';
import assert from 'node:assert/strict';
const readyFile='.napplet-space/publication-ready.json';
const old=JSON.parse(execFileSync('soyli',['status','--json'],{encoding:'utf8'}));
const context=JSON.parse(await readFile('.napplet-space/soy-backend.json','utf8'));
const backend=JSON.parse(await readFile('.napplet-space/active-backend-before.json','utf8'));
const browsers=[],players=[],checks=[],limitations=[],dialogs=[],approvals=[];
const check=(name,value)=>{assert.ok(value,name);checks.push(name);console.log('PASS',name);};
const startApp=async(p,url)=>{
  await p.page.goto(url,{waitUntil:'domcontentloaded'});
  await p.page.getByRole('button',{name:'Start Napcraft',exact:true}).click({timeout:45000});
  const iframe=p.page.locator('iframe[title="Napcraft"]');await iframe.waitFor();
  p.frame=await(await iframe.elementHandle()).contentFrame();
  await p.frame.locator('#app[data-ready=true]').waitFor({timeout:45000});
};
const signIn=async p=>{
  await p.page.getByRole('button',{name:'Connect',exact:true}).click();
  await p.page.getByRole('button',{name:'Connect browser extension',exact:true}).click();
  await p.page.getByRole('button',{name:'Connect',exact:true}).waitFor({state:'hidden'});
};
const panelText=async p=>(await p.page.locator('body').innerText()).slice(-7000);
// Public host labels differ from soyLI preview. Only approve these two observed
// capability dialogs; never social publishing, uploads or arbitrary connections.
const approvePlayer=(p,index)=>{
  let stop=false;
  const done=(async()=>{while(!stop&&!p.page.isClosed()){
    try {
      const network=p.page.getByRole('dialog',{name:'Allow network connection',exact:true});
      if(await network.isVisible()) {
        const message=await network.innerText();
        assert.match(message,/saved worlds and actions in worlds on backend 60517f645482/);
        const entry={player:index,type:'backend consent',message,opened:Date.now()};
        const first=!dialogs.some(d=>d.player===0&&d.type==='backend consent');dialogs.push(entry);
        if(index===0&&first)await new Promise(r=>setTimeout(r,20000));
        entry.stillOpen=await network.isVisible();entry.accepted=Date.now();
        await network.getByRole('button',{name:'Connect',exact:true}).click();
        console.log('PUBLIC APPROVAL',index,entry.accepted-entry.opened);
      }
      const peer=p.page.getByRole('dialog',{name:'Allow multiplayer connections',exact:true});
      if(await peer.isVisible())await peer.getByRole('button',{name:'Allow',exact:true}).click();
    }catch(error){if(!stop){console.log('Approval driver error',String(error));throw error;}}
    await new Promise(r=>setTimeout(r,100));
  }})();
  done.catch(()=>{});
  return {stop:()=>{stop=true},done};
};
const saved=(p,n)=>p.frame.waitForFunction(n=>Number(document.querySelector('canvas').dataset.confirmedEdits)===n&&document.querySelector('#app').dataset.pending==='0',n,{timeout:45000});
let legacy, publication;
try {
  for(let i=0;i<2;i++) {
    const browser=await chromium.launch({executablePath:executable,headless:true,args:['--disable-background-timer-throttling','--disable-renderer-backgrounding']});browsers.push(browser);
    const page=await browser.newPage({viewport:{width:1200,height:900}});
    const options={signDelayMs:0};const signer=i===0?await installTestIdentity(page,options):undefined;
    const player={page,options,signer};players.push(player);approvals.push(approvePlayer(player,i));
  }
  const [a,b]=players;
  const baselineUrl=process.env.NAPCRAFT_BASELINE_URL || old.snapshotUrl;
  await startApp(b,baselineUrl);
  legacy={target:{module:{napplet:context.napplet,name:'worlds'},release:backend.active},operation:'createWorld',input:{name:'Earlier interrupted test request'},requestId:crypto.randomUUID(),expiresAt:Math.floor(Date.now()/1000)-86400};
  await b.frame.evaluate(value=>window.napplet.storage.setItem('napcraft-pending-v1',value),JSON.stringify(legacy));
  console.log('READY: representative unfinished request saved in the old public build. Waiting for publication-ready.json.');
  const deadline=Date.now()+20*60*1000;
  while(Date.now()<deadline) {
    try{publication=JSON.parse(await readFile(readyFile,'utf8'));break;}catch{}
    await new Promise(r=>setTimeout(r,1000));
  }
  assert.ok(publication,'Publication signal arrived');
  await startApp(b,publication.snapshotUrl);
  const recovered=await b.frame.evaluate(()=>window.napplet.storage.getItem('napcraft-pending-v1'));
  if(recovered!==JSON.stringify(legacy)) {
    assert.equal(recovered,null,'New build has an empty storage scope');
    await startApp(b,baselineUrl);
    const original=await b.frame.evaluate(()=>window.napplet.storage.getItem('napcraft-pending-v1'));
    check('previous public release retains its exact pending request',original===JSON.stringify(legacy));
    limitations.push('Host storage is build-scoped: the updated app cannot read the previous build recovery request. Returning to the pinned previous release still finds it.');
    console.log('PLATFORM LIMIT',limitations.at(-1));
    await startApp(b,publication.snapshotUrl);
    // Separate, explicit same-build legacy-state test. This does NOT migrate
    // real player data or assert that cross-build migration works.
    await b.frame.evaluate(value=>window.napplet.storage.setItem('napcraft-pending-v1',value),JSON.stringify(legacy));
    await startApp(b,publication.snapshotUrl);
  } else check('old public-build recovery request survives the new release',true);
  await b.frame.locator('#recover').click();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('legacy-shaped request in current public build is explicitly preserved',JSON.stringify(kept[0].intent)===JSON.stringify(legacy));

  await a.page.goto(publication.snapshotUrl);await signIn(a);
  await a.page.getByRole('button',{name:'Start Napcraft',exact:true}).click();
  a.frame=await(await a.page.locator('iframe[title="Napcraft"]').elementHandle()).contentFrame();await a.frame.locator('#app[data-ready=true]').waitFor();
  a.options.signDelayMs=25000;
  await a.frame.locator('#create').click();await a.frame.locator('#name').fill('Napcraft public consent check');await a.frame.locator('#confirmCreate').click();
  // Account binding can be followed by a separate peer-connection grant.
  await a.frame.locator('#share').waitFor({timeout:120000});
  check('public host approval accepted after a 20-second wait',dialogs.some(d=>d.player===0&&d.stillOpen&&d.accepted-d.opened>=20000));
  const signatures=a.signer.signing;
  check('public backend accepts delayed extension signing',signatures.some(s=>s.finished-s.start>=25000));
  console.log('Public world created. Host text:',await panelText(a));
  a.options.signDelayMs=0;
  await a.frame.locator('#share').click();const code=await a.frame.locator('#shareCode').inputValue();await a.frame.locator('#close').click();
  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({timeout:60000});
  await Promise.all(players.map(p=>p.frame.waitForFunction(()=>document.querySelector('canvas').dataset.peerCount==='1',null,{timeout:60000})));
  check('two independent public browsers see each other',true);
  await a.frame.locator('#place').click();await Promise.all(players.map(p=>saved(p,1)));
  await b.frame.locator('#remove').click();await Promise.all(players.map(p=>saved(p,2)));
  check('both public players edits are confirmed and shared',true);
  for(const p of players) {
    await startApp(p,publication.snapshotUrl);
    await p.frame.locator('#join').click();await p.frame.locator('#code').fill(code);await p.frame.locator('#confirmJoin').click();await saved(p,2);
  }
  check('public world survives both browser reloads',true);
  await a.page.locator('iframe[title="Napcraft"]').screenshot({path:'.napplet-space/promotion/public-final.png'});
  await writeFile('.napplet-space/public-consent-results.json',JSON.stringify({passed:true,checks,limitations,dialogs,baselineUrl,code,publication,signing:signatures,browserProcesses:2,transport:'website-selected public ICE; no local TURN substitution'},null,2));
} catch(error) {
  for(const [i,p] of players.entries()) {
    console.log('PUBLIC FAILURE',i,await panelText(p).catch(()=>''));
    if(p.frame)console.log('APP FAILURE',i,await p.frame.locator('body').innerText().catch(()=>''));
    await p.page.screenshot({path:`.napplet-space/promotion/public-failure-${i}.png`}).catch(()=>{});
  }
  await writeFile('.napplet-space/public-consent-results.json',JSON.stringify({passed:false,checks,limitations,dialogs,error:String(error),publication},null,2));
  throw error;
} finally {approvals.forEach(a=>a.stop());await Promise.all(browsers.map(b=>b.close()));await Promise.allSettled(approvals.map(a=>a.done));}