@@ -7,21 +7,67 @@ export function getOfflineNavigationServiceWorkerFilePath(): string {
77 return path . join ( CLIENT_STATIC_FILES_PATH , OFFLINE_NAVIGATION_SERVICE_WORKER )
88}
99
10- // Generate an app-local service worker for offline navigations. This slice is
11- // pass-through: it only installs and claims clients so later slices can add
12- // cache population and fallback document handling.
10+ // Offline navigations use a generated, app-local service worker. It caches the
11+ // bootstrap manifest and fallback document during installation; document
12+ // navigations still go to the network until fallback handling is enabled .
1313export function createOfflineNavigationServiceWorker ( {
14+ cacheNamespace,
1415 manifestHref,
1516} : {
17+ cacheNamespace : string
1618 manifestHref : string
1719} ) : string {
1820 const metadata = JSON . stringify ( {
21+ cacheNamespace,
1922 manifestHref,
2023 source : 'offline-navigation-service-worker' ,
2124 } )
2225
2326 return `self.__NEXT_OFFLINE_NAVIGATION_SW=${ metadata } ;
24- self.addEventListener('install',(event)=>{event.waitUntil(self.skipWaiting())});
25- self.addEventListener('activate',(event)=>{event.waitUntil(self.clients.claim())});
27+ const CACHE_PREFIX='next-offline-navigation-v1:';
28+ function withDeploymentQuery(href){
29+ const url=new URL(href,self.location.origin);
30+ const deploymentParams=new URLSearchParams(self.location.search);
31+ deploymentParams.forEach((value,key)=>{
32+ if(!url.searchParams.has(key)){
33+ url.searchParams.set(key,value);
34+ }
35+ });
36+ return url.href;
37+ }
38+ async function fetchRequiredResource(href){
39+ const response=await fetch(withDeploymentQuery(href),{cache:'no-store'});
40+ if(!response.ok){
41+ throw new Error('Failed to cache offline navigation resource: '+href);
42+ }
43+ return response;
44+ }
45+ async function cacheOfflineNavigationResources(){
46+ const metadata=self.__NEXT_OFFLINE_NAVIGATION_SW;
47+ const cache=await caches.open(metadata.cacheNamespace);
48+ const manifestResponse=await fetchRequiredResource(metadata.manifestHref);
49+ const manifest=await manifestResponse.clone().json();
50+ await cache.put(metadata.manifestHref,manifestResponse);
51+ const fallbackResponse=await fetchRequiredResource(manifest.fallbackDocument.href);
52+ await cache.put(manifest.fallbackDocument.href,fallbackResponse);
53+ }
54+ self.addEventListener('install',(event)=>{
55+ event.waitUntil((async()=>{
56+ await cacheOfflineNavigationResources();
57+ await self.skipWaiting();
58+ })());
59+ });
60+ self.addEventListener('activate',(event)=>{
61+ event.waitUntil((async()=>{
62+ const metadata=self.__NEXT_OFFLINE_NAVIGATION_SW;
63+ const cacheNames=await caches.keys();
64+ await Promise.all(cacheNames.map((cacheName)=>{
65+ if(cacheName.startsWith(CACHE_PREFIX)&&cacheName!==metadata.cacheNamespace){
66+ return caches.delete(cacheName);
67+ }
68+ }));
69+ await self.clients.claim();
70+ })());
71+ });
2672`
2773}
0 commit comments