A queue held by one connection
The build now strips the worker’s indentation, and even with the fixes it weighs 3,868 bytes instead of 4,005. A star or a first page whose network hangs no longer holds back everything after it: the worker waits 45 seconds for those. A stalled install was also checked in Firefox and WebKit. Firefox stops such a worker by itself after about 60 seconds, so the limit went down from 60 to 45, as the 6-in-1 chose.
Where the bytes were
The worker (/sw.js) took 4,005 compressed bytes of the 4,096 in its budget, which left 91 for the next fix. Before raising the bound, I measured what the worker is made of. The code weighed 3,329 bytes; the file lists and reader-facing lines the build writes in, 734. The build already dropped the comments but kept the indentation at the start of each line, and that indentation alone weighed 145 bytes.
A minifier would have saved more, up to 650 bytes, but the published worker would stop reading line by line. The tests look for const NETWORK_WAIT = 10000; in it, validate:public reads the build line as JSON, and I check the live worker the same way myself. So the build now strips the indentation and nothing else. It refuses a template whose backtick literal spans lines, since there the indentation would be part of the text. I also tried folding two identical loops into one function, and that came out 23 bytes heavier: gzip already compresses repetition. With the fixes below the worker weighs 3,868 bytes, 228 remain, and the bound did not have to go up.
One connection held the whole queue
The worker fetches the pages of saved records, and the page a reader landed on at a first visit, by itself, in a queue, one page message at a time. Those requests had no time limit. If the network for one of those pages neither answered nor broke off, every later star and page would wait behind it until the browser stopped the worker: five minutes in Chromium.
Tests first. The first test stars a record whose page never answers, then stars another. The second makes a first visit straight to a record page that the worker then cannot fetch, and stars a record. So as not to wait out the real limit, the test gives the worker two seconds instead: the code is the same, only the number changes. On the old code both failed: the second star was never saved.
The fix has two parts. These requests now have the same limit as the shell files. And a saved record that does not arrive no longer stops the rest: before, a failure on one page ended the whole list, and a record saved after it was not kept until that page came through. Now that page is tried again with the next message, and the others are kept at once.
A page with no copy
When the network does not answer and there is no copy of the page, the worker does not show the offline page but keeps waiting: the 6-in-1 decided so on 27.09. There was no test for this, because it seemed to need the full 10 seconds of waiting. The new test gives the worker one second and lets the page through after three. The page itself arrives, not the offline page and not a copy line, and it is kept like any opened page. This test passed on the old code as well: the behaviour was already there, and now a test holds it.
Firefox and WebKit
Playwright’s Firefox and WebKit turned out to be on this machine, so I measured a stalled install in them too. The standard test does not work there: in WebKit and Firefox, context.route does not see the worker’s requests, and in WebKit an offline navigation fails as well. So I wrote a separate probe, with a small proxy in front of the site that does not answer one file.
WebKit breaks off such a request by itself after about 60 seconds, and the worker cleans up after itself. Firefox, after about the same 60 seconds, stops the worker, and then half of the new build’s cache stays behind, as it did in Chromium before the previous pass. Our limit was exactly 60 seconds and raced Firefox: in one run of three, Firefox got there first. I asked, and the 6-in-1 chose 45 seconds. That is enough for the largest shell file to arrive even over a slow mobile network. At 45 seconds Firefox cleaned up in all three runs, and so did WebKit.
What is not verified
The tests passed only in Chromium: pwa.spec.ts three times in a row, plus verify:code. There are no GitHub Actions minutes until 1 October, so CI has not seen this pass. Firefox was checked in an older build than Playwright expects (revision 1538 instead of 1543), because I did not install browsers without a need. Firefox and WebKit saw only the stalled install, not the order of stars. Real Android, iOS and Safari on macOS are not verified.
Entry written September 27, 2026
Commits this entry accounts for
45a1da9