Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
cbb10d7c19 | |||
ab4c302cea | |||
0017a559ca | |||
270230b0ca | |||
6cedd53d61 | |||
f518300d68 |
42
changelog.md
42
changelog.md
@ -1,5 +1,47 @@
|
||||
# Changelog
|
||||
|
||||
## 2025-02-04 - 3.0.63 - fix(core)
|
||||
Refactored caching strategy for service worker to improve compatibility and performance.
|
||||
|
||||
- Removed hard and soft caching distinctions.
|
||||
- Simplified cache setup process.
|
||||
- Improved browser caching control headers.
|
||||
|
||||
## 2025-02-04 - 3.0.62 - fix(Service Worker)
|
||||
Refactor and clean up the cache logic in the Service Worker to improve maintainability and handle Safari-specific cache behavior.
|
||||
|
||||
- Refactored logic for determining cached domains, enhancing the readability and maintainability of the code.
|
||||
- Improved handling of CORS settings in caching requests, notably bypassing caching for soft cached domains in Safari to avoid CORS issues.
|
||||
- Enhanced error response creation for failed resource fetching, maintaining clarity on why and how certain resources were not fetched or cached.
|
||||
- Revised the structure of the caching logic to ensure consistent behavior across all supported browsers.
|
||||
|
||||
## 2025-02-04 - 3.0.61 - fix(ServiceWorkerCacheManager)
|
||||
Fixed caching mechanism to better support Safari's handling of soft-cached domains.
|
||||
|
||||
- Added logic to differentiate between hard and soft cached domains.
|
||||
- Implemented special handling for soft cached domains on Safari by bypassing caching.
|
||||
- Ensured appropriate CORS headers are present in cached responses.
|
||||
- Improved error handling with informative 500 error responses.
|
||||
- Optimized caching logic to prevent redundant caching and potential issues with locked streams on Safari.
|
||||
|
||||
## 2025-02-04 - 3.0.61 - fix(ServiceWorkerCacheManager)
|
||||
Fixed caching mechanism to better support Safari's handling of soft-cached domains.
|
||||
|
||||
- Added logic to differentiate between hard and soft cached domains.
|
||||
- Implemented special handling for soft cached domains on Safari by bypassing caching.
|
||||
- Ensured appropriate CORS headers are present in cached responses.
|
||||
- Improved error handling with informative 500 error responses.
|
||||
- Optimized caching logic to prevent redundant caching and potential issues with locked streams on Safari.
|
||||
|
||||
## 2025-02-04 - 3.0.61 - fix(ServiceWorkerCacheManager)
|
||||
Fixed caching mechanism to better support Safari's handling of soft-cached domains.
|
||||
|
||||
- Added logic to differentiate between hard and soft cached domains.
|
||||
- Implemented special handling for soft cached domains on Safari by bypassing caching.
|
||||
- Ensured appropriate CORS headers are present in cached responses.
|
||||
- Improved error handling with informative 500 error responses.
|
||||
- Optimized caching logic to prevent redundant caching and potential issues with locked streams on Safari.
|
||||
|
||||
## 2025-02-04 - 3.0.60 - fix(cachemanager)
|
||||
Improve cache management and error handling
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@api.global/typedserver",
|
||||
"version": "3.0.60",
|
||||
"version": "3.0.63",
|
||||
"description": "A TypeScript-based project for easy serving of static files with support for live reloading, compression, and typed requests.",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
|
@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@api.global/typedserver',
|
||||
version: '3.0.60',
|
||||
version: '3.0.63',
|
||||
description: 'A TypeScript-based project for easy serving of static files with support for live reloading, compression, and typed requests.'
|
||||
}
|
||||
|
@ -16,18 +16,14 @@ export class CacheManager {
|
||||
}
|
||||
|
||||
private _setupCache = () => {
|
||||
const createMatchRequest = (requestArg: Request) => {
|
||||
// Create a matchRequest.
|
||||
const createMatchRequest = (requestArg: Request): Request => {
|
||||
// Create a matchRequest based on whether the request is internal or external.
|
||||
let matchRequest: Request;
|
||||
if (
|
||||
requestArg.url.startsWith(
|
||||
this.losslessServiceWorkerRef.serviceWindowRef.location.origin
|
||||
)
|
||||
) {
|
||||
// internal request
|
||||
if (requestArg.url.startsWith(this.losslessServiceWorkerRef.serviceWindowRef.location.origin)) {
|
||||
// Internal request; use the original.
|
||||
matchRequest = requestArg;
|
||||
} else {
|
||||
// For external requests, create a new Request with CORS settings.
|
||||
// External request; create a new Request with appropriate CORS settings.
|
||||
matchRequest = new Request(requestArg.url, {
|
||||
method: requestArg.method,
|
||||
headers: requestArg.headers,
|
||||
@ -40,9 +36,9 @@ export class CacheManager {
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a 500 response
|
||||
* Creates a 500 error response.
|
||||
*/
|
||||
const create500Response = async (requestArg: Request, responseArg: Response) => {
|
||||
const create500Response = async (requestArg: Request, responseArg: Response): Promise<Response> => {
|
||||
return new Response(
|
||||
`
|
||||
<html>
|
||||
@ -58,16 +54,15 @@ export class CacheManager {
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="note">
|
||||
<strong>serviceworker running, but status 500</strong><br>
|
||||
</div>
|
||||
serviceworker is unable to fetch this request<br>
|
||||
Here is some info about the request/response pair:<br>
|
||||
<br>
|
||||
requestUrl: ${requestArg.url}<br>
|
||||
responseType: ${responseArg.type}<br>
|
||||
responseBody: ${await responseArg.clone().text()}<br>
|
||||
<div class="note">
|
||||
<strong>serviceworker running, but status 500</strong><br>
|
||||
</div>
|
||||
serviceworker is unable to fetch this request<br>
|
||||
Here is some info about the request/response pair:<br>
|
||||
<br>
|
||||
requestUrl: ${requestArg.url}<br>
|
||||
responseType: ${responseArg.type}<br>
|
||||
responseBody: ${await responseArg.clone().text()}<br>
|
||||
</body>
|
||||
</html>
|
||||
`,
|
||||
@ -80,9 +75,9 @@ export class CacheManager {
|
||||
);
|
||||
};
|
||||
|
||||
// A list of local resources we always want to be cached.
|
||||
// Listen for fetch events.
|
||||
this.losslessServiceWorkerRef.serviceWindowRef.addEventListener('fetch', async (fetchEventArg: any) => {
|
||||
// Block scopes we don't want the serviceworker to handle.
|
||||
// Block specific scopes.
|
||||
const originalRequest: Request = fetchEventArg.request;
|
||||
const parsedUrl = new URL(originalRequest.url);
|
||||
if (
|
||||
@ -96,15 +91,15 @@ export class CacheManager {
|
||||
return;
|
||||
}
|
||||
|
||||
// Continue for the rest.
|
||||
// Create a deferred response.
|
||||
const done = plugins.smartpromise.defer<Response>();
|
||||
fetchEventArg.respondWith(done.promise);
|
||||
|
||||
if (
|
||||
(originalRequest.method === 'GET' &&
|
||||
(originalRequest.url.startsWith(this.losslessServiceWorkerRef.serviceWindowRef.location.origin) &&
|
||||
!originalRequest.url.includes('/api/') &&
|
||||
!originalRequest.url.includes('smartserve/reloadcheck'))) ||
|
||||
(originalRequest.url.startsWith(this.losslessServiceWorkerRef.serviceWindowRef.location.origin) &&
|
||||
!originalRequest.url.includes('/api/') &&
|
||||
!originalRequest.url.includes('smartserve/reloadcheck'))) ||
|
||||
originalRequest.url.includes('https://assetbroker.') ||
|
||||
originalRequest.url.includes('https://unpkg.com') ||
|
||||
originalRequest.url.includes('https://fonts.googleapis.com') ||
|
||||
@ -113,7 +108,7 @@ export class CacheManager {
|
||||
// Check for updates asynchronously.
|
||||
this.losslessServiceWorkerRef.updateManager.checkUpdate(this);
|
||||
|
||||
// Handle local or approved remote requests.
|
||||
// Try to serve from cache.
|
||||
const matchRequest = createMatchRequest(originalRequest);
|
||||
const cachedResponse = await caches.match(matchRequest);
|
||||
if (cachedResponse) {
|
||||
@ -122,28 +117,28 @@ export class CacheManager {
|
||||
return;
|
||||
}
|
||||
|
||||
// No cached response found; try to fetch and cache.
|
||||
// In case there is no cached response, fetch from the network.
|
||||
logger.log('info', `NOTYETCACHED: trying to cache ${matchRequest.url}`);
|
||||
const newResponse: Response = await fetch(matchRequest).catch(async err => {
|
||||
return await create500Response(matchRequest, new Response(err.message));
|
||||
});
|
||||
|
||||
// If status > 299 or opaque response, don't cache.
|
||||
// If the response status is an error or the response is opaque, do not cache it.
|
||||
if (newResponse.status > 299 || newResponse.type === 'opaque') {
|
||||
logger.log(
|
||||
'error',
|
||||
`NOTCACHED: can't cache response for ${matchRequest.url} due to status ${
|
||||
newResponse.status
|
||||
} and type ${newResponse.type}`
|
||||
`NOTCACHED: not caching response for ${matchRequest.url} due to status ${newResponse.status} and type ${newResponse.type}`
|
||||
);
|
||||
done.resolve(await create500Response(matchRequest, newResponse));
|
||||
// Simply return the network response without caching.
|
||||
done.resolve(newResponse);
|
||||
return;
|
||||
} else {
|
||||
// Cache the response.
|
||||
const cache = await caches.open(this.usedCacheNames.runtimeCacheName);
|
||||
const responseToPutToCache = newResponse.clone();
|
||||
|
||||
// Create new headers preserving all except caching-related headers.
|
||||
const headers = new Headers();
|
||||
responseToPutToCache.headers.forEach((value, key) => {
|
||||
// Preserve all headers except caching-related ones.
|
||||
if (![
|
||||
'Cache-Control',
|
||||
'cache-control',
|
||||
@ -156,7 +151,7 @@ export class CacheManager {
|
||||
}
|
||||
});
|
||||
|
||||
// Ensure CORS headers are present in cached response.
|
||||
// Ensure CORS headers are present.
|
||||
if (!headers.has('Access-Control-Allow-Origin')) {
|
||||
headers.set('Access-Control-Allow-Origin', '*');
|
||||
}
|
||||
@ -172,20 +167,15 @@ export class CacheManager {
|
||||
headers.set('Expires', '0');
|
||||
headers.set('Surrogate-Control', 'no-store');
|
||||
|
||||
// IMPORTANT: Read the entire response body as a blob so that
|
||||
// Safari does not have issues with a locked stream when caching.
|
||||
const bodyBlob = await responseToPutToCache.blob();
|
||||
const newCachedResponse = new Response(bodyBlob, {
|
||||
status: responseToPutToCache.status,
|
||||
statusText: responseToPutToCache.statusText,
|
||||
await cache.put(matchRequest, new Response(responseToPutToCache.body, {
|
||||
...responseToPutToCache,
|
||||
headers
|
||||
});
|
||||
await cache.put(matchRequest, newCachedResponse);
|
||||
}));
|
||||
logger.log('ok', `NOWCACHED: cached response for ${matchRequest.url} for subsequent requests!`);
|
||||
done.resolve(newResponse);
|
||||
}
|
||||
} else {
|
||||
// For remote requests that don't qualify for caching.
|
||||
// For remote requests not intended for caching, fetch directly from the origin.
|
||||
logger.log(
|
||||
'ok',
|
||||
`NOTCACHED: not caching any responses for ${originalRequest.url}. Fetching from origin now...`
|
||||
@ -201,8 +191,7 @@ export class CacheManager {
|
||||
|
||||
/**
|
||||
* Cleans all caches.
|
||||
* Should only be run when running a new service worker.
|
||||
* @param reasonArg
|
||||
* Should only be run when a new service worker is activated.
|
||||
*/
|
||||
public cleanCaches = async (reasonArg = 'no reason given') => {
|
||||
logger.log('info', `MAJOR CACHEEVENT: cleaning caches now! Reason: ${reasonArg}`);
|
||||
@ -225,9 +214,6 @@ export class CacheManager {
|
||||
const runtimeCache = await caches.open(this.usedCacheNames.runtimeCacheName);
|
||||
const cacheKeys = await runtimeCache.keys();
|
||||
for (const requestArg of cacheKeys) {
|
||||
// Get the cached response.
|
||||
const cachedResponse = runtimeCache.match(requestArg);
|
||||
|
||||
// Fetch a new response for comparison.
|
||||
const clonedRequest = requestArg.clone();
|
||||
const response = await plugins.smartpromise.timeoutWrap(fetch(clonedRequest), 5000); // Increased timeout for better mobile compatibility
|
||||
|
Reference in New Issue
Block a user