CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-devtools-protocol

Complete Chrome DevTools Protocol JSON definitions and TypeScript types for building debugging tools and browser automation

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

network-performance.mddocs/

Network and Performance

Network request/response analysis, performance monitoring, and resource optimization. This covers domains that provide comprehensive insights into network traffic, resource loading, and performance metrics.

Capabilities

Network Domain

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 Domain

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;
  }
}

PerformanceTimeline Domain

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 Domain

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;
  }
}

Tracing Domain

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[];
}

Common Usage Patterns

Network Traffic Analysis

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;
}

Performance Monitoring Dashboard

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[];
}

Install with Tessl CLI

npx tessl i tessl/npm-devtools-protocol

docs

advanced-web-features.md

browser-page-control.md

core-debugging.md

device-testing.md

dom-styling.md

index.md

network-performance.md

storage-data.md

tile.json