import react from '@vitejs/plugin-react' import { execSync } from 'child_process' import path from 'path' import { defineConfig } from 'vitest/config' import { VitePWA } from 'vite-plugin-pwa' import packageJson from './package.json' /// const getGitHash = () => { try { return JSON.stringify(execSync('git rev-parse --short HEAD').toString().trim()) } catch (error) { console.warn('Failed to retrieve commit hash:', error) return '"unknown"' } } const getAppVersion = () => { try { return JSON.stringify(packageJson.version) } catch (error) { console.warn('Failed to retrieve app version:', error) return '"unknown"' } } // https://vite.dev/config/ export default defineConfig({ define: { 'import.meta.env.GIT_COMMIT': getGitHash(), 'import.meta.env.APP_VERSION': getAppVersion() }, resolve: { alias: { '@': path.resolve(__dirname, './src') } }, test: { globals: true, environment: 'jsdom', setupFiles: './src/test/setup.ts' }, plugins: [ react(), VitePWA({ registerType: 'autoUpdate', workbox: { globPatterns: ['**/*.{js,css,html,png,jpg,svg,ico,webmanifest}'], globDirectory: 'dist/', maximumFileSizeToCacheInBytes: 5 * 1024 * 1024, cleanupOutdatedCaches: true, skipWaiting: true, clientsClaim: true, navigateFallback: '/index.html', navigateFallbackDenylist: [/^\/api\//, /^\/_/, /^\/admin/], // Exclude source files and development files from precaching globIgnores: [ '**/src/**', '**/node_modules/**', '**/*.map', '**/sw.js', '**/workbox-*.js' ], runtimeCaching: [ { // Exclude upload endpoints from service worker handling - use NetworkOnly to bypass cache // Match various upload URL patterns - comprehensive regex to catch all upload services // This ensures uploads (POST) and discovery endpoints (GET) bypass the service worker // Note: XMLHttpRequest should bypass service workers, but we add this as a safety measure urlPattern: ({ url, request }) => { const urlString = url.toString() const method = request.method?.toUpperCase() || 'GET' // Always bypass service worker for POST requests (uploads) if (method === 'POST') { return /(?:api\/v2\/nip96\/upload|\.well-known\/nostr\/nip96\.json|nostr\.build|nostrcheck\.me|void\.cat|\/upload|\/nip96\/)/i.test(urlString) } // Also bypass for GET requests to upload-related endpoints return /(?:\.well-known\/nostr\/nip96\.json|api\/v2\/nip96\/upload)/i.test(urlString) }, handler: 'NetworkOnly' }, { urlPattern: /^https:\/\/image\.nostr\.build\/.*/i, handler: 'CacheFirst', options: { cacheName: 'nostr-images', expiration: { maxEntries: 100, maxAgeSeconds: 30 * 24 * 60 * 60 // 30 days } } }, { urlPattern: /^https:\/\/cdn\.satellite\.earth\/.*/i, handler: 'CacheFirst', options: { cacheName: 'satellite-images', expiration: { maxEntries: 100, maxAgeSeconds: 30 * 24 * 60 * 60 // 30 days } } }, { urlPattern: /^https:\/\/.*\.(?:png|jpg|jpeg|svg|gif|webp)$/i, handler: 'CacheFirst', options: { cacheName: 'external-images', expiration: { maxEntries: 200, maxAgeSeconds: 7 * 24 * 60 * 60 // 7 days } } } ] }, devOptions: { enabled: true, type: 'module' }, manifest: { name: 'Jumble', short_name: 'Jumble', icons: [ { src: '/pwa-192x192.png', sizes: '192x192', type: 'image/png', purpose: 'any' }, { src: '/pwa-512x512.png', sizes: '512x512', type: 'image/png', purpose: 'any' }, { src: '/pwa-maskable-192x192.png', sizes: '192x192', type: 'image/png', purpose: 'maskable' }, { src: '/pwa-maskable-512x512.png', sizes: '512x512', type: 'image/png', purpose: 'maskable' } ], start_url: '/', display: 'standalone', background_color: '#FFFFFF', theme_color: '#FFFFFF', description: 'A user-friendly Nostr client focused on relay feed browsing and relay discovery' } }) ] })