worker/dyad-sw.js
Rama main · réplica de lectura del repositorio original.
/**
* dyad-sw.js – Service Worker for network request interception
* Intercepts all fetch requests and reports them to the client
*/
// Service Worker installation
self.addEventListener("install", (_event) => {
console.log("[Dyad SW] Installing...");
// Skip waiting to activate immediately
self.skipWaiting();
});
// Service Worker activation
self.addEventListener("activate", (event) => {
console.log("[Dyad SW] Activating...");
// Claim all clients immediately
event.waitUntil(self.clients.claim());
});
// Intercept all fetch requests
self.addEventListener("fetch", (event) => {
const request = event.request;
// ---- Guardrails: avoid breaking things we shouldn't touch ----
// Skip navigations (HTML document loads) to reduce dev-time weirdness.
if (request.mode === "navigate") return;
// Re-fetching script/worker requests from a service worker can change
// browser metadata like Sec-Fetch-Dest and break Nitro+Vite dev module
// serving (the dev server returns the wrong MIME type for an unexpected
// destination). Other destinations (`style`, `image`, `font`, etc.) are
// intentionally NOT filtered out — they don't trigger the same Vite/Nitro
// dev-server quirk, and the network panel relies on these events to
// surface CSS/image/font loads. If a future framework hits a similar
// MIME-type issue with another destination, narrow the filter here rather
// than dropping all observability for that resource type.
if (request.destination === "script" || request.destination === "worker")
return;
// Only handle http(s)
let urlObj;
try {
urlObj = new URL(request.url);
} catch {
return;
}
if (urlObj.protocol !== "http:" && urlObj.protocol !== "https:") return;
// Chrome SW footgun: only-if-cached must be same-origin or it throws.
if (request.cache === "only-if-cached" && request.mode !== "same-origin")
return;
// Skip noisy Vite and Next.js development module requests
const pathname = urlObj.pathname;
if (
// Vite
pathname.includes("/node_modules") || // Vite deps
pathname.includes("/@vite/") || // Vite client/HMR
pathname.includes("/__vite_ping") || // Vite ping
// Next.js
pathname.includes("/_next/static/") || // Static assets (chunks, CSS, media)
pathname.includes("/_next/webpack-hmr") || // Next.js HMR
pathname.includes("/__nextjs_original-stack-frame") || // Error overlay internals
pathname.includes("/__webpack_hmr") || // Webpack HMR
pathname.includes(".hot-update.") // HMR update chunks
) {
return;
}
const startTime = Date.now();
const url = request.url;
const method = request.method;
// Helper to send message to the initiating client or broadcast as fallback
const postMessage = (message) => {
const sendMessage = async () => {
// Prefer sending to the initiating client
if (event.clientId) {
const client = await self.clients.get(event.clientId);
if (client) {
client.postMessage(message);
return;
}
}
// Fallback: broadcast to all clients within SW scope
const clients = await self.clients.matchAll({
type: "window",
includeUncontrolled: true,
});
for (const client of clients) {
client.postMessage(message);
}
};
// Wrap with event.waitUntil to ensure completion
event.waitUntil(sendMessage());
};
// Send initial request info
postMessage({
type: "network-request",
method,
url,
requestType: "fetch",
timestamp: new Date().toISOString(),
});
// Pass through the request and monitor the response
event.respondWith(
fetch(event.request)
.then((response) => {
const duration = Date.now() - startTime;
// Send response info
postMessage({
type: "network-response",
method,
url,
status: response.status,
statusText: response.statusText,
duration,
requestType: "fetch",
timestamp: new Date().toISOString(),
});
// Return the response unchanged
return response;
})
.catch((error) => {
const duration = Date.now() - startTime;
// Send error info
postMessage({
type: "network-error",
method,
url,
status: 0,
error: error.message,
duration,
requestType: "fetch",
timestamp: new Date().toISOString(),
});
// Re-throw the error
throw error;
}),
);
});