# How GeoLibre Implements PWA Runtime Caching for CDN-Delivered Engines for Offline Support

> Discover how GeoLibre uses Vite and Workbox for PWA runtime caching to enable offline support for CDN-delivered WebAssembly engines after your first online visit.

- Repository: [Open Geospatial Solutions/GeoLibre](https://github.com/opengeos/GeoLibre)
- Tags: how-to-guide
- Published: 2026-08-15

---

**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`](https://github.com/opengeos/GeoLibre/blob/main/vite.config.ts), the rule isolates requests to specific CDN paths:

```ts
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`](https://github.com/opengeos/GeoLibre/blob/main/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`](https://developer.chrome.com/docs/workbox/modules/workbox-strategies#cachefirst) handler as shown at `vite.config.ts:81-82`:

```ts
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:

```ts
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`](https://github.com/opengeos/GeoLibre/blob/main/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`](https://github.com/opengeos/GeoLibre/blob/main/apps/geolibre-desktop/src/lib/offline-tiles.ts). The library provides manual warm-up functionality:

```ts
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`](https://github.com/opengeos/GeoLibre/blob/main/apps/geolibre-desktop/vite.config.ts) | Workbox `runtimeCaching` rule definition |
| PWA e2e tests | [`e2e/pwa.spec.ts`](https://github.com/opengeos/GeoLibre/blob/main/e2e/pwa.spec.ts) | Offline functionality verification |
| Tile offline utilities | [`apps/geolibre-desktop/src/lib/offline-tiles.ts`](https://github.com/opengeos/GeoLibre/blob/main/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`](https://github.com/opengeos/GeoLibre/blob/main/apps/geolibre-desktop/src/lib/duckdb-wasm-bundles.cdn.ts) | References `geolibre-cdn-engines` cache name |

## Summary

- **Targeted URL patterns** in [`vite.config.ts`](https://github.com/opengeos/GeoLibre/blob/main/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`](https://github.com/opengeos/GeoLibre/blob/main/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`](https://github.com/opengeos/GeoLibre/blob/main/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.