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:

  1. Service worker registers on first load
  2. Engine files fetch from jsDelivr and populate geolibre-cdn-engines cache
  3. Subsequent launches—including fully offline—serve engines from cache
  4. 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.ts isolate 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.ts confirming 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →