55 * It will only control pages under /ship-log-map/ (non-overlapping with other apps).
66 */
77
8- const APP_VERSION = '0.0.1' ; // <-- CI will inject package.json version (see README/CI step)
8+ /* ship-log-map: Service Worker (offline + update + installable) */
9+
10+ const APP_VERSION = '0.0.1' ;
911const CACHE_PREFIX = 'ship-log-map' ;
1012const PRECACHE = `${ CACHE_PREFIX } -precache-v${ APP_VERSION } ` ;
1113const RUNTIME = `${ CACHE_PREFIX } -runtime-v${ APP_VERSION } ` ;
1214
13- // Compute BASE_URL from the service worker script URL directory.
14- // Example: https://avidrucker.github.io/ship-log-map/service-worker.js -> '/ship-log-map/'
15+ // ✅ Compute BASE_URL from service worker location
1516const BASE_URL = ( ( ) => {
1617 const url = new URL ( self . location . href ) ;
17- const path = url . pathname . replace ( / [ ^ / ] + $ / , '' ) ; // drop filename
18+ const path = url . pathname . replace ( / [ ^ / ] + $ / , '' ) ;
1819 return path . endsWith ( '/' ) ? path : path + '/' ;
1920} ) ( ) ;
2021
22+ console . log ( '[SW] BASE_URL detected:' , BASE_URL ) ;
23+
2124const CORE_URLS = [
2225 `${ BASE_URL } ` ,
2326 `${ BASE_URL } index.html` ,
2427 `${ BASE_URL } manifest.webmanifest` ,
2528 `${ BASE_URL } offline.html` ,
26- // Icons (you will add these soon)
2729 `${ BASE_URL } logo192.png` ,
2830 `${ BASE_URL } logo512.png` ,
31+ `${ BASE_URL } favicon.ico` ,
2932] ;
3033
31- // Message channel (optional): allow page to request immediate activation
3234self . addEventListener ( 'message' , ( event ) => {
3335 if ( event . data && event . data . type === 'SKIP_WAITING' ) {
3436 self . skipWaiting ( ) ;
3537 }
3638} ) ;
3739
38- // Helper: fetch index.html and pull out hashed asset URLs that Vite injects (assets/*.js|css)
40+ // ✅ IMPROVED: Discover build assets more reliably
3941async function discoverBuildAssets ( ) {
4042 try {
4143 const res = await fetch ( `${ BASE_URL } index.html` , { cache : 'no-store' } ) ;
4244 if ( ! res . ok ) return [ ] ;
4345 const html = await res . text ( ) ;
4446 const urls = new Set ( ) ;
4547
46- const assetRegex = new RegExp ( `${ BASE_URL . replace ( / [ . * + ? ^ $ { } ( ) | [ \\ ] \\ \\ ] / g, '\\\\$&' ) } assets/[^"']+\\.(?:js|css)` , 'g' ) ;
47- for ( const m of html . matchAll ( assetRegex ) ) {
48- urls . add ( m [ 0 ] ) ;
48+ // Match script src and link href
49+ const scriptRegex = / < s c r i p t [ ^ > ] + s r c = [ " ' ] ( [ ^ " ' ] + ) [ " ' ] / gi;
50+ const linkRegex = / < l i n k [ ^ > ] + h r e f = [ " ' ] ( [ ^ " ' ] + ) [ " ' ] / gi;
51+
52+ let match ;
53+
54+ // Find all script tags
55+ while ( ( match = scriptRegex . exec ( html ) ) !== null ) {
56+ let src = match [ 1 ] ;
57+ // Convert relative paths to absolute
58+ if ( ! src . startsWith ( 'http' ) ) {
59+ if ( src . startsWith ( '/' ) ) {
60+ src = `${ self . location . origin } ${ src } ` ;
61+ } else {
62+ src = `${ self . location . origin } ${ BASE_URL } ${ src } ` ;
63+ }
64+ }
65+ // Only cache same-origin assets
66+ if ( src . startsWith ( self . location . origin ) ) {
67+ urls . add ( src ) ;
68+ }
4969 }
50-
51- // Also cache the Vite SVG and any CSS referenced by link tags
52- const viteSvg = `${ BASE_URL } vite.svg` ;
53- urls . add ( viteSvg ) ;
54-
55- // Look for <link rel="stylesheet" href="..."> too
56- 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;
57- let lm ;
58- while ( ( lm = linkHrefRegex . exec ( html ) ) !== null ) {
59- const href = lm [ 1 ] . startsWith ( 'http' ) ? lm [ 1 ] : new URL ( lm [ 1 ] , `${ location . origin } ${ BASE_URL } ` ) . pathname . replace ( location . origin , '' ) ;
60- // Only cache same-origin & within scope
61- if ( href . startsWith ( BASE_URL ) ) urls . add ( href ) ;
70+
71+ // Find all link tags (CSS, icons, etc.)
72+ while ( ( match = linkRegex . exec ( html ) ) !== null ) {
73+ let href = match [ 1 ] ;
74+ // Convert relative paths to absolute
75+ if ( ! href . startsWith ( 'http' ) ) {
76+ if ( href . startsWith ( '/' ) ) {
77+ href = `${ self . location . origin } ${ href } ` ;
78+ } else {
79+ href = `${ self . location . origin } ${ BASE_URL } ${ href } ` ;
80+ }
81+ }
82+ // Only cache same-origin assets
83+ if ( href . startsWith ( self . location . origin ) ) {
84+ urls . add ( href ) ;
85+ }
6286 }
6387
88+ console . log ( '[SW] Discovered assets:' , Array . from ( urls ) ) ;
6489 return Array . from ( urls ) ;
65- } catch ( _e ) {
90+ } catch ( err ) {
91+ console . error ( '[SW] Failed to discover assets:' , err ) ;
6692 return [ ] ;
6793 }
6894}
6995
70- // Install: precache the app shell and discovered build assets
96+ // Install: precache
7197self . addEventListener ( 'install' , ( event ) => {
98+ console . log ( '[SW] Installing...' ) ;
7299 event . waitUntil ( ( async ( ) => {
73100 const cache = await caches . open ( PRECACHE ) ;
74101 const assets = await discoverBuildAssets ( ) ;
75102 const toCache = [ ...CORE_URLS , ...assets ] ;
76- await cache . addAll ( toCache ) ;
77- // Take over quickly
103+
104+ console . log ( '[SW] Caching:' , toCache ) ;
105+
106+ // Cache each URL individually to avoid failures blocking install
107+ const results = await Promise . allSettled (
108+ toCache . map ( url => cache . add ( url ) . catch ( err => {
109+ console . warn ( '[SW] Failed to cache:' , url , err ) ;
110+ } ) )
111+ ) ;
112+
113+ const failed = results . filter ( r => r . status === 'rejected' ) ;
114+ if ( failed . length > 0 ) {
115+ console . warn ( '[SW] Some assets failed to cache:' , failed . length ) ;
116+ }
117+
78118 await self . skipWaiting ( ) ;
119+ console . log ( '[SW] Install complete' ) ;
79120 } ) ( ) ) ;
80121} ) ;
81122
82- // Activate: clean old caches and claim clients
123+ // Activate: clean old caches
83124self . addEventListener ( 'activate' , ( event ) => {
125+ console . log ( '[SW] Activating...' ) ;
84126 event . waitUntil ( ( async ( ) => {
85127 const names = await caches . keys ( ) ;
86128 await Promise . all (
87129 names
88130 . filter ( ( n ) => ! [ PRECACHE , RUNTIME ] . includes ( n ) && n . startsWith ( CACHE_PREFIX ) )
89- . map ( ( n ) => caches . delete ( n ) )
131+ . map ( ( n ) => {
132+ console . log ( '[SW] Deleting old cache:' , n ) ;
133+ return caches . delete ( n ) ;
134+ } )
90135 ) ;
91136 await self . clients . claim ( ) ;
137+ console . log ( '[SW] Activation complete' ) ;
92138 } ) ( ) ) ;
93139} ) ;
94140
95141// Strategy helpers
96142async function networkFirst ( request ) {
97143 try {
144+ console . log ( '[SW] Network first:' , request . url ) ;
98145 const fresh = await fetch ( request ) ;
99146 const cache = await caches . open ( RUNTIME ) ;
100- if ( fresh && fresh . ok ) cache . put ( request , fresh . clone ( ) ) ;
147+ if ( fresh && fresh . ok ) {
148+ cache . put ( request , fresh . clone ( ) ) ;
149+ }
101150 return fresh ;
102- } catch ( _e ) {
151+ } catch ( err ) {
152+ console . log ( '[SW] Network failed, trying cache:' , request . url ) ;
103153 const cached = await caches . match ( request ) ;
104- if ( cached ) return cached ;
105- // If it's a navigation, fall back to cached index or offline page
154+ if ( cached ) {
155+ console . log ( '[SW] Serving from cache:' , request . url ) ;
156+ return cached ;
157+ }
158+
159+ // Navigation fallback
106160 if ( request . mode === 'navigate' ) {
107161 const index = await caches . match ( `${ BASE_URL } index.html` ) ;
108- if ( index ) return index ;
162+ if ( index ) {
163+ console . log ( '[SW] Serving index.html fallback' ) ;
164+ return index ;
165+ }
109166 const offline = await caches . match ( `${ BASE_URL } offline.html` ) ;
110- if ( offline ) return offline ;
167+ if ( offline ) {
168+ console . log ( '[SW] Serving offline.html' ) ;
169+ return offline ;
170+ }
111171 }
112- throw _e ;
172+ throw err ;
113173 }
114174}
115175
116176async function cacheFirst ( request ) {
117177 const cached = await caches . match ( request ) ;
118- if ( cached ) return cached ;
178+ if ( cached ) {
179+ console . log ( '[SW] Cache hit:' , request . url ) ;
180+ return cached ;
181+ }
182+ console . log ( '[SW] Cache miss, fetching:' , request . url ) ;
119183 const res = await fetch ( request ) ;
120184 const cache = await caches . open ( RUNTIME ) ;
121- if ( res && res . ok ) cache . put ( request , res . clone ( ) ) ;
185+ if ( res && res . ok ) {
186+ cache . put ( request , res . clone ( ) ) ;
187+ }
122188 return res ;
123189}
124190
125191// Fetch handler
126192self . addEventListener ( 'fetch' , ( event ) => {
127193 const { request } = event ;
128194
129- // Only handle GET requests
130195 if ( request . method !== 'GET' ) return ;
131196
132197 const url = new URL ( request . url ) ;
133198
134- // Only handle requests within our scope (same origin + path starts with BASE_URL)
135- if ( url . origin !== location . origin || ! url . pathname . startsWith ( BASE_URL ) ) {
199+ // ✅ IMPORTANT: Only handle same-origin requests
200+ if ( url . origin !== self . location . origin ) {
201+ console . log ( '[SW] Ignoring cross-origin:' , request . url ) ;
136202 return ;
137203 }
138204
139- // Navigations -> Network-first with offline fallback
205+ // Navigation -> Network-first
140206 if ( request . mode === 'navigate' ) {
141207 event . respondWith ( networkFirst ( request ) ) ;
142208 return ;
143209 }
144210
145- // HTML files -> Network-first
211+ // HTML -> Network-first
146212 if ( url . pathname . endsWith ( '.html' ) ) {
147213 event . respondWith ( networkFirst ( request ) ) ;
148214 return ;
149215 }
150216
151- // Hashed build assets (Vite ) -> Cache-first (they are content-hashed)
152- if ( url . pathname . startsWith ( ` ${ BASE_URL } assets/` ) || url . pathname . endsWith ( '.js' ) || url . pathname . endsWith ( '.css' ) ) {
217+ // Assets (JS/CSS ) -> Cache-first
218+ if ( url . pathname . includes ( '/ assets/' ) || url . pathname . endsWith ( '.js' ) || url . pathname . endsWith ( '.css' ) ) {
153219 event . respondWith ( cacheFirst ( request ) ) ;
154220 return ;
155221 }
156222
157- // Everything else under scope -> Cache-first as a safe default
223+ // Everything else -> Cache-first
158224 event . respondWith ( cacheFirst ( request ) ) ;
159- } ) ;
225+ } ) ;
0 commit comments