UNPKG

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
"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