From 4c674604509ffe663bbba87eba15b61f04e598f2 Mon Sep 17 00:00:00 2001 From: jcoffey <51408202+jcoffey-dev@users.noreply.github.com> Date: Wed, 16 Sep 2026 13:29:57 -0700 Subject: [PATCH] Fetch the rest of a new build in the background (#394) The app page names only what it loads at start. The composer, settings, viewers and the rest were fetched when first used, and after every deploy that first use waited on the server -- and the worker's tidy-up dropped them again at the next deploy anyway. The build now writes the list of all its files into the page as an inert JSON block. The service worker keeps everything listed and, once a page names files it does not hold, fetches them three at a time; a load cut short is resumed at the next navigation. Language catalogs are listed apart and left to be cached when used, and nothing is fetched ahead when the browser is set to save data. --- web/public/sw.js | 53 ++++++++++++++++++++++++++++++++++++++++++---- web/vite.config.ts | 39 ++++++++++++++++++++++++++++++++-- 2 files changed, 86 insertions(+), 6 deletions(-) diff --git a/web/public/sw.js b/web/public/sw.js index af0539b..5d2f8cd 100644 --- a/web/public/sw.js +++ b/web/public/sw.js @@ -86,14 +86,59 @@ async function tidy() { } } -/** Keep the offline copy of the app page current, and tidy when it changes. */ +/** Keep the offline copy of the app page current, tidy when it changes, and fill in what it lists. */ async function refreshShell(res) { const html = await res.text(); const cache = await caches.open(VERSION); const prev = await cache.match(SHELL_KEY); - if (prev && (await prev.text()) === html) return; - await cache.put(SHELL_KEY, new Response(html, { headers: { "content-type": "text/html; charset=utf-8" } })); - await tidy(); + if (!prev || (await prev.text()) !== html) { + await cache.put(SHELL_KEY, new Response(html, { headers: { "content-type": "text/html; charset=utf-8" } })); + await tidy(); + } + await precache(html); +} + +/* + * Fetching the rest of the build before it is asked for. + * + * The app page lists every file of its build (see the asset-list plugin in + * vite.config.ts). Without this, the first time after a deploy that a reader + * opened the composer, settings or a viewer, it waited on the server for the + * code -- on a distant link, a visible pause. Now those files are fetched + * quietly once a page names them, a few at a time, and only those not held + * already; a load cut short is carried on at the next navigation, which calls + * this again. Language catalogs are left to be cached when used, and nothing + * is fetched ahead when the reader has asked the browser to save data. + */ +const PRECACHE_PARALLEL = 3; + +function precacheList(html) { + const m = html.match(/\n `); + }, + }, + }; +} + export default defineConfig({ base, - plugins: [react()], + plugins: [react(), assetList()], define: { __IHASMAIL_VERSION__: JSON.stringify(version) }, resolve: { alias: { "@": fileURLToPath(new URL("./src", import.meta.url)) },