|
| 1 | +import { |
| 2 | + cleanupOutdatedCaches, |
| 3 | + createHandlerBoundToURL, |
| 4 | + precacheAndRoute, |
| 5 | +} from 'workbox-precaching' |
| 6 | +import { clientsClaim } from 'workbox-core' |
| 7 | +import { NetworkFirst, StaleWhileRevalidate } from 'workbox-strategies' |
| 8 | +import { NavigationRoute, registerRoute } from 'workbox-routing' |
| 9 | +import { CacheableResponsePlugin } from 'workbox-cacheable-response' |
| 10 | +import { ExpirationPlugin } from 'workbox-expiration' |
| 11 | + |
| 12 | +declare let self: ServiceWorkerGlobalScope |
| 13 | + |
| 14 | +const cacheNames = ['npmx-packages', 'npmx-packages-code-and-docs', 'npmx-vercel-proxies'] as const |
| 15 | + |
| 16 | +async function createRuntimeCaches() { |
| 17 | + await Promise.all(cacheNames.map(c => caches.open(c))) |
| 18 | +} |
| 19 | +self.addEventListener('install', event => { |
| 20 | + event.waitUntil(createRuntimeCaches()) |
| 21 | +}) |
| 22 | + |
| 23 | +self.skipWaiting() |
| 24 | +clientsClaim() |
| 25 | + |
| 26 | +cleanupOutdatedCaches() |
| 27 | +precacheAndRoute(self.__WB_MANIFEST, { |
| 28 | + urlManipulation: ({ url }) => { |
| 29 | + const urls: URL[] = [] |
| 30 | + // search use query params, we need to include here any page using query params |
| 31 | + if (url.pathname.endsWith('_payload.json') || url.pathname.endsWith('/search')) { |
| 32 | + const newUrl = new URL(url.href) |
| 33 | + newUrl.search = '' |
| 34 | + urls.push(newUrl) |
| 35 | + } |
| 36 | + return urls |
| 37 | + }, |
| 38 | +}) |
| 39 | + |
| 40 | +// allow only fallback in dev: we don't want to cache anything |
| 41 | +let allowlist: undefined | RegExp[] |
| 42 | +if (import.meta.env.DEV) allowlist = [/^\/$/] |
| 43 | + |
| 44 | +// deny api and server page calls |
| 45 | +let denylist: undefined | RegExp[] |
| 46 | +if (import.meta.env.PROD) { |
| 47 | + denylist = [ |
| 48 | + // search page |
| 49 | + /^\/search$/, |
| 50 | + // api calls |
| 51 | + /^\/api\//, |
| 52 | + /^\/oauth\//, |
| 53 | + /^\/package\//, |
| 54 | + /^\/package-code\//, |
| 55 | + /^\/package-docs\//, |
| 56 | + /^\/_v\//, |
| 57 | + /^\/opensearch\.xml$/, |
| 58 | + // exclude sw: if the user navigates to it, fallback to index.html |
| 59 | + /^\/service-worker\.js$/, |
| 60 | + ] |
| 61 | + |
| 62 | + registerRoute( |
| 63 | + ({ sameOrigin, url }) => sameOrigin && url.pathname.startsWith('/package/'), |
| 64 | + new NetworkFirst({ |
| 65 | + cacheName: cacheNames[0], |
| 66 | + plugins: [ |
| 67 | + new CacheableResponsePlugin({ statuses: [200] }), |
| 68 | + new ExpirationPlugin({ maxEntries: 100, maxAgeSeconds: 60 }), |
| 69 | + ], |
| 70 | + }), |
| 71 | + ) |
| 72 | + registerRoute( |
| 73 | + ({ sameOrigin, url }) => |
| 74 | + sameOrigin && |
| 75 | + (url.pathname.startsWith('/package-docs/') || url.pathname.startsWith('/package-code/')), |
| 76 | + new StaleWhileRevalidate({ |
| 77 | + cacheName: cacheNames[1], |
| 78 | + plugins: [ |
| 79 | + new CacheableResponsePlugin({ statuses: [200] }), |
| 80 | + new ExpirationPlugin({ maxEntries: 100, maxAgeSeconds: 365 * 24 * 60 * 60 }), |
| 81 | + ], |
| 82 | + }), |
| 83 | + ) |
| 84 | + registerRoute( |
| 85 | + ({ sameOrigin, url }) => sameOrigin && url.pathname.startsWith('/_v/'), |
| 86 | + new NetworkFirst({ |
| 87 | + cacheName: cacheNames[1], |
| 88 | + plugins: [ |
| 89 | + new CacheableResponsePlugin({ statuses: [200] }), |
| 90 | + new ExpirationPlugin({ maxEntries: 100, maxAgeSeconds: 60 }), |
| 91 | + ], |
| 92 | + }), |
| 93 | + ) |
| 94 | +} |
| 95 | + |
| 96 | +// to allow work offline |
| 97 | +registerRoute(new NavigationRoute(createHandlerBoundToURL('/'), { allowlist, denylist })) |
0 commit comments