How GeoLibre Implements PWA Runtime Caching for CDN-Delivered Engines for Offline Support
GeoLibre uses Vite's Workbox integration to cache heavy WebAssembly engines from jsDelivr at runtime, enabling full offline functionality after the first online visit.
The opengeos/GeoLibre project is a geospatial PWA that ships multiple database and compute engines—including Pyodide, PGlite/PostGIS, CereusDB, DuckDB-WASM, and GDAL3—as WebAssembly binaries fetched from CDN. Because these engines total tens of megabytes, the PWA service worker implements a dedicated runtime caching strategy to ensure they remain available offline without bloating the initial install bundle.
How Runtime Caching Works for CDN Engines
The service worker is generated by Vite PWA's Workbox plugin. Rather than pre-caching engine binaries (which would delay first load), GeoLibre caches them on-demand during runtime using a targeted runtimeCaching rule.
Step 1: URL Pattern Matching for jsDelivr Engines
In vite.config.ts, the rule isolates requests to specific CDN paths:
urlPattern: ({ url }: { url: URL }) =>
url.hostname === "cdn.jsdelivr.net" &&
(url.pathname.startsWith("/pyodide/") ||
url.pathname.startsWith("/npm/@electric-sql/") ||
url.pathname.startsWith("/npm/@cereusdb/") ||
url.pathname.startsWith("/npm/@duckdb/") ||
url.pathname.startsWith("/npm/gdal3.js")),
This pattern at lines 71-80 of apps/geolibre-desktop/vite.config.ts precisely targets version-pinned WASM assets from jsDelivr, excluding unrelated CDN traffic.
Step 2: CacheFirst Strategy for Instant Offline Access
The rule applies a CacheFirst handler as shown at vite.config.ts:81-82:
handler: "CacheFirst",
How this works: The first request hits the network, stores the response, and all subsequent loads serve directly from cache. This guarantees instant access even when offline—critical for engine initialization where network latency would break the UX.
Step 3: Dedicated Cache Configuration with Eviction Policy
The cache options at vite.config.ts:83-86 configure boundaries to prevent unbounded growth:
options: {
cacheName: "geolibre-cdn-engines",
expiration: { maxEntries: 400, maxAgeSeconds: 60 * 60 * 24 * 30 },
cacheableResponse: { statuses: [0, 200] },
}
| Parameter | Value | Purpose |
|---|---|---|
cacheName |
geolibre-cdn-engines |
Isolates engine assets from other cached data |
maxEntries |
400 | Caps total stored files across all engines |
maxAgeSeconds |
2,592,000 (30 days) | Forces refresh of stale assets |
statuses |
[0, 200] |
Only caches successful responses |
Version-Locked URLs Enable Safe Long-Term Caching
Engine URLs embed exact package versions (e.g., pyodide/0.26.1/pyodide.js). This immutability design—confirmed in vite.config.ts:58-65—means:
- Cached entries are never stale; the URL itself encodes version
- New deployments fetch fresh assets automatically (new URLs)
- Old versions remain cacheable indefinitely without revalidation
This eliminates cache-busting complexity while ensuring users always receive the engine version shipped with their app build.
Service Worker Registration and Verification
The PWA registration happens at runtime. The Playwright test suite in e2e/pwa.spec.ts:43-99 validates the complete offline flow:
- Service worker registers on first load
- Engine files fetch from jsDelivr and populate
geolibre-cdn-enginescache - Subsequent launches—including fully offline—serve engines from cache
- SQL, Sedona spatial operations, and Python execution function without network
Extending the Pattern: Basemap Tile Caching
The same CacheFirst pattern applies to basemap tiles via apps/geolibre-desktop/src/lib/offline-tiles.ts. The library provides manual warm-up functionality:
import { warmTiles } from "./offline-tiles";
await warmTiles({
bbox: [-122.5, 37.6, -122.3, 37.8],
zoomRange: [10, 14],
});
This demonstrates how GeoLibre generalizes runtime caching across all heavy external assets, not just engines.
Implementation Files Reference
| File | Path | Responsibility |
|---|---|---|
| Vite configuration | apps/geolibre-desktop/vite.config.ts |
Workbox runtimeCaching rule definition |
| PWA e2e tests | e2e/pwa.spec.ts |
Offline functionality verification |
| Tile offline utilities | apps/geolibre-desktop/src/lib/offline-tiles.ts |
Basemap caching (same pattern) |
| DuckDB CDN bundles | apps/geolibre-desktop/src/lib/duckdb-wasm-bundles.cdn.ts |
References geolibre-cdn-engines cache name |
Summary
- Targeted URL patterns in
vite.config.tsisolate jsDelivr engine requests from other network traffic - CacheFirst strategy ensures engines load instantly after first fetch, even offline
- Bounded cache configuration (400 entries, 30-day TTL) prevents storage abuse
- Version-locked URLs guarantee cached assets match deployed code without manual invalidation
- Verified by e2e tests in
e2e/pwa.spec.tsconfirming full offline SQL/Python functionality
Frequently Asked Questions
What caching strategy does GeoLibre use for CDN engines?
GeoLibre uses Workbox's CacheFirst strategy for all CDN-delivered engines. The first network request populates the cache; subsequent requests serve directly from geolibre-cdn-engines storage, enabling offline use without revalidation delays.
Why not pre-cache the engines in the service worker?
Pre-caching would force users to download tens of megabytes of WASM binaries during the initial PWA install, blocking first interaction. Runtime caching defers this until each engine is first needed, improving time-to-interactive while still achieving offline capability.
How does GeoLibre prevent stale engine versions in cache?
Engine URLs contain exact version pins (e.g., /pyodide/0.26.1/). Since the URL changes with each deployment, stale cached entries simply never match new requests. The 30-day maxAgeSeconds provides a safety net for unused entries without risking version mismatches.
Can the cache size or expiration be adjusted?
Yes. Modify the expiration object in vite.config.ts at lines 83-86. Increasing maxEntries above 400 accommodates more engine versions or additional WASM modules; reducing maxAgeSeconds forces more frequent freshness checks at the cost of occasional network requests.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →