UNPKG

next-pwa-pack

Version:

PWA cache provider for Next.js/React apps (service worker, manifest, offline page, SPA cache, offline)

97 lines (96 loc) 3.15 kB
const connections = new Set(); function broadcastEvent(data) { const message = `data: ${JSON.stringify(data)}\n\n`; console.log("[PWA-SSE] Broadcasting:", data); console.log("[PWA-SSE] Active connections:", connections.size); connections.forEach((controller) => { try { controller.enqueue(message); } catch (error) { connections.delete(controller); } }); } export function withPWA(originalMiddleware, config = { webhookPath: "/api/pwa/revalidate", sseEndpoint: "/api/pwa/cache-events", revalidationSecret: process.env.REVALIDATION_SECRET, }) { return function PWAMiddleware(request) { const url = request.nextUrl; if (url.pathname === config?.webhookPath && request.method === "POST") { return handleRevalidation(request, config); } if (url.pathname === config?.sseEndpoint && request.method === "GET") { return handleSSE(request); } return originalMiddleware(request); }; } async function handleRevalidation(request, config) { try { const { tags, secret, urls } = await request.json(); if (secret !== config.revalidationSecret) { return new Response(JSON.stringify({ error: "Unauthorized" }), { status: 401, headers: { "Content-Type": "application/json", }, }); } console.log("[PWA] Revalidation request:", { tags, urls }); const revalidateEvent = { type: "revalidate", tags, urls, timestamp: new Date().toISOString(), }; broadcastEvent(revalidateEvent); return new Response(JSON.stringify({ success: true, message: "Revalidation completed", timestamp: new Date().toISOString(), }), { status: 200, headers: { "Content-Type": "application/json", }, }); } catch (error) { console.error("[PWA] Revalidation error:", error); return new Response(JSON.stringify({ error: "Internal server error" }), { status: 500, headers: { "Content-Type": "application/json", }, }); } } function handleSSE(request) { const stream = new ReadableStream({ start(controller) { connections.add(controller); const connectedEvent = { type: "connected", timestamp: new Date().toISOString(), }; controller.enqueue(`data: ${JSON.stringify(connectedEvent)}\n\n`); request.signal.addEventListener("abort", () => { connections.delete(controller); try { controller.close(); } catch { } }); }, }); return new Response(stream, { headers: { "Content-Type": "text/event-stream", "Cache-Control": "no-cache", Connection: "keep-alive", }, }); }