Network request/response analysis, performance monitoring, and resource optimization. This covers domains that provide comprehensive insights into network traffic, resource loading, and performance metrics.
Network request/response interception and analysis for monitoring all network activity.
namespace Protocol.Network {
type RequestId = string;
type LoaderId = string;
type MonotonicTime = number;
type TimeSinceEpoch = number;
type InterceptionId = string;
type ErrorReason = ('Failed' | 'Aborted' | 'TimedOut' | 'AccessDenied' | 'ConnectionClosed' | 'ConnectionReset' | 'ConnectionRefused' | 'ConnectionAborted' | 'ConnectionFailed' | 'NameNotResolved' | 'InternetDisconnected' | 'AddressUnreachable' | 'BlockedByClient' | 'BlockedByResponse');
type ResourceType = ('Document' | 'Stylesheet' | 'Image' | 'Media' | 'Font' | 'Script' | 'TextTrack' | 'XHR' | 'Fetch' | 'Prefetch' | 'EventSource' | 'WebSocket' | 'Manifest' | 'SignedExchange' | 'Ping' | 'CSPViolationReport' | 'Preflight' | 'Other');
interface Request {
url: string;
urlFragment?: string;
method: string;
headers: Headers;
postData?: string;
hasPostData?: boolean;
postDataEntries?: PostDataEntry[];
mixedContentType?: MixedContentType;
initialPriority: ResourcePriority;
referrerPolicy: ReferrerPolicy;
isLinkPreload?: boolean;
trustTokenParams?: TrustTokenParams;
isSameOrigin?: boolean;
}
interface Response {
url: string;
status: integer;
statusText: string;
headers: Headers;
headersText?: string;
mimeType: string;
charset?: string;
requestHeaders?: Headers;
requestHeadersText?: string;
connectionReused: boolean;
connectionId: number;
remoteIPAddress?: string;
remotePort?: integer;
fromDiskCache?: boolean;
fromServiceWorker?: boolean;
fromPrefetchCache?: boolean;
encodedDataLength: number;
timing?: ResourceTiming;
serviceWorkerResponseSource?: ServiceWorkerResponseSource;
responseTime?: TimeSinceEpoch;
cacheStorageCacheName?: string;
protocol?: string;
alternateProtocolUsage?: AlternateProtocolUsage;
securityState: Security.SecurityState;
securityDetails?: SecurityDetails;
}
interface ResourceTiming {
requestTime: number;
proxyStart: number;
proxyEnd: number;
dnsStart: number;
dnsEnd: number;
connectStart: number;
connectEnd: number;
sslStart: number;
sslEnd: number;
workerStart: number;
workerReady: number;
workerFetchStart: number;
workerRespondWithSettled: number;
sendStart: number;
sendEnd: number;
pushStart: number;
pushEnd: number;
receiveHeadersStart: number;
receiveHeadersEnd: number;
}
interface RequestWillBeSentEvent {
requestId: RequestId;
loaderId: LoaderId;
documentURL: string;
request: Request;
timestamp: MonotonicTime;
wallTime: TimeSinceEpoch;
initiator: Initiator;
redirectHasExtraInfo?: boolean;
type?: ResourceType;
frameId?: Page.FrameId;
hasUserGesture?: boolean;
}
interface ResponseReceivedEvent {
requestId: RequestId;
loaderId: LoaderId;
timestamp: MonotonicTime;
type: ResourceType;
response: Response;
hasExtraInfo?: boolean;
frameId?: Page.FrameId;
}
interface LoadingFinishedEvent {
requestId: RequestId;
timestamp: MonotonicTime;
encodedDataLength: number;
shouldReportCorbBlocking?: boolean;
}
interface LoadingFailedEvent {
requestId: RequestId;
timestamp: MonotonicTime;
type: ResourceType;
errorText: string;
canceled?: boolean;
blockedReason?: BlockedReason;
corsErrorStatus?: CorsErrorStatus;
}
interface GetResponseBodyRequest {
requestId: RequestId;
}
interface GetResponseBodyResponse {
body: string;
base64Encoded: boolean;
}
interface SetRequestInterceptionRequest {
patterns: RequestPattern[];
}
interface RequestPattern {
urlPattern?: string;
resourceType?: ResourceType;
interceptionStage?: InterceptionStage;
}
interface ContinueInterceptedRequestRequest {
interceptionId: InterceptionId;
errorReason?: ErrorReason;
rawResponse?: string;
url?: string;
method?: string;
postData?: string;
headers?: Headers;
authChallengeResponse?: AuthChallengeResponse;
}
}Usage Example:
import Protocol from "devtools-protocol/types/protocol";
class NetworkMonitor {
private pendingRequests: Map<string, Protocol.Network.Request> = new Map();
private completedRequests: Map<string, NetworkRequestInfo> = new Map();
setupNetworkListeners(): void {
this.onRequestWillBeSent = (event: Protocol.Network.RequestWillBeSentEvent) => {
console.log(`Request: ${event.request.method} ${event.request.url}`);
this.pendingRequests.set(event.requestId, event.request);
};
this.onResponseReceived = (event: Protocol.Network.ResponseReceivedEvent) => {
console.log(`Response: ${event.response.status} ${event.response.url}`);
};
this.onLoadingFinished = (event: Protocol.Network.LoadingFinishedEvent) => {
const request = this.pendingRequests.get(event.requestId);
if (request) {
this.completedRequests.set(event.requestId, {
request,
encodedDataLength: event.encodedDataLength,
timestamp: event.timestamp
});
this.pendingRequests.delete(event.requestId);
}
};
this.onLoadingFailed = (event: Protocol.Network.LoadingFailedEvent) => {
console.error(`Request failed: ${event.errorText} for ${event.requestId}`);
this.pendingRequests.delete(event.requestId);
};
}
async getResponseBody(requestId: string): Promise<{ body: string; base64Encoded: boolean }> {
const request: Protocol.Network.GetResponseBodyRequest = { requestId };
// Implementation would send Network.getResponseBody
return {
body: "Response content",
base64Encoded: false
};
}
async setupRequestInterception(patterns: Protocol.Network.RequestPattern[]): Promise<void> {
const request: Protocol.Network.SetRequestInterceptionRequest = { patterns };
// Implementation would send Network.setRequestInterception
}
analyzeNetworkPerformance(): NetworkAnalysis {
const requests = Array.from(this.completedRequests.values());
return {
totalRequests: requests.length,
totalBytes: requests.reduce((sum, req) => sum + req.encodedDataLength, 0),
averageResponseTime: this.calculateAverageResponseTime(requests),
resourceTypes: this.groupByResourceType(requests)
};
}
private calculateAverageResponseTime(requests: NetworkRequestInfo[]): number {
// Implementation would calculate based on timing data
return 0;
}
private groupByResourceType(requests: NetworkRequestInfo[]): Record<string, number> {
// Implementation would group requests by type
return {};
}
}
interface NetworkRequestInfo {
request: Protocol.Network.Request;
encodedDataLength: number;
timestamp: Protocol.Network.MonotonicTime;
}
interface NetworkAnalysis {
totalRequests: number;
totalBytes: number;
averageResponseTime: number;
resourceTypes: Record<string, number>;
}Performance timeline and metrics collection for analyzing runtime performance.
namespace Protocol.Performance {
interface Metric {
name: string;
value: number;
}
interface GetMetricsResponse {
metrics: Metric[];
}
interface SetTimeDomainRequest {
timeDomain: ('timeTicks' | 'threadTicks');
}
interface EnableRequest {
timeDomain?: ('timeTicks' | 'threadTicks');
}
interface MetricsEvent {
metrics: Metric[];
title: string;
}
}Performance timeline API for detailed performance measurement.
namespace Protocol.PerformanceTimeline {
interface LargestContentfulPaint {
renderTime?: Network.TimeSinceEpoch;
loadTime?: Network.TimeSinceEpoch;
size?: number;
elementId?: string;
url?: string;
nodeId?: DOM.NodeId;
}
interface LayoutShiftAttribution {
previousRect: DOM.Rect;
currentRect: DOM.Rect;
nodeId?: DOM.NodeId;
}
interface LayoutShift {
value: number;
hadRecentInput: boolean;
lastInputTime: Network.TimeSinceEpoch;
sources: LayoutShiftAttribution[];
}
interface TimelineEvent {
frameId: Page.FrameId;
type: string;
name: string;
time: Network.TimeSinceEpoch;
duration?: number;
lcpDetails?: LargestContentfulPaint;
layoutShiftDetails?: LayoutShift;
}
interface EnableRequest {
eventTypes: string[];
}
interface TimelineEventAddedEvent {
event: TimelineEvent;
}
}Memory usage monitoring and analysis for performance optimization.
namespace Protocol.Memory {
interface GetDOMCountersResponse {
documents: integer;
nodes: integer;
jsEventListeners: integer;
}
interface PrepareForLeakDetectionResponse {}
interface GetAllTimeSamplingProfileResponse {
profile: SamplingProfile;
}
interface GetBrowserSamplingProfileResponse {
profile: SamplingProfile;
}
interface GetSamplingProfileResponse {
profile: SamplingProfile;
}
interface SamplingProfile {
samples: SamplingProfileNode[];
modules: Module[];
}
interface SamplingProfileNode {
size: number;
total: number;
stack: string[];
}
interface Module {
name: string;
uuid: string;
baseAddress: string;
size: number;
}
}Performance tracing and timeline recording for detailed analysis.
namespace Protocol.Tracing {
interface TraceConfig {
recordMode?: ('recordUntilFull' | 'recordContinuously' | 'recordAsMuchAsPossible' | 'echoToConsole');
traceBufferSizeInKb?: number;
enableSampling?: boolean;
enableSystrace?: boolean;
enableArgumentFilter?: boolean;
includedCategories?: string[];
excludedCategories?: string[];
syntheticDelays?: string[];
memoryDumpConfig?: MemoryDumpConfig;
}
interface MemoryDumpConfig {
[key: string]: any;
}
interface StartRequest {
categories?: string;
options?: string;
bufferUsageReportingInterval?: number;
transferMode?: ('ReportEvents' | 'ReturnAsStream');
streamFormat?: ('json' | 'proto');
streamCompression?: ('none' | 'gzip');
traceConfig?: TraceConfig;
perfettoConfig?: string;
tracingBackend?: ('auto' | 'chrome' | 'system');
}
interface EndRequest {
stream?: IO.StreamHandle;
}
interface GetCategoriesResponse {
categories: string[];
}
interface RequestMemoryDumpRequest {
deterministic?: boolean;
levelOfDetail?: ('background' | 'light' | 'detailed');
}
interface RequestMemoryDumpResponse {
dumpGuid: string;
success: boolean;
}
interface BufferUsageEvent {
percentFull?: number;
eventCount?: integer;
value?: number;
}
interface DataCollectedEvent {
value: any[];
}
interface TracingCompleteEvent {
dataLossOccurred: boolean;
stream?: IO.StreamHandle;
traceFormat?: ('json' | 'proto');
streamCompression?: ('none' | 'gzip');
}
}Usage Example:
import Protocol from "devtools-protocol/types/protocol";
class PerformanceProfiler {
async startPerformanceMonitoring(): Promise<void> {
// Enable performance domain
const perfRequest: Protocol.Performance.EnableRequest = {
timeDomain: 'timeTicks'
};
// Enable performance timeline
const timelineRequest: Protocol.PerformanceTimeline.EnableRequest = {
eventTypes: ['largest-contentful-paint', 'layout-shift', 'first-input']
};
// Start tracing
const tracingRequest: Protocol.Tracing.StartRequest = {
categories: "devtools.timeline,v8.execute,blink.console,blink.user_timing",
transferMode: 'ReportEvents',
traceConfig: {
recordMode: 'recordUntilFull',
includedCategories: ['devtools.timeline', 'v8.execute']
}
};
// Implementation would send respective enable/start commands
}
async getPerformanceMetrics(): Promise<Protocol.Performance.Metric[]> {
// Implementation would send Performance.getMetrics
return [
{ name: "Timestamp", value: Date.now() },
{ name: "Documents", value: 1 },
{ name: "Nodes", value: 150 },
{ name: "JSEventListeners", value: 25 },
{ name: "LayoutCount", value: 12 },
{ name: "RecalcStyleCount", value: 8 }
];
}
setupPerformanceListeners(): void {
this.onTimelineEvent = (event: Protocol.PerformanceTimeline.TimelineEventAddedEvent) => {
const { event: timelineEvent } = event;
switch (timelineEvent.type) {
case 'largest-contentful-paint':
console.log("LCP event:", timelineEvent.lcpDetails);
break;
case 'layout-shift':
console.log("Layout shift:", timelineEvent.layoutShiftDetails);
break;
default:
console.log("Timeline event:", timelineEvent.name);
}
};
this.onTracingDataCollected = (event: Protocol.Tracing.DataCollectedEvent) => {
// Process tracing data
console.log("Tracing data collected:", event.value.length, "events");
};
this.onTracingComplete = (event: Protocol.Tracing.TracingCompleteEvent) => {
console.log("Tracing complete, data loss:", event.dataLossOccurred);
};
}
async measureUserTimings(): Promise<UserTiming[]> {
// Implementation would collect performance.mark() and performance.measure() data
return [
{ name: "custom-mark-1", startTime: 1000, duration: 0, entryType: "mark" },
{ name: "custom-measure-1", startTime: 1000, duration: 50, entryType: "measure" }
];
}
async analyzeMemoryUsage(): Promise<MemoryAnalysis> {
// Get DOM counters
// Implementation would send Memory.getDOMCounters
const domCounters = {
documents: 1,
nodes: 150,
jsEventListeners: 25
};
// Get sampling profile
// Implementation would send Memory.getSamplingProfile
const samplingProfile: Protocol.Memory.SamplingProfile = {
samples: [],
modules: []
};
return {
domCounters,
samplingProfile,
recommendations: this.generateMemoryRecommendations(domCounters)
};
}
private generateMemoryRecommendations(counters: { documents: number; nodes: number; jsEventListeners: number }): string[] {
const recommendations: string[] = [];
if (counters.nodes > 1000) {
recommendations.push("Consider reducing DOM complexity - high node count detected");
}
if (counters.jsEventListeners > 100) {
recommendations.push("High number of event listeners - consider event delegation");
}
return recommendations;
}
}
interface UserTiming {
name: string;
startTime: number;
duration: number;
entryType: string;
}
interface MemoryAnalysis {
domCounters: { documents: number; nodes: number; jsEventListeners: number };
samplingProfile: Protocol.Memory.SamplingProfile;
recommendations: string[];
}import Protocol from "devtools-protocol/types/protocol";
class NetworkAnalyzer {
private requests: Map<string, NetworkRequestData> = new Map();
analyzeNetworkTraffic(): NetworkInsights {
const allRequests = Array.from(this.requests.values());
return {
totalRequests: allRequests.length,
totalBytes: this.calculateTotalBytes(allRequests),
slowRequests: this.findSlowRequests(allRequests),
failedRequests: this.findFailedRequests(allRequests),
resourceBreakdown: this.analyzeResourceTypes(allRequests),
cacheAnalysis: this.analyzeCacheUsage(allRequests),
securityIssues: this.findSecurityIssues(allRequests)
};
}
private calculateTotalBytes(requests: NetworkRequestData[]): { sent: number; received: number } {
return requests.reduce(
(totals, req) => ({
sent: totals.sent + (req.request.postData?.length || 0),
received: totals.received + (req.response?.encodedDataLength || 0)
}),
{ sent: 0, received: 0 }
);
}
private findSlowRequests(requests: NetworkRequestData[], threshold: number = 1000): NetworkRequestData[] {
return requests.filter(req => {
const timing = req.response?.timing;
if (!timing) return false;
const totalTime = timing.receiveHeadersEnd - timing.requestTime;
return totalTime > threshold;
});
}
private findFailedRequests(requests: NetworkRequestData[]): NetworkRequestData[] {
return requests.filter(req => req.failed || (req.response && req.response.status >= 400));
}
private analyzeResourceTypes(requests: NetworkRequestData[]): Record<Protocol.Network.ResourceType, number> {
const breakdown: Record<string, number> = {};
requests.forEach(req => {
const type = req.resourceType || 'Other';
breakdown[type] = (breakdown[type] || 0) + 1;
});
return breakdown as Record<Protocol.Network.ResourceType, number>;
}
private analyzeCacheUsage(requests: NetworkRequestData[]): CacheAnalysis {
let fromCache = 0;
let fromServiceWorker = 0;
let fromPrefetchCache = 0;
requests.forEach(req => {
const response = req.response;
if (response?.fromDiskCache) fromCache++;
if (response?.fromServiceWorker) fromServiceWorker++;
if (response?.fromPrefetchCache) fromPrefetchCache++;
});
return {
fromCache,
fromServiceWorker,
fromPrefetchCache,
cacheHitRate: fromCache / requests.length
};
}
private findSecurityIssues(requests: NetworkRequestData[]): SecurityIssue[] {
const issues: SecurityIssue[] = [];
requests.forEach(req => {
// Check for mixed content
if (req.request.url.startsWith('http:') && req.documentURL?.startsWith('https:')) {
issues.push({
type: 'mixed-content',
url: req.request.url,
description: 'HTTP resource loaded from HTTPS page'
});
}
// Check for weak security state
if (req.response?.securityState === 'insecure') {
issues.push({
type: 'insecure-connection',
url: req.request.url,
description: 'Insecure connection detected'
});
}
});
return issues;
}
}
interface NetworkRequestData {
requestId: string;
request: Protocol.Network.Request;
response?: Protocol.Network.Response;
resourceType?: Protocol.Network.ResourceType;
failed?: boolean;
errorText?: string;
documentURL?: string;
}
interface NetworkInsights {
totalRequests: number;
totalBytes: { sent: number; received: number };
slowRequests: NetworkRequestData[];
failedRequests: NetworkRequestData[];
resourceBreakdown: Record<Protocol.Network.ResourceType, number>;
cacheAnalysis: CacheAnalysis;
securityIssues: SecurityIssue[];
}
interface CacheAnalysis {
fromCache: number;
fromServiceWorker: number;
fromPrefetchCache: number;
cacheHitRate: number;
}
interface SecurityIssue {
type: string;
url: string;
description: string;
}import Protocol from "devtools-protocol/types/protocol";
class PerformanceDashboard {
private metrics: Protocol.Performance.Metric[] = [];
private timelineEvents: Protocol.PerformanceTimeline.TimelineEvent[] = [];
private tracingData: any[] = [];
async generatePerformanceReport(): Promise<PerformanceReport> {
const coreWebVitals = this.calculateCoreWebVitals();
const resourceMetrics = this.analyzeResourcePerformance();
const runtimeMetrics = this.analyzeRuntimePerformance();
const memoryAnalysis = await this.analyzeMemoryUsage();
return {
coreWebVitals,
resourceMetrics,
runtimeMetrics,
memoryAnalysis,
recommendations: this.generateRecommendations(coreWebVitals, resourceMetrics, runtimeMetrics)
};
}
private calculateCoreWebVitals(): CoreWebVitals {
let lcp: number | null = null;
let fid: number | null = null;
let cls: number = 0;
this.timelineEvents.forEach(event => {
switch (event.type) {
case 'largest-contentful-paint':
if (event.lcpDetails?.renderTime) {
lcp = event.lcpDetails.renderTime;
}
break;
case 'first-input':
if (event.duration) {
fid = event.duration;
}
break;
case 'layout-shift':
if (event.layoutShiftDetails) {
cls += event.layoutShiftDetails.value;
}
break;
}
});
return { lcp, fid, cls };
}
private analyzeResourcePerformance(): ResourceMetrics {
// Analyze resource loading from tracing data
const resourceEvents = this.tracingData.filter(event =>
event.cat === 'loading' || event.cat === 'devtools.timeline'
);
return {
totalLoadTime: this.calculateTotalLoadTime(resourceEvents),
criticalResourceCount: this.countCriticalResources(resourceEvents),
renderBlockingResources: this.findRenderBlockingResources(resourceEvents)
};
}
private analyzeRuntimePerformance(): RuntimeMetrics {
const jsExecutionTime = this.metrics.find(m => m.name === 'ScriptDuration')?.value || 0;
const layoutCount = this.metrics.find(m => m.name === 'LayoutCount')?.value || 0;
const styleRecalcCount = this.metrics.find(m => m.name === 'RecalcStyleCount')?.value || 0;
return {
jsExecutionTime,
layoutCount,
styleRecalcCount,
mainThreadBlockingTime: this.calculateMainThreadBlockingTime()
};
}
private async analyzeMemoryUsage(): Promise<MemoryMetrics> {
// Get memory metrics
const domCounters = {
documents: this.metrics.find(m => m.name === 'Documents')?.value || 0,
nodes: this.metrics.find(m => m.name === 'Nodes')?.value || 0,
jsEventListeners: this.metrics.find(m => m.name === 'JSEventListeners')?.value || 0
};
return {
domCounters,
heapSize: this.metrics.find(m => m.name === 'JSHeapUsedSize')?.value || 0,
memoryLeaks: this.detectPotentialMemoryLeaks(domCounters)
};
}
private generateRecommendations(
vitals: CoreWebVitals,
resources: ResourceMetrics,
runtime: RuntimeMetrics
): string[] {
const recommendations: string[] = [];
if (vitals.lcp && vitals.lcp > 2500) {
recommendations.push("Improve Largest Contentful Paint - optimize critical resources and images");
}
if (vitals.fid && vitals.fid > 100) {
recommendations.push("Reduce First Input Delay - minimize JavaScript execution time");
}
if (vitals.cls > 0.1) {
recommendations.push("Improve Cumulative Layout Shift - reserve space for dynamic content");
}
if (runtime.jsExecutionTime > 1000) {
recommendations.push("Optimize JavaScript execution - consider code splitting and lazy loading");
}
if (resources.renderBlockingResources > 10) {
recommendations.push("Reduce render-blocking resources - minimize CSS and defer non-critical JS");
}
return recommendations;
}
private calculateTotalLoadTime(events: any[]): number {
// Implementation would analyze loading events
return 0;
}
private countCriticalResources(events: any[]): number {
// Implementation would count critical path resources
return 0;
}
private findRenderBlockingResources(events: any[]): number {
// Implementation would identify render-blocking resources
return 0;
}
private calculateMainThreadBlockingTime(): number {
// Implementation would calculate blocking time from tracing data
return 0;
}
private detectPotentialMemoryLeaks(counters: { documents: number; nodes: number; jsEventListeners: number }): string[] {
const leaks: string[] = [];
if (counters.nodes > 10000) {
leaks.push("High DOM node count - potential memory leak");
}
if (counters.jsEventListeners > 1000) {
leaks.push("High event listener count - check for proper cleanup");
}
return leaks;
}
}
interface PerformanceReport {
coreWebVitals: CoreWebVitals;
resourceMetrics: ResourceMetrics;
runtimeMetrics: RuntimeMetrics;
memoryAnalysis: MemoryMetrics;
recommendations: string[];
}
interface CoreWebVitals {
lcp: number | null; // Largest Contentful Paint
fid: number | null; // First Input Delay
cls: number; // Cumulative Layout Shift
}
interface ResourceMetrics {
totalLoadTime: number;
criticalResourceCount: number;
renderBlockingResources: number;
}
interface RuntimeMetrics {
jsExecutionTime: number;
layoutCount: number;
styleRecalcCount: number;
mainThreadBlockingTime: number;
}
interface MemoryMetrics {
domCounters: { documents: number; nodes: number; jsEventListeners: number };
heapSize: number;
memoryLeaks: string[];
}