Cloudflare Pages 가 /index.html 을 / 로 308 리다이렉트하는데, 서비스워커가 그 리다이렉트 응답을 캐시해 두면 navigation 요청에 응답할 수 없어(스펙상 금지) 두 번째 실행부터 ERR_FAILED 로 앱이 아예 열리지 않았다. - sw.js: navigation 은 네트워크 우선 + 캐시된 './' 오프라인 폴백, 프리캐시는 리다이렉트 플래그 없는 새 Response 로 복사해 저장 (tetrig-v2) - manifest.webmanifest: start_url 을 './' 로 바꿔 리다이렉트 자체를 회피 - android/: Bubblewrap TWA 프로젝트 (kr.tkrmagid.tetrig, launchUrl '/') - .well-known/assetlinks.json: 서명 키 SHA-256 등록 → 주소창 없는 전체화면 실행
51 lines
1.6 KiB
JavaScript
51 lines
1.6 KiB
JavaScript
// TETRIG 서비스 워커 — 오프라인 캐시 (앱처럼 설치/실행)
|
|
const CACHE = 'tetrig-v2';
|
|
const ASSETS = [
|
|
'./',
|
|
'./manifest.webmanifest',
|
|
'./icon-192.png',
|
|
'./icon-512.png',
|
|
'./apple-touch-icon.png',
|
|
];
|
|
|
|
// Cloudflare Pages 는 /index.html 을 / 로 308 리다이렉트한다.
|
|
// 리다이렉트된 Response 를 그대로 캐시하면 navigation 요청에 응답할 수 없어
|
|
// (spec 상 금지) 두 번째 실행부터 ERR_FAILED 가 난다. 새 Response 로 복사해 저장한다.
|
|
async function cacheClean(cache, url) {
|
|
const res = await fetch(url, { cache: 'reload' });
|
|
if (!res.ok) return;
|
|
const body = await res.blob();
|
|
await cache.put(url, new Response(body, { status: 200, statusText: 'OK', headers: res.headers }));
|
|
}
|
|
|
|
self.addEventListener('install', (e) => {
|
|
e.waitUntil(
|
|
caches.open(CACHE).then((c) => Promise.all(ASSETS.map((u) => cacheClean(c, u))))
|
|
);
|
|
self.skipWaiting();
|
|
});
|
|
|
|
self.addEventListener('activate', (e) => {
|
|
e.waitUntil(
|
|
caches.keys().then((keys) =>
|
|
Promise.all(keys.filter((k) => k !== CACHE).map((k) => caches.delete(k)))
|
|
)
|
|
);
|
|
self.clients.claim();
|
|
});
|
|
|
|
self.addEventListener('fetch', (e) => {
|
|
if (e.request.method !== 'GET') return;
|
|
|
|
// 페이지 진입: 네트워크 우선, 실패하면 캐시된 시작 문서로 오프라인 동작
|
|
if (e.request.mode === 'navigate') {
|
|
e.respondWith(
|
|
fetch(e.request).catch(async () => (await caches.match('./')) || Response.error())
|
|
);
|
|
return;
|
|
}
|
|
|
|
// 나머지 정적 에셋: 캐시 우선
|
|
e.respondWith(caches.match(e.request).then((r) => r || fetch(e.request)));
|
|
});
|