Split app.js into one script per feature; versioning covers every public file; README for the new features

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
nmemmertandClaude Sonnet 5.5 committed 2026-10-22 07:57:51 -04:00
1 parent c1f4a220f9
commit 1564941ea5
20 files changed
+2297 -2275

No files matched your search

+1 -1
View File
@@ -18,7 +18,7 @@ const lrcLib = require('../lyrics');
ok('word timing inside a line is read', l[1].words && l[1].words.length === 2 && l[1].words[0].t === 14000 && l[1].words[1].t === 14800 && l[1].text === 'Second line', l[1]);
ok('a line that repeats (two times) appears twice, in order', l[2].text === 'Chorus' && l[3].text === 'Chorus' && l[3].t === 30250, l.slice(2));
const client = (() => { // the same reader in the app itself must agree
const src = fs.readFileSync(path.join(root, 'public', 'app.js'), 'utf8'), a = src.indexOf('function lyrParseLrc'), b = src.indexOf('\n}\n', a) + 3;
const src = fs.readFileSync(path.join(root, 'public', 'lyrics-view.js'), 'utf8'), a = src.indexOf('function lyrParseLrc'), b = src.indexOf('\n}\n', a) + 3;
return new Function(src.slice(a, b) + '; return lyrParseLrc;')();
})();
ok('and the app reads it the same way', JSON.stringify(client('[ti:x]\n[00:10.50]Hello there\n[00:14.00]<00:14.00>Second <00:14.80>line\n[00:20.00][00:30.25]Chorus\n')) === JSON.stringify(l));
+3 -3
View File
@@ -1,6 +1,6 @@
const fs=require('fs'), vm=require('vm');
const src=fs.readFileSync(''+require('path').join(__dirname,'..','public','app.js')+'','utf8');
const a=src.indexOf("const shuffled = a =>"), b=src.indexOf("// ---- People (admin)");
const src=['music.js','player.js','lyrics-view.js'].map(f=>fs.readFileSync(require('path').join(__dirname,'..','public',f),'utf8')).join('\n');
const a=src.indexOf("const shuffled = a =>"), b=src.length;
let code=src.slice(a,b);
const listeners={};
class FakeAudio{constructor(){this.paused=true;this.currentTime=0;this.duration=100;this.readyState=4;this.error=null;this._l={};this.src='';}
@@ -8,7 +8,7 @@ class FakeAudio{constructor(){this.paused=true;this.currentTime=0;this.duration=
play(){this.paused=false;this.fire('play');return Promise.resolve()} pause(){if(!this.paused){this.paused=true;this.fire('pause')}} getAttribute(){return this.src||null} load(){}}
const toasts=[];const store={d:{},get(k,d){return this.d[k]??d},set(k,v){this.d[k]=v},del(k){delete this.d[k]}};
const el=()=>({remove(){},classList:{toggle(){},add(){}},setAttribute(){},dataset:{},style:{},querySelector:()=>el(),querySelectorAll:()=>[],set innerHTML(v){},set textContent(v){}});
const ctx={Audio:FakeAudio,store,toast:m=>toasts.push(m),px:{art:()=>'x'},icon:()=>'',esc:s=>s,fmtDur:()=>'',$:()=>el(),document:{querySelectorAll:()=>[],querySelector:()=>null},allowed:()=>true,
const ctx={sleepT:{eot:false},sleepClear(){},Audio:FakeAudio,store,toast:m=>toasts.push(m),px:{art:()=>'x'},icon:()=>'',esc:s=>s,fmtDur:()=>'',$:()=>el(),document:{querySelectorAll:()=>[],querySelector:()=>null},allowed:()=>true,
navigator:{onLine:true},addEventListener:(e,f)=>{listeners[e]=f},setTimeout,clearTimeout,fetch:()=>new Promise(()=>{}),URL:{createObjectURL:()=>'blob:x',revokeObjectURL(){}},
Image:class{},MediaMetadata:class{},openSheet:()=>({}),closeSheet(){},actionSheet(){},Response,URLSearchParams,AbortController,Promise,Set,Map,Date,Math,saveStateHook:0,location:{hash:'#/tasks'}};
vm.createContext(ctx); vm.runInContext(code+";this.pl=pl;this.next=next;this.prev=prev;this.setShuffle=setShuffle;this.setRepeat=setRepeat;this.playQueue=playQueue;this.advance=advance;this.cur=cur;this.recover=recover;this.srcOf=srcOf;",ctx);
+1 -1
View File
@@ -59,7 +59,7 @@ const wait = ms => new Promise(r => setTimeout(r, ms));
const home = await (await fetch(base + '/', { headers: { cookie } })).text(), cfg2 = await (await fetch(base + '/api/config', { headers: { cookie } })).json();
ok('the app page links its code with the current build number, so a cached old copy can never be used', home.includes(`/app.js?v=${cfg2.build}`) && home.includes(`/style.css?v=${cfg2.build}`), home.match(/app\.js[^"]*/));
const login = await (await fetch(base + '/login')).text(); ok('so does the sign-in page', login.includes(`/login.js?v=${cfg2.build}`), login.match(/login\.js[^"]*/));
const va = await fetch(base + `/app.js?v=${cfg2.build}`, { headers: { cookie } }); ok('the versioned address serves the file', va.status === 200 && (await va.text()).includes('addPersonSheet'), va.status);
const va = await fetch(base + `/app.js?v=${cfg2.build}`, { headers: { cookie } }); ok('the versioned address serves the file', va.status === 200 && (await va.text()).includes('routesOf'), va.status);
// hardening: headers, cross-site writes, health check, backup, links setting
const h1 = await fetch(base + '/login'); await h1.text();
ok('responses carry security headers', h1.headers.get('x-content-type-options') === 'nosniff' && /frame-ancestors 'self'/.test(h1.headers.get('content-security-policy') || ''), [...h1.headers]);