UNPKG

sysrot-hub

Version:

CLI de nueva generación para proyectos Next.js 14+ con IA multi-modelo, Web3 integration, internacionalización completa y roadmap realista 2025-2026

297 lines (257 loc) 8.4 kB
// Service Worker for SysRot Hub // Version 1.0.5 const CACHE_NAME = 'sysrot-hub-v1'; const STATIC_CACHE_NAME = 'sysrot-hub-static-v1'; const DYNAMIC_CACHE_NAME = 'sysrot-hub-dynamic-v1'; // Cache strategies const CACHE_STRATEGIES = { CACHE_FIRST: 'cache-first', NETWORK_FIRST: 'network-first', STALE_WHILE_REVALIDATE: 'stale-while-revalidate' }; // Assets to cache immediately const STATIC_ASSETS = [ '/', '/ejemplos', '/blog', '/offline', '/manifest.json', '/icons/favicon-32x32.png', '/icons/favicon-16x16.png', '/icons/apple-touch-icon.png', '/_next/static/css/', '/_next/static/js/', ]; // Routes and their cache strategies const ROUTE_CACHE_STRATEGIES = { '/api/': CACHE_STRATEGIES.NETWORK_FIRST, '/_next/static/': CACHE_STRATEGIES.CACHE_FIRST, '/_next/image': CACHE_STRATEGIES.STALE_WHILE_REVALIDATE, '/icons/': CACHE_STRATEGIES.CACHE_FIRST, '/images/': CACHE_STRATEGIES.STALE_WHILE_REVALIDATE, }; // Install event - cache static assets self.addEventListener('install', (event) => { console.log('Service Worker: Installing...'); event.waitUntil( caches.open(STATIC_CACHE_NAME) .then((cache) => { console.log('Service Worker: Caching static assets'); return cache.addAll(STATIC_ASSETS); }) .then(() => { console.log('Service Worker: Static assets cached'); return self.skipWaiting(); }) .catch((error) => { console.error('Service Worker: Error caching static assets', error); }) ); }); // Activate event - clean old caches self.addEventListener('activate', (event) => { console.log('Service Worker: Activating...'); event.waitUntil( caches.keys().then((cacheNames) => { return Promise.all( cacheNames.map((cacheName) => { if (cacheName !== STATIC_CACHE_NAME && cacheName !== DYNAMIC_CACHE_NAME && cacheName !== CACHE_NAME) { console.log('Service Worker: Deleting old cache', cacheName); return caches.delete(cacheName); } }) ); }).then(() => { console.log('Service Worker: Claiming clients'); return self.clients.claim(); }) ); }); // Fetch event - handle requests with caching strategies self.addEventListener('fetch', (event) => { const { request } = event; const url = new URL(request.url); // Skip non-GET requests if (request.method !== 'GET') { return; } // Skip chrome-extension and other non-http requests if (!url.protocol.startsWith('http')) { return; } // Determine cache strategy based on URL const strategy = getCacheStrategy(url.pathname); event.respondWith( handleRequest(request, strategy) .catch(() => { // Fallback to offline page for navigation requests if (request.destination === 'document') { return caches.match('/offline'); } // Return a simple offline response for other requests return new Response('Offline', { status: 503, statusText: 'Service Unavailable' }); }) ); }); // Determine cache strategy for a given path function getCacheStrategy(pathname) { for (const [route, strategy] of Object.entries(ROUTE_CACHE_STRATEGIES)) { if (pathname.startsWith(route)) { return strategy; } } return CACHE_STRATEGIES.STALE_WHILE_REVALIDATE; // Default strategy } // Handle request based on cache strategy async function handleRequest(request, strategy) { switch (strategy) { case CACHE_STRATEGIES.CACHE_FIRST: return cacheFirst(request); case CACHE_STRATEGIES.NETWORK_FIRST: return networkFirst(request); case CACHE_STRATEGIES.STALE_WHILE_REVALIDATE: return staleWhileRevalidate(request); default: return staleWhileRevalidate(request); } } // Cache First Strategy async function cacheFirst(request) { const cachedResponse = await caches.match(request); if (cachedResponse) { return cachedResponse; } const networkResponse = await fetch(request); await addToCache(request, networkResponse.clone()); return networkResponse; } // Network First Strategy async function networkFirst(request) { try { const networkResponse = await fetch(request); await addToCache(request, networkResponse.clone()); return networkResponse; } catch (error) { const cachedResponse = await caches.match(request); if (cachedResponse) { return cachedResponse; } throw error; } } // Stale While Revalidate Strategy async function staleWhileRevalidate(request) { const cachedResponse = await caches.match(request); const networkResponsePromise = fetch(request).then((networkResponse) => { addToCache(request, networkResponse.clone()); return networkResponse; }); return cachedResponse || networkResponsePromise; } // Add response to appropriate cache async function addToCache(request, response) { // Only cache successful responses if (!response || response.status !== 200 || response.type !== 'basic') { return; } const url = new URL(request.url); const cacheName = url.pathname.startsWith('/_next/static/') ? STATIC_CACHE_NAME : DYNAMIC_CACHE_NAME; const cache = await caches.open(cacheName); await cache.put(request, response); } // Background sync for form submissions (if supported) self.addEventListener('sync', (event) => { if (event.tag === 'background-sync') { console.log('Service Worker: Background sync triggered'); event.waitUntil(doBackgroundSync()); } }); async function doBackgroundSync() { // Handle queued form submissions or API calls const pendingRequests = await getPendingRequests(); for (const request of pendingRequests) { try { await fetch(request); await removePendingRequest(request); } catch (error) { console.error('Service Worker: Background sync failed for request', error); } } } // Utility functions for background sync async function getPendingRequests() { // In a real implementation, you'd store these in IndexedDB return []; } async function removePendingRequest(request) { // Remove from IndexedDB storage } // Push notification handling self.addEventListener('push', (event) => { const options = { body: event.data ? event.data.text() : 'New update available!', icon: '/icons/android-chrome-192x192.png', badge: '/icons/android-chrome-192x192.png', vibrate: [100, 50, 100], data: { dateOfArrival: Date.now(), primaryKey: 1 }, actions: [ { action: 'explore', title: 'Explore', icon: '/icons/checkmark.png' }, { action: 'close', title: 'Close', icon: '/icons/xmark.png' } ] }; event.waitUntil( self.registration.showNotification('SysRot Hub', options) ); }); // Notification click handling self.addEventListener('notificationclick', (event) => { event.notification.close(); if (event.action === 'explore') { event.waitUntil( clients.openWindow('/') ); } }); // Share target handling (for PWA share functionality) self.addEventListener('fetch', (event) => { const url = new URL(event.request.url); if (url.pathname === '/share-target' && event.request.method === 'POST') { event.respondWith(handleShareTarget(event.request)); } }); async function handleShareTarget(request) { const formData = await request.formData(); const title = formData.get('title') || ''; const text = formData.get('text') || ''; const url = formData.get('url') || ''; // Store the shared content in IndexedDB or send to server console.log('Shared content:', { title, text, url }); // Redirect to the main app return Response.redirect('/?shared=true', 302); } // Performance monitoring self.addEventListener('message', (event) => { if (event.data && event.data.type === 'PERFORMANCE_MEASURE') { console.log('Performance measure:', event.data.measure); // Send to analytics service } }); console.log('Service Worker: Loaded and ready');