diff --git a/frontend/sw-update.js b/frontend/sw-update.js index d40de07..5853665 100644 --- a/frontend/sw-update.js +++ b/frontend/sw-update.js @@ -161,6 +161,9 @@ const reg = root.navigator && await root.navigator.serviceWorker.getRegistration(); const worker = reg && (reg.waiting || reg.active); const status = await askCacheStatus(worker); + // Unknown protocol is a retryable failure, never an empty successful + // legacy refresh followed by a reload into the old persistent manifest. + if (worker && !status) throw new Error('Worker update status unavailable; try again'); if (status && status.assetSync) { const result = await core.syncAssets(m,{cache,fetchFn:opts.fetchFn}); if (!reg.waiting) await core.commit(m,cache); diff --git a/frontend/sw-update.test.js b/frontend/sw-update.test.js index 7bb6f6e..6ea1ce1 100644 --- a/frontend/sw-update.test.js +++ b/frontend/sw-update.test.js @@ -362,3 +362,9 @@ test('explicit playback override clears the worker guard and reloads once',async assert.deepStrictEqual(messages,[{type:'PLAYING',value:false},{type:'SKIP_WAITING'}]); container.fireControllerChange();container.fireControllerChange();assert.equal(reloads,1); }); +test('an unanswered worker status cannot fall through to an empty legacy refresh',async()=>{ + const vm=require('node:vm'),fs=require('node:fs'); + const sandbox={AssetSyncCore:{CACHE:'ytplayer-assets'},navigator:{serviceWorker:{getRegistration:async()=>({active:{postMessage(){}}})}}}; + vm.runInNewContext(fs.readFileSync(__dirname+'/sw-update.js','utf8'),sandbox); + await assert.rejects(sandbox.SwUpdate.refreshShellInPlace({cachesApi:{open:async()=>({}),keys:async()=>[]},fetchFn:async()=>({ok:true,json:async()=>({buildTag:'new'})}),isShellCache:()=>true}),/Worker update status unavailable/); +});