# Execution Plan: Migrate to Native SvelteKit PWA **Objective:** Migrate away from @vite-pwa/sveltekit plugin to native SvelteKit PWA implementation with dedicated manifest.json, while preserving all existing functionality including push notifications, share target, and offline capabilities. **Outcome:** `MigrateToNativeSvelteKitPWA` --- ## Current State Analysis ### Current SvelteKitPWA Plugin Implementation **Plugin Configuration (vite.config.ts):** ```typescript SvelteKitPWA({ srcDir: './src', strategies: 'injectManifest', filename: 'service-worker.ts', scope: '/', base: '/', selfDestroying: process.env.SELF_DESTROYING_SW === 'true', injectRegister: process.env.NODE_ENV === 'test' ? false : 'auto', injectManifest: { swSrc: 'src/service-worker.ts', swDest: 'service-worker.js', injectionPoint: 'self.__WB_MANIFEST', globPatterns: ['**/*.{js,css,html,ico,png,svg,webp,woff,woff2}'], maximumFileSizeToCacheInBytes: 4 * 1024 * 1024, }, manifest: { short_name: 'InstaChef', name: 'InstaChef Recipe Saver', start_url: '/', scope: '/', display: 'standalone', theme_color: "#ffffff", background_color: "#ffffff", icons: [ { src: '/favicon.png', sizes: '192x192', type: 'image/png' }, { src: '/favicon.png', sizes: '512x512', type: 'image/png' } ], share_target: { action: '/share', method: 'GET', enctype: 'application/x-www-form-urlencoded', params: { title: 'title', text: 'text', url: 'url' } } }, workbox: { globPatterns: ['client/**/*.{js,css,ico,png,svg,webp,woff,woff2}'], cleanupOutdatedCaches: true, skipWaiting: false, clientsClaim: false, maximumFileSizeToCacheInBytes: 4 * 1024 * 1024, runtimeCaching: [{ urlPattern: /^https:\/\/api\./, handler: 'NetworkFirst', options: { cacheName: 'api-cache', networkTimeoutSeconds: 10 } }] }, devOptions: { enabled: process.env.NODE_ENV !== 'test', suppressWarnings: true, navigateFallback: '/', } }) ``` **Current Service Worker Implementation:** - Uses workbox imports: `cleanupOutdatedCaches`, `createHandlerBoundToURL`, `precacheAndRoute` - Uses workbox routing: `NavigationRoute`, `registerRoute` - Relies on `self.__WB_MANIFEST` injection for precaching - Comprehensive push notification handling with custom actions - Background sync for retry operations - Message passing for client communication **SvelteKit Configuration Status:** ```javascript // svelte.config.js - Currently DISABLED to avoid conflicts serviceWorker: { register: false } ``` ### Critical Functionality That Must Be Preserved **✅ PWA Features:** - PWA installation capability - Offline functionality with precaching - Share target for Instagram URLs to `/share` route - PWA manifest with proper icons and theme colors **✅ Push Notification System:** - Push notification subscription/unsubscription - Custom notification actions (view, retry, dismiss) - Notification click handlers with client navigation - Background sync for retry operations - Service worker to client message passing **✅ Caching Strategy:** - Static asset precaching - Navigation route caching with fallbacks - API cache with network-first strategy - Automatic cache cleanup for updates **✅ Development Experience:** - Service worker disabled in test environment - Proper development vs production behavior - Hot reloading compatibility --- ## Target Architecture Analysis ### Native SvelteKit PWA Approach **Key Differences from Plugin:** 1. **Manifest Management**: Manual `static/manifest.json` instead of vite.config.ts generation 2. **Service Worker APIs**: SvelteKit's `$service-worker` module instead of workbox 3. **Caching Strategy**: Manual cache management using `build`, `files`, `version` arrays 4. **Registration**: SvelteKit's built-in registration instead of plugin registration **SvelteKit Service Worker Module:** ```typescript import { build, files, version } from '$service-worker'; // build: array of built app files // files: array of static files // version: deployment version string for cache naming ``` **Benefits of Migration:** - ✅ Remove external plugin dependency - ✅ Align with SvelteKit best practices and roadmap - ✅ More control over service worker behavior - ✅ Simplified build process - ✅ Better TypeScript integration - ✅ Reduced bundle size without workbox overhead --- ## Cross-Reference Dependency Analysis ### Direct Dependencies 1. **Service Worker Registration**: `src/lib/client/PushNotificationManager.ts` expects service worker to be available 2. **Share Target**: `src/routes/share/+page.svelte` relies on manifest share_target configuration 3. **Queue System**: Background sync functionality for retry operations 4. **Notification Handlers**: Client code sends messages to service worker via `postMessage()` ### Hidden Dependencies 1. **Build Process**: Vite plugin currently handles TypeScript compilation and manifest generation 2. **Development Server**: Plugin provides development mode service worker behavior 3. **Cache Invalidation**: Workbox handles cache versioning automatically 4. **Routing**: NavigationRoute handling for SPA behavior offline ### Integration Points 1. **Push Notification Flow**: Client → Service Worker → Push Server → Notification Display 2. **Share Target Flow**: External App → Manifest → `/share` Route → Queue API 3. **Cache Flow**: Service Worker → Cache API → Static Assets/API Responses 4. **Offline Flow**: Navigation Request → Service Worker → Cache → Response --- ## Story Breakdown ### Story 1: Create Native PWA Manifest **Priority:** High **Dependencies:** None **Estimated Effort:** 2-3 hours **Objective:** Extract PWA manifest configuration from vite.config.ts to a dedicated `static/manifest.json` file following W3C Web App Manifest specification. **Tasks:** 1. Create `static/manifest.json` with exact configuration from vite.config.ts 2. Ensure share_target configuration is preserved exactly 3. Add manifest link to `src/app.html` if not present 4. Validate manifest.json against W3C specification 5. Test manifest loading in browser 6. Verify PWA installation prompt still appears **Technical Details:** **File:** `static/manifest.json` ```json { "short_name": "InstaChef", "name": "InstaChef Recipe Saver", "start_url": "/", "scope": "/", "display": "standalone", "theme_color": "#ffffff", "background_color": "#ffffff", "icons": [ { "src": "/favicon.png", "sizes": "192x192", "type": "image/png" }, { "src": "/favicon.png", "sizes": "512x512", "type": "image/png" } ], "share_target": { "action": "/share", "method": "GET", "enctype": "application/x-www-form-urlencoded", "params": { "title": "title", "text": "text", "url": "url" } } } ``` **File:** `src/app.html` - Add manifest link if missing: ```html ``` **Acceptance Criteria:** - ✅ `static/manifest.json` created with identical configuration - ✅ Share target functionality preserved (test with external app) - ✅ PWA installation prompt appears in supported browsers - ✅ Manifest validation passes in browser DevTools - ✅ All manifest properties display correctly in browser - ✅ PWA installation works with same behavior as before **Testing Strategy:** 1. Validate manifest.json syntax and W3C compliance 2. Test PWA installation in Chrome, Firefox, Safari, Edge 3. Test share target from Instagram mobile app 4. Verify manifest properties in DevTools Application tab 5. Test icon display in app installer and home screen **Files:** - `static/manifest.json` (create) - `src/app.html` (modify if needed) --- ### Story 2: Remove SvelteKitPWA Plugin Dependencies **Priority:** High **Dependencies:** Story 1 **Estimated Effort:** 1-2 hours **Objective:** Remove @vite-pwa/sveltekit plugin and all related configuration from the project. **Tasks:** 1. Remove @vite-pwa/sveltekit from package.json dependencies 2. Remove SvelteKitPWA plugin import and configuration from vite.config.ts 3. Clean up any vite-pwa related development dependencies 4. Remove dev-dist/ generated files if present 5. Update build process documentation if needed **Technical Details:** **File:** `package.json` ```json // Remove: "@vite-pwa/sveltekit": "..." ``` **File:** `vite.config.ts` ```typescript // Remove import: import { SvelteKitPWA } from '@vite-pwa/sveltekit'; // Remove from plugins array: SvelteKitPWA({ /* entire configuration */ }) ``` **Acceptance Criteria:** - ✅ @vite-pwa/sveltekit dependency removed from package.json - ✅ SvelteKitPWA plugin removed from vite.config.ts - ✅ No vite-pwa imports or references remaining - ✅ Project builds successfully without plugin - ✅ No build warnings related to missing plugin - ✅ Development server starts without plugin errors **Testing Strategy:** 1. Run `npm install` to verify dependency removal 2. Run `npm run build` to ensure build process works 3. Run `npm run dev` to ensure development server works 4. Check for any console warnings or errors 5. Verify no plugin-generated files remain **Files:** - `package.json` (modify) - `vite.config.ts` (modify) --- ### Story 3: Migrate Service Worker to SvelteKit Native **Priority:** Critical **Dependencies:** Story 1, Story 2 **Estimated Effort:** 4-6 hours **Objective:** Rewrite service worker to use SvelteKit's native `$service-worker` module instead of workbox, while preserving all existing functionality including push notifications, caching, and background sync. **Tasks:** 1. Replace workbox imports with SvelteKit `$service-worker` imports 2. Implement manual caching using `build`, `files`, `version` arrays 3. Replace workbox precaching with manual cache management 4. Preserve all push notification event handlers exactly 5. Preserve background sync functionality 6. Implement navigation routing without workbox NavigationRoute 7. Add environment detection for development vs production 8. Test all service worker functionality thoroughly **Technical Details:** **File:** `src/service-worker.ts` - New Implementation: ```typescript /// /// /// /// import { build, files, version } from '$service-worker'; declare let self: ServiceWorkerGlobalScope; // Create a unique cache name for this deployment const CACHE = `cache-${version}`; const ASSETS = [ ...build, // the app itself ...files // everything in `static` ]; // Global error handlers (preserve existing) self.addEventListener('error', (event) => { console.error('[SW] Global error:', event.error); }); self.addEventListener('unhandledrejection', (event) => { console.error('[SW] Unhandled promise rejection:', event.reason); event.preventDefault(); }); console.log('[SW] Service worker script loading...'); // Install event - cache all assets self.addEventListener('install', (event) => { console.log('[SW] Installing service worker...'); async function addFilesToCache() { const cache = await caches.open(CACHE); await cache.addAll(ASSETS); console.log(`[SW] Cached ${ASSETS.length} assets`); } event.waitUntil(addFilesToCache()); }); // Activate event - clean up old caches self.addEventListener('activate', (event) => { console.log('[SW] Activating service worker...'); async function deleteOldCaches() { for (const key of await caches.keys()) { if (key !== CACHE) { console.log('[SW] Deleting old cache:', key); await caches.delete(key); } } } event.waitUntil(deleteOldCaches()); }); // Fetch event - serve from cache with network fallback self.addEventListener('fetch', (event) => { // ignore POST requests etc if (event.request.method !== 'GET') return; async function respond() { const url = new URL(event.request.url); const cache = await caches.open(CACHE); // `build`/`files` can always be served from the cache if (ASSETS.includes(url.pathname)) { const response = await cache.match(url.pathname); if (response) { return response; } } // for everything else, try the network first, but // fall back to the cache if we're offline try { const response = await fetch(event.request); // if we're offline, fetch can return a value that is not a Response // instead of throwing - and we can't pass this non-Response to respondWith if (!(response instanceof Response)) { throw new Error('invalid response from fetch'); } if (response.status === 200) { cache.put(event.request, response.clone()); } return response; } catch (err) { const response = await cache.match(event.request); if (response) { return response; } // if there's no cache, then just error out // as there is nothing we can do to respond to this request throw err; } } event.respondWith(respond()); }); // PRESERVE ALL EXISTING PUSH NOTIFICATION FUNCTIONALITY // (Copy exactly from existing service worker - push, notificationclick, notificationclose handlers) // ... [preserve existing push notification code] ... // PRESERVE BACKGROUND SYNC FUNCTIONALITY // (Copy exactly from existing service worker) // ... [preserve existing sync handlers] ... // PRESERVE MESSAGE HANDLING // (Copy exactly from existing service worker) // ... [preserve existing message handlers] ... ``` **Key Migrations:** 1. **Workbox Precaching → Manual Caching**: Replace `precacheAndRoute(self.__WB_MANIFEST)` with manual cache management using `build` and `files` arrays 2. **NavigationRoute → Manual Routing**: Replace workbox NavigationRoute with manual fetch handler logic 3. **Cleanup → Manual Cleanup**: Replace `cleanupOutdatedCaches()` with manual cache key comparison 4. **Manifest Injection → Module Import**: Replace `self.__WB_MANIFEST` with `build`, `files`, `version` imports **Acceptance Criteria:** - ✅ Service worker uses SvelteKit's `$service-worker` module - ✅ Manual caching works for all static assets - ✅ Navigation routing works offline - ✅ All push notification functionality preserved exactly - ✅ Background sync continues to work - ✅ Service worker to client message passing works - ✅ Cache cleanup works on updates - ✅ Development and production modes both work - ✅ No service worker errors in console **Testing Strategy:** 1. Test service worker registration and activation 2. Test offline functionality by going offline in DevTools 3. Test all push notification scenarios (subscribe, receive, click actions) 4. Test background sync with queue retry operations 5. Test cache behavior with network disabled 6. Test service worker updates with version changes 7. Verify message passing between client and service worker **Files:** - `src/service-worker.ts` (major rewrite) --- ### Story 4: Enable SvelteKit Service Worker Registration **Priority:** High **Dependencies:** Story 3 **Estimated Effort:** 1-2 hours **Objective:** Enable SvelteKit's built-in service worker registration and ensure proper coordination without conflicts. **Tasks:** 1. Enable `serviceWorker.register: true` in svelte.config.js 2. Verify service worker registration timing and behavior 3. Ensure no conflicts with development mode 4. Test service worker registration across browsers 5. Verify proper service worker lifecycle events **Technical Details:** **File:** `svelte.config.js` ```javascript import adapter from '@sveltejs/adapter-node'; import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; /** @type {import('@sveltejs/kit').Config} */ const config = { preprocess: vitePreprocess(), kit: { adapter: adapter(), serviceWorker: { register: true, // Enable SvelteKit's service worker registration // Can add additional options if needed } } }; export default config; ``` **Acceptance Criteria:** - ✅ Service worker registers automatically on page load - ✅ Only one service worker registration visible in DevTools - ✅ Service worker lifecycle events work correctly (install, activate) - ✅ No registration conflicts or duplicate registrations - ✅ Works in development and production environments - ✅ Service worker updates properly when code changes **Testing Strategy:** 1. Clear service workers in DevTools and reload page 2. Verify single service worker registration in Application tab 3. Test service worker lifecycle events in console 4. Test across different browsers 5. Test development vs production registration behavior **Files:** - `svelte.config.js` (modify) --- ### Story 5: Comprehensive Testing and Validation **Priority:** Critical **Dependencies:** All previous stories **Estimated Effort:** 3-4 hours **Objective:** Thoroughly test all PWA functionality to ensure no regressions and all features work as expected in the native SvelteKit implementation. **Tasks:** 1. Test PWA installation flow across browsers 2. Test share target functionality from external apps 3. Test all push notification scenarios 4. Test offline functionality and caching 5. Test service worker lifecycle and updates 6. Cross-browser compatibility testing 7. Performance validation vs previous implementation **Testing Scenarios:** **PWA Installation:** - [ ] Installation prompt appears in Chrome - [ ] Installation prompt appears in Edge - [ ] Installation works on Android Chrome - [ ] Installed app opens in standalone mode - [ ] App icons display correctly after installation **Share Target:** - [ ] Share from Instagram mobile app works - [ ] Share target parameters (title, text, url) received correctly - [ ] Share page processes URLs automatically - [ ] Manual URL input still works - [ ] Share target works across different share sources **Push Notifications:** - [ ] Push notification subscription works - [ ] Push notifications display with correct content - [ ] Custom notification actions work (view, retry, dismiss) - [ ] Notification click navigation works correctly - [ ] Background sync triggers on retry action - [ ] Service worker message passing works - [ ] Push notifications work after service worker updates **Offline Functionality:** - [ ] Static assets load when offline - [ ] Navigation works when offline - [ ] Cached API responses serve when offline - [ ] Cache updates when back online - [ ] Cache cleanup works on app updates **Cross-Browser Testing:** - [ ] Chrome (desktop and mobile) - [ ] Firefox (desktop and mobile) - [ ] Safari (desktop and mobile) - [ ] Edge (desktop) - [ ] Samsung Internet (mobile) **Performance Validation:** - [ ] Service worker registration time comparable - [ ] Cache loading performance maintained - [ ] Bundle size reduced (no workbox) - [ ] PWA audit scores maintained or improved **Acceptance Criteria:** - ✅ All existing functionality works exactly as before - ✅ No regressions in any PWA features - ✅ Cross-browser compatibility maintained - ✅ Performance meets or exceeds previous implementation - ✅ All tests pass in continuous integration - ✅ PWA audit scores are maintained or improved **Files:** - Test documentation updates if needed --- ## Risk Assessment ### High Risk Areas 1. **Push Notification Functionality**: Complex service worker message handling could break 2. **Share Target Integration**: External app sharing could fail with manifest changes 3. **Cache Strategy**: Manual cache management could introduce bugs vs workbox 4. **Service Worker Registration**: Timing issues could cause registration failures ### Medium Risk Areas 1. **Offline Functionality**: Different caching approach might affect offline behavior 2. **Build Process**: Removing plugin changes build pipeline significantly 3. **Development Experience**: Service worker behavior could change in development ### Low Risk Areas 1. **Manifest Properties**: Direct translation should work reliably 2. **Static Asset Serving**: SvelteKit handles this well natively 3. **PWA Installation**: Standard manifest.json is well supported ### Mitigation Strategies 1. **Comprehensive Testing**: Extensive testing across all scenarios and browsers 2. **Incremental Migration**: Complete each story fully before moving to next 3. **Rollback Plan**: Keep plugin configuration in version control for quick rollback 4. **Staging Environment**: Test fully in staging before production deployment --- ## Implementation Roadmap ### Phase 1: Foundation (Stories 1-2) - 3-5 hours - Create native manifest.json - Remove plugin dependencies - Establish baseline for migration ### Phase 2: Service Worker Migration (Story 3) - 4-6 hours - Rewrite service worker with SvelteKit APIs - Preserve all existing functionality - Most complex and critical phase ### Phase 3: Registration & Testing (Stories 4-5) - 4-6 hours - Enable SvelteKit registration - Comprehensive testing across all scenarios - Performance and compatibility validation **Total Estimated Effort:** 11-17 hours --- ## Success Criteria ### Functional Requirements - ✅ All existing PWA functionality works identically - ✅ Push notifications work across all scenarios - ✅ Share target works from external apps - ✅ Offline functionality maintained - ✅ PWA installation works across browsers ### Technical Requirements - ✅ No external PWA plugin dependencies - ✅ Uses SvelteKit native service worker APIs - ✅ Manual manifest.json in static/ directory - ✅ Service worker registration through SvelteKit - ✅ Maintains or improves performance ### Quality Requirements - ✅ No regressions in existing functionality - ✅ Cross-browser compatibility maintained - ✅ PWA audit scores maintained or improved - ✅ Development experience not degraded - ✅ Build process simplified --- ## Definition of Done - [ ] All user stories completed with acceptance criteria met - [ ] Comprehensive testing completed across all browsers - [ ] No regressions in existing functionality - [ ] Performance validated against baseline - [ ] Documentation updated as needed - [ ] Code review completed - [ ] Staging deployment tested successfully - [ ] Ready for production deployment