circuit-bricks
Version:
A modular, Lego-style SVG circuit component system for React (ALPHA - Not for production use)
258 lines (256 loc) • 6.66 kB
JavaScript
"use client";
import { isBrowser } from "./ssrUtils-Ca211nnK.mjs";
import React, { useCallback, useEffect, useRef, useState } from "react";
import { jsx } from "react/jsx-runtime";
//#region src/utils/performanceUtils.tsx
/**
* Default performance configuration
*/
const defaultConfig = {
enabled: true,
sampleRate: .1,
maxSamples: 100,
logToConsole: false,
trackMemory: true
};
/**
* Performance monitor class
*/
var PerformanceMonitor = class {
config;
samples = [];
observers = [];
constructor(config = {}) {
this.config = {
...defaultConfig,
...config
};
}
/**
* Update configuration
*/
updateConfig(config) {
this.config = {
...this.config,
...config
};
}
/**
* Check if monitoring should occur for this render
*/
shouldMeasure() {
return this.config.enabled && Math.random() < this.config.sampleRate;
}
/**
* Start measuring a render
*/
startMeasure(label = "circuit-render") {
if (!this.shouldMeasure() || !isBrowser()) return () => null;
const startTime = performance.now();
const startMemory = this.getMemoryUsage();
return (componentCount = 0, wireCount = 0) => {
const endTime = performance.now();
const renderTime = endTime - startTime;
const endMemory = this.getMemoryUsage();
const metrics = {
renderTime,
componentCount,
wireCount,
memoryUsage: endMemory - startMemory,
timestamp: Date.now()
};
this.addSample(metrics);
return metrics;
};
}
/**
* Get memory usage (if available)
*/
getMemoryUsage() {
if (!this.config.trackMemory || !isBrowser()) return 0;
const memory = performance.memory;
return memory ? memory.usedJSHeapSize : 0;
}
/**
* Add a performance sample
*/
addSample(metrics) {
this.samples.push(metrics);
if (this.samples.length > this.config.maxSamples) this.samples = this.samples.slice(-this.config.maxSamples);
if (this.config.logToConsole) console.log("Circuit Performance:", metrics);
this.observers.forEach((observer) => observer(metrics));
}
/**
* Subscribe to performance updates
*/
subscribe(observer) {
this.observers.push(observer);
return () => {
const index = this.observers.indexOf(observer);
if (index > -1) this.observers.splice(index, 1);
};
}
/**
* Get performance statistics
*/
getStats() {
if (this.samples.length === 0) return null;
const renderTimes = this.samples.map((s) => s.renderTime);
const componentCounts = this.samples.map((s) => s.componentCount);
const wireCounts = this.samples.map((s) => s.wireCount);
return {
sampleCount: this.samples.length,
renderTime: {
avg: renderTimes.reduce((a, b) => a + b, 0) / renderTimes.length,
min: Math.min(...renderTimes),
max: Math.max(...renderTimes),
p95: this.percentile(renderTimes, .95),
p99: this.percentile(renderTimes, .99)
},
componentCount: {
avg: componentCounts.reduce((a, b) => a + b, 0) / componentCounts.length,
min: Math.min(...componentCounts),
max: Math.max(...componentCounts)
},
wireCount: {
avg: wireCounts.reduce((a, b) => a + b, 0) / wireCounts.length,
min: Math.min(...wireCounts),
max: Math.max(...wireCounts)
}
};
}
/**
* Calculate percentile
*/
percentile(values, p) {
const sorted = [...values].sort((a, b) => a - b);
const index = Math.ceil(sorted.length * p) - 1;
return sorted[index] || 0;
}
/**
* Clear all samples
*/
clearSamples() {
this.samples = [];
}
/**
* Export samples for analysis
*/
exportSamples() {
return [...this.samples];
}
};
const globalMonitor = new PerformanceMonitor();
/**
* Hook for measuring component render performance
*/
const useRenderPerformance = (componentName, componentCount = 0, wireCount = 0) => {
const measureRef = useRef(null);
useEffect(() => {
measureRef.current = globalMonitor.startMeasure(`${componentName}-render`);
return () => {
if (measureRef.current) measureRef.current(componentCount, wireCount);
};
});
};
/**
* Hook for subscribing to performance metrics
*/
const usePerformanceMetrics = () => {
const [metrics, setMetrics] = useState(null);
const [stats, setStats] = useState(globalMonitor.getStats());
useEffect(() => {
const unsubscribe = globalMonitor.subscribe((newMetrics) => {
setMetrics(newMetrics);
setStats(globalMonitor.getStats());
});
return unsubscribe;
}, []);
const clearMetrics = useCallback(() => {
globalMonitor.clearSamples();
setStats(null);
}, []);
const exportMetrics = useCallback(() => {
return globalMonitor.exportSamples();
}, []);
return {
latestMetrics: metrics,
stats,
clearMetrics,
exportMetrics
};
};
/**
* Configure global performance monitoring
*/
const configurePerformanceMonitoring = (config) => {
globalMonitor.updateConfig(config);
};
/**
* Performance-aware component wrapper
*/
const withPerformanceMonitoring = (Component, componentName) => {
return React.memo((props) => {
useRenderPerformance(componentName);
return /* @__PURE__ */ jsx(
Component,
// 0-1, percentage of renders to measure
// Maximum number of samples to keep
// 10% of renders
{ ...props }
);
});
};
/**
* Debounced function utility for performance optimization
*/
const useDebounce = (func, delay) => {
const timeoutRef = useRef(void 0);
return useCallback((...args) => {
clearTimeout(timeoutRef.current);
timeoutRef.current = setTimeout(() => func(...args), delay);
}, [func, delay]);
};
/**
* Throttled function utility for performance optimization
*/
const useThrottle = (func, delay) => {
const lastCallRef = useRef(0);
return useCallback((...args) => {
const now = Date.now();
if (now - lastCallRef.current >= delay) {
lastCallRef.current = now;
return func(...args);
}
}, [func, delay]);
};
/**
* Frame rate monitor
*/
const useFrameRate = () => {
const [fps, setFps] = useState(60);
const frameCountRef = useRef(0);
const lastTimeRef = useRef(performance.now());
useEffect(() => {
if (!isBrowser()) return;
let animationId;
const measureFPS = () => {
frameCountRef.current++;
const currentTime = performance.now();
if (currentTime - lastTimeRef.current >= 1e3) {
setFps(frameCountRef.current);
frameCountRef.current = 0;
lastTimeRef.current = currentTime;
}
animationId = requestAnimationFrame(measureFPS);
};
animationId = requestAnimationFrame(measureFPS);
return () => {
cancelAnimationFrame(animationId);
};
}, []);
return fps;
};
//#endregion
export { configurePerformanceMonitoring, globalMonitor, useDebounce, useFrameRate, usePerformanceMetrics, useRenderPerformance, useThrottle, withPerformanceMonitoring };
//# sourceMappingURL=performanceUtils-TDiXtVeZ.mjs.map