import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; import { VitePWA } from 'vite-plugin-pwa'; export default defineConfig({ plugins: [ react(), VitePWA({ registerType: 'autoUpdate', // ⚠️ CRITICAL (T-03-20): generateSW migrated to injectManifest so the custom // sw.ts can add push + notificationclick handlers while preserving the /callback // denylist. The denylist is re-implemented explicitly in src/sw.ts. strategies: 'injectManifest', srcDir: 'src', filename: 'sw.ts', // rolldownOptions: force the SW output to IIFE so the output file is sw.js // (not sw.mjs). Vite 8 + vite-plugin-pwa 1.3.x defaults to ES module SW format // when the source is .ts, producing sw.mjs. The registerSW.js generated by // the plugin registers '/sw.js', so the filenames must match. rolldownOptions: { output: { format: 'iife', }, }, injectManifest: { // Prevent /callback-related URLs from appearing in the precache manifest. globIgnores: ['**/node_modules/**', '**/callback**'], }, manifest: { name: 'FamilySync', short_name: 'FamilySync', description: 'Family calendar and lists', theme_color: '#e8915a', background_color: '#ffffff', display: 'standalone', scope: '/', start_url: '/', // WR-08: these stable icon filenames are produced by the `pwa:icons` // npm script, which runs the assets generator and then // `scripts/copy-pwa-icons.mjs` to rename the preset outputs to these // names. Keep this list in sync with the COPIES table in that script. icons: [ { src: '/icon-192.png', sizes: '192x192', type: 'image/png' }, { src: '/icon-512.png', sizes: '512x512', type: 'image/png' }, { src: '/icon-maskable-512.png', sizes: '512x512', type: 'image/png', purpose: 'maskable', }, ], }, }), ], server: { // Dev box reached through the Pangolin/newt tunnel: Vite's default host check // 403s any non-localhost Host header ("Blocked request"), which the tunnel // health check reads as unhealthy. `true` accepts any host (fine for a private // throwaway dev tunnel); replace with e.g. ['familysync.example.com'] to scope it. allowedHosts: true, // Listen on all interfaces so newt can reach Vite via the box IP, not just localhost. host: true, proxy: { '/health': 'http://localhost:3000', '/api': 'http://localhost:3000', '/callback': 'http://localhost:3000', }, }, });