1+ import type { APIRoute } from 'astro'
2+
3+ export const prerender = true
4+
5+ const OFFLINE_CACHE = 'webstackbuilders-offline-v1'
6+ const ASSET_CACHE = 'webstackbuilders-assets-v1'
7+ const IMAGE_CACHE = 'webstackbuilders-images-v1'
8+ const OFFLINE_URL = '/offline'
9+
10+ export const buildServiceWorkerScript = ( ) : string => {
11+ return [
12+ `const OFFLINE_CACHE = '${ OFFLINE_CACHE } '` ,
13+ `const ASSET_CACHE = '${ ASSET_CACHE } '` ,
14+ `const IMAGE_CACHE = '${ IMAGE_CACHE } '` ,
15+ `const OFFLINE_URL = '${ OFFLINE_URL } '` ,
16+ '' ,
17+ "self.addEventListener('install', event => {" ,
18+ ' event.waitUntil(' ,
19+ " caches.open(OFFLINE_CACHE).then(cache => cache.add(OFFLINE_URL)).catch(() => undefined)" ,
20+ ' )' ,
21+ ' self.skipWaiting()' ,
22+ '})' ,
23+ '' ,
24+ "self.addEventListener('activate', event => {" ,
25+ ' event.waitUntil(self.clients.claim())' ,
26+ '})' ,
27+ '' ,
28+ 'const cacheAsset = async (cacheName, request, response) => {' ,
29+ ' if (!response || !response.ok) {' ,
30+ ' return response' ,
31+ ' }' ,
32+ '' ,
33+ ' const cache = await caches.open(cacheName)' ,
34+ ' await cache.put(request, response.clone())' ,
35+ ' return response' ,
36+ '}' ,
37+ '' ,
38+ 'const staleWhileRevalidate = async request => {' ,
39+ ' const cache = await caches.open(ASSET_CACHE)' ,
40+ ' const cached = await cache.match(request)' ,
41+ ' const network = fetch(request)' ,
42+ ' .then(response => cacheAsset(ASSET_CACHE, request, response))' ,
43+ ' .catch(() => undefined)' ,
44+ '' ,
45+ ' if (cached) {' ,
46+ ' void network' ,
47+ ' return cached' ,
48+ ' }' ,
49+ '' ,
50+ ' return network || fetch(request)' ,
51+ '}' ,
52+ '' ,
53+ 'const cacheFirst = async request => {' ,
54+ ' const cache = await caches.open(IMAGE_CACHE)' ,
55+ ' const cached = await cache.match(request)' ,
56+ ' if (cached) {' ,
57+ ' return cached' ,
58+ ' }' ,
59+ '' ,
60+ ' const response = await fetch(request)' ,
61+ ' return cacheAsset(IMAGE_CACHE, request, response)' ,
62+ '}' ,
63+ '' ,
64+ 'const handleNavigation = async request => {' ,
65+ ' try {' ,
66+ ' return await fetch(request)' ,
67+ ' } catch {' ,
68+ ' const cachedOffline = await caches.match(OFFLINE_URL)' ,
69+ ' if (cachedOffline) {' ,
70+ ' return cachedOffline' ,
71+ ' }' ,
72+ '' ,
73+ " return new Response('Offline', { status: 503, statusText: 'Offline' })" ,
74+ ' }' ,
75+ '}' ,
76+ '' ,
77+ "self.addEventListener('fetch', event => {" ,
78+ ' const { request } = event' ,
79+ " if (request.method !== 'GET') {" ,
80+ ' return' ,
81+ ' }' ,
82+ '' ,
83+ " if (request.mode === 'navigate') {" ,
84+ ' event.respondWith(handleNavigation(request))' ,
85+ ' return' ,
86+ ' }' ,
87+ '' ,
88+ " if (request.destination === 'style' || request.destination === 'script') {" ,
89+ ' event.respondWith(staleWhileRevalidate(request))' ,
90+ ' return' ,
91+ ' }' ,
92+ '' ,
93+ " if (request.destination === 'image') {" ,
94+ ' event.respondWith(cacheFirst(request))' ,
95+ ' }' ,
96+ '})' ,
97+ ] . join ( '\n' )
98+ }
99+
100+ export const GET : APIRoute = _context => {
101+ return new Response ( buildServiceWorkerScript ( ) , {
102+ headers : {
103+ 'Content-Type' : 'application/javascript; charset=utf-8' ,
104+ 'Cache-Control' : 'no-cache, no-store, must-revalidate' ,
105+ 'Service-Worker-Allowed' : '/' ,
106+ } ,
107+ } )
108+ }
0 commit comments