Initial commit
This commit is contained in:
186
skills/website-debug/scripts/browser-network.js
Executable file
186
skills/website-debug/scripts/browser-network.js
Executable file
@@ -0,0 +1,186 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* browser-network.js - Monitor network requests
|
||||
*
|
||||
* Usage:
|
||||
* ./browser-network.js # Show recent requests
|
||||
* ./browser-network.js --watch # Watch requests in real-time
|
||||
* ./browser-network.js --failures # Show only failed requests
|
||||
* ./browser-network.js --xhr # Show only XHR/fetch requests
|
||||
*/
|
||||
|
||||
import puppeteer from "puppeteer-core";
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
const watch = args.includes("--watch");
|
||||
const failures = args.includes("--failures");
|
||||
const xhrOnly = args.includes("--xhr");
|
||||
const port = args.find(a => a.startsWith("--port="))?.split("=")[1] || "9222";
|
||||
|
||||
if (args.includes("--help") || args.includes("-h")) {
|
||||
console.log(`
|
||||
browser-network.js - Network request monitoring
|
||||
|
||||
Usage:
|
||||
./browser-network.js [options]
|
||||
|
||||
Options:
|
||||
--watch Watch requests in real-time
|
||||
--failures Show only failed requests (4xx, 5xx, network errors)
|
||||
--xhr Show only XHR/fetch requests (API calls)
|
||||
--port=PORT Connect to custom debug port (default: 9222)
|
||||
|
||||
Output includes:
|
||||
- Request method and URL
|
||||
- Response status
|
||||
- Response time
|
||||
- Content type
|
||||
- Size (when available)
|
||||
|
||||
Examples:
|
||||
./browser-network.js
|
||||
./browser-network.js --watch
|
||||
./browser-network.js --failures
|
||||
./browser-network.js --xhr --watch
|
||||
`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
try {
|
||||
const browser = await puppeteer.connect({
|
||||
browserURL: `http://localhost:${port}`,
|
||||
defaultViewport: null
|
||||
});
|
||||
|
||||
const pages = await browser.pages();
|
||||
const page = pages[pages.length - 1];
|
||||
|
||||
if (!page) {
|
||||
console.error("✗ No active tab found");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const client = await page.target().createCDPSession();
|
||||
await client.send("Network.enable");
|
||||
|
||||
const requests = new Map();
|
||||
|
||||
const formatSize = (bytes) => {
|
||||
if (!bytes) return "?";
|
||||
if (bytes < 1024) return `${bytes}B`;
|
||||
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
|
||||
return `${(bytes / 1024 / 1024).toFixed(1)}MB`;
|
||||
};
|
||||
|
||||
const shouldShow = (req, resp) => {
|
||||
if (failures) {
|
||||
const status = resp?.status || 0;
|
||||
return status >= 400 || status === 0;
|
||||
}
|
||||
if (xhrOnly) {
|
||||
const type = req.type || req.resourceType;
|
||||
return type === "XHR" || type === "Fetch";
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
const formatRequest = (req, resp, timing) => {
|
||||
const method = req.method || "GET";
|
||||
const url = new URL(req.url);
|
||||
const path = url.pathname + url.search;
|
||||
const status = resp?.status || "pending";
|
||||
const time = timing ? `${Math.round(timing)}ms` : "?";
|
||||
const size = formatSize(resp?.encodedDataLength);
|
||||
const type = resp?.mimeType?.split("/")[1]?.split(";")[0] || "?";
|
||||
|
||||
const statusColor = status >= 400 ? "❌" : status >= 300 ? "↩️" : "✓";
|
||||
|
||||
return `${statusColor} ${method.padEnd(6)} ${status} ${path.slice(0, 60).padEnd(60)} ${time.padStart(7)} ${size.padStart(8)} ${type}`;
|
||||
};
|
||||
|
||||
if (watch) {
|
||||
console.log("Watching network requests... (Ctrl+C to stop)\n");
|
||||
console.log(" Method Status URL".padEnd(80) + "Time".padStart(10) + "Size".padStart(10) + " Type");
|
||||
console.log("-".repeat(110));
|
||||
|
||||
client.on("Network.requestWillBeSent", ({ requestId, request }) => {
|
||||
requests.set(requestId, { request, startTime: Date.now() });
|
||||
});
|
||||
|
||||
client.on("Network.responseReceived", ({ requestId, response }) => {
|
||||
const req = requests.get(requestId);
|
||||
if (req) {
|
||||
req.response = response;
|
||||
}
|
||||
});
|
||||
|
||||
client.on("Network.loadingFinished", ({ requestId, encodedDataLength }) => {
|
||||
const req = requests.get(requestId);
|
||||
if (req) {
|
||||
const timing = Date.now() - req.startTime;
|
||||
if (req.response) {
|
||||
req.response.encodedDataLength = encodedDataLength;
|
||||
}
|
||||
if (shouldShow(req.request, req.response)) {
|
||||
console.log(formatRequest(req.request, req.response, timing));
|
||||
}
|
||||
requests.delete(requestId);
|
||||
}
|
||||
});
|
||||
|
||||
client.on("Network.loadingFailed", ({ requestId, errorText }) => {
|
||||
const req = requests.get(requestId);
|
||||
if (req) {
|
||||
const timing = Date.now() - req.startTime;
|
||||
console.log(`❌ ${req.request.method.padEnd(6)} ERR ${req.request.url.slice(0, 60).padEnd(60)} ${String(timing + "ms").padStart(7)} - ${errorText}`);
|
||||
requests.delete(requestId);
|
||||
}
|
||||
});
|
||||
|
||||
// Keep alive
|
||||
await new Promise(() => {});
|
||||
} else {
|
||||
// Get recent requests via Performance API
|
||||
const requests = await page.evaluate(() => {
|
||||
return performance.getEntriesByType("resource").map(entry => ({
|
||||
name: entry.name,
|
||||
type: entry.initiatorType,
|
||||
duration: Math.round(entry.duration),
|
||||
size: entry.transferSize,
|
||||
status: entry.responseStatus
|
||||
}));
|
||||
});
|
||||
|
||||
if (requests.length === 0) {
|
||||
console.log("No requests captured. Try --watch to monitor in real-time.");
|
||||
} else {
|
||||
console.log("Recent network requests:\n");
|
||||
console.log("Type".padEnd(10) + "Status".padEnd(8) + "Duration".padEnd(10) + "Size".padEnd(10) + "URL");
|
||||
console.log("-".repeat(100));
|
||||
|
||||
requests
|
||||
.filter(r => {
|
||||
if (failures) return r.status >= 400;
|
||||
if (xhrOnly) return r.type === "xmlhttprequest" || r.type === "fetch";
|
||||
return true;
|
||||
})
|
||||
.slice(-30)
|
||||
.forEach(r => {
|
||||
const url = new URL(r.name);
|
||||
const path = url.pathname.slice(0, 50);
|
||||
console.log(
|
||||
`${r.type.slice(0, 9).padEnd(10)}${String(r.status || "?").padEnd(8)}${String(r.duration + "ms").padEnd(10)}${formatSize(r.size).padEnd(10)}${path}`
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
await browser.disconnect();
|
||||
}
|
||||
} catch (e) {
|
||||
if (e.message?.includes("ECONNREFUSED")) {
|
||||
console.error("✗ Cannot connect to browser. Run: ./browser-start.js");
|
||||
} else {
|
||||
console.error(`✗ Network monitoring failed: ${e.message}`);
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
Reference in New Issue
Block a user