mono/packages/ui/src/sw.ts
2026-02-18 22:44:09 +01:00

98 lines
3.2 KiB
TypeScript

/// <reference lib="webworker" />
import { clientsClaim } from 'workbox-core'
import { NetworkFirst } from 'workbox-strategies';
import { cleanupOutdatedCaches, createHandlerBoundToURL, precacheAndRoute } from 'workbox-precaching'
import { registerRoute, NavigationRoute } from 'workbox-routing'
import { set } from 'idb-keyval'
const SW_VERSION = '1.0.5-debug';
console.log(`[SW] Initializing Version: ${SW_VERSION}`);
declare let self: ServiceWorkerGlobalScope
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'SKIP_WAITING') {
self.skipWaiting()
}
})
// self.__WB_MANIFEST is default injection point
precacheAndRoute(self.__WB_MANIFEST)
// clean old assets
cleanupOutdatedCaches()
// allow only fallback in dev: we don't want to cache everything
let allowlist: undefined | RegExp[]
if (location.hostname === 'localhost' || location.hostname.includes('127.0.0.1'))
allowlist = [/^\/$/]
// Handle Share Target POST requests
// MUST be registered before NavigationRoute to take precedence
registerRoute(
({ request, url }) => request.method === 'POST' && url.pathname === '/upload-share-target',
async ({ event, request }) => {
console.log('SW: Intercepting Share Target request!');
try {
const formData = await request.formData()
const files = formData.getAll('file')
const title = formData.get('title')
const text = formData.get('text')
const url = formData.get('url')
console.log('SW: Share data received:', { filesLen: files.length, title, text, url });
// Store in IDB
await set('share-target', { files, title, text, url, timestamp: Date.now() })
console.log('SW: Data stored in IDB');
// Redirect to the app with success status
return Response.redirect('/new?shared=true&sw_status=success', 303)
} catch (err) {
console.error('SW: Share Target Error:', err);
// Safe error string
const errMsg = err instanceof Error ? err.message : String(err);
return Response.redirect('/new?shared=true&sw_status=error&sw_error=' + encodeURIComponent(errMsg), 303);
}
},
'POST'
);
// Navigation handler: Prefer network to get server injection, fallback to index.html
const navigationHandler = async (params: any) => {
try {
const strategy = new NetworkFirst({
cacheName: 'pages',
networkTimeoutSeconds: 3, // Fallback to cache if network is slow
plugins: [
{
cacheWillUpdate: async ({ response }) => {
return response && response.status === 200 ? response : null;
}
}
]
});
return await strategy.handle(params);
} catch (error) {
return createHandlerBoundToURL('index.html')(params);
}
};
// to allow work offline
registerRoute(new NavigationRoute(
navigationHandler,
{
allowlist,
denylist: [/^\/upload-share-target/]
}
))
self.addEventListener('activate', () => {
clientsClaim();
});