html2canvas-pro
Version:
Screenshots with JavaScript. Next generation!
208 lines • 6.07 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.NoOpPerformanceMonitor = exports.PerformanceMonitor = void 0;
/**
* Performance Monitor
*
* Tracks performance metrics throughout the rendering pipeline.
* Provides insights into where time is spent during rendering.
*
* Usage:
* ```typescript
* const monitor = new PerformanceMonitor(context);
*
* monitor.start('clone');
* await cloneDocument();
* monitor.end('clone');
*
* const summary = monitor.getSummary();
* ```
*/
class PerformanceMonitor {
constructor(context, enabled = true) {
this.context = context;
this.activeMetrics = new Map();
this.completedMetrics = [];
this.enabled = enabled;
// Fallback for environments without performance.now()
this.getTime =
typeof performance !== 'undefined' && typeof performance.now === 'function'
? () => performance.now()
: () => Date.now();
}
/**
* Start measuring a performance metric
*
* @param name - Unique name for this metric
* @param metadata - Optional metadata to attach
*/
start(name, metadata) {
if (!this.enabled) {
return;
}
if (this.activeMetrics.has(name)) {
this.context?.logger.warn(`Performance metric '${name}' already started. Overwriting.`);
}
this.activeMetrics.set(name, {
name,
startTime: this.getTime(),
metadata
});
}
/**
* End measuring a performance metric
*
* @param name - Name of the metric to end
* @returns The completed metric, or undefined if not found
*/
end(name) {
if (!this.enabled) {
return undefined;
}
const metric = this.activeMetrics.get(name);
if (!metric) {
this.context?.logger.warn(`Performance metric '${name}' not found. Was start() called?`);
return undefined;
}
metric.endTime = this.getTime();
metric.duration = metric.endTime - metric.startTime;
this.completedMetrics.push(metric);
this.activeMetrics.delete(name);
this.context?.logger.debug(`⏱️ ${name}: ${metric.duration.toFixed(2)}ms`, metric.metadata);
return metric;
}
/**
* Measure a synchronous function
*
* @param name - Name for this measurement
* @param fn - Function to measure
* @param metadata - Optional metadata
* @returns The function's return value
*/
measure(name, fn, metadata) {
this.start(name, metadata);
try {
const result = fn();
this.end(name);
return result;
}
catch (error) {
this.end(name);
throw error;
}
}
/**
* Measure an asynchronous function
*
* @param name - Name for this measurement
* @param fn - Async function to measure
* @param metadata - Optional metadata
* @returns Promise resolving to the function's return value
*/
async measureAsync(name, fn, metadata) {
this.start(name, metadata);
try {
const result = await fn();
this.end(name);
return result;
}
catch (error) {
this.end(name);
throw error;
}
}
/**
* Get all completed metrics
*
* @returns Array of completed performance metrics
*/
getMetrics() {
return [...this.completedMetrics];
}
/**
* Get a specific metric by name
*
* @param name - Metric name
* @returns The metric, or undefined if not found
*/
getMetric(name) {
return this.completedMetrics.find((m) => m.name === name);
}
/**
* Get performance summary
*
* @returns Aggregated performance data
*/
getSummary() {
const totalDuration = this.completedMetrics.reduce((sum, metric) => sum + (metric.duration || 0), 0);
const breakdown = this.completedMetrics.map((metric) => ({
name: metric.name,
duration: metric.duration || 0,
percentage: totalDuration > 0 ? (((metric.duration || 0) / totalDuration) * 100).toFixed(1) + '%' : '0%'
}));
return {
totalDuration,
metrics: this.getMetrics(),
breakdown
};
}
/**
* Log performance summary to console
*/
logSummary() {
if (!this.enabled || this.completedMetrics.length === 0 || !this.context) {
return;
}
const summary = this.getSummary();
this.context.logger.info(`\n📊 Performance Summary (Total: ${summary.totalDuration.toFixed(2)}ms):`);
summary.breakdown
.sort((a, b) => b.duration - a.duration)
.forEach((item) => {
this.context.logger.info(` ${item.name.padEnd(20)} ${item.duration.toFixed(2).padStart(8)}ms ${item.percentage.padStart(6)}`);
});
}
/**
* Clear all metrics
*/
clear() {
this.activeMetrics.clear();
this.completedMetrics.splice(0);
}
/**
* Check if monitoring is enabled
*/
isEnabled() {
return this.enabled;
}
/**
* Get active (uncompleted) metrics
* Useful for debugging leaked measurements
*/
getActiveMetrics() {
return Array.from(this.activeMetrics.keys());
}
}
exports.PerformanceMonitor = PerformanceMonitor;
/**
* Create a no-op performance monitor for production
* Has minimal overhead when disabled
*/
class NoOpPerformanceMonitor extends PerformanceMonitor {
constructor() {
super(null, false);
}
start() {
// No-op
}
end() {
return undefined;
}
measure(_name, fn) {
return fn();
}
async measureAsync(_name, fn) {
return await fn();
}
}
exports.NoOpPerformanceMonitor = NoOpPerformanceMonitor;
//# sourceMappingURL=performance-monitor.js.map