@@ -7,24 +7,70 @@ 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 first
11- // slice is intentionally pass-through: it only installs and claims clients so
12- // later slices can add cache population and fallback document handling.
10+ // Generate an app-local service worker for offline navigations. At this stage
11+ // it only installs build-scoped bootstrap artifacts into CacheStorage; document
12+ // navigations still go to the network until the fallback handling slice .
1313export function createOfflineNavigationServiceWorker ( {
1414 buildId,
15+ cacheNamespace,
1516 manifestHref,
1617} : {
1718 buildId : string
19+ cacheNamespace : string
1820 manifestHref : string
1921} ) : string {
2022 const metadata = JSON . stringify ( {
2123 buildId,
24+ cacheNamespace,
2225 manifestHref,
2326 source : 'offline-navigation-service-worker' ,
2427 } )
2528
2629 return `self.__NEXT_OFFLINE_NAVIGATION_SW=${ metadata } ;
27- self.addEventListener('install',(event)=>{event.waitUntil(self.skipWaiting())});
28- self.addEventListener('activate',(event)=>{event.waitUntil(self.clients.claim())});
30+ const CACHE_PREFIX='next-offline-navigation-v1:';
31+ function withDeploymentQuery(href){
32+ const url=new URL(href,self.location.origin);
33+ const deploymentParams=new URLSearchParams(self.location.search);
34+ deploymentParams.forEach((value,key)=>{
35+ if(!url.searchParams.has(key)){
36+ url.searchParams.set(key,value);
37+ }
38+ });
39+ return url.href;
40+ }
41+ async function fetchRequiredResource(href){
42+ const response=await fetch(withDeploymentQuery(href),{cache:'no-store'});
43+ if(!response.ok){
44+ throw new Error('Failed to cache offline navigation resource: '+href);
45+ }
46+ return response;
47+ }
48+ async function cacheOfflineNavigationResources(){
49+ const metadata=self.__NEXT_OFFLINE_NAVIGATION_SW;
50+ const cache=await caches.open(metadata.cacheNamespace);
51+ const manifestResponse=await fetchRequiredResource(metadata.manifestHref);
52+ const manifest=await manifestResponse.clone().json();
53+ await cache.put(metadata.manifestHref,manifestResponse);
54+ const fallbackResponse=await fetchRequiredResource(manifest.fallbackDocument.href);
55+ await cache.put(manifest.fallbackDocument.href,fallbackResponse);
56+ }
57+ self.addEventListener('install',(event)=>{
58+ event.waitUntil((async()=>{
59+ await cacheOfflineNavigationResources();
60+ await self.skipWaiting();
61+ })());
62+ });
63+ self.addEventListener('activate',(event)=>{
64+ event.waitUntil((async()=>{
65+ const metadata=self.__NEXT_OFFLINE_NAVIGATION_SW;
66+ const cacheNames=await caches.keys();
67+ await Promise.all(cacheNames.map((cacheName)=>{
68+ if(cacheName.startsWith(CACHE_PREFIX)&&cacheName!==metadata.cacheNamespace){
69+ return caches.delete(cacheName);
70+ }
71+ }));
72+ await self.clients.claim();
73+ })());
74+ });
2975`
3076}
0 commit comments