Files
typedserver/ts_web_serviceworker/classes.config.ts

233 lines
6.0 KiB
TypeScript

import * as plugins from './plugins.js';
/**
* Configuration interface for service worker settings
*/
export interface IServiceWorkerConfig {
// Cache settings
cache: {
maxAge: number; // Maximum cache age in milliseconds (default: 24 hours)
offlineGracePeriod: number; // Grace period when offline (default: 7 days)
runtimeCacheName: string; // Name of the runtime cache
};
// Update check settings
update: {
minCheckInterval: number; // Minimum interval between update checks (default: 100 seconds)
debounceTime: number; // Debounce time for update tasks (default: 2000ms)
revalidationDebounce: number; // Debounce time for revalidation (default: 6000ms)
};
// Network settings
network: {
requestTimeout: number; // Default request timeout (default: 5000ms)
maxRetries: number; // Maximum retry attempts (default: 3)
retryDelay: number; // Delay between retries (default: 1000ms)
};
// Blocked domains - requests to these domains bypass the service worker
blockedDomains: string[];
// Blocked paths - requests with these path prefixes bypass the service worker
blockedPaths: string[];
// External cacheable domains - external domains that should be cached
cacheableDomains: string[];
}
/**
* Default configuration values
*/
const DEFAULT_CONFIG: IServiceWorkerConfig = {
cache: {
maxAge: 24 * 60 * 60 * 1000, // 24 hours
offlineGracePeriod: 7 * 24 * 60 * 60 * 1000, // 7 days
runtimeCacheName: 'runtime',
},
update: {
minCheckInterval: 100000, // 100 seconds
debounceTime: 2000,
revalidationDebounce: 6000,
},
network: {
requestTimeout: 5000,
maxRetries: 3,
retryDelay: 1000,
},
blockedDomains: [
'paddle.com',
'paypal.com',
'reception.lossless.one',
'umami.',
],
blockedPaths: [
'/socket.io',
'/api/',
'smartserve/reloadcheck',
],
cacheableDomains: [
'assetbroker.',
'unpkg.com',
'fonts.googleapis.com',
'fonts.gstatic.com',
],
};
/**
* ServiceWorkerConfig manages the configuration for the service worker.
* Configuration is persisted to WebStore and can be updated at runtime.
*/
export class ServiceWorkerConfig {
private static readonly STORE_KEY = 'sw_config';
private config: IServiceWorkerConfig;
private store: plugins.webstore.WebStore;
constructor(store: plugins.webstore.WebStore) {
this.store = store;
this.config = { ...DEFAULT_CONFIG };
}
/**
* Loads configuration from WebStore, falling back to defaults
*/
public async load(): Promise<void> {
try {
if (await this.store.check(ServiceWorkerConfig.STORE_KEY)) {
const storedConfig = await this.store.get(ServiceWorkerConfig.STORE_KEY);
this.config = this.mergeConfig(DEFAULT_CONFIG, storedConfig);
}
} catch (error) {
console.warn('Failed to load service worker config, using defaults:', error);
this.config = { ...DEFAULT_CONFIG };
}
}
/**
* Saves current configuration to WebStore
*/
public async save(): Promise<void> {
await this.store.set(ServiceWorkerConfig.STORE_KEY, this.config);
}
/**
* Gets the current configuration
*/
public get(): IServiceWorkerConfig {
return this.config;
}
/**
* Updates configuration with partial values
*/
public async update(partialConfig: Partial<IServiceWorkerConfig>): Promise<void> {
this.config = this.mergeConfig(this.config, partialConfig);
await this.save();
}
/**
* Resets configuration to defaults
*/
public async reset(): Promise<void> {
this.config = { ...DEFAULT_CONFIG };
await this.save();
}
// Getters for common configuration values
public get cacheMaxAge(): number {
return this.config.cache.maxAge;
}
public get offlineGracePeriod(): number {
return this.config.cache.offlineGracePeriod;
}
public get runtimeCacheName(): string {
return this.config.cache.runtimeCacheName;
}
public get minCheckInterval(): number {
return this.config.update.minCheckInterval;
}
public get updateDebounceTime(): number {
return this.config.update.debounceTime;
}
public get revalidationDebounce(): number {
return this.config.update.revalidationDebounce;
}
public get requestTimeout(): number {
return this.config.network.requestTimeout;
}
public get maxRetries(): number {
return this.config.network.maxRetries;
}
public get retryDelay(): number {
return this.config.network.retryDelay;
}
/**
* Checks if a URL should be blocked from service worker handling
*/
public shouldBlockUrl(url: string): boolean {
try {
const parsedUrl = new URL(url);
// Check blocked domains
for (const domain of this.config.blockedDomains) {
if (parsedUrl.hostname.includes(domain)) {
return true;
}
}
// Check blocked paths
for (const path of this.config.blockedPaths) {
if (parsedUrl.pathname.includes(path)) {
return true;
}
}
// Check if URL starts with blocked domain pattern
if (url.startsWith('https://umami.')) {
return true;
}
return false;
} catch {
return false;
}
}
/**
* Checks if an external URL should be cached
*/
public shouldCacheExternalUrl(url: string): boolean {
for (const domain of this.config.cacheableDomains) {
if (url.includes(domain)) {
return true;
}
}
return false;
}
/**
* Deep merges two configuration objects
*/
private mergeConfig(
base: IServiceWorkerConfig,
override: Partial<IServiceWorkerConfig>
): IServiceWorkerConfig {
return {
cache: { ...base.cache, ...override.cache },
update: { ...base.update, ...override.update },
network: { ...base.network, ...override.network },
blockedDomains: override.blockedDomains ?? base.blockedDomains,
blockedPaths: override.blockedPaths ?? base.blockedPaths,
cacheableDomains: override.cacheableDomains ?? base.cacheableDomains,
};
}
}