11// public/service-worker.js
22
33/* ship-log-map: Service Worker (offline + update + installable)
4- * IMPORTANT: Must be served at <BASE_URL>/service-worker.js
5- * Example on GitHub Pages: https://<user>.github.io/ship-log-map/service-worker.js
4+ * IMPORTANT: Served at <BASE_URL>/service-worker.js (e.g., /ship-log-map/service-worker.js)
65 */
76
8- const APP_VERSION = '0.0.1' ; // optionally replaced by CI
7+ const APP_VERSION = '0.0.1' ;
98const CACHE_PREFIX = 'ship-log-map' ;
109const PRECACHE = `${ CACHE_PREFIX } -precache-v${ APP_VERSION } ` ;
1110const RUNTIME = `${ CACHE_PREFIX } -runtime-v${ APP_VERSION } ` ;
1211
1312// Derive BASE_URL from this file's own URL directory, e.g. '/ship-log-map/'
1413const BASE_URL = ( ( ) => {
1514 const url = new URL ( self . location . href ) ;
16- const path = url . pathname . replace ( / [ ^ / ] + $ / , '' ) ; // drop filename
15+ const path = url . pathname . replace ( / [ ^ / ] + $ / , '' ) ;
1716 return path . endsWith ( '/' ) ? path : path + '/' ;
1817} ) ( ) ;
1918
20- // Core URLs we always want available offline
19+ // Core app-shell we always want offline
2120const CORE_URLS = [
2221 `${ BASE_URL } ` ,
2322 `${ BASE_URL } index.html` ,
2423 `${ BASE_URL } manifest.webmanifest` ,
2524 `${ BASE_URL } offline.html` ,
2625 `${ BASE_URL } logo192.png` ,
2726 `${ BASE_URL } logo512.png` ,
28- // Vite’s default dev asset; harmless if missing in prod:
29- `${ BASE_URL } vite.svg` ,
27+ `${ BASE_URL } vite.svg` , // harmless if missing in prod
3028] ;
3129
32- // Support “skip waiting” from the app
3330self . addEventListener ( 'message' , ( event ) => {
34- if ( event . data && event . data . type === 'SKIP_WAITING' ) {
35- self . skipWaiting ( ) ;
36- }
31+ if ( event . data && event . data . type === 'SKIP_WAITING' ) self . skipWaiting ( ) ;
3732} ) ;
3833
39- // ---- Build asset discovery (read index.html, collect /assets/*.js|. css and linked styles) ----
34+ // Discover built assets from index.html (Vite emits /assets/*.{js, css})
4035async function discoverBuildAssets ( ) {
4136 try {
4237 const res = await fetch ( `${ BASE_URL } index.html` , { cache : 'no-store' } ) ;
4338 if ( ! res . ok ) return [ ] ;
4439 const html = await res . text ( ) ;
4540 const urls = new Set ( ) ;
4641
47- // Capture hashed assets emitted by Vite under <base>/assets/
42+ // Find / assets/*.{js,css} under our BASE_URL
4843 const assetRegex = new RegExp (
4944 `${ BASE_URL . replace ( / [ . * + ? ^ $ { } ( ) | [ \\ ] \\ \\ ] / g, '\\\\$&' ) } assets/[^"']+\\.(?:js|css)` ,
5045 'g'
5146 ) ;
52- for ( const m of html . matchAll ( assetRegex ) ) {
53- urls . add ( m [ 0 ] ) ;
54- }
47+ for ( const m of html . matchAll ( assetRegex ) ) urls . add ( m [ 0 ] ) ;
5548
56- // Capture additional linked stylesheets (handle relative + absolute)
49+ // Also follow <link rel="stylesheet" href="...">
5750 const linkHrefRegex = / < l i n k \s + [ ^ > ] * r e l = [ " ' ] s t y l e s h e e t [ " ' ] [ ^ > ] * h r e f = [ " ' ] ( [ ^ " ' ] + ) [ " ' ] / gi;
5851 const baseForHtml = new URL ( BASE_URL , self . location . origin ) ;
5952 let lm ;
6053 while ( ( lm = linkHrefRegex . exec ( html ) ) !== null ) {
61- const hrefAbs = new URL ( lm [ 1 ] , baseForHtml ) . href ;
54+ const hrefAbs = new URL ( lm [ 1 ] , baseForHtml ) . href ; // ✅ fix
6255 const u = new URL ( hrefAbs ) ;
63- // Same-origin and within our scope
6456 if ( u . origin === self . location . origin && u . pathname . startsWith ( BASE_URL ) ) {
6557 urls . add ( u . pathname ) ;
6658 }
6759 }
6860
6961 return Array . from ( urls ) ;
70- } catch ( _e ) {
71- // Fail-soft: no dynamic assets discovered
62+ } catch {
7263 return [ ] ;
7364 }
7465}
7566
76- // ---- Install: precache core + discovered build assets ----
7767self . addEventListener ( 'install' , ( event ) => {
7868 event . waitUntil ( ( async ( ) => {
7969 const cache = await caches . open ( PRECACHE ) ;
8070 const dynamicAssets = await discoverBuildAssets ( ) ;
81- const toCache = [ ...CORE_URLS , ...dynamicAssets ] ;
82- await cache . addAll ( toCache ) ;
71+ await cache . addAll ( [ ...CORE_URLS , ...dynamicAssets ] ) ;
8372 await self . skipWaiting ( ) ;
8473 } ) ( ) ) ;
8574} ) ;
8675
87- // ---- Activate: clean old caches & take control ----
8876self . addEventListener ( 'activate' , ( event ) => {
8977 event . waitUntil ( ( async ( ) => {
9078 const names = await caches . keys ( ) ;
@@ -97,14 +85,13 @@ self.addEventListener('activate', (event) => {
9785 } ) ( ) ) ;
9886} ) ;
9987
100- // ---- Strategies ----
10188async function networkFirst ( request ) {
10289 try {
10390 const fresh = await fetch ( request ) ;
10491 const cache = await caches . open ( RUNTIME ) ;
10592 if ( fresh && fresh . ok ) cache . put ( request , fresh . clone ( ) ) ;
10693 return fresh ;
107- } catch ( _e ) {
94+ } catch {
10895 const cached = await caches . match ( request ) ;
10996 if ( cached ) return cached ;
11097 if ( request . mode === 'navigate' ) {
@@ -113,7 +100,7 @@ async function networkFirst(request) {
113100 const offline = await caches . match ( `${ BASE_URL } offline.html` ) ;
114101 if ( offline ) return offline ;
115102 }
116- throw _e ;
103+ throw new Error ( 'Network and cache both failed' ) ;
117104 }
118105}
119106
@@ -126,30 +113,24 @@ async function cacheFirst(request) {
126113 return res ;
127114}
128115
129- // ---- Fetch routing ----
130116self . addEventListener ( 'fetch' , ( event ) => {
131117 const { request } = event ;
132-
133- // Only deal with GETs and requests within our scope
134118 if ( request . method !== 'GET' ) return ;
135119
136120 const url = new URL ( request . url ) ;
137121 if ( url . origin !== self . location . origin ) return ;
138122 if ( ! url . pathname . startsWith ( BASE_URL ) ) return ;
139123
140- // Navigations: network-first with offline fallback
141124 if ( request . mode === 'navigate' ) {
142125 event . respondWith ( networkFirst ( request ) ) ;
143126 return ;
144127 }
145128
146- // HTML files: network-first (fresh content when online)
147129 if ( url . pathname . endsWith ( '.html' ) ) {
148130 event . respondWith ( networkFirst ( request ) ) ;
149131 return ;
150132 }
151133
152- // Hashed build assets: cache-first (content-hashed by Vite)
153134 if (
154135 url . pathname . startsWith ( `${ BASE_URL } assets/` ) ||
155136 url . pathname . endsWith ( '.js' ) ||
@@ -159,6 +140,5 @@ self.addEventListener('fetch', (event) => {
159140 return ;
160141 }
161142
162- // Default: cache-first
163143 event . respondWith ( cacheFirst ( request ) ) ;
164144} ) ;
0 commit comments