Step 1 of 3

Step 2 of 3

Step 3 of 3

Manifest

      
	{
		"name": "My App",
		"short_name": "MyApp",
			"icons": [
				{
					"src": "/images/icons-vector.svg",
					"type": "image/svg+xml",
					"sizes": "512x512"
				},
				{
					"src": "/images/icons-192.png",
					"type": "image/png",
					"sizes": "192x192"
				},
				{
					"src": "/images/icons-512.png",
					"type": "image/png",
					"sizes": "512x512"
				}
			],
		"id": "/?source=pwa",
		"start_url": "/?source=pwa",
		"background_color": "#000000",
		"display": "standalone",
		"scope": "/?source=pwa",
		"theme_color": "#000000",
		"shortcuts": [],
		"description": "My awesome PWA app",
		"screenshots": [
			{
				"src": "/images/screenshot1.png",
				"type": "image/png",
				"sizes": "540x720",
				"form_factor": "narrow"
			},
			{
				"src": "/images/screenshot2.jpg",
				"type": "image/jpg",
				"sizes": "720x540",
				"form_factor": "wide"
			}
		]
	}
      

Service Worker

      
    const CACHE_NAME = 'V1-CACHE';
    const FILES_TO_CACHE = ['/'];

    // Install event
    self.addEventListener('install', (event) => {
      event.waitUntil(
        caches.open(CACHE_NAME).then((cache) => cache.addAll(FILES_TO_CACHE))
      )
    })

    // Fetch event
    self.addEventListener('fetch', (event) => {
      const url = new URL(event.request.url)

      // Define route-based strategies
      // This is temp as an example of route based strategy
      // The user should be able to configure this or we can start 
      // With sensible defaults
      const routes = {
        '/api': 'networkFirst',
        '.css': 'cacheFirst',
        '.js': 'staleWhileRevalidate',
      };

      let strategy = Object.keys(routes).find((route) => {
        if (route.startsWith('.')) {
          // Match by file extension
          return url.pathname.endsWith(route)
        }
        // Match by path
        return url.pathname.startsWith(route)
      });

      // Default to the selected option if no match
      strategy = routes[strategy] || 'cacheFirst'

      // Apply the selected strategy
      if (strategy === 'cacheFirst') {
        event.respondWith(
          caches.match(event.request).then((response) =>
            response || fetch(event.request)
          )
        );
      } else if (strategy === 'networkFirst') {
        event.respondWith(
          fetch(event.request)
            .then((response) => {
              const clone = response.clone();
              caches.open(CACHE_NAME).then((cache) =>
                cache.put(event.request, clone)
              );
              return response;
            })
            .catch(() => caches.match(event.request))
        );
      } else if (strategy === 'staleWhileRevalidate') {
        event.respondWith(
          caches.match(event.request).then((cachedResponse) => {
            const fetchPromise = fetch(event.request).then((response) => {
              const clone = response.clone();
              caches.open(CACHE_NAME).then((cache) =>
                cache.put(event.request, clone)
              );
              return response;
            });
            return cachedResponse || fetchPromise;
          })
        );
      }
    });

    // Activate event
    self.addEventListener('activate', (event) => {
      event.waitUntil(
        caches.keys().then((cacheNames) =>
          Promise.all(
            cacheNames.map((cacheName) => {
              if (cacheName !== CACHE_NAME) {
                return caches.delete(cacheName)
              }
            })
          )
        )
      );
    });

    self.skipWaiting();