CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-electron

Build cross-platform desktop apps with JavaScript, HTML, and CSS

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

development.mddocs/

Development & Debugging

Development tools, debugging utilities, crash reporting, and performance monitoring for building and maintaining Electron applications.

Capabilities

Crash Reporter

Crash reporting system for collecting and submitting crash information.

/**
 * Submit crash reports to a remote server
 */
interface CrashReporter {
  /** Starts crash reporting */
  start(options: CrashReporterStartOptions): void;
  
  /** Returns the last crash report */
  getLastCrashReport(): CrashReport | null;
  
  /** Returns all uploaded crash reports */
  getUploadedReports(): CrashReport[];
  
  /** Returns the date and ID of the last crash report */
  getUploadToServer(): boolean;
  
  /** Sets extra key-value pairs that will be sent along with crash reports */
  addExtraParameter(key: string, value: string): void;
  
  /** Removes an extra parameter that was set with addExtraParameter */
  removeExtraParameter(key: string): void;
  
  /** Returns an object with all extra parameters that will be sent with crash reports */
  getParameters(): Record<string, string>;
}

declare const crashReporter: CrashReporter;

Usage Examples:

const { crashReporter, app } = require('electron');

// Setup crash reporting
function setupCrashReporting() {
  crashReporter.start({
    productName: 'MyElectronApp',
    companyName: 'MyCompany',
    submitURL: 'https://crashes.myapp.com/submit',
    uploadToServer: true,
    ignoreSystemCrashHandler: false,
    rateLimit: true,
    compress: true,
    extra: {
      'app-version': app.getVersion(),
      'platform': process.platform,
      'user-id': getUserId(),
      'session-id': getSessionId()
    }
  });
  
  console.log('Crash reporting initialized');
}

// Add runtime information
function addCrashMetadata() {
  crashReporter.addExtraParameter('memory-usage', process.memoryUsage().heapUsed.toString());
  crashReporter.addExtraParameter('uptime', process.uptime().toString());
  crashReporter.addExtraParameter('node-version', process.version);
  crashReporter.addExtraParameter('electron-version', process.versions.electron);
}

// Get crash reports
function getCrashReports() {
  const lastReport = crashReporter.getLastCrashReport();
  if (lastReport) {
    console.log('Last crash:', lastReport);
  }
  
  const uploadedReports = crashReporter.getUploadedReports();
  console.log('Uploaded crash reports:', uploadedReports.length);
  
  return { lastReport, uploadedReports };
}

// Simulate crash for testing
function simulateCrash() {
  if (process.env.NODE_ENV === 'development') {
    process.crash();
  }
}

Content Tracing

Performance tracing and profiling for analyzing application performance.

/**
 * Collect tracing data from Chromium's content module for finding performance bottlenecks and slow operations
 */
interface ContentTracing {
  /** Get a set of category groups */
  getCategories(): Promise<string[]>;
  
  /** Start recording on all processes */
  startRecording(options: TraceCategoriesAndOptions | TraceConfig): Promise<void>;
  
  /** Stop recording on all processes */
  stopRecording(resultFilePath?: string): Promise<string>;
  
  /** Start monitoring on all processes */
  startMonitoring(options: TraceCategoriesAndOptions): Promise<void>;
  
  /** Stop monitoring on all processes */
  stopMonitoring(): Promise<void>;
  
  /** Capture monitoring snapshot on all processes */
  captureMonitoringSnapshot(resultFilePath?: string): Promise<string>;
  
  /** Get the maximum usage across processes of trace buffer as a percentage of the full state */
  getTraceBufferUsage(): Promise<TraceBufferUsageReturnValue>;
}

declare const contentTracing: ContentTracing;

Usage Examples:

const { contentTracing } = require('electron');
const fs = require('fs');
const path = require('path');

// Performance profiling
async function startPerformanceProfiling() {
  try {
    // Get available categories
    const categories = await contentTracing.getCategories();
    console.log('Available trace categories:', categories);
    
    // Start recording with specific categories
    await contentTracing.startRecording({
      categoryFilter: 'blink,cc,gpu,renderer.scheduler,v8,disabled-by-default-devtools.timeline',
      traceOptions: 'record-continuously,enable-sampling,enable-systrace'
    });
    
    console.log('Performance tracing started');
    
    // Run for 10 seconds then stop
    setTimeout(async () => {
      await stopPerformanceProfiling();
    }, 10000);
    
  } catch (error) {
    console.error('Failed to start tracing:', error);
  }
}

async function stopPerformanceProfiling() {
  try {
    const tracePath = path.join(__dirname, 'traces', `trace-${Date.now()}.json`);
    const resultPath = await contentTracing.stopRecording(tracePath);
    
    console.log('Performance trace saved to:', resultPath);
    
    // Analyze trace file
    analyzePerfTrace(resultPath);
    
  } catch (error) {
    console.error('Failed to stop tracing:', error);
  }
}

// Memory monitoring
async function monitorMemoryUsage() {
  try {
    await contentTracing.startMonitoring({
      categoryFilter: 'blink.console,disabled-by-default-memory-infra',
      traceOptions: 'record-continuously'
    });
    
    console.log('Memory monitoring started');
    
    // Capture snapshots every 5 seconds
    const interval = setInterval(async () => {
      try {
        const snapshotPath = path.join(__dirname, 'memory-snapshots', `memory-${Date.now()}.json`);
        await contentTracing.captureMonitoringSnapshot(snapshotPath);
        console.log('Memory snapshot saved:', snapshotPath);
      } catch (error) {
        console.error('Failed to capture memory snapshot:', error);
      }
    }, 5000);
    
    // Stop after 1 minute
    setTimeout(async () => {
      clearInterval(interval);
      await contentTracing.stopMonitoring();
      console.log('Memory monitoring stopped');
    }, 60000);
    
  } catch (error) {
    console.error('Failed to start memory monitoring:', error);
  }
}

// Check trace buffer usage
async function checkTraceBufferUsage() {
  try {
    const usage = await contentTracing.getTraceBufferUsage();
    console.log('Trace buffer usage:', usage);
    
    if (usage.percentFull > 80) {
      console.warn('Trace buffer is getting full!');
    }
    
    return usage;
  } catch (error) {
    console.error('Failed to get trace buffer usage:', error);
    return null;
  }
}

// Analyze performance trace
function analyzePerfTrace(tracePath) {
  try {
    const traceData = JSON.parse(fs.readFileSync(tracePath, 'utf8'));
    
    // Basic analysis
    const events = traceData.traceEvents || [];
    const totalEvents = events.length;
    const timespan = getTraceTimespan(events);
    
    console.log(`Trace analysis:
      - Total events: ${totalEvents}
      - Timespan: ${timespan}ms
      - Categories: ${getUniqueCategories(events).join(', ')}`);
    
    // Find slow operations
    const slowOperations = findSlowOperations(events);
    if (slowOperations.length > 0) {
      console.log('Slow operations detected:', slowOperations);
    }
    
  } catch (error) {
    console.error('Failed to analyze trace:', error);
  }
}

function getTraceTimespan(events) {
  if (events.length === 0) return 0;
  
  const timestamps = events.map(e => e.ts).filter(ts => ts);
  return (Math.max(...timestamps) - Math.min(...timestamps)) / 1000;
}

function getUniqueCategories(events) {
  const categories = new Set();
  events.forEach(event => {
    if (event.cat) {
      event.cat.split(',').forEach(cat => categories.add(cat.trim()));
    }
  });
  return Array.from(categories);
}

function findSlowOperations(events, threshold = 16) {
  return events
    .filter(event => event.dur && event.dur > threshold * 1000) // Convert to microseconds
    .map(event => ({
      name: event.name,
      category: event.cat,
      duration: event.dur / 1000, // Convert to milliseconds
      timestamp: event.ts
    }))
    .sort((a, b) => b.duration - a.duration)
    .slice(0, 10); // Top 10 slowest operations
}

Net Log

Network logging for debugging network issues.

/**
 * Logging network events for a session
 */
interface NetLog {
  /** Starts recording network events to path */
  startLogging(path: string, options?: NetLogStartLoggingOptions): Promise<void>;
  
  /** Stops recording network events */
  stopLogging(): Promise<void>;
  
  /** Returns whether there is currently a network log being recorded */
  currentlyLogging: boolean;
  
  /** A string property that returns the path to the current log file */
  currentlyLoggingPath: string;
}

Usage Examples:

const { session } = require('electron');
const path = require('path');

// Network debugging
async function startNetworkLogging() {
  const netLog = session.defaultSession.netLog;
  
  if (netLog.currentlyLogging) {
    console.log('Network logging already active');
    return;
  }
  
  try {
    const logPath = path.join(__dirname, 'logs', `network-${Date.now()}.json`);
    
    await netLog.startLogging(logPath, {
      captureMode: 'everything',
      maxFileSize: 100 * 1024 * 1024 // 100MB
    });
    
    console.log('Network logging started:', logPath);
    
    // Stop after 5 minutes
    setTimeout(async () => {
      await stopNetworkLogging();
    }, 5 * 60 * 1000);
    
  } catch (error) {
    console.error('Failed to start network logging:', error);
  }
}

async function stopNetworkLogging() {
  const netLog = session.defaultSession.netLog;
  
  if (!netLog.currentlyLogging) {
    console.log('Network logging not active');
    return;
  }
  
  try {
    await netLog.stopLogging();
    console.log('Network logging stopped');
  } catch (error) {
    console.error('Failed to stop network logging:', error);
  }
}

// Debug specific network issues
function debugNetworkIssues() {
  const { webContents } = require('electron');
  
  // Monitor all webContents for network events
  webContents.getAllWebContents().forEach(contents => {
    contents.on('did-fail-load', (event, errorCode, errorDescription, validatedURL) => {
      console.error('Failed to load:', {
        url: validatedURL,
        error: errorCode,
        description: errorDescription
      });
    });
    
    contents.on('did-fail-provisional-load', (event, errorCode, errorDescription, validatedURL) => {
      console.error('Provisional load failed:', {
        url: validatedURL,
        error: errorCode,
        description: errorDescription
      });
    });
    
    contents.on('certificate-error', (event, url, error, certificate) => {
      console.error('Certificate error:', {
        url: url,
        error: error,
        issuer: certificate.issuer
      });
    });
  });
}

Debugger

Chrome DevTools debugging interface.

/**
 * An alternate transport for Chrome's remote debugging protocol
 */
interface Debugger extends EventEmitter {
  /** Attaches the debugger to the webContents */
  attach(protocolVersion?: string): void;
  
  /** Returns whether a debugger is attached to the webContents */
  isAttached(): boolean;
  
  /** Detaches the debugger from the webContents */
  detach(): void;
  
  /** Send given command to the debugging target */
  sendCommand(method: string, commandParams?: any): Promise<any>;
}

Usage Examples:

const { BrowserWindow } = require('electron');

// Debug a specific window
function setupDebugger(window) {
  const debugger = window.webContents.debugger;
  
  try {
    debugger.attach('1.3');
    console.log('Debugger attached');
  } catch (error) {
    console.error('Failed to attach debugger:', error);
    return;
  }
  
  // Handle debugger events
  debugger.on('detach', (event, reason) => {
    console.log('Debugger detached:', reason);
  });
  
  debugger.on('message', (event, method, params) => {
    if (method === 'Network.responseReceived') {
      console.log('Network response:', params);
    }
  });
  
  // Enable network domain
  debugger.sendCommand('Network.enable').then(() => {
    console.log('Network domain enabled');
  });
  
  // Enable runtime domain
  debugger.sendCommand('Runtime.enable').then(() => {
    console.log('Runtime domain enabled');
  });
  
  return debugger;
}

// CPU profiling
async function profileCPU(window, duration = 10000) {
  const debugger = window.webContents.debugger;
  
  try {
    if (!debugger.isAttached()) {
      debugger.attach('1.3');
    }
    
    // Enable profiler
    await debugger.sendCommand('Profiler.enable');
    
    // Start profiling
    await debugger.sendCommand('Profiler.start');
    console.log('CPU profiling started');
    
    // Stop after specified duration
    setTimeout(async () => {
      try {
        const profile = await debugger.sendCommand('Profiler.stop');
        
        // Save profile
        const fs = require('fs');
        const profilePath = `cpu-profile-${Date.now()}.json`;
        fs.writeFileSync(profilePath, JSON.stringify(profile.profile, null, 2));
        
        console.log('CPU profile saved:', profilePath);
        
      } catch (error) {
        console.error('Failed to stop profiling:', error);
      }
    }, duration);
    
  } catch (error) {
    console.error('Failed to start CPU profiling:', error);
  }
}

// Memory heap snapshot
async function takeHeapSnapshot(window) {
  const debugger = window.webContents.debugger;
  
  try {
    if (!debugger.isAttached()) {
      debugger.attach('1.3');
    }
    
    // Enable heap profiler
    await debugger.sendCommand('HeapProfiler.enable');
    
    // Take snapshot
    await debugger.sendCommand('HeapProfiler.takeHeapSnapshot');
    console.log('Heap snapshot taken');
    
    // Listen for snapshot data
    debugger.on('message', (event, method, params) => {
      if (method === 'HeapProfiler.addHeapSnapshotChunk') {
        // Process snapshot chunk
        console.log('Received heap snapshot chunk');
      }
    });
    
  } catch (error) {
    console.error('Failed to take heap snapshot:', error);
  }
}

Types

interface CrashReporterStartOptions {
  productName: string;
  companyName: string;
  submitURL: string;
  uploadToServer?: boolean;
  ignoreSystemCrashHandler?: boolean;
  rateLimit?: boolean;
  compress?: boolean;
  extra?: Record<string, string>;
  crashesDirectory?: string;
}

interface CrashReport {
  id: string;
  date: Date;
  productName: string;
  productVersion: string;
  process: string;
  reason: string;
  extra: Record<string, string>;
}

interface TraceCategoriesAndOptions {
  categoryFilter: string;
  traceOptions: string;
}

interface TraceConfig {
  recording_mode?: 'record-until-full' | 'record-continuously' | 'record-as-much-as-possible' | 'trace-to-console';
  enable_sampling?: boolean;
  enable_systrace?: boolean;
  enable_argument_filter?: boolean;
  included_categories?: string[];
  excluded_categories?: string[];
  synthetic_delays?: string[];
  memory_dump_config?: Record<string, any>;
}

interface TraceBufferUsageReturnValue {
  value: number;
  percentage: number;
  percentFull: number;
}

interface NetLogStartLoggingOptions {
  captureMode?: 'default' | 'includeCookiesAndCredentials' | 'includeSocketBytes' | 'everything';
  maxFileSize?: number;
}

Install with Tessl CLI

npx tessl i tessl/npm-electron

docs

app-lifecycle.md

development.md

index.md

ipc.md

menu-system.md

network-protocols.md

platform-features.md

security.md

system-integration.md

window-management.md

tile.json