koala73 / koala73/worldmonitor

perf(pwa): SW globPatterns still precaches every app chunk — 882KB/74 requests of unused lazy chunks (residual of #4578)

Open
#5,373 0 comments 0 reactions 0 assignees View on GitHub
area:frontend enhancement performance
Dominant language
TypeScript
Stars
86.6k
Forks
13.1k
Avg merge
8h 4m
Merged PRs (30d)
825

Description

## Problem

`globPatterns` still precaches **every** `.js`/`.css` the build emits, including in-app lazy chunks the dashboard never loads. On the DebugBear mobile profile (US East, 1.6 Mbps, 150 ms RTT, slow device, Chrome 147, 2026-07-18 07:05 UTC) that is **74 requests / 882 KB of net-new transfer — 24.2 % of the page's 3,638 KB** — for code this page did not execute.

This is the **residual of #4578**. That issue was closed by narrowing `globIgnores` to exclude *off-page/public* directories (`pro/**`, `favico/**`, `textures/**`, later `blog/**` via #4899). The **glob itself was never narrowed**, so the app's own `/assets/*` chunks were never in scope of that fix and have been precached the whole time.

## Root cause

`vite.config.ts:1013` (origin/main `c45acd49c`):

```js
globPatterns: ['**/*.{js,css,ico,png,svg,woff2}'],
globIgnores: [
'**/ml*.js', '**/onnx*.wasm', '**/locale-*.js', '**/clerk-*.js',
'**/*.woff2',
'pro/**', 'favico/**', 'textures/**', // #4578
'blog/**', // #4891 / #4899
],
```

Every entry in `globIgnores` is a *directory or filename* exclusion. Nothing excludes `assets/**`, so each new code-split chunk **auto-joins the precache set at build time**. The config has not changed since #5220 (2026-07-11) — the wave grew because the app did (10-widget MCP fleet #5199, China coverage, procurement #5266, per-domain rpc-clients).

That makes this a **silently compounding** regression: every feature that adds a chunk adds first-visit bandwidth, with no signal at review time.

## Evidence

Source: DebugBear analysis **[83831737](https://www.debugbear.com/viewResult/83831737)** (project `103025`, page `693052`) — `www.worldmonitor.app/dashboard`, 303 requests, test config **US East, 1.6 Mbps, 150 ms RTT, slow device, Chrome 147, Lighthouse 13.4.0**, run **2026-07-18 07:05 UTC**. Day-over-day comparison baseline is **[83766301](https://www.debugbear.com/viewResult/83766301)** (2026-07-17 07:03 UTC).

> Trend figures (29 samples, 2026-06-30 → 2026-07-18) come from the series export for the same page; each sample carries its own `analysis.id`, so any row is re-openable at `debugbear.com/viewResult/`.

Largest net-new precache items, none executed by this page:

```
277.7 KB maplibre-DfDOI80j.js ← entire map engine
78.7 KB main-BU6Lo6Ic.css
39.9 KB panels-economy-CIhfp2uk.js
36.1 KB panels-markets-Di7EvD6u.js
31.0 KB panels-risk-BToxMI9N.js
26.0 KB search-manager-BbOE8F4U.js
25.8 KB panels-energy-BJU2dFhz.js
20.8 KB panels-defense-Dziz3eM6.js
18.3 KB oref-locations-EKm8gxKt.js
17.6 KB protomaps-CrcXoKbV.js
15.1 KB satellite.es-DES7etDd.js
11.3 KB maplibre-B46vZ9Yf.css
```

`maplibre` alone is 277.7 KB — and the map is demand-gated on mobile, the same reasoning that got `textures/**` excluded in #4578.

Verified **not** double-downloads: of 64 URLs requested more than once, 63 second-occurrences transfer 0 bytes (HTTP cache hit); only 17 KB is true re-download. The waste is unused chunks, not redundancy.

## Correlated lab signal

DebugBear lab Speed Index shows a clean step change with **zero distribution overlap**, consistent with a growing precache wave competing for bandwidth inside the visual-completion window:

| window | n | range | mean |
|---|---|---|---|
| pre 2026-07-10 | 19 | 3,975–5,333 ms | 4,530 ms |
| post 2026-07-10 | 10 | 5,386–6,585 ms | **5,970 ms (+23 %)** |

Deliberate CLS/deferral trades in that window (#5205, #5161, #5220, #5167, later #5333/#5344) account for part of it and bought a real field win — CrUX CLS p75 0.18 → 0.09, LCP p75 3,761 → 1,503 ms. The precache growth is the part that is pure loss.

## Proposed fix

Narrow `globPatterns` to the true app shell and let `runtimeCaching` handle the rest on demand:

- Precache: entry chunk, its static imports, the shell CSS, `manifest.webmanifest`, the small root favicons already in `includeAssets`.
- Do **not** precache: lazy panel chunks (`panels-*`), map engine (`maplibre*`, `protomaps*`), per-domain `rpc-client-*`, demand-gated data chunks.
- Add a `StaleWhileRevalidate` runtime-cache route for `/assets/*` so offline/repeat-visit behaviour for lazy chunks is preserved without first-visit cost.

Guard against silent regrowth: assert a **budget on the generated precache manifest** (entry count + total bytes) in the existing deploy-config test surface (`tests/deploy-config.test.mjs`), so a new chunk joining the precache set fails CI instead of quietly costing first-visit bandwidth. Without this the issue recurs a third time.

## Scope

- Residual of #4578 (closed 2026-07-01 — fixed off-page assets only, never `assets/**`). Not a dup: different asset class, different fix.
- Independent of #5372, which defers SW registration: that one changes *when* the wave runs, this one changes *what* is in it. Doing only one leaves most of the cost.
- Related: #5165 — its "wave 2" panel chunks are SW precache fetches, not deferred panel loads (evidence in #5372).
- Related: #4487 (parent CWV epic that #5165 sits under). Filed as a sibling rather than a child: #4487 tracks *field* Core Web Vitals, and this is first-visit bandwidth showing up in lab Speed Index / TTI — reparent if you'd rather track it there.

## Acceptance

- Generated precache manifest entry count and total bytes both drop; recorded before/after.
- `maplibre*`, `panels-*`, and `rpc-client-*` absent from the precache manifest.
- Fresh-profile waterfall shows no `/assets/*` `fetch` for chunks the page did not execute.
- Offline repeat visit still works for the shell; lazy chunks resolve from runtime cache.
- CI budget guard fails when a new chunk enters the precache set.
- Re-run the DebugBear mobile profile; record Speed Index, Visually Complete, TTI, total transfer. Read against **CV 28 %** run-to-run TBT noise (2,969–8,756 ms over 29 samples) — single-run TBT deltas are not evidence.

Contributor guide

Open the contributing guide

Research direction

Start with vite.config.ts around line 1013 and inspect how the generated precache manifest is produced. Then read tests/deploy-config.test.mjs and run its deploy-config checks. Done means the manifest excludes maplibre*, panels-* and rpc-client-* chunks, stays within a guarded entry/byte budget, and preserves shell offline support with lazy chunks handled by runtime caching.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript, vite
Domain
build-system, performance, testing
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.