Back to Limb Race
SOURCE / PINNED RELEASE

Made of little things.

Limb Race

Release
6a8ae4fc626e…
Author-recorded commit
008c01ee6454…
License
LICENSE
Author’s source reference
nostr://npub182jczunncwe0jn6frpqwq3e0qjws7yqqnc3auccqv9nte2dnd63scjm4rf/wss%3A%2F%2Fgit.napplet.soy%2F/n-c2869f12ad8

Archive hash verified: 7be8492d1f89b891…. The source-to-build association is the author’s claim; it has not been independently rebuilt.

tests/sharing-smoke.mjs
// Real soyLI host + isolated relay and ephemeral test identities. No public writes.
// Bun supplies the local WebSocket server; browser dependencies come from conformance.
import {createRequire} from 'node:module';
import {realpathSync,readFileSync,writeFileSync,mkdirSync} from 'node:fs';
import assert from 'node:assert/strict';
const require=createRequire(realpathSync(new URL('../node_modules/@napplet/conformance-cli/package.json',import.meta.url)));
const {chromium}=require('playwright');
const {generateSecretKey,getPublicKey,finalizeEvent,verifyEvent,matchFilters}=require('nostr-tools');
const events=[],keys=[generateSecretKey(),generateSecretKey()];let refuse=false,preview,browser,url;
const relay=Bun.serve({hostname:'127.0.0.1',port:0,fetch:(r,s)=>s.upgrade(r)?undefined:new Response(),websocket:{message(ws,raw){
 const [type,value,...filters]=JSON.parse(String(raw));
 if(type==='REQ'){
  const matches=new Map();for(const filter of filters)for(const e of events.filter(e=>matchFilters([filter],e)).sort((a,b)=>b.created_at-a.created_at||a.id.localeCompare(b.id)).slice(0,filter.limit??100))matches.set(e.id,e);
  for(const e of matches.values())ws.send(JSON.stringify(['EVENT',value,e]));ws.send(JSON.stringify(['EOSE',value]));
 }else if(type==='EVENT'){
  const ok=verifyEvent(value)&&!refuse;if(ok&&!events.some(e=>e.id===value.id))events.push(value);
  ws.send(JSON.stringify(['OK',value.id,ok,ok?'':'restricted: fixture storage is full']));
 }
}}});
const relayUrl=`ws://127.0.0.1:${relay.port}`,configPath=new URL('../napplet.json',import.meta.url),original=readFileSync(configPath,'utf8');
const config=JSON.parse(original);config.relays=[relayUrl];
const errors=[];mkdirSync(new URL('../.napplet-space/promotion/',import.meta.url),{recursive:true});
const pause=ms=>new Promise(resolve=>setTimeout(resolve,ms));
async function until(fn,expected=true,timeout=15000){const start=Date.now();let last;while(Date.now()-start<timeout){last=await fn();if(typeof expected==='function'?expected(last):last===expected)return last;await pause(100);}throw new Error(`Timed out, last value: ${JSON.stringify(last)}`);}
try{
 // A transient loopback hint is required by the real host's network policy. Restore byte-for-byte below.
 writeFileSync(configPath,JSON.stringify(config));
 assert.deepEqual(JSON.parse(readFileSync(configPath,'utf8')).relays,[relayUrl]);
 assert.equal(new URL(relayUrl).hostname,'127.0.0.1');
 preview=Bun.spawn(['soyli','dev','--port','0','--no-open'],{cwd:new URL('..',import.meta.url).pathname,stdout:'pipe',stderr:'inherit'});
 const reader=preview.stdout.getReader();let log='';
 while(!url){const p=await reader.read();if(p.done)throw new Error('Preview exited before startup: '+log);log+=new TextDecoder().decode(p.value);url=log.match(/http:\/\/127\.0\.0\.1:\d+\//)?.[0];}
 console.log('PREVIEW',url);void (async()=>{while(!(await reader.read()).done){}reader.releaseLock();})();
 browser=await chromium.launch({headless:true});
 async function viewer(key){
  const context=await browser.newContext({viewport:{width:1100,height:900}});
  // Defense in depth: this integration test can never send traffic to a public service.
  await context.route('**/*',route=>new URL(route.request().url()).hostname==='127.0.0.1'?route.continue():route.abort());
  await context.routeWebSocket('**',socket=>{if(new URL(socket.url()).hostname==='127.0.0.1')socket.connectToServer();else socket.close();});
  await context.exposeFunction('fixtureSign',template=>finalizeEvent(template,key));
  await context.addInitScript(({pubkey,relayUrl})=>{
   if(window!==top)return;
   // Test host only: simulate an extension and explicitly selected local relay.
   localStorage.setItem('napplet:network',JSON.stringify({relays:[relayUrl],blossom:[]}));
   window.nostr={getPublicKey:async()=>pubkey,signEvent:template=>window.fixtureSign(template)};
  },{pubkey:getPublicKey(key),relayUrl});
  const page=await context.newPage();page.setDefaultTimeout(18000);page.on('pageerror',e=>errors.push(e.message));await page.goto(url);
  const f=page.frameLocator('iframe');await f.locator('#track-picker').waitFor();return {page,f,context};
 }
 const a=await viewer(keys[0]),b=await viewer(keys[1]),racerName=`Sharing racer ${getPublicKey(keys[0]).slice(0,6)}`;
 const frame=v=>v.page.frames().find(f=>f.url()==='about:srcdoc');
 async function connect(v){await v.page.getByRole('button',{name:'Connect browser extension',exact:true}).click();await until(()=>frame(v).evaluate(()=>napplet.identity.getPublicKey()),x=>!!x);}
 async function community(v){if(!await v.f.locator('#tracks-dialog').isVisible())await v.f.locator('#track-picker').click();await v.f.locator('#levels-community').click();await until(()=>v.f.locator('#level-list-status').innerText(),t=>!t.includes('Loading'));}
 async function publish(v,approve=true){await v.f.locator('#publish-level').click();await v.page.getByRole('button',{name:approve?'Approve & publish':'Cancel',exact:true}).click();await until(()=>v.f.locator('#level-status').innerText(),t=>!t.includes('Saving draft')&&!t.includes('Waiting'));}
 async function closeEditor(v){await v.f.locator('[data-close="level-editor"]').click();}
 await connect(a);
 await a.f.locator('#track-picker').click();await a.f.locator('#create-level').click();await a.f.locator('#level-name').fill('Sharing fixture');
 await a.f.getByRole('button',{name:'+ Step up',exact:true}).click();await a.f.getByRole('button',{name:'+ Drop',exact:true}).click();await a.f.getByRole('button',{name:'+ Trench',exact:true}).click();assert.equal(await a.f.locator('.piece-chip').count(),6);
 await publish(a,false);assert.match(await a.f.locator('#level-status').innerText(),/not approved/);assert.equal(events.length,0);
 await publish(a);assert.match(await a.f.locator('#level-status').innerText(),/Published to Community/);assert.equal(events.length,1);console.log('PASS consent denial, retry, signed publication');
 await community(b);assert.equal(await b.f.locator('.level-card').count(),1);assert.match(await b.f.locator('.level-card').innerText(),/Sharing fixture/);assert.equal(await b.f.getByRole('button',{name:'Edit published',exact:true}).count(),0);assert.equal(await frame(b).evaluate(()=>typeof window.nostr),'undefined');
 await b.f.getByRole('button',{name:'Remix',exact:true}).click();await b.f.locator('#publish-level').click();await until(()=>b.f.locator('#level-status').innerText(),t=>t.includes('Connect your Nostr'));await connect(b);await publish(b);assert.equal(events.length,2);assert.notEqual(events[0].pubkey,events[1].pubkey);console.log('PASS anonymous discovery, guest publishing fallback, second-identity remix');
 await closeEditor(b);await closeEditor(a);await a.page.reload();await a.f.locator('#track-picker').waitFor();await connect(a);await community(a);await a.f.locator('.level-card').filter({hasText:'Sharing fixture'}).filter({has:a.f.getByRole('button',{name:'Edit published',exact:true})}).getByRole('button',{name:'Edit published',exact:true}).click();
 await a.f.locator('#level-name').fill('Sharing fixture updated');refuse=true;await publish(a);assert.doesNotMatch(await a.f.locator('#level-status').innerText(),/^Published/);assert.equal(events.length,2);refuse=false;await publish(a);assert.match(await a.f.locator('#level-status').innerText(),/Published/);assert.equal(events.length,3);console.log('PASS owner update after reload and relay-rejection retry');
 // A second tab using the same test identity publishes another revision. The old draft must be refused.
 const newerBody={...JSON.parse(events[2].content),title:'Changed elsewhere'};const external=finalizeEvent({...events[2],created_at:events[2].created_at+1,content:JSON.stringify(newerBody)},keys[0]);events.push(external);
 await a.f.locator('#level-name').fill('My stale edit');await a.f.locator('#publish-level').click();await until(()=>a.f.locator('#level-status').innerText(),t=>t.includes('changed elsewhere'));assert.equal(events.length,4);console.log('PASS stale edit rejected without overwriting');
 await closeEditor(a);await community(a);await a.f.locator('.level-card').filter({hasText:'Changed elsewhere'}).getByRole('button',{name:'Edit published',exact:true}).click();
 await a.f.locator('#unpublish-level').click();await a.page.getByRole('button',{name:'Approve & publish',exact:true}).click();await until(()=>a.f.locator('#level-status').innerText(),t=>t.includes('Unpublished'));await community(b);assert.equal(await b.f.locator('.level-card').count(),1);assert.match(await b.f.locator('.level-card').innerText(),/remix/);console.log('PASS tombstone hides the original; remix remains');
 const malformed=finalizeEvent({...events[1],created_at:Math.floor(Date.now()/1000),tags:events[1].tags.map(t=>t[0]==='d'?['d',t[1].replace(/:[^:]+$/,':bad-fixture')]:t),content:JSON.stringify({...JSON.parse(events[1].content),data:{physics:999,pieces:[]}})},keys[1]);events.push(malformed);await b.f.locator('#refresh-levels').click();await until(()=>b.f.locator('#level-list-status').innerText(),t=>t.includes('invalid'));assert.equal(await b.f.locator('.level-card').count(),1);console.log('PASS malformed public level skipped');
 await closeEditor(a);await a.f.locator('#track-picker').click();await a.f.locator('#create-level').click();await a.f.locator('#level-name').fill('Identity retry fixture');await publish(a,false);await a.page.getByRole('button',{name:'Disconnect',exact:true}).click();await until(()=>frame(a).evaluate(()=>napplet.identity.getPublicKey()),x=>!x);await a.f.locator('#publish-level').click();await until(()=>a.f.locator('#level-status').innerText(),t=>t.includes('identity changed'));await connect(a);console.log('PASS pending publication rejects an identity change');await a.f.locator('#level-name').fill('Raceable starter');await a.f.locator('#test-level').click();await a.f.locator('[data-preset="runner"]').click();await a.f.locator('#race').click();await a.f.locator('#message-title').filter({hasText:'Look at you go.'}).waitFor({timeout:45000});assert.equal(await a.f.locator('#share-result').isVisible(),false);await a.f.locator('#message-action').click();await a.f.locator('.score-row').first().click();assert.match(await a.f.locator('#design-detail').innerText(),/Raceable starter/);await a.f.locator('[data-close="design-dialog"]').click();
 const saved=await frame(a).evaluate(async()=>JSON.parse(await napplet.storage.getItem('limb-race-v2')));assert.ok(saved.runs.some(r=>r.level?.title==='Raceable starter'));console.log('PASS custom race and saved geometry');
 // Complete an original course and share its actual drawing to the isolated v2 board.
 await a.f.locator('#track-picker').click();await a.f.locator('#levels-builtin').click();await a.f.locator('[data-track="meadow"]').click();await a.f.locator('#racer-name').fill(racerName);await a.f.locator('#race').click();await a.f.locator('#message-title').filter({hasText:'Look at you go.'}).waitFor({timeout:45000});await a.f.locator('#share-result').click();await a.f.locator('#share-result').filter({hasText:'Time shared'}).waitFor();await a.f.locator('#message-action').click();
 await b.f.locator('[data-close="tracks-dialog"]').click();await b.f.locator('#shared').click();await b.f.locator('button.score-row').filter({hasText:racerName}).waitFor();await b.f.locator('button.score-row').filter({hasText:racerName}).click();await b.f.locator('#design-dialog').waitFor({state:'visible'});assert.match(await b.f.locator('#design-detail').innerText(),/Shared personal best/);await b.f.locator('#use-design').click();assert.match(await b.f.locator('#ink-count').innerText(),/\d/);console.log('PASS other identity inspects and reuses real shared race drawing');
 const backend=JSON.parse(readFileSync(new URL('../soy-backend.json',import.meta.url),'utf8'));
 async function boardCall(v,tool,args={}){return frame(v).evaluate(async({backend,tool,args})=>{const r=await napplet.cvm.callTool(backend.provider,tool,{napplet:backend.napplet,board:'meadow-classic-rigid-v2-draw1',...args});if(r.isError)throw new Error(JSON.stringify(r));return r.structuredContent;},{backend,tool,args});}
 const row=(await boardCall(a,'soy_board_read')).own,before=await boardCall(b,'soy_board_entry',{actor:row.actor,revision:row.revision});
 const probe={version:1,physics:2,limbs:[[[0,0],[1000,1000]],[[0,0],[-1000,-1000]]]};
 for(const score of [row.score,row.score+100])await boardCall(a,'soy_board_submit',{score,name:racerName+' faster',data:probe});
 assert.deepEqual((await boardCall(b,'soy_board_entry',{actor:row.actor,revision:row.revision})).entry,before.entry);
 await boardCall(a,'soy_board_submit',{score:row.score-1,name:racerName+' faster',data:probe});assert.equal((await boardCall(b,'soy_board_entry',{actor:row.actor,revision:row.revision})).stale,true);
 await b.f.locator('button.score-row').filter({hasText:racerName}).click();await until(()=>b.f.locator('#score-status').innerText(),t=>t.includes('newer best'));assert.equal(await b.f.locator('#design-dialog').isVisible(),false);await b.f.locator('#refresh').click();await b.f.locator('button.score-row').filter({hasText:racerName+' faster'}).waitFor();await b.f.locator('button.score-row').filter({hasText:racerName+' faster'}).click();await b.f.locator('#design-dialog').waitFor({state:'visible'});await b.f.locator('#use-design').click();assert.match(await b.f.locator('#ink-count').innerText(),/^2/);console.log('PASS equal/worse scores preserve attachments; stale revisions rejected; better run updates both');
 // Capture real editor layout at desktop and narrow frames, in both runtime themes.
 await a.f.locator('#track-picker').click();await a.f.locator('#create-level').click();await a.f.locator('#level-name').fill('Cliffside workshop');await a.f.getByRole('button',{name:'+ Step up',exact:true}).click();
 for(const scheme of ['light','dark'])for(const [w,h] of [[320,560],[390,844],[844,390],[900,600],[200,160],[2400,1200]]){
  await a.page.setViewportSize({width:w,height:h});await a.page.locator('iframe').evaluate((el,{w,h,scheme})=>{el.style.cssText=`position:fixed;inset:0;width:${w}px;height:${h}px;min-height:0;border:0;z-index:9999`;el.contentWindow.postMessage({type:'theme.changed',theme:{colors:scheme==='dark'?{background:'#172521',text:'#e5ecdf',primary:'#a8cf8b'}:{background:'#f8f6ed',text:'#292d23',primary:'#72ac98'}}},'*');},{w,h,scheme});
  await pause(100);const layout=await frame(a).evaluate(()=>({w:innerWidth,h:innerHeight,overflow:document.documentElement.scrollWidth>innerWidth,dialogOverflow:document.querySelector('#level-editor').scrollWidth>document.querySelector('#level-editor').clientWidth}));assert.equal(layout.w,w);assert.equal(layout.h,h);assert.equal(layout.overflow,false);if(w>=240)assert.equal(layout.dialogOverflow,false,JSON.stringify({w,h,scheme,layout}));
  if((w===900||w===390)&&scheme==='light')await a.f.locator('body').screenshot({path:`.napplet-space/promotion/editor-${w}.png`});
 }
 console.log('PASS editor size and theme matrix');assert.deepEqual(errors,[]);console.log('SHARING SMOKE PASSED');
}catch(error){if(browser)for(const context of browser.contexts())for(const page of context.pages())console.error('UI',await page.locator('body').innerText().catch(()=>''),await page.frameLocator('iframe').locator('body').innerText().catch(()=>''));throw error;}
finally{
 await browser?.close();if(preview){preview.kill('SIGTERM');await preview.exited;}relay.stop(true);writeFileSync(configPath,original);
 if(url){let stopped=false;try{await fetch(url,{signal:AbortSignal.timeout(1000)});}catch{stopped=true;}assert.ok(stopped,'owned preview listener closed');}console.log('Preview stopped; original configuration restored.');
}