sw.js
1// sw.js
2import { registerRoute, Route } from "workbox-routing";
3import { StaleWhileRevalidate, CacheFirst } from "workbox-strategies";
4import { ExpirationPlugin } from "workbox-expiration";
5
6// Log service worker startup
7console.log('[SW] Service worker starting up');
8
9// Global error handler for service worker
10self.addEventListener('error', (event) => {
11 console.error('[SW] Global error:', event.error);
12});
13
14self.addEventListener('unhandledrejection', (event) => {
15 console.error('[SW] Unhandled promise rejection:', event.reason);
16});
17
18// A new route that matches same-origin image requests and handles
19// them with the cache-first, falling back to network strategy:
20
21const tileRoute = new Route(
22 ({ url }) => {
23 const u = url.toString();
24 const r = /.+\/tiles\//gm;
25 console.log("[SW] url=", u, "match=", u.match(r) !== null);
26 return u.match(r) !== null;
27 },
28 new StaleWhileRevalidate({
29 cacheName: "tiles",
30 }),
31);
32
33// Register the new route
34registerRoute(tileRoute);
35
36// CacheFirst route for route GeoJSON (immutable, content-addressed)
37// Routes are identified by hash and never change once created
38registerRoute(
39 ({ url }) => {
40 // Match /route/{hash}.geojson URLs
41 const isMatch = url.pathname.startsWith('/route/') && url.pathname.endsWith('.geojson');
42 console.log('[SW] Checking route:', url.pathname, 'match:', isMatch);
43 return isMatch;
44 },
45 new CacheFirst({
46 cacheName: "route-geojson",
47 plugins: [
48 new ExpirationPlugin({
49 maxEntries: 100, // Max 100 routes cached
50 maxAgeSeconds: 60 * 60 * 24 * 30, // 30 days
51 }),
52 {
53 // Custom plugin for debugging
54 cacheWillUpdate: async ({ request, response }) => {
55 console.log('[SW] cacheWillUpdate:', request.url, 'status:', response.status);
56 return response;
57 },
58 handlerWillRespond: async ({ request, response }) => {
59 console.log('[SW] handlerWillRespond:', request.url, 'status:', response?.status);
60 return response;
61 },
62 },
63 ],
64 })
65);