error-monitor-plugin-perf 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +178 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +32 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});class o{constructor(e={}){this.name="performance",this.version="1.0.0",this.core=null,this.observer=null,this.config={collectWebVitals:!0,collectResources:!0,threshold:100,...e}}setup(e){typeof window>"u"||(this.core=e,this.config.collectWebVitals&&this.collectWebVitals(),this.config.collectResources&&this.collectResourceTiming(),this.addPageLoadBreadcrumb())}collectWebVitals(){if("PerformanceObserver"in window)try{this.observer=new PerformanceObserver(e=>{for(const t of e.getEntries())this.handlePerformanceEntry(t)}),this.observer.observe({entryTypes:["largest-contentful-paint","first-input","layout-shift","navigation"]})}catch{console.warn("[PerformancePlugin] PerformanceObserver not supported")}}handlePerformanceEntry(e){if(!this.core)return;const t=this.config.threshold||100;switch(e.entryType){case"largest-contentful-paint":e.startTime>t&&this.core.capture({type:"custom",message:`LCP exceeded threshold: ${Math.round(e.startTime)}ms`,context:{metric:"lcp",value:e.startTime,threshold:t}});break;case"first-input":e.processingStart-e.startTime>t&&this.core.capture({type:"custom",message:`FID exceeded threshold: ${Math.round(e.processingStart-e.startTime)}ms`,context:{metric:"fid",value:e.processingStart-e.startTime,threshold:t}});break;case"layout-shift":if(!e.hadRecentInput){const r=e.value;r>.1&&this.core.capture({type:"custom",message:`CLS detected: ${r.toFixed(3)}`,context:{metric:"cls",value:r}})}break;case"navigation":this.handleNavigationTiming(e);break}}handleNavigationTiming(e){if(!this.core)return;const t={dns:e.domainLookupEnd-e.domainLookupStart,tcp:e.connectEnd-e.connectStart,ttfb:e.responseStart-e.requestStart,domParse:e.domContentLoadedEventEnd-e.domInteractive,fcp:e.loadEventEnd-e.domContentLoadedEventEnd};this.core.addBreadcrumb({timestamp:Date.now(),type:"performance",message:"Navigation timing",data:t})}collectResourceTiming(){this.core&&(document.readyState==="complete"?this.reportResourceTiming():window.addEventListener("load",()=>{setTimeout(()=>this.reportResourceTiming(),0)}))}reportResourceTiming(){if(!this.core)return;const t=performance.getEntriesByType("resource").filter(r=>r.responseEnd-r.startTime>(this.config.threshold||100));t.length>0&&this.core.capture({type:"custom",message:`Slow resources detected: ${t.length}`,context:{metric:"resources",resources:t.map(r=>({name:r.name,duration:Math.round(r.responseEnd-r.startTime),size:r.transferSize}))}})}addPageLoadBreadcrumb(){this.core&&window.addEventListener("load",()=>{const e=performance.timing,t=e.loadEventEnd-e.navigationStart;this.core.addBreadcrumb({timestamp:Date.now(),type:"navigation",message:"Page loaded",data:{loadTime:t,domReady:e.domContentLoadedEventEnd-e.navigationStart}})})}getWebVitals(){const e={},t=performance.getEntriesByType("navigation");if(t.length>0){const r=t[0];e.ttfb=r.responseStart-r.requestStart,e.fcp=r.loadEventEnd-r.domContentLoadedEventEnd}return e}teardown(){this.observer&&(this.observer.disconnect(),this.observer=null)}}function i(s){return new o(s)}exports.PerformancePlugin=o;exports.createPerformancePlugin=i;exports.default=o;
|
|
2
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../src/index.ts"],"sourcesContent":["/**\n * Performance Plugin\n * 性能监控插件\n */\n\nimport type { Plugin, Core } from 'error-monitor-core'\n\n/**\n * Web Vitals 指标\n */\nexport interface WebVitals {\n // First Contentful Paint\n fcp?: number\n // Largest Contentful Paint\n lcp?: number\n // Cumulative Layout Shift\n cls?: number\n // First Input Delay\n fid?: number\n // Time to First Byte\n ttfb?: number\n}\n\n/**\n * 性能配置\n */\nexport interface PerformanceConfig {\n // 是否自动采集Web Vitals\n collectWebVitals?: boolean\n // 是否采集资源加载时间\n collectResources?: boolean\n // 采集阈值(ms)\n threshold?: number\n}\n\n/**\n * 性能监控插件\n */\nexport class PerformancePlugin implements Plugin {\n name = 'performance'\n version = '1.0.0'\n private config: PerformanceConfig\n private core: Core | null = null\n private observer: PerformanceObserver | null = null\n\n constructor(config: PerformanceConfig = {}) {\n this.config = {\n collectWebVitals: true,\n collectResources: true,\n threshold: 100,\n ...config\n }\n }\n\n /**\n * 插件设置\n */\n setup(core: Core): void {\n if (typeof window === 'undefined') {\n return\n }\n\n this.core = core\n\n // 采集Web Vitals\n if (this.config.collectWebVitals) {\n this.collectWebVitals()\n }\n\n // 采集资源加载时间\n if (this.config.collectResources) {\n this.collectResourceTiming()\n }\n\n // 添加页面加载性能面包屑\n this.addPageLoadBreadcrumb()\n }\n\n /**\n * 采集Web Vitals\n */\n private collectWebVitals(): void {\n if (!('PerformanceObserver' in window)) {\n return\n }\n\n try {\n this.observer = new PerformanceObserver((list) => {\n for (const entry of list.getEntries()) {\n this.handlePerformanceEntry(entry as any)\n }\n })\n\n // 观察各类性能指标\n this.observer.observe({\n entryTypes: [\n 'largest-contentful-paint',\n 'first-input',\n 'layout-shift',\n 'navigation'\n ]\n })\n } catch (e) {\n console.warn('[PerformancePlugin] PerformanceObserver not supported')\n }\n }\n\n /**\n * 处理性能条目\n */\n private handlePerformanceEntry(entry: any): void {\n if (!this.core) return\n\n const threshold = this.config.threshold || 100\n\n switch (entry.entryType) {\n case 'largest-contentful-paint':\n if (entry.startTime > threshold) {\n this.core.capture({\n type: 'custom',\n message: `LCP exceeded threshold: ${Math.round(entry.startTime)}ms`,\n context: {\n metric: 'lcp',\n value: entry.startTime,\n threshold\n }\n })\n }\n break\n\n case 'first-input':\n if (entry.processingStart - entry.startTime > threshold) {\n this.core.capture({\n type: 'custom',\n message: `FID exceeded threshold: ${Math.round(entry.processingStart - entry.startTime)}ms`,\n context: {\n metric: 'fid',\n value: entry.processingStart - entry.startTime,\n threshold\n }\n })\n }\n break\n\n case 'layout-shift':\n if (!entry.hadRecentInput) {\n const clsValue = entry.value\n if (clsValue > 0.1) {\n this.core.capture({\n type: 'custom',\n message: `CLS detected: ${clsValue.toFixed(3)}`,\n context: {\n metric: 'cls',\n value: clsValue\n }\n })\n }\n }\n break\n\n case 'navigation':\n this.handleNavigationTiming(entry)\n break\n }\n }\n\n /**\n * 处理导航时间\n */\n private handleNavigationTiming(entry: any): void {\n if (!this.core) return\n\n const metrics = {\n // DNS查询时间\n dns: entry.domainLookupEnd - entry.domainLookupStart,\n // TCP连接时间\n tcp: entry.connectEnd - entry.connectStart,\n // 请求响应时间\n ttfb: entry.responseStart - entry.requestStart,\n // DOM解析时间\n domParse: entry.domContentLoadedEventEnd - entry.domInteractive,\n // 首次内容绘制\n fcp: entry.loadEventEnd - entry.domContentLoadedEventEnd\n }\n\n // 添加性能面包屑\n this.core.addBreadcrumb({\n timestamp: Date.now(),\n type: 'performance',\n message: 'Navigation timing',\n data: metrics\n })\n }\n\n /**\n * 采集资源加载时间\n */\n private collectResourceTiming(): void {\n if (!this.core) return\n\n // 页面加载完成后采集\n if (document.readyState === 'complete') {\n this.reportResourceTiming()\n } else {\n window.addEventListener('load', () => {\n setTimeout(() => this.reportResourceTiming(), 0)\n })\n }\n }\n\n /**\n * 上报资源加载时间\n */\n private reportResourceTiming(): void {\n if (!this.core) return\n\n const resources = performance.getEntriesByType('resource') as PerformanceResourceTiming[]\n\n const slowResources = resources.filter(resource => {\n const loadTime = resource.responseEnd - resource.startTime\n return loadTime > (this.config.threshold || 100)\n })\n\n if (slowResources.length > 0) {\n this.core.capture({\n type: 'custom',\n message: `Slow resources detected: ${slowResources.length}`,\n context: {\n metric: 'resources',\n resources: slowResources.map(r => ({\n name: r.name,\n duration: Math.round(r.responseEnd - r.startTime),\n size: r.transferSize\n }))\n }\n })\n }\n }\n\n /**\n * 添加页面加载性能面包屑\n */\n private addPageLoadBreadcrumb(): void {\n if (!this.core) return\n\n window.addEventListener('load', () => {\n const timing = performance.timing\n const pageLoadTime = timing.loadEventEnd - timing.navigationStart\n\n this.core!.addBreadcrumb({\n timestamp: Date.now(),\n type: 'navigation',\n message: 'Page loaded',\n data: {\n loadTime: pageLoadTime,\n domReady: timing.domContentLoadedEventEnd - timing.navigationStart\n }\n })\n })\n }\n\n /**\n * 获取Web Vitals\n */\n getWebVitals(): WebVitals {\n const vitals: WebVitals = {}\n\n // 从PerformanceObserver获取数据\n const entries = performance.getEntriesByType('navigation') as any[]\n\n if (entries.length > 0) {\n const entry = entries[0]\n vitals.ttfb = entry.responseStart - entry.requestStart\n vitals.fcp = entry.loadEventEnd - entry.domContentLoadedEventEnd\n }\n\n return vitals\n }\n\n /**\n * 插件销毁\n */\n teardown(): void {\n if (this.observer) {\n this.observer.disconnect()\n this.observer = null\n }\n }\n}\n\n/**\n * 创建性能插件实例\n */\nexport function createPerformancePlugin(config?: PerformanceConfig): PerformancePlugin {\n return new PerformancePlugin(config)\n}\n\nexport default PerformancePlugin\n"],"names":["PerformancePlugin","config","core","list","entry","threshold","clsValue","metrics","slowResources","resource","timing","pageLoadTime","vitals","entries","createPerformancePlugin"],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Plugin, Core } from 'error-monitor-core';
|
|
2
|
+
|
|
3
|
+
export interface WebVitals {
|
|
4
|
+
fcp?: number;
|
|
5
|
+
lcp?: number;
|
|
6
|
+
cls?: number;
|
|
7
|
+
fid?: number;
|
|
8
|
+
ttfb?: number;
|
|
9
|
+
}
|
|
10
|
+
export interface PerformanceConfig {
|
|
11
|
+
collectWebVitals?: boolean;
|
|
12
|
+
collectResources?: boolean;
|
|
13
|
+
threshold?: number;
|
|
14
|
+
}
|
|
15
|
+
export declare class PerformancePlugin implements Plugin {
|
|
16
|
+
name: string;
|
|
17
|
+
version: string;
|
|
18
|
+
private config;
|
|
19
|
+
private core;
|
|
20
|
+
private observer;
|
|
21
|
+
constructor(config?: PerformanceConfig);
|
|
22
|
+
setup(core: Core): void;
|
|
23
|
+
private collectWebVitals;
|
|
24
|
+
private handlePerformanceEntry;
|
|
25
|
+
private handleNavigationTiming;
|
|
26
|
+
private collectResourceTiming;
|
|
27
|
+
private reportResourceTiming;
|
|
28
|
+
private addPageLoadBreadcrumb;
|
|
29
|
+
getWebVitals(): WebVitals;
|
|
30
|
+
teardown(): void;
|
|
31
|
+
}
|
|
32
|
+
export declare function createPerformancePlugin(config?: PerformanceConfig): PerformancePlugin;
|
|
33
|
+
export default PerformancePlugin;
|
|
34
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAA;AAKtD,MAAM,WAAW,SAAS;IAExB,GAAG,CAAC,EAAE,MAAM,CAAA;IAEZ,GAAG,CAAC,EAAE,MAAM,CAAA;IAEZ,GAAG,CAAC,EAAE,MAAM,CAAA;IAEZ,GAAG,CAAC,EAAE,MAAM,CAAA;IAEZ,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAKD,MAAM,WAAW,iBAAiB;IAEhC,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAE1B,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAE1B,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAKD,qBAAa,iBAAkB,YAAW,MAAM;IAC9C,IAAI,SAAgB;IACpB,OAAO,SAAU;IACjB,OAAO,CAAC,MAAM,CAAmB;IACjC,OAAO,CAAC,IAAI,CAAoB;IAChC,OAAO,CAAC,QAAQ,CAAmC;gBAEvC,MAAM,GAAE,iBAAsB;IAY1C,KAAK,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IAwBvB,OAAO,CAAC,gBAAgB;IA6BxB,OAAO,CAAC,sBAAsB;IA2D9B,OAAO,CAAC,sBAAsB;IA4B9B,OAAO,CAAC,qBAAqB;IAgB7B,OAAO,CAAC,oBAAoB;IA6B5B,OAAO,CAAC,qBAAqB;IAsB7B,YAAY,IAAI,SAAS;IAkBzB,QAAQ,IAAI,IAAI;CAMjB;AAKD,wBAAgB,uBAAuB,CAAC,MAAM,CAAC,EAAE,iBAAiB,GAAG,iBAAiB,CAErF;AAED,eAAe,iBAAiB,CAAA"}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
class s {
|
|
2
|
+
constructor(e = {}) {
|
|
3
|
+
this.name = "performance", this.version = "1.0.0", this.core = null, this.observer = null, this.config = {
|
|
4
|
+
collectWebVitals: !0,
|
|
5
|
+
collectResources: !0,
|
|
6
|
+
threshold: 100,
|
|
7
|
+
...e
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* 插件设置
|
|
12
|
+
*/
|
|
13
|
+
setup(e) {
|
|
14
|
+
typeof window > "u" || (this.core = e, this.config.collectWebVitals && this.collectWebVitals(), this.config.collectResources && this.collectResourceTiming(), this.addPageLoadBreadcrumb());
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* 采集Web Vitals
|
|
18
|
+
*/
|
|
19
|
+
collectWebVitals() {
|
|
20
|
+
if ("PerformanceObserver" in window)
|
|
21
|
+
try {
|
|
22
|
+
this.observer = new PerformanceObserver((e) => {
|
|
23
|
+
for (const t of e.getEntries())
|
|
24
|
+
this.handlePerformanceEntry(t);
|
|
25
|
+
}), this.observer.observe({
|
|
26
|
+
entryTypes: [
|
|
27
|
+
"largest-contentful-paint",
|
|
28
|
+
"first-input",
|
|
29
|
+
"layout-shift",
|
|
30
|
+
"navigation"
|
|
31
|
+
]
|
|
32
|
+
});
|
|
33
|
+
} catch {
|
|
34
|
+
console.warn("[PerformancePlugin] PerformanceObserver not supported");
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* 处理性能条目
|
|
39
|
+
*/
|
|
40
|
+
handlePerformanceEntry(e) {
|
|
41
|
+
if (!this.core) return;
|
|
42
|
+
const t = this.config.threshold || 100;
|
|
43
|
+
switch (e.entryType) {
|
|
44
|
+
case "largest-contentful-paint":
|
|
45
|
+
e.startTime > t && this.core.capture({
|
|
46
|
+
type: "custom",
|
|
47
|
+
message: `LCP exceeded threshold: ${Math.round(e.startTime)}ms`,
|
|
48
|
+
context: {
|
|
49
|
+
metric: "lcp",
|
|
50
|
+
value: e.startTime,
|
|
51
|
+
threshold: t
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
break;
|
|
55
|
+
case "first-input":
|
|
56
|
+
e.processingStart - e.startTime > t && this.core.capture({
|
|
57
|
+
type: "custom",
|
|
58
|
+
message: `FID exceeded threshold: ${Math.round(e.processingStart - e.startTime)}ms`,
|
|
59
|
+
context: {
|
|
60
|
+
metric: "fid",
|
|
61
|
+
value: e.processingStart - e.startTime,
|
|
62
|
+
threshold: t
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
break;
|
|
66
|
+
case "layout-shift":
|
|
67
|
+
if (!e.hadRecentInput) {
|
|
68
|
+
const o = e.value;
|
|
69
|
+
o > 0.1 && this.core.capture({
|
|
70
|
+
type: "custom",
|
|
71
|
+
message: `CLS detected: ${o.toFixed(3)}`,
|
|
72
|
+
context: {
|
|
73
|
+
metric: "cls",
|
|
74
|
+
value: o
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
break;
|
|
79
|
+
case "navigation":
|
|
80
|
+
this.handleNavigationTiming(e);
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* 处理导航时间
|
|
86
|
+
*/
|
|
87
|
+
handleNavigationTiming(e) {
|
|
88
|
+
if (!this.core) return;
|
|
89
|
+
const t = {
|
|
90
|
+
// DNS查询时间
|
|
91
|
+
dns: e.domainLookupEnd - e.domainLookupStart,
|
|
92
|
+
// TCP连接时间
|
|
93
|
+
tcp: e.connectEnd - e.connectStart,
|
|
94
|
+
// 请求响应时间
|
|
95
|
+
ttfb: e.responseStart - e.requestStart,
|
|
96
|
+
// DOM解析时间
|
|
97
|
+
domParse: e.domContentLoadedEventEnd - e.domInteractive,
|
|
98
|
+
// 首次内容绘制
|
|
99
|
+
fcp: e.loadEventEnd - e.domContentLoadedEventEnd
|
|
100
|
+
};
|
|
101
|
+
this.core.addBreadcrumb({
|
|
102
|
+
timestamp: Date.now(),
|
|
103
|
+
type: "performance",
|
|
104
|
+
message: "Navigation timing",
|
|
105
|
+
data: t
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* 采集资源加载时间
|
|
110
|
+
*/
|
|
111
|
+
collectResourceTiming() {
|
|
112
|
+
this.core && (document.readyState === "complete" ? this.reportResourceTiming() : window.addEventListener("load", () => {
|
|
113
|
+
setTimeout(() => this.reportResourceTiming(), 0);
|
|
114
|
+
}));
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* 上报资源加载时间
|
|
118
|
+
*/
|
|
119
|
+
reportResourceTiming() {
|
|
120
|
+
if (!this.core) return;
|
|
121
|
+
const t = performance.getEntriesByType("resource").filter((o) => o.responseEnd - o.startTime > (this.config.threshold || 100));
|
|
122
|
+
t.length > 0 && this.core.capture({
|
|
123
|
+
type: "custom",
|
|
124
|
+
message: `Slow resources detected: ${t.length}`,
|
|
125
|
+
context: {
|
|
126
|
+
metric: "resources",
|
|
127
|
+
resources: t.map((o) => ({
|
|
128
|
+
name: o.name,
|
|
129
|
+
duration: Math.round(o.responseEnd - o.startTime),
|
|
130
|
+
size: o.transferSize
|
|
131
|
+
}))
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* 添加页面加载性能面包屑
|
|
137
|
+
*/
|
|
138
|
+
addPageLoadBreadcrumb() {
|
|
139
|
+
this.core && window.addEventListener("load", () => {
|
|
140
|
+
const e = performance.timing, t = e.loadEventEnd - e.navigationStart;
|
|
141
|
+
this.core.addBreadcrumb({
|
|
142
|
+
timestamp: Date.now(),
|
|
143
|
+
type: "navigation",
|
|
144
|
+
message: "Page loaded",
|
|
145
|
+
data: {
|
|
146
|
+
loadTime: t,
|
|
147
|
+
domReady: e.domContentLoadedEventEnd - e.navigationStart
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* 获取Web Vitals
|
|
154
|
+
*/
|
|
155
|
+
getWebVitals() {
|
|
156
|
+
const e = {}, t = performance.getEntriesByType("navigation");
|
|
157
|
+
if (t.length > 0) {
|
|
158
|
+
const o = t[0];
|
|
159
|
+
e.ttfb = o.responseStart - o.requestStart, e.fcp = o.loadEventEnd - o.domContentLoadedEventEnd;
|
|
160
|
+
}
|
|
161
|
+
return e;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* 插件销毁
|
|
165
|
+
*/
|
|
166
|
+
teardown() {
|
|
167
|
+
this.observer && (this.observer.disconnect(), this.observer = null);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
function n(r) {
|
|
171
|
+
return new s(r);
|
|
172
|
+
}
|
|
173
|
+
export {
|
|
174
|
+
s as PerformancePlugin,
|
|
175
|
+
n as createPerformancePlugin,
|
|
176
|
+
s as default
|
|
177
|
+
};
|
|
178
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":["../src/index.ts"],"sourcesContent":["/**\n * Performance Plugin\n * 性能监控插件\n */\n\nimport type { Plugin, Core } from 'error-monitor-core'\n\n/**\n * Web Vitals 指标\n */\nexport interface WebVitals {\n // First Contentful Paint\n fcp?: number\n // Largest Contentful Paint\n lcp?: number\n // Cumulative Layout Shift\n cls?: number\n // First Input Delay\n fid?: number\n // Time to First Byte\n ttfb?: number\n}\n\n/**\n * 性能配置\n */\nexport interface PerformanceConfig {\n // 是否自动采集Web Vitals\n collectWebVitals?: boolean\n // 是否采集资源加载时间\n collectResources?: boolean\n // 采集阈值(ms)\n threshold?: number\n}\n\n/**\n * 性能监控插件\n */\nexport class PerformancePlugin implements Plugin {\n name = 'performance'\n version = '1.0.0'\n private config: PerformanceConfig\n private core: Core | null = null\n private observer: PerformanceObserver | null = null\n\n constructor(config: PerformanceConfig = {}) {\n this.config = {\n collectWebVitals: true,\n collectResources: true,\n threshold: 100,\n ...config\n }\n }\n\n /**\n * 插件设置\n */\n setup(core: Core): void {\n if (typeof window === 'undefined') {\n return\n }\n\n this.core = core\n\n // 采集Web Vitals\n if (this.config.collectWebVitals) {\n this.collectWebVitals()\n }\n\n // 采集资源加载时间\n if (this.config.collectResources) {\n this.collectResourceTiming()\n }\n\n // 添加页面加载性能面包屑\n this.addPageLoadBreadcrumb()\n }\n\n /**\n * 采集Web Vitals\n */\n private collectWebVitals(): void {\n if (!('PerformanceObserver' in window)) {\n return\n }\n\n try {\n this.observer = new PerformanceObserver((list) => {\n for (const entry of list.getEntries()) {\n this.handlePerformanceEntry(entry as any)\n }\n })\n\n // 观察各类性能指标\n this.observer.observe({\n entryTypes: [\n 'largest-contentful-paint',\n 'first-input',\n 'layout-shift',\n 'navigation'\n ]\n })\n } catch (e) {\n console.warn('[PerformancePlugin] PerformanceObserver not supported')\n }\n }\n\n /**\n * 处理性能条目\n */\n private handlePerformanceEntry(entry: any): void {\n if (!this.core) return\n\n const threshold = this.config.threshold || 100\n\n switch (entry.entryType) {\n case 'largest-contentful-paint':\n if (entry.startTime > threshold) {\n this.core.capture({\n type: 'custom',\n message: `LCP exceeded threshold: ${Math.round(entry.startTime)}ms`,\n context: {\n metric: 'lcp',\n value: entry.startTime,\n threshold\n }\n })\n }\n break\n\n case 'first-input':\n if (entry.processingStart - entry.startTime > threshold) {\n this.core.capture({\n type: 'custom',\n message: `FID exceeded threshold: ${Math.round(entry.processingStart - entry.startTime)}ms`,\n context: {\n metric: 'fid',\n value: entry.processingStart - entry.startTime,\n threshold\n }\n })\n }\n break\n\n case 'layout-shift':\n if (!entry.hadRecentInput) {\n const clsValue = entry.value\n if (clsValue > 0.1) {\n this.core.capture({\n type: 'custom',\n message: `CLS detected: ${clsValue.toFixed(3)}`,\n context: {\n metric: 'cls',\n value: clsValue\n }\n })\n }\n }\n break\n\n case 'navigation':\n this.handleNavigationTiming(entry)\n break\n }\n }\n\n /**\n * 处理导航时间\n */\n private handleNavigationTiming(entry: any): void {\n if (!this.core) return\n\n const metrics = {\n // DNS查询时间\n dns: entry.domainLookupEnd - entry.domainLookupStart,\n // TCP连接时间\n tcp: entry.connectEnd - entry.connectStart,\n // 请求响应时间\n ttfb: entry.responseStart - entry.requestStart,\n // DOM解析时间\n domParse: entry.domContentLoadedEventEnd - entry.domInteractive,\n // 首次内容绘制\n fcp: entry.loadEventEnd - entry.domContentLoadedEventEnd\n }\n\n // 添加性能面包屑\n this.core.addBreadcrumb({\n timestamp: Date.now(),\n type: 'performance',\n message: 'Navigation timing',\n data: metrics\n })\n }\n\n /**\n * 采集资源加载时间\n */\n private collectResourceTiming(): void {\n if (!this.core) return\n\n // 页面加载完成后采集\n if (document.readyState === 'complete') {\n this.reportResourceTiming()\n } else {\n window.addEventListener('load', () => {\n setTimeout(() => this.reportResourceTiming(), 0)\n })\n }\n }\n\n /**\n * 上报资源加载时间\n */\n private reportResourceTiming(): void {\n if (!this.core) return\n\n const resources = performance.getEntriesByType('resource') as PerformanceResourceTiming[]\n\n const slowResources = resources.filter(resource => {\n const loadTime = resource.responseEnd - resource.startTime\n return loadTime > (this.config.threshold || 100)\n })\n\n if (slowResources.length > 0) {\n this.core.capture({\n type: 'custom',\n message: `Slow resources detected: ${slowResources.length}`,\n context: {\n metric: 'resources',\n resources: slowResources.map(r => ({\n name: r.name,\n duration: Math.round(r.responseEnd - r.startTime),\n size: r.transferSize\n }))\n }\n })\n }\n }\n\n /**\n * 添加页面加载性能面包屑\n */\n private addPageLoadBreadcrumb(): void {\n if (!this.core) return\n\n window.addEventListener('load', () => {\n const timing = performance.timing\n const pageLoadTime = timing.loadEventEnd - timing.navigationStart\n\n this.core!.addBreadcrumb({\n timestamp: Date.now(),\n type: 'navigation',\n message: 'Page loaded',\n data: {\n loadTime: pageLoadTime,\n domReady: timing.domContentLoadedEventEnd - timing.navigationStart\n }\n })\n })\n }\n\n /**\n * 获取Web Vitals\n */\n getWebVitals(): WebVitals {\n const vitals: WebVitals = {}\n\n // 从PerformanceObserver获取数据\n const entries = performance.getEntriesByType('navigation') as any[]\n\n if (entries.length > 0) {\n const entry = entries[0]\n vitals.ttfb = entry.responseStart - entry.requestStart\n vitals.fcp = entry.loadEventEnd - entry.domContentLoadedEventEnd\n }\n\n return vitals\n }\n\n /**\n * 插件销毁\n */\n teardown(): void {\n if (this.observer) {\n this.observer.disconnect()\n this.observer = null\n }\n }\n}\n\n/**\n * 创建性能插件实例\n */\nexport function createPerformancePlugin(config?: PerformanceConfig): PerformancePlugin {\n return new PerformancePlugin(config)\n}\n\nexport default PerformancePlugin\n"],"names":["PerformancePlugin","config","core","list","entry","threshold","clsValue","metrics","slowResources","resource","r","timing","pageLoadTime","vitals","entries","createPerformancePlugin"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "error-monitor-plugin-perf",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Performance monitoring plugin for error-monitor-sdk",
|
|
5
|
+
"main": "./dist/index.cjs",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"keywords": [
|
|
12
|
+
"error",
|
|
13
|
+
"monitor",
|
|
14
|
+
"plugin",
|
|
15
|
+
"performance",
|
|
16
|
+
"web-vitals"
|
|
17
|
+
],
|
|
18
|
+
"author": "",
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"error-monitor-core": "workspace:*"
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "vite build",
|
|
25
|
+
"dev": "vite build --watch",
|
|
26
|
+
"clean": "rm -rf dist"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"vite": "^5.1.6",
|
|
30
|
+
"vite-plugin-dts": "^3.7.3"
|
|
31
|
+
}
|
|
32
|
+
}
|