From 8f13cba3203665a9719e8d54325ff77e5579fece Mon Sep 17 00:00:00 2001 From: Giancarmine Salucci Date: Mon, 22 Dec 2025 05:26:09 +0100 Subject: [PATCH] feat: add plan for migrating to native SvelteKit PWA - Comprehensive plan to migrate from @vite-pwa/sveltekit to native implementation - 5 stories with clear dependencies and acceptance criteria - Preserves all existing PWA, push notification, and share target functionality - Uses SvelteKit's native service worker APIs and manual manifest.json --- docs/plans/MigrateToNativeSvelteKitPWA.md | 684 ++++++++++++++++++++++ 1 file changed, 684 insertions(+) create mode 100644 docs/plans/MigrateToNativeSvelteKitPWA.md diff --git a/docs/plans/MigrateToNativeSvelteKitPWA.md b/docs/plans/MigrateToNativeSvelteKitPWA.md new file mode 100644 index 0000000..787e75f --- /dev/null +++ b/docs/plans/MigrateToNativeSvelteKitPWA.md @@ -0,0 +1,684 @@ +# 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 \ No newline at end of file