perf-observer-kit
Version:
A lightweight, flexible library for monitoring web performance metrics including Core Web Vitals, resource loading performance, long tasks, and navigation timing.
1 lines • 61.9 kB
JavaScript
(function (global,factory){typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports):typeof define === 'function' && define.amd ? define(['exports'],factory):(global = typeof globalThis !== 'undefined' ? globalThis:global || self,factory(global.PerfObserverKit ={}));})(this,(function (exports){'use strict';exports.MetricType = void 0;(function (MetricType){MetricType["WEB_VITALS"] = "coreWebVitals";MetricType["RESOURCES"] = "resources";MetricType["LONG_TASKS"] = "longTasks";MetricType["NAVIGATION"] = "navigation";MetricType["BROWSER_INFO"] = "browserInfo";})(exports.MetricType || (exports.MetricType ={}));const browserSupport ={hasPerformanceAPI(){return typeof window !== 'undefined' && typeof performance !== 'undefined';},hasPerformanceObserver(){return typeof window !== 'undefined' && typeof PerformanceObserver !== 'undefined';},supportsEntryType(entryType){if (!this.hasPerformanceObserver()){return false;}if (PerformanceObserver.supportedEntryTypes){return PerformanceObserver.supportedEntryTypes.includes(entryType);}switch (entryType){case 'navigation':return typeof PerformanceNavigationTiming !== 'undefined';case 'resource':return typeof PerformanceResourceTiming !== 'undefined';case 'largest-contentful-paint':case 'layout-shift':case 'first-input':case 'longtask':try{const observer = new PerformanceObserver(() =>{});observer.observe({type:entryType,buffered:true});observer.disconnect();return true;}catch (e){return false;}default:return false;}}};exports.LogLevel = void 0;(function (LogLevel){LogLevel[LogLevel["NONE"] = 0] = "NONE";LogLevel[LogLevel["ERROR"] = 1] = "ERROR";LogLevel[LogLevel["WARN"] = 2] = "WARN";LogLevel[LogLevel["INFO"] = 3] = "INFO";LogLevel[LogLevel["DEBUG"] = 4] = "DEBUG";})(exports.LogLevel || (exports.LogLevel ={}));const IS_DEV = typeof process === 'undefined' || !process.env || "production" !== 'production';class Logger{constructor(options ={}){var _a,_b,_c;this.level = (_a = options.level) !== null && _a !== void 0 ? _a:exports.LogLevel.INFO;this.prefix = (_b = options.prefix) !== null && _b !== void 0 ? _b:'[PerfObserverKit]';this.disableInProduction = (_c = options.disableInProduction) !== null && _c !== void 0 ? _c:false;}setLevel(level){this.level = level;}setOptions(options){if (options.level !== undefined){this.level = options.level;}if (options.prefix !== undefined){this.prefix = options.prefix;}if (options.disableInProduction !== undefined){this.disableInProduction = options.disableInProduction;}}debug(...args){if (this.shouldLog(exports.LogLevel.DEBUG));}info(...args){if (this.shouldLog(exports.LogLevel.INFO));}warn(...args){if (this.shouldLog(exports.LogLevel.WARN)){console.warn(this.prefix,...args);}}error(...args){if (this.shouldLog(exports.LogLevel.ERROR)){console.error(this.prefix,...args);}}shouldLog(messageLevel){if (!IS_DEV && this.disableInProduction){return false;}return messageLevel <= this.level;}getConfiguration(){return{level:this.level,levelName:exports.LogLevel[this.level],disableInProduction:this.disableInProduction,isProduction:!IS_DEV};}}const logger = new Logger();class NetworkMetricsCollector{static getNetworkInformation(){if (typeof navigator !== 'undefined' && 'connection' in navigator){const connection = navigator.connection;if (connection){return{downlink:connection.downlink,effectiveType:connection.effectiveType,rtt:connection.rtt,saveData:connection.saveData};}}return undefined;}static getNetworkContext(extraContext ={}){const networkInfo = this.getNetworkInformation();return{networkInfo:networkInfo ?{downlink:networkInfo.downlink,effectiveType:networkInfo.effectiveType,rtt:networkInfo.rtt,saveData:networkInfo.saveData}:undefined,url:typeof window !== 'undefined' ? window.location.href:undefined,visibilityState:typeof document !== 'undefined' ? document.visibilityState:undefined,...extraContext};}static rateNetworkQuality(networkInfo){if (!networkInfo){return 'needs-improvement';}if (networkInfo.effectiveType === '4g' && networkInfo.downlink && networkInfo.downlink >= 5){return 'good';}else if (networkInfo.effectiveType === '4g' || networkInfo.effectiveType === '3g'){return 'needs-improvement';}else{return 'poor';}}}function calculateTime(end,start){return (typeof end === 'number' && typeof start === 'number') ? Math.max(end - start,0):0;}function calculateTimeDelta(end,start){const delta = end - start;return delta > 0 ? delta:0;}class BaseObserver{constructor(options){this.observer = null;this.isPageVisible = true;this.userHasInteracted = false;this.visibilityChangeHandler = null;this.userInteractionHandler = null;this.pageshowHandler = null;this.onUpdate = options.onUpdate;}start(){this.setupVisibilityTracking();this.setupUserInteractionTracking();this.setupPageshowListener();this.observe();}stop(){if (this.observer){this.observer.disconnect();this.observer = null;}this.cleanupEventListeners();}cleanupEventListeners(){if (this.visibilityChangeHandler && typeof document !== 'undefined'){document.removeEventListener('visibilitychange',this.visibilityChangeHandler);this.visibilityChangeHandler = null;}if (this.userInteractionHandler && typeof document !== 'undefined'){document.removeEventListener('click',this.userInteractionHandler);document.removeEventListener('keydown',this.userInteractionHandler);this.userInteractionHandler = null;}if (this.pageshowHandler && typeof window !== 'undefined'){window.removeEventListener('pageshow',this.pageshowHandler);this.pageshowHandler = null;}}setupVisibilityTracking(){if (typeof document === 'undefined'){return;}this.isPageVisible = document.visibilityState === 'visible';this.visibilityChangeHandler = (event) =>{this.isPageVisible = document.visibilityState === 'visible';logger.debug('页面可见性变化:',this.isPageVisible ? '可见':'隐藏');this.onVisibilityChange(this.isPageVisible);};document.addEventListener('visibilitychange',this.visibilityChangeHandler);}setupUserInteractionTracking(){if (typeof document === 'undefined'){return;}this.userInteractionHandler = (event) =>{if (this.userHasInteracted){return;}this.userHasInteracted = true;logger.debug('用户已交互');};document.addEventListener('click',this.userInteractionHandler);document.addEventListener('keydown',this.userInteractionHandler);}setupPageshowListener(){if (typeof window === 'undefined'){return;}this.pageshowHandler = (event) =>{if (event.persisted){logger.info('页面从bfcache恢复');this.userHasInteracted = false;this.onBFCacheRestore(event);}};window.addEventListener('pageshow',this.pageshowHandler);}getNetworkInformation(){return NetworkMetricsCollector.getNetworkInformation();}getNetworkContext(extraContext ={}){const currentUrl = typeof window !== 'undefined' ? window.location.href:undefined;return NetworkMetricsCollector.getNetworkContext({...extraContext,userHasInteracted:this.userHasInteracted,url:currentUrl});}calculateTimeDelta(end,start){return calculateTimeDelta(end,start);}onVisibilityChange(isVisible){}onBFCacheRestore(event){}}class FCPObserver extends BaseObserver{constructor(options){super(options);this.fcpObserver = null;this.metricReported = false;this.firstHiddenTime = this.initFirstHiddenTime();this.setupFirstHiddenTimeListener();}initFirstHiddenTime(){if (typeof document !== 'undefined' && document.visibilityState === 'hidden'){return 0;}return Infinity;}setupFirstHiddenTimeListener(){if (typeof document === 'undefined') return;const updateHiddenTime = () =>{if (document.visibilityState === 'hidden' && this.firstHiddenTime === Infinity){this.firstHiddenTime = performance.now();logger.debug(`记录页面首次隐藏时间:${this.firstHiddenTime}ms`);}};document.addEventListener('visibilitychange',updateHiddenTime,{once:true});document.addEventListener('unload',updateHiddenTime,{once:true});}getActivationStart(){if (typeof performance === 'undefined') return 0;const entries = performance.getEntriesByType('navigation');if (!entries || entries.length === 0) return 0;const navigationEntry = entries[0];const activationStart = navigationEntry === null || navigationEntry === void 0 ? void 0:navigationEntry.activationStart;return activationStart ? activationStart:0;}assignFCPRating(value){if (value <= FCPObserver.FCP_GOOD_THRESHOLD){return 'good';}else if (value <= FCPObserver.FCP_NEEDS_IMPROVEMENT_THRESHOLD){return 'needs-improvement';}else{return 'poor';}}observe(){if (typeof PerformanceObserver === 'undefined'){logger.error('PerformanceObserver API不可用,无法监控FCP');return;}try{this.fcpObserver = new PerformanceObserver((entryList) =>{const entries = entryList.getEntries();for (const entry of entries){if (entry.name === 'first-contentful-paint'){if (entry.startTime < this.firstHiddenTime){const fcpValue = Math.max(entry.startTime - this.getActivationStart(),0);const fcp ={name:'FCP',value:fcpValue,unit:'ms',timestamp:new Date().getTime(),url:typeof window !== 'undefined' ? window.location.href:undefined,networkMetrics:this.getNetworkInformation()};fcp.rating = this.assignFCPRating(fcp.value);this.onUpdate(fcp);this.metricReported = true;}else{logger.debug('页面在FCP前已隐藏,忽略此FCP事件');}if (this.fcpObserver){this.fcpObserver.disconnect();this.fcpObserver = null;}break;}}});this.fcpObserver.observe({type:'paint',buffered:true});}catch (error){logger.error('FCP监控不受支持',error);}}stop(){if (this.fcpObserver){this.fcpObserver.disconnect();this.fcpObserver = null;}super.stop();}onVisibilityChange(isVisible){if (!isVisible && this.firstHiddenTime === Infinity){this.firstHiddenTime = performance.now();logger.debug(`页面隐藏,更新firstHiddenTime:${this.firstHiddenTime}ms`);}}onBFCacheRestore(event){this.metricReported = false;this.firstHiddenTime = this.initFirstHiddenTime();this.setupFirstHiddenTimeListener();const restoreTime = event.timeStamp;const doubleRAF = (callback) =>{requestAnimationFrame(() =>{requestAnimationFrame(() =>{callback();});});};doubleRAF(() =>{const currentTime = performance.now();const timeFromRestore = currentTime - restoreTime;const fcp ={name:'FCP',value:timeFromRestore,unit:'ms',timestamp:currentTime,url:typeof window !== 'undefined' ? window.location.href:undefined,context:{bfcacheRestore:true,restoreTime:restoreTime}};fcp.rating = this.assignFCPRating(fcp.value);logger.info(`从bfcache恢复到现在的时间:${timeFromRestore}ms,作为新的FCP值`);this.onUpdate(fcp);this.metricReported = true;});this.observe();}}FCPObserver.FCP_GOOD_THRESHOLD = 1800;FCPObserver.FCP_NEEDS_IMPROVEMENT_THRESHOLD = 3000;class LCPObserver extends BaseObserver{constructor(options){super(options);this.lcpObserver = null;this.metricReported = false;this.firstHiddenTime = this.initFirstHiddenTime();this.setupFirstHiddenTimeListener();}initFirstHiddenTime(){if (typeof document !== 'undefined' && document.visibilityState === 'hidden'){return 0;}return Infinity;}setupFirstHiddenTimeListener(){if (typeof document === 'undefined') return;const updateHiddenTime = () =>{if (document.visibilityState === 'hidden' && this.firstHiddenTime === Infinity){this.firstHiddenTime = performance.now();logger.debug(`记录页面首次隐藏时间:${this.firstHiddenTime}ms`);}};document.addEventListener('visibilitychange',updateHiddenTime,{once:true});document.addEventListener('unload',updateHiddenTime,{once:true});}getActivationStart(){if (typeof performance === 'undefined') return 0;const entries = performance.getEntriesByType('navigation');if (!entries || entries.length === 0) return 0;const navigationEntry = entries[0];const activationStart = navigationEntry === null || navigationEntry === void 0 ? void 0:navigationEntry.activationStart;return activationStart ? activationStart:0;}observe(){this.startLCPMonitoring();}assignLCPRating(value){if (value <= LCPObserver.LCP_GOOD_THRESHOLD){return 'good';}else if (value <= LCPObserver.LCP_NEEDS_IMPROVEMENT_THRESHOLD){return 'needs-improvement';}else{return 'poor';}}startLCPMonitoring(){if (typeof PerformanceObserver === 'undefined'){logger.error('PerformanceObserver API不可用,无法监控LCP');return;}try{this.lcpObserver = new PerformanceObserver((entryList) =>{const entries = entryList.getEntries();entries.forEach(entry =>{if (entry.startTime < this.firstHiddenTime){const lcpValue = Math.max(entry.startTime - this.getActivationStart(),0);const lcp ={name:'LCP',value:lcpValue,unit:'ms',timestamp:new Date().getTime(),url:typeof window !== 'undefined' ? window.location.href:undefined,networkMetrics:this.getNetworkInformation(),context:{elementId:entry.element ? entry.element.id || null:null,elementTagName:entry.element ? entry.element.tagName || null:null,elementType:entry.element ? entry.element.type || null:null,size:entry.size || 0}};lcp.rating = this.assignLCPRating(lcp.value);this.onUpdate(lcp);}else{logger.debug('页面在LCP前已隐藏,忽略此LCP事件');}});});this.lcpObserver.observe({type:'largest-contentful-paint',buffered:true});this.setupStopListening();}catch (error){logger.error('LCP监控不受支持',error);}}setupStopListening(){const stopListening = () =>{if (this.metricReported || !this.lcpObserver) return;const records = this.lcpObserver.takeRecords();if (records && records.length > 0){const lastEntry = records[records.length - 1];if (lastEntry && lastEntry.startTime < this.firstHiddenTime){const lcpValue = Math.max(lastEntry.startTime - this.getActivationStart(),0);const lcp ={name:'LCP',value:lcpValue,unit:'ms',timestamp:new Date().getTime(),url:typeof window !== 'undefined' ? window.location.href:undefined,networkMetrics:this.getNetworkInformation(),context:{elementId:lastEntry.element ? lastEntry.element.id || null:null,elementTagName:lastEntry.element ? lastEntry.element.tagName || null:null,elementType:lastEntry.element ? lastEntry.element.type || null:null,size:lastEntry.size || 0,finalReport:true}};lcp.rating = this.assignLCPRating(lcp.value);this.onUpdate(lcp);}}this.lcpObserver.disconnect();this.lcpObserver = null;this.metricReported = true;logger.debug('LCP测量完成,指标已报告');};const addInteractionListener = (type) =>{if (typeof document === 'undefined') return;document.addEventListener(type,() =>{setTimeout(() =>{logger.debug(`检测到用户${type}交互,停止LCP监听`);stopListening();},0);},{once:true,capture:true});};['keydown','click'].forEach(addInteractionListener);if (typeof document !== 'undefined'){document.addEventListener('visibilitychange',() =>{if (document.visibilityState === 'hidden'){logger.debug('页面隐藏,停止LCP监听');stopListening();}});}}stop(){if (this.lcpObserver){this.lcpObserver.disconnect();this.lcpObserver = null;}super.stop();}onVisibilityChange(isVisible){if (!isVisible && this.firstHiddenTime === Infinity){this.firstHiddenTime = performance.now();logger.debug(`页面隐藏,更新firstHiddenTime:${this.firstHiddenTime}ms`);}}onBFCacheRestore(event){if (this.lcpObserver){this.lcpObserver.disconnect();this.lcpObserver = null;}this.metricReported = false;this.firstHiddenTime = this.initFirstHiddenTime();this.setupFirstHiddenTimeListener();const restoreTime = event.timeStamp;const doubleRAF = (callback) =>{requestAnimationFrame(() =>{requestAnimationFrame(() =>{callback();});});};doubleRAF(() =>{const currentTime = performance.now();const timeFromRestore = currentTime - restoreTime;const lcp ={name:'LCP',value:timeFromRestore,unit:'ms',timestamp:currentTime,url:typeof window !== 'undefined' ? window.location.href:undefined,context:{bfcacheRestore:true,restoreTime:restoreTime}};lcp.rating = this.assignLCPRating(lcp.value);logger.info(`从bfcache恢复到现在的时间:${timeFromRestore}ms,作为新的LCP值`);this.onUpdate(lcp);this.metricReported = true;});this.startLCPMonitoring();}}LCPObserver.LCP_GOOD_THRESHOLD = 2500;LCPObserver.LCP_NEEDS_IMPROVEMENT_THRESHOLD = 4000;class FIDObserver extends BaseObserver{constructor(options){super(options);this.fidObserver = null;}observe(){try{this.fidObserver = new PerformanceObserver((entryList) =>{const entries = entryList.getEntries();for (const entry of entries){if (entry.name === 'first-input'){const fidEntry = entry;const fid ={name:'FID',value:fidEntry.processingStart - fidEntry.startTime,unit:'ms',timestamp:new Date().getTime(),};if (fid.value <= 100){fid.rating = 'good';}else if (fid.value <= 300){fid.rating = 'needs-improvement';}else{fid.rating = 'poor';}this.onUpdate(fid);if (this.fidObserver){this.fidObserver.disconnect();this.fidObserver = null;}break;}}});this.fidObserver.observe({type:'first-input',buffered:true});}catch (error){logger.error('FID监控不受支持',error);}}stop(){if (this.fidObserver){this.fidObserver.disconnect();this.fidObserver = null;}super.stop();}onVisibilityChange(isVisible){if (!isVisible){logger.debug('页面隐藏,FID已经收集或仍在等待首次输入事件');}else{logger.debug('页面重新可见,FID状态不变');}}onBFCacheRestore(event){}}class CLSObserver extends BaseObserver{constructor(options){super(options);this.clsObserver = null;this.sessionCount = 0;this.sessionValues = [0];this.sessionGap = 1000;this.sessionMax = 5;this.maxSessionEntries = 100;this.prevReportedValue = 0;this.lastEntryTime = 0;this.shouldResetOnNextVisible = false;this.reportDebounceTimer = null;this.reportDebounceDelay = 500;this.firstHiddenTime = this.initFirstHiddenTime();this.setupFirstHiddenTimeListener();}initFirstHiddenTime(){if (typeof document !== 'undefined' && document.visibilityState === 'hidden'){return 0;}return Infinity;}setupFirstHiddenTimeListener(){if (typeof document === 'undefined') return;const updateHiddenTime = () =>{if (document.visibilityState === 'hidden' && this.firstHiddenTime === Infinity){this.firstHiddenTime = performance.now();logger.debug(`记录页面首次隐藏时间:${this.firstHiddenTime}ms`);}};document.addEventListener('visibilitychange',updateHiddenTime,{once:true});document.addEventListener('unload',updateHiddenTime,{once:true});}assignCLSRating(value){if (value <= CLSObserver.CLS_GOOD_THRESHOLD){return 'good';}else if (value <= CLSObserver.CLS_NEEDS_IMPROVEMENT_THRESHOLD){return 'needs-improvement';}else{return 'poor';}}observe(){if (typeof PerformanceObserver === 'undefined'){logger.error('PerformanceObserver API不可用,无法监控CLS');return;}try{this.clsObserver = new PerformanceObserver((entryList) =>{const entries = entryList.getEntries();if (document.visibilityState !== 'visible'){logger.debug('页面不可见,忽略布局偏移事件');return;}let newSessionStarted = false;for (const entry of entries){const layoutShift = entry;if (!layoutShift.hadRecentInput && layoutShift.startTime < this.firstHiddenTime){const currentTime = layoutShift.startTime;if (this.shouldResetOnNextVisible || currentTime - this.lastEntryTime > this.sessionGap){this.startNewSession(layoutShift.value);newSessionStarted = true;this.shouldResetOnNextVisible = false;}else{if (this.sessionCount < this.maxSessionEntries){this.sessionCount++;const currentSessionIndex = this.sessionValues.length - 1;this.sessionValues[currentSessionIndex] += layoutShift.value;}}this.lastEntryTime = currentTime;this.debouncedReportCLS();}}if (newSessionStarted){this.reportCLS(this.calculateCLS());}});this.clsObserver.observe({type:'layout-shift',buffered:true});logger.debug('CLS观察者已启动,开始监控布局偏移');}catch (error){logger.error('CLS监控不受支持',error);}}startNewSession(initialValue){this.sessionValues.push(initialValue);this.sessionCount = 1;if (this.sessionValues.length > this.sessionMax){let minValue = Infinity;let minIndex = 0;for (let i = 0;i < this.sessionValues.length;i++){if (this.sessionValues[i] < minValue){minValue = this.sessionValues[i];minIndex = i;}}this.sessionValues.splice(minIndex,1);}logger.debug(`开始新的CLS会话,当前会话数:${this.sessionValues.length}`);}debouncedReportCLS(){if (this.reportDebounceTimer !== null){window.clearTimeout(this.reportDebounceTimer);}this.reportDebounceTimer = window.setTimeout(() =>{const clsValue = this.calculateCLS();if (Math.abs(clsValue - this.prevReportedValue) >= 0.01){this.reportCLS(clsValue);}this.reportDebounceTimer = null;},this.reportDebounceDelay);}calculateCLS(){return Math.max(...this.sessionValues);}reportCLS(clsValue){const cls ={name:'CLS',value:clsValue,unit:'',timestamp:new Date().getTime(),url:typeof window !== 'undefined' ? window.location.href:undefined,context:{shiftCount:this.sessionCount,sessionCount:this.sessionValues.length,sessionValues:[...this.sessionValues],largestSession:this.calculateCLS(),isPageVisible:document.visibilityState === 'visible',firstHiddenTime:this.firstHiddenTime === Infinity ? null:this.firstHiddenTime}};cls.rating = this.assignCLSRating(cls.value);logger.debug(`报告CLS值:${cls.value},评级:${cls.rating},页面可见性:${document.visibilityState}`);this.onUpdate(cls);this.prevReportedValue = clsValue;}stop(){if (this.reportDebounceTimer !== null){window.clearTimeout(this.reportDebounceTimer);this.reportDebounceTimer = null;}if (this.clsObserver){this.clsObserver.disconnect();this.clsObserver = null;}super.stop();}onVisibilityChange(isVisible){if (!isVisible){if (this.firstHiddenTime === Infinity){this.firstHiddenTime = performance.now();logger.debug(`页面隐藏,更新firstHiddenTime:${this.firstHiddenTime}ms`);}const clsValue = this.calculateCLS();this.reportCLS(clsValue);this.shouldResetOnNextVisible = true;}else{if (this.shouldResetOnNextVisible){logger.debug('页面重新可见,准备开始新的CLS会话');}}}onBFCacheRestore(event){this.sessionValues = [0];this.sessionCount = 0;this.prevReportedValue = 0;this.lastEntryTime = 0;if (this.reportDebounceTimer !== null){window.clearTimeout(this.reportDebounceTimer);this.reportDebounceTimer = null;}this.firstHiddenTime = this.initFirstHiddenTime();this.setupFirstHiddenTimeListener();logger.info('CLS值已在bfcache恢复后重置');if (this.clsObserver){this.clsObserver.observe({type:'layout-shift',buffered:true});}}}CLSObserver.CLS_GOOD_THRESHOLD = 0.1;CLSObserver.CLS_NEEDS_IMPROVEMENT_THRESHOLD = 0.25;class INPObserver extends BaseObserver{constructor(options){super(options);this.inpObserver = null;this.interactionEvents = [];this.lastReportedINP = 0;this.minReportingChange = 10;this.firstHiddenTime = this.initFirstHiddenTime();this.setupFirstHiddenTimeListener();}initFirstHiddenTime(){if (typeof document !== 'undefined' && document.visibilityState === 'hidden'){return 0;}return Infinity;}setupFirstHiddenTimeListener(){if (typeof document === 'undefined') return;const updateHiddenTime = () =>{if (document.visibilityState === 'hidden' && this.firstHiddenTime === Infinity){this.firstHiddenTime = performance.now();logger.debug(`记录页面首次隐藏时间:${this.firstHiddenTime}ms`);}};document.addEventListener('visibilitychange',updateHiddenTime,{once:true});document.addEventListener('unload',updateHiddenTime,{once:true});}assignINPRating(value){if (value <= INPObserver.INP_GOOD_THRESHOLD){return 'good';}else if (value <= INPObserver.INP_NEEDS_IMPROVEMENT_THRESHOLD){return 'needs-improvement';}else{return 'poor';}}observe(){if (typeof PerformanceObserver === 'undefined'){logger.error('PerformanceObserver API不可用,无法监控INP');return;}try{const eventTypes = ['click','keydown','pointerdown'];this.inpObserver = new PerformanceObserver((entryList) =>{if (document.visibilityState !== 'visible') return;const events = entryList.getEntries();for (const event of events){if (event.startTime < this.firstHiddenTime){const timing = event;if (eventTypes.includes(timing.name)){this.interactionEvents.push({duration:timing.duration,name:timing.name,startTime:timing.startTime});this.calculateAndReportINP();}}}});try{this.inpObserver.observe({type:'event',buffered:true,...{durationThreshold:16}});}catch (error){this.inpObserver.observe({type:'event',buffered:true});logger.warn('浏览器不支持durationThreshold参数,使用默认配置');}}catch (error){logger.error('INP监控不受支持',error);}}calculateINP(){if (this.interactionEvents.length === 0){return 0;}const sortedDurations = this.interactionEvents.map(event => event.duration).sort((a,b) => a - b);const percentile = 0.75;const index = Math.floor(sortedDurations.length * percentile);if (sortedDurations.length === 1){return sortedDurations[0];}return sortedDurations[index];}calculateAndReportINP(){const inpValue = this.calculateINP();if (Math.abs(inpValue - this.lastReportedINP) >= this.minReportingChange){const inp ={name:'INP',value:inpValue,unit:'ms',timestamp:new Date().getTime(),url:typeof window !== 'undefined' ? window.location.href:undefined,context:{interactionCount:this.interactionEvents.length,percentile:75,highestDuration:Math.max(...this.interactionEvents.map(e => e.duration)),medianDuration:this.interactionEvents.length > 0 ? this.interactionEvents.map(e => e.duration).sort((a,b) => a - b)[Math.floor(this.interactionEvents.length / 2)]:0,eventTypes:[...new Set(this.interactionEvents.map(e => e.name))],firstHiddenTime:this.firstHiddenTime === Infinity ? null:this.firstHiddenTime}};inp.rating = this.assignINPRating(inp.value);logger.debug(`报告INP值:${inp.value}ms,评级:${inp.rating},基于${this.interactionEvents.length}个交互`);this.onUpdate(inp);this.lastReportedINP = inpValue;}}stop(){if (this.inpObserver){this.inpObserver.disconnect();this.inpObserver = null;}super.stop();}onVisibilityChange(isVisible){if (!isVisible && this.firstHiddenTime === Infinity){this.firstHiddenTime = performance.now();logger.debug(`页面隐藏,更新firstHiddenTime:${this.firstHiddenTime}ms`);if (this.interactionEvents.length > 0){this.calculateAndReportINP();}}}onBFCacheRestore(event){this.interactionEvents = [];this.lastReportedINP = 0;this.firstHiddenTime = this.initFirstHiddenTime();this.setupFirstHiddenTimeListener();logger.info('INP监测已在bfcache恢复后重置');if (this.inpObserver){this.inpObserver.disconnect();this.inpObserver = null;}this.observe();}}INPObserver.INP_GOOD_THRESHOLD = 200;INPObserver.INP_NEEDS_IMPROVEMENT_THRESHOLD = 500;class CoreWebVitalsObserver{constructor(options){this.metrics ={};this.fcpObserver = null;this.lcpObserver = null;this.fidObserver = null;this.clsObserver = null;this.inpObserver = null;this.onUpdate = options.onUpdate;this.options ={enabled:options.enabled !== undefined ? options.enabled:false,fcp:options.fcp !== undefined ? options.fcp:false,lcp:options.lcp !== undefined ? options.lcp:false,fid:options.fid !== undefined ? options.fid:false,cls:options.cls !== undefined ? options.cls:false,inp:options.inp !== undefined ? options.inp:false,backgroundLoadThreshold:options.backgroundLoadThreshold,...options};logger.debug('核心Web指标观察者已创建,初始配置:',{enabled:this.options.enabled,fcp:this.options.fcp,lcp:this.options.lcp,fid:this.options.fid,cls:this.options.cls,inp:this.options.inp});}start(){logger.info('开始监控核心Web指标');if (this.options.fcp){logger.debug('启动FCP监测');this.startFCPMonitoring();}if (this.options.lcp){logger.debug('启动LCP监测');this.startLCPMonitoring();}if (this.options.fid){logger.debug('启动FID监测');this.startFIDMonitoring();}if (this.options.cls){logger.debug('启动CLS监测');this.startCLSMonitoring();}if (this.options.inp){logger.debug('启动INP监测');this.startINPMonitoring();}logger.debug('核心Web指标监控启动完成');}stop(){logger.info('停止所有核心Web指标监控');if (this.fcpObserver){this.fcpObserver.stop();this.fcpObserver = null;logger.debug('FCP监控已停止');}if (this.lcpObserver){this.lcpObserver.stop();this.lcpObserver = null;logger.debug('LCP监控已停止');}if (this.fidObserver){this.fidObserver.stop();this.fidObserver = null;logger.debug('FID监控已停止');}if (this.clsObserver){this.clsObserver.stop();this.clsObserver = null;logger.debug('CLS监控已停止');}if (this.inpObserver){this.inpObserver.stop();this.inpObserver = null;logger.debug('INP监控已停止');}logger.debug('所有核心Web指标监控均已停止');}notifyMetricsUpdate(){logger.debug('发送核心Web指标更新通知');this.onUpdate(this.metrics);}startFCPMonitoring(){try{this.fcpObserver = new FCPObserver({onUpdate:(metric) =>{this.metrics.fcp = metric;logger.debug('FCP指标已更新:',metric.value.toFixed(2) + 'ms',`(${metric.rating})`);this.notifyMetricsUpdate();}});this.fcpObserver.start();}catch (error){logger.error('启动FCP监控失败:',error);}}startLCPMonitoring(){try{this.lcpObserver = new LCPObserver({onUpdate:(metric) =>{this.metrics.lcp = metric;logger.debug('LCP指标已更新:',metric.value.toFixed(2) + 'ms',`(${metric.rating})`);this.notifyMetricsUpdate();},backgroundLoadThreshold:this.options.backgroundLoadThreshold});this.lcpObserver.start();}catch (error){logger.error('启动LCP监控失败:',error);}}startFIDMonitoring(){try{this.fidObserver = new FIDObserver({onUpdate:(metric) =>{this.metrics.fid = metric;logger.debug('FID指标已更新:',metric.value.toFixed(2) + 'ms',`(${metric.rating})`);this.notifyMetricsUpdate();}});this.fidObserver.start();}catch (error){logger.error('启动FID监控失败:',error);}}startCLSMonitoring(){try{this.clsObserver = new CLSObserver({onUpdate:(metric) =>{this.metrics.cls = metric;logger.debug('CLS指标已更新:',metric.value.toFixed(4),`(${metric.rating})`);this.notifyMetricsUpdate();}});this.clsObserver.start();}catch (error){logger.error('启动CLS监控失败:',error);}}startINPMonitoring(){try{this.inpObserver = new INPObserver({onUpdate:(metric) =>{this.metrics.inp = metric;logger.debug('INP指标已更新:',metric.value.toFixed(2) + 'ms',`(${metric.rating})`);this.notifyMetricsUpdate();}});this.inpObserver.start();}catch (error){logger.error('启动INP监控失败:',error);}}}class ResourceTimingObserver{constructor(onUpdate,excludedPatterns = [],allowedResourceTypes){this.observer = null;this.resources = [];this.excludedPatterns = [];this.allowedResourceTypes = ['script','link','img','css','font'];this.onUpdate = onUpdate;this.excludedPatterns = excludedPatterns;if (allowedResourceTypes === null || allowedResourceTypes === void 0 ? void 0:allowedResourceTypes.length){this.allowedResourceTypes = allowedResourceTypes;}logger.debug('资源计时观察者已创建',{excludedPatterns:this.excludedPatterns.length,allowedResourceTypes:this.allowedResourceTypes});}start(){logger.info('开始监控资源加载性能');if (typeof PerformanceObserver === 'undefined'){logger.warn('PerformanceObserver API不可用,无法监控资源加载性能');return;}try{this.observer = new PerformanceObserver(this.handleEntries.bind(this));this.observer.observe({type:'resource',buffered:true});logger.debug('资源计时观察者已启动');}catch (error){logger.error('资源计时监控不支持',error);}}handleEntries(entryList){const entries = entryList.getEntries();let hasNewEntries = false;let newEntriesCount = 0;for (const entry of entries){if (entry.entryType !== 'resource') continue;const resourceEntry = entry;if (!this.shouldProcessEntry(resourceEntry)) continue;const resourceMetric = this.buildResourceMetric(resourceEntry);this.resources.push(resourceMetric);hasNewEntries = true;newEntriesCount++;logger.debug('记录资源性能指标:',{type:resourceEntry.initiatorType,url:this.shortenUrl(resourceEntry.name),size:`${(resourceEntry.transferSize / 1024).toFixed(2)}KB`,duration:`${resourceEntry.duration.toFixed(2)}ms`});}if (hasNewEntries){logger.info(`新增${newEntriesCount}个资源性能指标,总计${this.resources.length}个`);this.onUpdate(this.resources);this.resources = [];}}shouldProcessEntry(resourceEntry){if (!this.allowedResourceTypes.includes(resourceEntry.initiatorType)){logger.debug('忽略不在允许类型中的资源:',resourceEntry.initiatorType,this.shortenUrl(resourceEntry.name));return false;}if (this.isExcluded(resourceEntry.name)){logger.debug('忽略在排除列表中的资源:',this.shortenUrl(resourceEntry.name));return false;}const isDuplicate = this.resources.some(r => r.url === resourceEntry.name && r.startTime === resourceEntry.startTime);if (isDuplicate){logger.debug('忽略重复的资源:',this.shortenUrl(resourceEntry.name));}return !isDuplicate;}buildResourceMetric(resourceEntry){const dnsTime = calculateTimeDelta(resourceEntry.domainLookupEnd,resourceEntry.domainLookupStart);const tcpTime = calculateTimeDelta(resourceEntry.connectEnd,resourceEntry.connectStart);const sslTime = resourceEntry.secureConnectionStart > 0 ? calculateTimeDelta(resourceEntry.connectEnd,resourceEntry.secureConnectionStart):0;const ttfb = calculateTimeDelta(resourceEntry.responseStart,resourceEntry.requestStart);const requestTime = calculateTimeDelta(resourceEntry.responseStart,resourceEntry.fetchStart);const responseTime = calculateTimeDelta(resourceEntry.responseEnd,resourceEntry.responseStart);const networkMetrics = NetworkMetricsCollector.getNetworkInformation();return{url:resourceEntry.name,initiatorType:resourceEntry.initiatorType,startTime:resourceEntry.startTime,duration:resourceEntry.duration,transferSize:resourceEntry.transferSize,decodedBodySize:resourceEntry.decodedBodySize,encodedSize:resourceEntry.encodedBodySize || undefined,responseEnd:resourceEntry.responseEnd,ttfb,dnsTime,tcpTime,sslTime,requestTime,responseTime,networkMetrics,timestamp:new Date().getTime(),metric:'resources'};}isExcluded(url){if (!this.excludedPatterns.length){return false;}return this.excludedPatterns.some(pattern =>{if (pattern instanceof RegExp){return pattern.test(url);}return url.includes(pattern);});}stop(){logger.info('停止资源性能监控');if (this.observer){this.observer.disconnect();this.observer = null;logger.debug('资源计时观察者已断开连接');}}getResources(){return this.resources;}clearResources(){const count = this.resources.length;this.resources = [];logger.info(`清除了${count}个资源性能指标`);if (typeof performance !== 'undefined' && typeof performance.clearResourceTimings === 'function'){performance.clearResourceTimings();logger.debug('清除了浏览器性能条目缓存');}}shortenUrl(url){try{const urlObj = new URL(url);const pathname = urlObj.pathname;return urlObj.hostname + pathname.substring(pathname.lastIndexOf('/'));}catch (e){return url.length > 40 ? url.substring(0,25) + '...' + url.substring(url.length - 12):url;}}}class LongTasksObserver{constructor(options){this.observer = null;this.longTasks = [];this.onUpdate = options.onUpdate;this.options ={enabled:options.enabled !== undefined ? options.enabled:true,threshold:options.threshold || 50,maxEntries:options.maxEntries || 100,...options};logger.debug('长任务观察者已创建,配置:',{enabled:this.options.enabled,threshold:this.options.threshold,maxEntries:this.options.maxEntries});}start(){logger.info('开始长任务监控');try{this.observer = new PerformanceObserver((entryList) =>{const entries = entryList.getEntries();let newTasksCount = 0;for (const entry of entries){if (entry.duration < (this.options.threshold || 50)){continue;}const attribution = this.getAttribution(entry);const longTask ={duration:entry.duration,startTime:entry.startTime,attribution,timestamp:new Date().getTime(),metric:'longTasks'};this.longTasks.push(longTask);newTasksCount++;logger.debug('检测到长任务:',{duration:`${entry.duration.toFixed(2)}ms`,source:attribution || 'unknown'});const maxEntries = this.options.maxEntries || 100;if (this.longTasks.length > maxEntries){this.longTasks.shift();logger.debug('超出最大长任务记录数,移除最旧的条目');}}if (newTasksCount > 0){logger.info(`检测到${newTasksCount}个新的长任务,总计${this.longTasks.length}个`);this.onUpdate(this.longTasks);this.longTasks = [];}});this.observer.observe({type:'longtask',buffered:true});logger.debug('长任务观察者已启动,阈值为',this.options.threshold,'ms');}catch (error){logger.error('长任务监控不受支持',error);}}stop(){logger.info('停止长任务监控');if (this.observer){this.observer.disconnect();this.observer = null;logger.debug('长任务观察者已断开连接');}}getLongTasks(){return this.longTasks;}clearLongTasks(){const count = this.longTasks.length;this.longTasks = [];logger.info(`清除了${count}个长任务记录`);}getAttribution(entry){if ('attribution' in entry){const attribution = entry.attribution;if (Array.isArray(attribution) && attribution.length > 0){const attributionEntry = attribution[0];if (attributionEntry.containerName){return attributionEntry.containerName;}else if (attributionEntry.containerSrc){return attributionEntry.containerSrc;}else if (attributionEntry.containerId){return attributionEntry.containerId;}else if (attributionEntry.containerType){return attributionEntry.containerType;}}}return 'unknown';}}class NavigationObserver{constructor(options){this.metrics ={};this.isStarted = false;this.hasCollectedMetrics = false;this.perfObserver = null;this.timeoutId = null;this.MAX_WAIT_TIME_MS = 10000;this.MAX_POLL_ATTEMPTS = 10;this.POLL_INTERVAL_MS = 200;this.metricsUpdateCallback = options.onUpdate;this.options ={enabled:options.enabled !== undefined ? options.enabled:true,includeRawTiming:options.includeRawTiming || false,onUpdate:options.onUpdate};logger.debug('创建导航计时观察者',{enabled:this.options.enabled,includeRawTiming:this.options.includeRawTiming});}start(){if (!this.canStart()){return;}this.isStarted = true;this.timeoutId = window.setTimeout(() =>{logger.warn(`等待超过${this.MAX_WAIT_TIME_MS}ms,强制收集可用的导航性能数据`);this.collectNavigationData(true);this.cleanupResources();},this.MAX_WAIT_TIME_MS);if (typeof PerformanceObserver !== 'undefined'){try{this.perfObserver = new PerformanceObserver((entries) =>{const navigationEntries = entries.getEntries() .filter(entry => entry.entryType === 'navigation');if (navigationEntries.length === 0){return;}const latestEntry = navigationEntries[navigationEntries.length - 1];if (this.isLoadEventComplete(latestEntry)){this.processNavigationData(latestEntry);this.cleanupResources();}});this.perfObserver.observe({type:'navigation',buffered:true});logger.debug('开始使用PerformanceObserver监测导航性能');}catch (error){logger.error('PerformanceObserver初始化失败',error);this.startWithTraditionalMethod();}}else{this.startWithTraditionalMethod();}}stop(){logger.info('停止导航性能数据收集');this.isStarted = false;this.cleanupResources();}getMetrics(){return this.metrics;}canStart(){if (!this.options.enabled){logger.debug('导航计时观察者已禁用');return false;}if (this.isStarted){logger.debug('导航计时观察者已处于活动状态');return false;}if (this.hasCollectedMetrics){logger.debug('导航计时观察者已收集过指标');return false;}return true;}clearTimeout(){if (this.timeoutId !== null){window.clearTimeout(this.timeoutId);this.timeoutId = null;}}cleanupResources(){if (this.perfObserver){this.perfObserver.disconnect();this.perfObserver = null;}this.clearTimeout();}startWithTraditionalMethod(){logger.info('使用传统方法收集导航性能数据');if (document.readyState === 'complete'){this.pollForNavigationData();}else{window.addEventListener('load',() =>{setTimeout(() => this.pollForNavigationData(),100);});}}pollForNavigationData(attemptCount = 0){if (attemptCount > this.MAX_POLL_ATTEMPTS){logger.warn(`轮询${this.MAX_POLL_ATTEMPTS}次后未获取到完整数据,强制收集可用数据`);this.collectNavigationData(true);return;}const navEntry = this.getLatestNavigationEntry();if (!navEntry){logger.warn('未找到导航性能条目');return;}if (this.isLoadEventComplete(navEntry)){this.collectNavigationData();this.clearTimeout();}else{setTimeout(() => this.pollForNavigationData(attemptCount + 1),this.POLL_INTERVAL_MS);}}getLatestNavigationEntry(){if (!('performance' in window) || !('getEntriesByType' in performance)){return null;}const entries = performance.getEntriesByType('navigation');if (!entries || entries.length === 0){return null;}return entries[entries.length - 1];}collectNavigationData(forceProcess = false){const navEntry = this.getLatestNavigationEntry();if (!navEntry){logger.warn('无法收集导航性能数据:未找到导航性能条目');return;}if (!forceProcess && !this.isLoadEventComplete(navEntry)){logger.warn('导航性能数据不完整:load事件未完成');return;}this.processNavigationData(navEntry);}isLoadEventComplete(entry){const hasValidStart = typeof entry.loadEventStart === 'number' && entry.loadEventStart > 0;const hasValidEnd = typeof entry.loadEventEnd === 'number' && entry.loadEventEnd > 0;const isComplete = hasValidStart && hasValidEnd && entry.loadEventEnd >= entry.loadEventStart;if (!isComplete){logger.debug('load事件数据不完整',{loadEventStart:entry.loadEventStart,loadEventEnd:entry.loadEventEnd});}return isComplete;}processNavigationData(entry){var _a,_b,_c,_d;const timingMetrics ={unloadTime:calculateTime(entry.unloadEventEnd,entry.unloadEventStart),redirectTime:calculateTime(entry.redirectEnd,entry.redirectStart),serviceWorkerTime:entry.workerStart > 0 ? calculateTime(entry.workerStart,entry.fetchStart):undefined,appCacheTime:calculateTime(entry.domainLookupStart,entry.fetchStart),dnsTime:calculateTime(entry.domainLookupEnd,entry.domainLookupStart),tcpTime:calculateTime(entry.connectEnd,entry.connectStart),sslTime:entry.secureConnectionStart > 0 ? calculateTime(entry.connectEnd,entry.secureConnectionStart):undefined,requestTime:calculateTime(entry.responseStart,entry.requestStart),ttfb:calculateTime(entry.responseStart,entry.requestStart),resourceFetchTime:calculateTime(entry.responseStart,entry.fetchStart),responseTime:calculateTime(entry.responseEnd,entry.responseStart),initDOMTime:calculateTime(entry.domInteractive,entry.responseEnd),processingTime:calculateTime(entry.domComplete,entry.domInteractive),contentLoadTime:calculateTime(entry.domContentLoadedEventEnd,entry.domContentLoadedEventStart),domContentLoaded:calculateTime(entry.domContentLoadedEventEnd,entry.startTime),loadEventDuration:calculateTime(entry.loadEventEnd,entry.loadEventStart),frontEndTime:calculateTime(entry.loadEventEnd,entry.responseEnd),totalLoadTime:calculateTime(entry.loadEventEnd,entry.startTime)};const networkMetrics = NetworkMetricsCollector.getNetworkInformation();const pageUrl = typeof window !== 'undefined' ? window.location.href:entry.name;logger.info('导航性能指标',{url:entry.name,ttfb:`${((_a = timingMetrics.ttfb) === null || _a === void 0 ? void 0:_a.toFixed(2)) || 0}ms`,domContentLoaded:`${((_b = timingMetrics.domContentLoaded) === null || _b === void 0 ? void 0:_b.toFixed(2)) || 0}ms`,loadEventDuration:`${((_c = timingMetrics.loadEventDuration) === null || _c === void 0 ? void 0:_c.toFixed(2)) || 0}ms`,totalLoadTime:`${((_d = timingMetrics.totalLoadTime) === null || _d === void 0 ? void 0:_d.toFixed(2)) || 0}ms`});this.metrics ={...timingMetrics,url:pageUrl,metric:'navigation',networkMetrics,timestamp:Date.now(),complete:true};if (this.options.includeRawTiming){this.metrics.rawTiming ={navigationStart:entry.startTime,unloadEventStart:entry.unloadEventStart,unloadEventEnd:entry.unloadEventEnd,redirectStart:entry.redirectStart,redirectEnd:entry.redirectEnd,fetchStart:entry.fetchStart,domainLookupStart:entry.domainLookupStart,domainLookupEnd:entry.domainLookupEnd,connectStart:entry.connectStart,connectEnd:entry.connectEnd,secureConnectionStart:entry.secureConnectionStart,requestStart:entry.requestStart,responseStart:entry.responseStart,responseEnd:entry.responseEnd,domInteractive:entry.domInteractive,domContentLoadedEventStart:entry.domContentLoadedEventStart,domContentLoadedEventEnd:entry.domContentLoadedEventEnd,domComplete:entry.domComplete,loadEventStart:entry.loadEventStart,loadEventEnd:entry.loadEventEnd,type:entry.type,redirectCount:entry.redirectCount,};}this.metricsUpdateCallback(this.metrics);this.hasCollectedMetrics = true;}}class BrowserInfoCollector{static getBrowserInfo(){if (typeof window === 'undefined' || typeof navigator === 'undefined'){return{};}try{return{userAgent:navigator.userAgent,language:navigator.language,platform:navigator.platform,vendor:navigator.vendor,screenSize:{width:window.screen.width,height:window.screen.height},windowSize:{width:window.innerWidth,height:window.innerHeight},devicePixelRatio:window.devicePixelRatio,cookiesEnabled:navigator.cookieEnabled};}catch (error){console.warn('无法获取完整的浏览器信息',error);return{};}}static detectBrowser(){var _a,_b,_c,_d,_e,_f,_g,_h,_j;if (typeof navigator === 'undefined'){return{name:'unknown',version:'unknown'};}const userAgent = navigator.userAgent;let browserName = 'unknown';let version = 'unknown';try{if (userAgent.indexOf('Firefox') > -1){browserName = 'Firefox';version = ((_a = userAgent.match(/Firefox\/([\d.]+)/)) === null || _a === void 0 ? void 0:_a[1]) || '';}else if (userAgent.indexOf('Edge') > -1 || userAgent.indexOf('Edg/') > -1){browserName = 'Edge';version = ((_b = userAgent.match(/Edge\/([\d.]+)/)) === null || _b === void 0 ? void 0:_b[1]) || ((_c = userAgent.match(/Edg\/([\d.]+)/)) === null || _c === void 0 ? void 0:_c[1]) || '';}else if (userAgent.indexOf('Chrome') > -1){browserName = 'Chrome';version = ((_d = userAgent.match(/Chrome\/([\d.]+)/)) === null || _d === void 0 ? void 0:_d[1]) || '';}else if (userAgent.indexOf('Safari') > -1 && userAgent.indexOf('Chrome') === -1){browserName = 'Safari';version = ((_e = userAgent.match(/Version\/([\d.]+)/)) === null || _e === void 0 ? void 0:_e[1]) || '';}else if (userAgent.indexOf('MSIE') > -1 || userAgent.indexOf('Trident/') > -1){browserName = 'Internet Explorer';version = ((_f = userAgent.match(/MSIE ([\d.]+)/)) === null || _f === void 0 ? void 0:_f[1]) || ((_g = userAgent.match(/rv:([\d.]+)/)) === null || _g === void 0 ? void 0:_g[1]) || '';}else if (userAgent.indexOf('Opera') > -1 || userAgent.indexOf('OPR/') > -1){browserName = 'Opera';version = ((_h = userAgent.match(/Opera\/([\d.]+)/)) === null || _h === void 0 ? void 0:_h[1]) || ((_j = userAgent.match(/OPR\/([\d.]+)/)) === null || _j === void 0 ? void 0:_j[1]) || '';}return{name:browserName,version};}catch (error){console.warn('无法检测浏览器版本',error);return{name:'unknown',version:'unknown'};}}static detectOS(){var _a,_b,_c,_d,_e;if (typeof navigator === 'undefined'){return{name:'unknown',version:'unknown'};}const userAgent = navigator.userAgent;let osName = 'unknown';let version = 'unknown';try{if (userAgent.indexOf('Windows') > -1){osName = 'Windows';if (userAgent.indexOf('Windows NT 10.0') > -1) version = '10';else if (userAgent.indexOf('Windows NT 6.3') > -1) version = '8.1';else if (userAgent.indexOf('Windows NT 6.2') > -1) version = '8';else if (userAgent.indexOf('Windows NT 6.1') > -1) version = '7';else if (userAgent.indexOf('Windows NT 6.0') > -1) version = 'Vista';else if (userAgent.indexOf('Windows NT 5.1') > -1) version = 'XP';}else if (userAgent.indexOf('Mac OS X') > -1){osName = 'macOS';version = ((_b = (_a = userAgent.match(/Mac OS X ([0-9_\.]+)/)) === null || _a === void 0 ? void 0:_a[1]) === null || _b === void 0 ? void 0:_b.replace(/_/g,'.')) || '';}else if (userAgent.indexOf('Android') > -1){osName = 'Android';version = ((_c = userAgent.match(/Android ([0-9\.]+)/)) === null || _c === void 0 ? void 0:_c[1]) || '';}else if (userAgent.indexOf('iOS') > -1 || userAgent.indexOf('iPhone') > -1 || userAgent.indexOf('iPad') > -1){osName = 'iOS';version = ((_e = (_d = userAgent.match(/OS ([0-9_\.]+)/)) === null || _d === void 0 ? void 0:_d[1]) === null || _e === void 0 ? void 0:_e.replace(/_/g,'.')) || '';}else if (userAgent.indexOf('Linux') > -1){osName = 'Linux';}return{name:osName,version};}catch (error){console.warn('无法检测操作系统',error);return{name:'unknown',version:'unknown'};}}static getEnvironmentInfo(){const browserInfo = this.getBrowserInfo();const browserDetails = this.detectBrowser();const osDetails = this.detectOS();return{...browserInfo,browser:browserDetails,os:osDetails};}}class BrowserInfoObserver{constructor(options){this.browserInfo ={};this.resizeHandler = null;this._resizeTimeout = 0;this.onUpdate = options.onUpdate;this.options ={enabled:options.enabled !== undefined ? options.enabled:true,trackResize:options.trackResize !== undefined ? options.trackResize:false,includeOSDetails:options.includeOSDetails !== undefined ? options.includeOSDetails:true,includeSizeInfo:options.includeSizeInfo !== undefined ? options.includeSizeInfo:true,onUpdate:options.onUpdate};}start(){if (!this.options.enabled){return;}try{this.collectBrowserInfo();if (this.options.trackResize && typeof window !== 'undefined'){this.resizeHandler = this.handleResize.bind(this);window.addEventListener('resize',this.resizeHandler);logger.debug('开始监听窗口大小变化');}logger.info('浏览器信息观察者已启动');}catch (error){logger.error('启动浏览器信息观察者时出错',error);}}stop(){if (this.resizeHandler && typeof window !== 'undefined'){window.removeEventListener('resize',this.resizeHandler);this.resizeHandler = null;}logger.info('浏览器信息观察者已停止');}getBrowserInfo(){return this.browserInfo;}refresh(){this.collectBrowserInfo();}collectBrowserInfo(){try{const fullInfo = BrowserInfoCollector.getEnvironmentInfo();if (!this.options.includeOSDetails){delete fullInfo.os;}if (!this.options.includeSizeInfo){delete fullInfo.screenSize;delete fullInfo.windowSize;delete fullInfo.devicePixelRatio;}if (typeof window !== 'undefined'){fullInfo.url = window.location.href;}this.browserInfo = fullInfo;this.onUpdate(this.browserInfo);}catch (error){logger.error('收集浏览器信息时出错',error);}}handleResize(){if (typeof window === 'undefined'){return;}if (this.resizeHandler){window.clearTimeout(this._resizeTimeout);this._resizeTimeout = window.setTimeout(() =>{logger.debug('窗口大小已更改,更新浏览器信息');this.collectBrowserInfo();},500);}}}const VERSION = '0.0.6';class PerfObserverKit{constructor(options ={}){this.coreWebVitalsObserver = null;this.resourceTimingObserver = null;this.longTasksObserver = null;this.navigationObserver = null;this.browserInfoObserver = null;this.metrics ={coreWebVitals:{},resources:[],longTasks:[],navigation:{},browserInfo:{}};this.isRunning = false;this.checkBrowserCompatibility();this.validateOptions(options);const logLevel = this.determineLogLevel(options);this.options ={onMetrics:options.onMetrics || null,debug:options.debug !== undefined ? options.debug:false,logLevel,autoStart:options.autoStart !== undefined ? options.autoStart:false,samplingRate:options.samplingRate !== undefined ? options.samplingRate:0,coreWebVitals:this.normalizeCoreWebVitalsOptions(options.coreWebVitals),resources:this.normalizeResourceOptions(options.resources),longTasks:this.normalizeModuleOptions(options.longTasks,false),navigation:this.normalizeModuleOptions(options.navigation,false),browserInfo:this.normalizeModuleOptions(options.browserInfo,false)};logger.setOptions({level:this.options.logLevel,disableInProduction:false});logger.debug('PerfObserverKit初始化完成,配置:',this.options);if (this.options.autoStart){this.start();}}validateOptions(options){if (options.samplingRate !== undefined && (typeof options.samplingRate !== 'number' || options.samplingRate < 0 || options.samplingRate > 1)){logger.warn('无效的采样率设置,应为0到1之间的数字,将使用默认值0');options.samplingRate = 0;}if (options.onMetrics !== undefined && typeof options.onMetrics !== 'function'){logger.warn('onMetrics必须是一个函数,将使用默认空函数');options.onMetrics = () =>{};}}checkBrowserCompatibility(){if (!browserSupport.hasPerformanceAPI()){logger.warn('当前浏览器不支持Performance API');}if (!browserSupport.hasPerformanceObserver()){logger.warn('当前浏览器不支持PerformanceObserver');}}determineLogLevel(options){if (options.logLevel !== undefined){const level = Number(options.logLevel);if (level >= exports.LogLevel.NONE && level <= exports.LogLevel.DEBUG){return level;}}return options.debug ? exports.LogLevel.DEBUG:exports.LogLevel.WARN;}normalizeModuleOptions(options,defaultEnabled){try{if (typeof options === 'boolean'){return{enabled:options};}if (options && typeof options === 'object'){return{...options,enabled:options.enabled !== undefined ? options.enabled:defaultEnabled};}return{enabled:defaultEnabled};}catch (error){logger.error('规范化模块配置失败:',error);return{enabled:defaultEnabled};}}normalizeResourceOptions(options){try{const normalizedOptions = this.normalizeModuleOptions(options,true);return{...normalizedOptions,maxResources:normalizedOptions.maxResources !== undefined ? normalizedOptions.maxResources:100,excludedPatterns:normalizedOptions.excludedPatterns || [],allowedTypes:normalizedOptions.allowedTypes || [],captureNetworkInfo:normalizedOptions.captureNetworkInfo !== undefined ? normalizedOptions.captureNetworkInfo:true,maxEntries:normalizedOptions.maxEntries !== undefined ? normalizedOptions.maxEntries:100};}catch (error){logger.error('规范化资源计时选项失败:',error);return{enabled:false,maxResources:100,excludedPatterns:[],allowedTypes:[],captureNetworkInfo:true,maxEntries:100};}}normalizeCoreWebVitalsOptions(options){try{const normalizedOptions = this.normalizeModuleOptions(options,false);if (options === true){return{enabled:true,fcp:true,lcp:true,fid:false,cls:false,inp:false,maxLongTasks:50,maxResources:100};}return{...normalizedOptions,fcp:normalizedOptions.fcp !== undefined ? normalizedOptions.fcp:false,lcp:normalizedOptions.lcp !== undefined ? normalizedOptions.lcp:false,fid:normalizedOptions.fid !== undefined ? normalizedOptions.fid:false,cls:normalizedOptions.cls !== undefined ? normalizedOptions.cls:false,inp:normalizedOptions.inp !== undefined ? normalizedOptions.inp:false,maxLongTasks:normalizedOptions.maxLongTasks !== undefined ? normalizedOptions.maxLongTasks:50,maxResources:normalizedOptions.maxResources !== undefined ? normalizedOptions.maxResources:100};}catch (error){logger.error('规范化核心Web指标选项失败:',error);return{enabled:false,fcp:false,lcp:false,fid:false,cls:false,inp:false,maxLongTasks:50,maxResources:100};}}start(){if (this.isRunning){logger.warn('性能监控已经在运行中');return;}logger.info('开始监控性能指标');if (this.options.samplingRate > 0 && Math.random() > this.options.samplingRate){logger.debug(`根据采样率(${this.options.samplingRate})决定不收集此会话的性能数据`);this.isRunning = true;return;}this.startObserver('coreWebVitals',this.startCoreWebVitalsMonitoring.bind(this));this.startObserver('resources',this.startResourceTimingMonitoring.bind(this));this.startObserver('longTasks',this.startLongTasksMonitoring.bind(this));this.startObserver('navigation',this.startNavigationMonitoring.bind(this));this.startObserver('browserInfo',this.startBrowserInfoMonitoring.bind(this));this.isRunning = true;logger.debug('所有启用的性能监控模块已启动');}startObserver(name,startMethod){const option = this.options[name];if (option && option.enabled){startMethod();}}stop(){if (!this.isRunning){logger.warn('性能监控未在运行中');return;}logger.info('停止监控性能指标');this.cleanupObserver(this.coreWebVitalsObserver);this.cleanupObserver(this.resourceTimingObserver);this.cleanupObserver(this.longTasksObserver);this.cleanupObserver(this.navigationObserver);this.cleanupObserver(this.browserInfoObserver);this.coreWebVitalsObserver = null;this.resourceTimingObserver = null;this.longTasksObserver = null;this.navigationObserver = null;this.browserInfoObserver = null;this.isRunning = false;logger.debug('所有性能监控模块已停止');}cleanupObserver(observer){if (observer){try{observer.stop();}catch (error){logger.error('停止观察器失败:',error);}}}getMetrics(){return this.metrics;}clearMetrics(){logger.debug('清除指标数据,保留浏览器信息');const currentBrowserInfo = this.metrics.browserInfo;this.metrics ={coreWebVitals:{},resources:[],longTasks:[],navigation:{},browserInfo:currentBrowserInfo};}setLogLevel(level){if (level >= exports.LogLevel.NONE && level <= exports.LogLevel.DEBUG){this.options.logLevel = level;logger.setLevel(level);logger.debug('已更新日志级别为:',level);}else{logger.warn('无效的日志级别:',level);}}setDebugMode(enabled){this.options.debug = enabled;if (enabled && this.options.logLevel < exports.LogLevel.DEBUG){this.setLogLevel(exports.LogLevel.DEBUG);}logger.setOptions({disableInProduction:false});logger.debug('调试模式已' + (enabled ? '启用':'禁用'));if (enabled){const config = logger.getConfiguration();logger.debug('日志配置状态:',config);logger.debug('当前监控状态:',{isRunning:this.isRunning,activeObservers:{coreWebVitals:!!this.coreWebVitalsObserver,resources:!!this.resourceTimingObserver,longTasks:!!this.longTasksObserver,navigation:!!this.navigationObserver,browserInfo:!!this.browserInfoObserver}});}}startCoreWebVitalsMonitoring(){try{const requiredEntryTypes = [ 'paint','largest-contentful-paint','first-input','layout-shift' ];const unsupportedTypes = requiredEntryTypes.filter(type => !browserSupport.supportsEntryType(type));if (unsupportedTypes.length > 0){logger.warn(`部分核心Web指标在当前浏览器中不受支持:${unsupportedTypes.join(',')}`);}const options = this.options.coreWebVitals;this.coreWebVitalsObserver = new CoreWebVitalsObserver({onUpdate:(coreWebVitalsMetrics) =>{this.metrics.coreWebVitals = coreWebVitalsMetrics;this.notifyMetricsUpdate(exports.MetricType.WEB_VITALS,coreWebVitalsMetrics);},enabled:options.enabled,fcp:options.fcp,lcp:options.lcp,fid:options.fid,cls:options.cls,inp:options.inp,backgroundLoadThreshold:options.backgroundLoadThreshold});this.coreWebVitalsObserver.start();logger.debug('核心Web指标监控已启动');}catch (error){logger.error('启动核心Web指标监控失败:',error);}}startResourceTimingMonitoring(){try{if (!browserSupport.supportsEntryType('resource')){logger.warn('当前浏览器不支持资源计时监控');return;}const options = this.options.resources;this.resourceTimingObserver = new ResourceTimingObserver((resources) =>{this.notifyMetricsUpdate(exports.MetricType.RESOURCES,resources);this.metrics.resources.push(...resources);this.metrics.resources = this.metrics.resources.slice(-options.maxResources);},options.excludedPatterns,options.allowedTypes);this.resourceTimingObserver.start();logger.debug('资源计时监控已启动');}catch (error){logger.error('启动资源计时监控失败:',error);}}startLongTasksMonitoring(){try{if (!browserSupport.supportsEntryType('longtask')){logger.warn('当前浏览器不支持长任务监控');return;}const options = this.options.longTasks;const maxLongTasks = options.maxLongTasks || 50;this.longTasksObserver = new LongTasksObserver({onUpdate:(longTasks) =>{this.notifyMetricsUpdate(exports.MetricType.LONG_TASKS,longTasks);this.metrics.longTasks.push(...longTasks);this.metrics.longTasks = this.metrics.longTasks.slice(-maxLongTasks);},enabled:options.enabled,threshold:options.threshold,maxEntries:options.maxEntries});this.longTasksObserver.start();logger.debug('长任务监控已启动');}catch (error){logger.error('启动长任务监控失败:',error);}}startNavigationMonitoring(){try{if (!browserSupport.supportsEntryType('navigation')){logger.warn('当前浏览器不支持导航计时监控');return;}const options = this.options.navigation;this.navigationObserver = new NavigationObserver({onUpdate:(navigationMetrics) =>{this.metrics.navigation = navigationMetrics;this.notifyMetricsUpdate(exports.MetricType.NAVIGATION,navigationMetrics);},enabled:options.enabled,includeRawTiming:options.includeRawTiming});this.navigationObserver.start();logger.debug('导航计时监控已启动');}catch (error){logger.error('启动导航计时监控失败:',error);}}startBrowserInfoMonitoring(){try{const options = this.options.browserInfo;if (!options.enabled){logger.debug('浏览器信息监控未启用');return;}this.browserInfoObserver = new BrowserInfoObserver({onUpdate:(browserInfo) =>{this.metrics.browserInfo = browserInfo;this.notifyMetricsUpdate(exports.MetricType.BROWSER_INFO,browserInfo);},enabled:options.enabled,trackResize:options.trackResize,includeOSDetails:options.includeOSDetails,includeSizeInfo:options.includeSizeInfo});this.browserInfoObserver.start();logger.debug('浏览器信息监控已启动');}catch (error){logger.error('启动浏览器信息监控失败:',error);}}notifyMetricsUpdate(type,metrics){try{if (this.options.onMetrics){this.options.onMetrics(type,metrics);}}catch (error){logger.error('指标更新回调执行失败:',error);}}getVersion(){return VERSION;}static checkBrowserSupport(){const details ={performanceAPI:browserSupport.hasPerformanceAPI(),performanceObserver:browserSupport.hasPerformanceObserver(),navigation:browserSupport.supportsEntryType('navigation'),longtask:browserSupport.supportsEntryType('longtask'),resources:browserSupport.supportsEntryType('resource'),paint:browserSupport.supportsEntryType('paint'),largestContentfulPaint:browserSupport.supportsEntryType('largest-contentful-paint'),firstInput:browserSupport.supportsEntryType('first-input'),layoutShift:browserSupport.supportsEntryType('layout-shift')};const supported = details.performanceAPI && details.performanceObserver;return{supported,details};}getStatus(){return{isRunning:this.isRunning,metrics:{coreWebVitals:!!this.coreWebVitalsObserver,resources:!!this.resourceTimingObserver,longTasks:!!this.longTasksObserver,navigation:!!this.navigationObserver,browserInfo:!!this.browserInfoObserver}};}}const LibraryExports ={PerfObserverKit:PerfObserverKit,CoreWebVitalsObserver:CoreWebVitalsObserver,MetricType:exports.MetricType,logger:logger,LogLevel:exports.LogLevel};exports.CoreWebVitalsObserver = CoreWebVitalsObserver;exports.PerfObserverKit = PerfObserverKit;exports["default"] = LibraryExports;exports.logger = logger;Object.defineProperty(exports,'__esModule',{value:true});}));