9 directories, 58 files

tinai

Home / tinai
const CACHE_NAME = 'tinai-v2';

self.addEventListener('install', () => {
	self.skipWaiting();
});

self.addEventListener('activate', (event) => {
	event.waitUntil((async () => {
		const keys = await caches.keys();
		await Promise.all(keys.filter((key) => key !== CACHE_NAME).map((key) => caches.delete(key)));
		await self.clients.claim();
	})());
});

self.addEventListener('fetch', (event) => {
	const request = event.request;

	// API endpoints are never intercepted for any method: they carry live
	// streaming responses (NDJSON) and per-user auth, so caching or proxying
	// them through the worker would only add latency.
	const url = new URL(request.url);
	if (url.pathname.endsWith('api.php') || url.pathname.includes('/api/')) {
		return;
	}

	if (request.method !== 'GET') return;
	if (url.origin !== self.location.origin) return;

	// Cache-bust app assets: a fresh query per fetch keeps code updates
	// applying without hard reloads (nginx serves the file for any query).
	let effective = request;
	if (/\.(js|css)$/.test(url.pathname) && !url.search) {
		const busted = new URL(request.url);
		busted.searchParams.set('v', Date.now());
		effective = new Request(busted, request);
	}

	event.respondWith((async () => {
		try {
			// 'no-cache' revalidates with the server; the request's default mode
			// would serve heuristic-fresh stale copies straight from the HTTP cache.
			const response = await fetch(effective, { cache: 'no-cache' });
			if (response.ok) {
				const cache = await caches.open(CACHE_NAME);
				await cache.put(request, response.clone());
			}
			return response;
		} catch (error) {
			const cached = await caches.match(request);
			if (cached) return cached;
			throw error;
		}
	})());
});
🌐
sw.js ×
Type: Web, text/plain
1.69 Kilobytes
Last Modified 2026-11-04 09:48:28
⬇ Download File