@be-link/cls-logger 1.0.1-beta.25 → 1.0.1-beta.26

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.esm.js CHANGED
@@ -1,5 +1,3 @@
1
- import { onFCP, onLCP, onCLS, onINP, onTTFB } from 'web-vitals';
2
-
3
1
  function isPlainObject(value) {
4
2
  return Object.prototype.toString.call(value) === '[object Object]';
5
3
  }
@@ -1991,10 +1989,14 @@ function getPagePath() {
1991
1989
  return '';
1992
1990
  }
1993
1991
  }
1994
- /**
1995
- * 安装浏览器性能监控
1996
- * 使用 Google web-vitals 库实现 Core Web Vitals 指标采集
1997
- */
1992
+ function reportMetric(report, reportType, metric, value, extra = {}) {
1993
+ report(reportType, {
1994
+ pagePath: getPagePath(),
1995
+ metric,
1996
+ value,
1997
+ ...extra,
1998
+ });
1999
+ }
1998
2000
  function installBrowserPerformanceMonitor(report, options) {
1999
2001
  if (typeof window === 'undefined')
2000
2002
  return;
@@ -2003,100 +2005,145 @@ function installBrowserPerformanceMonitor(report, options) {
2003
2005
  return;
2004
2006
  w.__beLinkClsLoggerPerfInstalled__ = true;
2005
2007
  const ignoreUrls = [...DEFAULT_IGNORE, ...(options.ignoreUrls ?? [])];
2006
- // Web Vitals: 使用 Google web-vitals 库
2007
- // 这个库会自动处理:
2008
- // - LCP 在用户交互后停止观察
2009
- // - firstHiddenTime 过滤(页面隐藏后的数据不计入)
2010
- // - CLS 5秒会话窗口算法
2011
- // - BFCache 恢复时自动重置指标
2012
- if (options.webVitals) {
2013
- // FCP
2008
+ // Navigation timing: TTFB
2009
+ if (options.navigationTiming) {
2014
2010
  try {
2015
- onFCP((metric) => {
2016
- report(options.reportType, {
2017
- pagePath: getPagePath(),
2018
- metric: 'FCP',
2019
- value: metric.value,
2020
- unit: 'ms',
2021
- rating: metric.rating,
2022
- id: metric.id,
2023
- navigationType: metric.navigationType,
2024
- });
2025
- });
2011
+ const navEntries = performance.getEntriesByType?.('navigation');
2012
+ const nav = Array.isArray(navEntries) ? navEntries[0] : undefined;
2013
+ if (nav && typeof nav === 'object') {
2014
+ const ttfb = typeof nav.responseStart === 'number' && typeof nav.requestStart === 'number'
2015
+ ? nav.responseStart - nav.requestStart
2016
+ : -1;
2017
+ if (ttfb >= 0 && sampleHit$1(options.sampleRate))
2018
+ reportMetric(report, options.reportType, 'TTFB', ttfb, { unit: 'ms' });
2019
+ }
2026
2020
  }
2027
2021
  catch {
2028
2022
  // ignore
2029
2023
  }
2030
- // LCP(自动处理用户交互后停止)
2024
+ }
2025
+ // Web Vitals: FCP/LCP/CLS/FID
2026
+ if (options.webVitals && typeof globalThis.PerformanceObserver === 'function') {
2027
+ // FCP
2031
2028
  try {
2032
- onLCP((metric) => {
2033
- report(options.reportType, {
2034
- pagePath: getPagePath(),
2035
- metric: 'LCP',
2036
- value: metric.value,
2037
- unit: 'ms',
2038
- rating: metric.rating,
2039
- id: metric.id,
2040
- navigationType: metric.navigationType,
2041
- });
2029
+ const po = new PerformanceObserver((list) => {
2030
+ try {
2031
+ if (!sampleHit$1(options.sampleRate))
2032
+ return;
2033
+ for (const entry of list.getEntries()) {
2034
+ if (entry?.name === 'first-contentful-paint' && typeof entry.startTime === 'number') {
2035
+ reportMetric(report, options.reportType, 'FCP', entry.startTime, { unit: 'ms' });
2036
+ }
2037
+ }
2038
+ }
2039
+ catch {
2040
+ // ignore
2041
+ }
2042
2042
  });
2043
+ po.observe({ type: 'paint', buffered: true });
2043
2044
  }
2044
2045
  catch {
2045
2046
  // ignore
2046
2047
  }
2047
- // CLS(自动处理会话窗口)
2048
+ // LCP(最后一次为准)
2049
+ let lastLcp = null;
2048
2050
  try {
2049
- onCLS((metric) => {
2050
- report(options.reportType, {
2051
- pagePath: getPagePath(),
2052
- metric: 'CLS',
2053
- value: metric.value,
2054
- unit: 'score',
2055
- rating: metric.rating,
2056
- id: metric.id,
2057
- navigationType: metric.navigationType,
2058
- });
2051
+ const po = new PerformanceObserver((list) => {
2052
+ try {
2053
+ const entries = list.getEntries();
2054
+ if (entries && entries.length)
2055
+ lastLcp = entries[entries.length - 1];
2056
+ }
2057
+ catch {
2058
+ // ignore
2059
+ }
2059
2060
  });
2061
+ po.observe({ type: 'largest-contentful-paint', buffered: true });
2062
+ const flushLcp = () => {
2063
+ try {
2064
+ if (!lastLcp)
2065
+ return;
2066
+ if (!sampleHit$1(options.sampleRate))
2067
+ return;
2068
+ if (typeof lastLcp.startTime === 'number')
2069
+ reportMetric(report, options.reportType, 'LCP', lastLcp.startTime, { unit: 'ms' });
2070
+ lastLcp = null;
2071
+ }
2072
+ catch {
2073
+ // ignore
2074
+ }
2075
+ };
2076
+ window.addEventListener('pagehide', flushLcp, { once: true });
2077
+ document.addEventListener('visibilitychange', () => {
2078
+ if (document.visibilityState === 'hidden')
2079
+ flushLcp();
2080
+ }, { once: true });
2060
2081
  }
2061
2082
  catch {
2062
2083
  // ignore
2063
2084
  }
2064
- // INP(替代 FID,2024年3月起成为核心指标)
2085
+ // CLS
2065
2086
  try {
2066
- onINP((metric) => {
2067
- report(options.reportType, {
2068
- pagePath: getPagePath(),
2069
- metric: 'INP',
2070
- value: metric.value,
2071
- unit: 'ms',
2072
- rating: metric.rating,
2073
- id: metric.id,
2074
- navigationType: metric.navigationType,
2075
- });
2087
+ let clsValue = 0;
2088
+ const po = new PerformanceObserver((list) => {
2089
+ try {
2090
+ for (const entry of list.getEntries()) {
2091
+ if (entry && entry.hadRecentInput)
2092
+ continue;
2093
+ if (typeof entry.value === 'number')
2094
+ clsValue += entry.value;
2095
+ }
2096
+ }
2097
+ catch {
2098
+ // ignore
2099
+ }
2076
2100
  });
2101
+ po.observe({ type: 'layout-shift', buffered: true });
2102
+ const flushCls = () => {
2103
+ try {
2104
+ if (!sampleHit$1(options.sampleRate))
2105
+ return;
2106
+ reportMetric(report, options.reportType, 'CLS', clsValue, { unit: 'score' });
2107
+ }
2108
+ catch {
2109
+ // ignore
2110
+ }
2111
+ };
2112
+ window.addEventListener('pagehide', flushCls, { once: true });
2113
+ document.addEventListener('visibilitychange', () => {
2114
+ if (document.visibilityState === 'hidden')
2115
+ flushCls();
2116
+ }, { once: true });
2077
2117
  }
2078
2118
  catch {
2079
2119
  // ignore
2080
2120
  }
2081
- // TTFB
2121
+ // FID
2082
2122
  try {
2083
- onTTFB((metric) => {
2084
- report(options.reportType, {
2085
- pagePath: getPagePath(),
2086
- metric: 'TTFB',
2087
- value: metric.value,
2088
- unit: 'ms',
2089
- rating: metric.rating,
2090
- id: metric.id,
2091
- navigationType: metric.navigationType,
2092
- });
2123
+ const po = new PerformanceObserver((list) => {
2124
+ try {
2125
+ if (!sampleHit$1(options.sampleRate))
2126
+ return;
2127
+ for (const entry of list.getEntries()) {
2128
+ const startTime = typeof entry.startTime === 'number' ? entry.startTime : -1;
2129
+ const processingStart = typeof entry.processingStart === 'number' ? entry.processingStart : -1;
2130
+ if (startTime >= 0 && processingStart >= 0) {
2131
+ reportMetric(report, options.reportType, 'FID', processingStart - startTime, { unit: 'ms' });
2132
+ break;
2133
+ }
2134
+ }
2135
+ }
2136
+ catch {
2137
+ // ignore
2138
+ }
2093
2139
  });
2140
+ po.observe({ type: 'first-input', buffered: true });
2094
2141
  }
2095
2142
  catch {
2096
2143
  // ignore
2097
2144
  }
2098
2145
  }
2099
- // Resource timing:资源加载耗时(web-vitals 不包含此功能,保留原有实现)
2146
+ // Resource timing:资源加载耗时
2100
2147
  if (options.resourceTiming && typeof globalThis.PerformanceObserver === 'function') {
2101
2148
  try {
2102
2149
  const po = new PerformanceObserver((list) => {
@@ -2181,6 +2228,7 @@ function installWebPerformanceMonitor(report, opts = {}) {
2181
2228
  sampleRate: raw.sampleRate ?? 1,
2182
2229
  ignoreUrls: raw.ignoreUrls ?? [],
2183
2230
  webVitals: raw.webVitals ?? true,
2231
+ navigationTiming: raw.navigationTiming ?? true,
2184
2232
  resourceTiming: raw.resourceTiming ?? true,
2185
2233
  maxTextLength: raw.maxTextLength ?? 2000,
2186
2234
  };
@@ -2272,6 +2320,7 @@ function installMiniPerformanceMonitor(report, opts = {}) {
2272
2320
  sampleRate: raw.sampleRate ?? 1,
2273
2321
  ignoreUrls: raw.ignoreUrls ?? [],
2274
2322
  webVitals: raw.webVitals ?? true,
2323
+ navigationTiming: raw.navigationTiming ?? true,
2275
2324
  resourceTiming: raw.resourceTiming ?? true,
2276
2325
  maxTextLength: raw.maxTextLength ?? 2000,
2277
2326
  };
package/dist/index.js CHANGED
@@ -2,8 +2,6 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var webVitals = require('web-vitals');
6
-
7
5
  function isPlainObject(value) {
8
6
  return Object.prototype.toString.call(value) === '[object Object]';
9
7
  }
@@ -1995,10 +1993,14 @@ function getPagePath() {
1995
1993
  return '';
1996
1994
  }
1997
1995
  }
1998
- /**
1999
- * 安装浏览器性能监控
2000
- * 使用 Google web-vitals 库实现 Core Web Vitals 指标采集
2001
- */
1996
+ function reportMetric(report, reportType, metric, value, extra = {}) {
1997
+ report(reportType, {
1998
+ pagePath: getPagePath(),
1999
+ metric,
2000
+ value,
2001
+ ...extra,
2002
+ });
2003
+ }
2002
2004
  function installBrowserPerformanceMonitor(report, options) {
2003
2005
  if (typeof window === 'undefined')
2004
2006
  return;
@@ -2007,100 +2009,145 @@ function installBrowserPerformanceMonitor(report, options) {
2007
2009
  return;
2008
2010
  w.__beLinkClsLoggerPerfInstalled__ = true;
2009
2011
  const ignoreUrls = [...DEFAULT_IGNORE, ...(options.ignoreUrls ?? [])];
2010
- // Web Vitals: 使用 Google web-vitals 库
2011
- // 这个库会自动处理:
2012
- // - LCP 在用户交互后停止观察
2013
- // - firstHiddenTime 过滤(页面隐藏后的数据不计入)
2014
- // - CLS 5秒会话窗口算法
2015
- // - BFCache 恢复时自动重置指标
2016
- if (options.webVitals) {
2017
- // FCP
2012
+ // Navigation timing: TTFB
2013
+ if (options.navigationTiming) {
2018
2014
  try {
2019
- webVitals.onFCP((metric) => {
2020
- report(options.reportType, {
2021
- pagePath: getPagePath(),
2022
- metric: 'FCP',
2023
- value: metric.value,
2024
- unit: 'ms',
2025
- rating: metric.rating,
2026
- id: metric.id,
2027
- navigationType: metric.navigationType,
2028
- });
2029
- });
2015
+ const navEntries = performance.getEntriesByType?.('navigation');
2016
+ const nav = Array.isArray(navEntries) ? navEntries[0] : undefined;
2017
+ if (nav && typeof nav === 'object') {
2018
+ const ttfb = typeof nav.responseStart === 'number' && typeof nav.requestStart === 'number'
2019
+ ? nav.responseStart - nav.requestStart
2020
+ : -1;
2021
+ if (ttfb >= 0 && sampleHit$1(options.sampleRate))
2022
+ reportMetric(report, options.reportType, 'TTFB', ttfb, { unit: 'ms' });
2023
+ }
2030
2024
  }
2031
2025
  catch {
2032
2026
  // ignore
2033
2027
  }
2034
- // LCP(自动处理用户交互后停止)
2028
+ }
2029
+ // Web Vitals: FCP/LCP/CLS/FID
2030
+ if (options.webVitals && typeof globalThis.PerformanceObserver === 'function') {
2031
+ // FCP
2035
2032
  try {
2036
- webVitals.onLCP((metric) => {
2037
- report(options.reportType, {
2038
- pagePath: getPagePath(),
2039
- metric: 'LCP',
2040
- value: metric.value,
2041
- unit: 'ms',
2042
- rating: metric.rating,
2043
- id: metric.id,
2044
- navigationType: metric.navigationType,
2045
- });
2033
+ const po = new PerformanceObserver((list) => {
2034
+ try {
2035
+ if (!sampleHit$1(options.sampleRate))
2036
+ return;
2037
+ for (const entry of list.getEntries()) {
2038
+ if (entry?.name === 'first-contentful-paint' && typeof entry.startTime === 'number') {
2039
+ reportMetric(report, options.reportType, 'FCP', entry.startTime, { unit: 'ms' });
2040
+ }
2041
+ }
2042
+ }
2043
+ catch {
2044
+ // ignore
2045
+ }
2046
2046
  });
2047
+ po.observe({ type: 'paint', buffered: true });
2047
2048
  }
2048
2049
  catch {
2049
2050
  // ignore
2050
2051
  }
2051
- // CLS(自动处理会话窗口)
2052
+ // LCP(最后一次为准)
2053
+ let lastLcp = null;
2052
2054
  try {
2053
- webVitals.onCLS((metric) => {
2054
- report(options.reportType, {
2055
- pagePath: getPagePath(),
2056
- metric: 'CLS',
2057
- value: metric.value,
2058
- unit: 'score',
2059
- rating: metric.rating,
2060
- id: metric.id,
2061
- navigationType: metric.navigationType,
2062
- });
2055
+ const po = new PerformanceObserver((list) => {
2056
+ try {
2057
+ const entries = list.getEntries();
2058
+ if (entries && entries.length)
2059
+ lastLcp = entries[entries.length - 1];
2060
+ }
2061
+ catch {
2062
+ // ignore
2063
+ }
2063
2064
  });
2065
+ po.observe({ type: 'largest-contentful-paint', buffered: true });
2066
+ const flushLcp = () => {
2067
+ try {
2068
+ if (!lastLcp)
2069
+ return;
2070
+ if (!sampleHit$1(options.sampleRate))
2071
+ return;
2072
+ if (typeof lastLcp.startTime === 'number')
2073
+ reportMetric(report, options.reportType, 'LCP', lastLcp.startTime, { unit: 'ms' });
2074
+ lastLcp = null;
2075
+ }
2076
+ catch {
2077
+ // ignore
2078
+ }
2079
+ };
2080
+ window.addEventListener('pagehide', flushLcp, { once: true });
2081
+ document.addEventListener('visibilitychange', () => {
2082
+ if (document.visibilityState === 'hidden')
2083
+ flushLcp();
2084
+ }, { once: true });
2064
2085
  }
2065
2086
  catch {
2066
2087
  // ignore
2067
2088
  }
2068
- // INP(替代 FID,2024年3月起成为核心指标)
2089
+ // CLS
2069
2090
  try {
2070
- webVitals.onINP((metric) => {
2071
- report(options.reportType, {
2072
- pagePath: getPagePath(),
2073
- metric: 'INP',
2074
- value: metric.value,
2075
- unit: 'ms',
2076
- rating: metric.rating,
2077
- id: metric.id,
2078
- navigationType: metric.navigationType,
2079
- });
2091
+ let clsValue = 0;
2092
+ const po = new PerformanceObserver((list) => {
2093
+ try {
2094
+ for (const entry of list.getEntries()) {
2095
+ if (entry && entry.hadRecentInput)
2096
+ continue;
2097
+ if (typeof entry.value === 'number')
2098
+ clsValue += entry.value;
2099
+ }
2100
+ }
2101
+ catch {
2102
+ // ignore
2103
+ }
2080
2104
  });
2105
+ po.observe({ type: 'layout-shift', buffered: true });
2106
+ const flushCls = () => {
2107
+ try {
2108
+ if (!sampleHit$1(options.sampleRate))
2109
+ return;
2110
+ reportMetric(report, options.reportType, 'CLS', clsValue, { unit: 'score' });
2111
+ }
2112
+ catch {
2113
+ // ignore
2114
+ }
2115
+ };
2116
+ window.addEventListener('pagehide', flushCls, { once: true });
2117
+ document.addEventListener('visibilitychange', () => {
2118
+ if (document.visibilityState === 'hidden')
2119
+ flushCls();
2120
+ }, { once: true });
2081
2121
  }
2082
2122
  catch {
2083
2123
  // ignore
2084
2124
  }
2085
- // TTFB
2125
+ // FID
2086
2126
  try {
2087
- webVitals.onTTFB((metric) => {
2088
- report(options.reportType, {
2089
- pagePath: getPagePath(),
2090
- metric: 'TTFB',
2091
- value: metric.value,
2092
- unit: 'ms',
2093
- rating: metric.rating,
2094
- id: metric.id,
2095
- navigationType: metric.navigationType,
2096
- });
2127
+ const po = new PerformanceObserver((list) => {
2128
+ try {
2129
+ if (!sampleHit$1(options.sampleRate))
2130
+ return;
2131
+ for (const entry of list.getEntries()) {
2132
+ const startTime = typeof entry.startTime === 'number' ? entry.startTime : -1;
2133
+ const processingStart = typeof entry.processingStart === 'number' ? entry.processingStart : -1;
2134
+ if (startTime >= 0 && processingStart >= 0) {
2135
+ reportMetric(report, options.reportType, 'FID', processingStart - startTime, { unit: 'ms' });
2136
+ break;
2137
+ }
2138
+ }
2139
+ }
2140
+ catch {
2141
+ // ignore
2142
+ }
2097
2143
  });
2144
+ po.observe({ type: 'first-input', buffered: true });
2098
2145
  }
2099
2146
  catch {
2100
2147
  // ignore
2101
2148
  }
2102
2149
  }
2103
- // Resource timing:资源加载耗时(web-vitals 不包含此功能,保留原有实现)
2150
+ // Resource timing:资源加载耗时
2104
2151
  if (options.resourceTiming && typeof globalThis.PerformanceObserver === 'function') {
2105
2152
  try {
2106
2153
  const po = new PerformanceObserver((list) => {
@@ -2185,6 +2232,7 @@ function installWebPerformanceMonitor(report, opts = {}) {
2185
2232
  sampleRate: raw.sampleRate ?? 1,
2186
2233
  ignoreUrls: raw.ignoreUrls ?? [],
2187
2234
  webVitals: raw.webVitals ?? true,
2235
+ navigationTiming: raw.navigationTiming ?? true,
2188
2236
  resourceTiming: raw.resourceTiming ?? true,
2189
2237
  maxTextLength: raw.maxTextLength ?? 2000,
2190
2238
  };
@@ -2276,6 +2324,7 @@ function installMiniPerformanceMonitor(report, opts = {}) {
2276
2324
  sampleRate: raw.sampleRate ?? 1,
2277
2325
  ignoreUrls: raw.ignoreUrls ?? [],
2278
2326
  webVitals: raw.webVitals ?? true,
2327
+ navigationTiming: raw.navigationTiming ?? true,
2279
2328
  resourceTiming: raw.resourceTiming ?? true,
2280
2329
  maxTextLength: raw.maxTextLength ?? 2000,
2281
2330
  };