1
1
import { config as unoConfig } from "$(REMOTE_WEBAPP_PATH)$(REMOTE_BASE_PATH)/uno-config.js" ;
2
2
3
-
4
3
if ( unoConfig . environmentVariables [ "UNO_BOOTSTRAP_DEBUGGER_ENABLED" ] !== "True" ) {
5
4
console . debug ( "[ServiceWorker] Initializing" ) ;
6
5
let uno_enable_tracing = unoConfig . uno_enable_tracing ;
@@ -15,65 +14,120 @@ if (unoConfig.environmentVariables["UNO_BOOTSTRAP_DEBUGGER_ENABLED"] !== "True")
15
14
// worker to fail installing.
16
15
for ( var i = 0 ; i < unoConfig . offline_files . length ; i ++ ) {
17
16
try {
17
+ const currentFile = unoConfig . offline_files [ i ] ;
18
18
if ( uno_enable_tracing ) {
19
- console . debug ( `[ServiceWorker] cache ${ key } ` ) ;
19
+ console . debug ( `[ServiceWorker] caching ${ currentFile } ` ) ;
20
20
}
21
21
22
- await cache . add ( unoConfig . offline_files [ i ] ) ;
22
+ await cache . add ( currentFile ) ;
23
23
}
24
24
catch ( e ) {
25
- console . debug ( `[ServiceWorker] Failed to fetch ${ unoConfig . offline_files [ i ] } ` ) ;
25
+ console . debug ( `[ServiceWorker] Failed to fetch ${ unoConfig . offline_files [ i ] } : ${ e . message } ` ) ;
26
26
}
27
27
}
28
28
29
29
// Add the runtime's own files to the cache. We cannot use the
30
30
// existing cached content from the runtime as the keys contain a
31
31
// hash we cannot reliably compute.
32
- var c = await fetch ( "$(REMOTE_WEBAPP_PATH)_framework/blazor.boot.json" ) ;
33
- const monoConfigResources = ( await c . json ( ) ) . resources ;
32
+ try {
33
+ var c = await fetch ( "$(REMOTE_WEBAPP_PATH)_framework/blazor.boot.json" ) ;
34
+ // Response validation to catch HTTP errors early
35
+ // This prevents trying to parse invalid JSON from error responses
36
+ if ( ! c . ok ) {
37
+ throw new Error ( `Failed to fetch blazor.boot.json: ${ c . status } ${ c . statusText } ` ) ;
38
+ }
34
39
35
- var entries = {
36
- ...( monoConfigResources . coreAssembly || { } )
37
- , ...( monoConfigResources . assembly || { } )
38
- , ...( monoConfigResources . lazyAssembly || { } )
39
- , ...( monoConfigResources . jsModuleWorker || { } )
40
- , ...( monoConfigResources . jsModuleGlobalization || { } )
41
- , ...( monoConfigResources . jsModuleNative || { } )
42
- , ...( monoConfigResources . jsModuleRuntime || { } )
43
- , ...( monoConfigResources . wasmNative || { } )
44
- , ...( monoConfigResources . icu || { } )
45
- , ...( monoConfigResources . coreAssembly || { } )
46
- } ;
40
+ const bootJson = await c . json ( ) ;
41
+ const monoConfigResources = bootJson . resources || { } ;
47
42
48
- for ( var key in entries ) {
49
- var uri = `$(REMOTE_WEBAPP_PATH)_framework/${ key } ` ;
43
+ var entries = {
44
+ ...( monoConfigResources . coreAssembly || { } ) ,
45
+ ...( monoConfigResources . assembly || { } ) ,
46
+ ...( monoConfigResources . lazyAssembly || { } ) ,
47
+ ...( monoConfigResources . jsModuleWorker || { } ) ,
48
+ ...( monoConfigResources . jsModuleGlobalization || { } ) ,
49
+ ...( monoConfigResources . jsModuleNative || { } ) ,
50
+ ...( monoConfigResources . jsModuleRuntime || { } ) ,
51
+ ...( monoConfigResources . wasmNative || { } ) ,
52
+ ...( monoConfigResources . icu || { } )
53
+ } ;
50
54
51
- if ( uno_enable_tracing ) {
52
- console . debug ( `[ServiceWorker] cache ${ uri } ` ) ;
53
- }
55
+ for ( var key in entries ) {
56
+ var uri = `$(REMOTE_WEBAPP_PATH)_framework/${ key } ` ;
57
+
58
+ try {
59
+ if ( uno_enable_tracing ) {
60
+ console . debug ( `[ServiceWorker] cache ${ uri } ` ) ;
61
+ }
54
62
55
- await cache . add ( uri ) ;
63
+ await cache . add ( uri ) ;
64
+ } catch ( e ) {
65
+ console . error ( `[ServiceWorker] Failed to cache ${ uri } :` , e . message ) ;
66
+ }
67
+ }
68
+ } catch ( e ) {
69
+ // Centralized error handling for the entire boot.json processing
70
+ console . error ( '[ServiceWorker] Error processing blazor.boot.json:' , e . message ) ;
56
71
}
57
72
} )
58
73
) ;
59
74
} ) ;
60
75
76
+ // Cache cleanup logic to prevent storage bloat
77
+ // This removes any old caches that might have been created by previous
78
+ // versions of the service worker, helping prevent storage quota issues
61
79
self . addEventListener ( 'activate' , event => {
62
- event . waitUntil ( self . clients . claim ( ) ) ;
80
+ event . waitUntil (
81
+ caches . keys ( ) . then ( function ( cacheNames ) {
82
+ return Promise . all (
83
+ cacheNames . filter ( function ( cacheName ) {
84
+ return cacheName !== '$(CACHE_KEY)' ;
85
+ } ) . map ( function ( cacheName ) {
86
+ console . debug ( '[ServiceWorker] Deleting old cache:' , cacheName ) ;
87
+ return caches . delete ( cacheName ) ;
88
+ } )
89
+ ) ;
90
+ } ) . then ( function ( ) {
91
+ return self . clients . claim ( ) ;
92
+ } )
93
+ ) ;
63
94
} ) ;
64
95
65
96
self . addEventListener ( 'fetch' , event => {
66
- event . respondWith ( async function ( ) {
67
- try {
68
- // Network first mode to get fresh content every time, then fallback to
69
- // cache content if needed.
70
- return await fetch ( event . request ) ;
71
- } catch ( err ) {
72
- return caches . match ( event . request ) . then ( response => {
73
- return response || fetch ( event . request ) ;
74
- } ) ;
75
- }
76
- } ( ) ) ;
97
+ event . respondWith (
98
+ ( async function ( ) {
99
+ // FIXED: Critical fix for "already used" Request objects #956
100
+ // Request objects can only be used once in a fetch operation
101
+ // Cloning the request allows for reuse in fallback scenarios
102
+ const requestClone = event . request . clone ( ) ;
103
+
104
+ try {
105
+ // Network first mode to get fresh content every time, then fallback to
106
+ // cache content if needed.
107
+ return await fetch ( requestClone ) ;
108
+ } catch ( err ) {
109
+ // Logging to track network failures
110
+ console . debug ( `[ServiceWorker] Network fetch failed, falling back to cache for: ${ requestClone . url } ` ) ;
111
+
112
+ const cachedResponse = await caches . match ( event . request ) ;
113
+ if ( cachedResponse ) {
114
+ return cachedResponse ;
115
+ }
116
+
117
+ // Graceful error handling with a proper HTTP response
118
+ // Rather than letting the fetch fail with a generic error,
119
+ // we return a controlled 503 Service Unavailable response
120
+ console . error ( `[ServiceWorker] Resource not available in cache or network: ${ requestClone . url } ` ) ;
121
+ return new Response ( 'Network error occurred, and resource was not found in cache.' , {
122
+ status : 503 ,
123
+ statusText : 'Service Unavailable' ,
124
+ headers : new Headers ( {
125
+ 'Content-Type' : 'text/plain'
126
+ } )
127
+ } ) ;
128
+ }
129
+ } ) ( )
130
+ ) ;
77
131
} ) ;
78
132
}
79
133
else {
0 commit comments