lighthouse 12.8.2-dev.20251006 → 12.8.2-dev.20251008

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.
Files changed (91) hide show
  1. package/core/audits/insights/dom-size-insight.js +5 -1
  2. package/core/audits/insights/font-display-insight.js +3 -1
  3. package/core/audits/insights/insight-audit.d.ts +4 -2
  4. package/core/audits/insights/insight-audit.js +22 -3
  5. package/core/audits/predictive-perf.js +2 -2
  6. package/core/audits/redirects.js +0 -1
  7. package/core/audits/seo/crawlable-anchors.js +2 -3
  8. package/core/computed/metrics/lcp-breakdown.d.ts +10 -5
  9. package/core/computed/metrics/lcp-breakdown.js +50 -23
  10. package/core/computed/metrics/time-to-first-byte.js +33 -10
  11. package/core/computed/metrics/timing-summary.js +3 -2
  12. package/core/config/default-config.js +0 -12
  13. package/core/gather/gatherers/anchor-elements.js +8 -24
  14. package/core/gather/gatherers/inspector-issues.js +1 -28
  15. package/core/gather/gatherers/trace-elements.d.ts +0 -9
  16. package/core/gather/gatherers/trace-elements.js +0 -36
  17. package/core/lib/network-request.d.ts +0 -7
  18. package/core/lib/network-request.js +0 -16
  19. package/package.json +2 -4
  20. package/shared/localization/locales/ar-XB.json +20 -38
  21. package/shared/localization/locales/ar.json +20 -38
  22. package/shared/localization/locales/bg.json +9 -27
  23. package/shared/localization/locales/ca.json +9 -27
  24. package/shared/localization/locales/cs.json +9 -27
  25. package/shared/localization/locales/da.json +9 -27
  26. package/shared/localization/locales/de.json +9 -27
  27. package/shared/localization/locales/el.json +9 -27
  28. package/shared/localization/locales/en-GB.json +9 -27
  29. package/shared/localization/locales/en-US.json +18 -45
  30. package/shared/localization/locales/en-XA.json +0 -27
  31. package/shared/localization/locales/en-XL.json +18 -45
  32. package/shared/localization/locales/es-419.json +9 -27
  33. package/shared/localization/locales/es.json +9 -27
  34. package/shared/localization/locales/fi.json +9 -27
  35. package/shared/localization/locales/fil.json +9 -27
  36. package/shared/localization/locales/fr.json +9 -27
  37. package/shared/localization/locales/he.json +31 -49
  38. package/shared/localization/locales/hi.json +9 -27
  39. package/shared/localization/locales/hr.json +9 -27
  40. package/shared/localization/locales/hu.json +9 -27
  41. package/shared/localization/locales/id.json +9 -27
  42. package/shared/localization/locales/it.json +9 -27
  43. package/shared/localization/locales/ja.json +9 -27
  44. package/shared/localization/locales/ko.json +10 -28
  45. package/shared/localization/locales/lt.json +9 -27
  46. package/shared/localization/locales/lv.json +10 -28
  47. package/shared/localization/locales/nl.json +9 -27
  48. package/shared/localization/locales/no.json +9 -27
  49. package/shared/localization/locales/pl.json +9 -27
  50. package/shared/localization/locales/pt-PT.json +9 -27
  51. package/shared/localization/locales/pt.json +9 -27
  52. package/shared/localization/locales/ro.json +10 -28
  53. package/shared/localization/locales/ru.json +9 -27
  54. package/shared/localization/locales/sk.json +9 -27
  55. package/shared/localization/locales/sl.json +9 -27
  56. package/shared/localization/locales/sr-Latn.json +9 -27
  57. package/shared/localization/locales/sr.json +9 -27
  58. package/shared/localization/locales/sv.json +9 -27
  59. package/shared/localization/locales/ta.json +9 -27
  60. package/shared/localization/locales/te.json +10 -28
  61. package/shared/localization/locales/th.json +9 -27
  62. package/shared/localization/locales/tr.json +9 -27
  63. package/shared/localization/locales/uk.json +9 -27
  64. package/shared/localization/locales/vi.json +9 -27
  65. package/shared/localization/locales/zh-HK.json +9 -27
  66. package/shared/localization/locales/zh-TW.json +10 -28
  67. package/shared/localization/locales/zh.json +9 -27
  68. package/tsconfig.json +0 -3
  69. package/types/artifacts.d.ts +4 -43
  70. package/core/audits/byte-efficiency/offscreen-images.d.ts +0 -63
  71. package/core/audits/byte-efficiency/offscreen-images.js +0 -240
  72. package/core/audits/dobetterweb/no-document-write.d.ts +0 -16
  73. package/core/audits/dobetterweb/no-document-write.js +0 -86
  74. package/core/audits/dobetterweb/uses-passive-event-listeners.d.ts +0 -16
  75. package/core/audits/dobetterweb/uses-passive-event-listeners.js +0 -69
  76. package/core/audits/metrics/first-meaningful-paint.d.ts +0 -12
  77. package/core/audits/metrics/first-meaningful-paint.js +0 -47
  78. package/core/computed/critical-request-chains.d.ts +0 -42
  79. package/core/computed/critical-request-chains.js +0 -143
  80. package/core/computed/viewport-meta.d.ts +0 -37
  81. package/core/computed/viewport-meta.js +0 -71
  82. package/core/gather/gatherers/cache-contents.d.ts +0 -11
  83. package/core/gather/gatherers/cache-contents.js +0 -56
  84. package/core/gather/gatherers/dobetterweb/domstats.d.ts +0 -10
  85. package/core/gather/gatherers/dobetterweb/domstats.js +0 -102
  86. package/core/gather/gatherers/dobetterweb/optimized-images.d.ts +0 -48
  87. package/core/gather/gatherers/dobetterweb/optimized-images.js +0 -169
  88. package/core/gather/gatherers/dobetterweb/response-compression.d.ts +0 -23
  89. package/core/gather/gatherers/dobetterweb/response-compression.js +0 -136
  90. package/types/internal/metaviewport-parser.d.ts +0 -13
  91. package/types/internal/parse-cache-control.d.ts +0 -20
@@ -85,7 +85,11 @@ class DOMSizeInsight extends Audit {
85
85
  maxChildren: maxChildren.numChildren,
86
86
  maxDepth: maxDepth.depth,
87
87
  };
88
- return details;
88
+ return {
89
+ details,
90
+ numericValue: totalElements,
91
+ numericUnit: 'element',
92
+ };
89
93
  });
90
94
  }
91
95
  }
@@ -46,7 +46,9 @@ class FontDisplayInsight extends Audit {
46
46
  url: font.request.args.data.url,
47
47
  wastedMs: font.wastedTime,
48
48
  }));
49
- return Audit.makeTableDetails(headings, items);
49
+ const details = Audit.makeTableDetails(headings, items);
50
+ details.skipSumming = ['wastedMs'];
51
+ return details;
50
52
  });
51
53
  }
52
54
  }
@@ -11,13 +11,15 @@ export type CreateDetailsExtras = {
11
11
  * @param {LH.Artifacts} artifacts
12
12
  * @param {LH.Audit.Context} context
13
13
  * @param {T} insightName
14
- * @param {(insight: import('@paulirish/trace_engine/models/trace/insights/types.js').InsightModels[T], extras: CreateDetailsExtras) => {details: LH.Audit.Details, warnings: Array<string | LH.IcuMessage>}|LH.Audit.Details|undefined} createDetails
14
+ * @param {(insight: import('@paulirish/trace_engine/models/trace/insights/types.js').InsightModels[T], extras: CreateDetailsExtras) => {details: LH.Audit.Details, warnings?: Array<string | LH.IcuMessage>, numericValue?: number, numericUnit?: LH.Audit.NumericProduct['numericUnit']}|LH.Audit.Details|undefined} createDetails
15
15
  * @template {keyof import('@paulirish/trace_engine/models/trace/insights/types.js').InsightModelsType} T
16
16
  * @return {Promise<LH.Audit.Product>}
17
17
  */
18
18
  export function adaptInsightToAuditProduct<T extends keyof import("@paulirish/trace_engine/models/trace/insights/types.js").InsightModelsType>(artifacts: LH.Artifacts, context: LH.Audit.Context, insightName: T, createDetails: (insight: import("@paulirish/trace_engine/models/trace/insights/types.js").InsightModels[T], extras: CreateDetailsExtras) => {
19
19
  details: LH.Audit.Details;
20
- warnings: Array<string | LH.IcuMessage>;
20
+ warnings?: Array<string | LH.IcuMessage>;
21
+ numericValue?: number;
22
+ numericUnit?: LH.Audit.NumericProduct["numericUnit"];
21
23
  } | LH.Audit.Details | undefined): Promise<LH.Audit.Product>;
22
24
  /**
23
25
  * @param {LH.Artifacts.TraceElement[]} traceElements
@@ -42,7 +42,7 @@ async function getInsightSet(artifacts, context) {
42
42
  * @param {LH.Artifacts} artifacts
43
43
  * @param {LH.Audit.Context} context
44
44
  * @param {T} insightName
45
- * @param {(insight: import('@paulirish/trace_engine/models/trace/insights/types.js').InsightModels[T], extras: CreateDetailsExtras) => {details: LH.Audit.Details, warnings: Array<string | LH.IcuMessage>}|LH.Audit.Details|undefined} createDetails
45
+ * @param {(insight: import('@paulirish/trace_engine/models/trace/insights/types.js').InsightModels[T], extras: CreateDetailsExtras) => {details: LH.Audit.Details, warnings?: Array<string | LH.IcuMessage>, numericValue?: number, numericUnit?: LH.Audit.NumericProduct['numericUnit']}|LH.Audit.Details|undefined} createDetails
46
46
  * @template {keyof import('@paulirish/trace_engine/models/trace/insights/types.js').InsightModelsType} T
47
47
  * @return {Promise<LH.Audit.Product>}
48
48
  */
@@ -70,11 +70,17 @@ async function adaptInsightToAuditProduct(artifacts, context, insightName, creat
70
70
  });
71
71
 
72
72
  const warnings = [...insight.warnings ?? []];
73
+ /** @type {number|undefined} */
74
+ let numericValue;
75
+ /** @type {LH.Audit.NumericProduct['numericUnit']|undefined} */
76
+ let numericUnit;
73
77
 
74
78
  let details;
75
- if (cbResult && 'warnings' in cbResult) {
79
+ if (cbResult && 'details' in cbResult) {
76
80
  details = cbResult.details;
77
- warnings.push(...cbResult.warnings);
81
+ if (cbResult.warnings) warnings.push(...cbResult.warnings);
82
+ numericValue = cbResult.numericValue;
83
+ numericUnit = cbResult.numericUnit;
78
84
  } else {
79
85
  details = cbResult;
80
86
  }
@@ -163,6 +169,19 @@ async function adaptInsightToAuditProduct(artifacts, context, insightName, creat
163
169
  scoreDisplayMode = Audit.SCORING_MODES.INFORMATIVE;
164
170
  }
165
171
 
172
+ if (numericValue !== undefined && numericUnit !== undefined) {
173
+ return {
174
+ scoreDisplayMode,
175
+ score,
176
+ numericValue,
177
+ numericUnit,
178
+ metricSavings,
179
+ warnings: warnings.length ? warnings : undefined,
180
+ displayValue,
181
+ details,
182
+ };
183
+ }
184
+
166
185
  return {
167
186
  scoreDisplayMode,
168
187
  score,
@@ -76,8 +76,8 @@ class PredictivePerf extends Audit {
76
76
  pessimisticLCP: lcp.pessimisticEstimate.timeInMs,
77
77
 
78
78
  roughEstimateOfTTFB: timingSummary.metrics.timeToFirstByte,
79
- roughEstimateOfLCPLoadStart: timingSummary.metrics.lcpLoadStart,
80
- roughEstimateOfLCPLoadEnd: timingSummary.metrics.lcpLoadEnd,
79
+ roughEstimateOfLCPLoadStart: timingSummary.metrics.lcpLoadDelay,
80
+ roughEstimateOfLCPLoadEnd: timingSummary.metrics.lcpLoadDuration,
81
81
  };
82
82
 
83
83
  const score = Audit.computeLogNormalScore(
@@ -89,7 +89,6 @@ class Redirects extends Audit {
89
89
  const gatherContext = artifacts.GatherContext;
90
90
  const {URL, SourceMaps} = artifacts;
91
91
 
92
- // TODO(v13): use trace processor / network tree insight.
93
92
  const processedTrace = await ProcessedTrace.request(trace, context);
94
93
  const networkRecords = await NetworkRecords.request(devtoolsLog, context);
95
94
  const documentRequests = Redirects.getDocumentRequestChain(networkRecords, processedTrace);
@@ -57,12 +57,10 @@ class CrawlableAnchors extends Audit {
57
57
  href,
58
58
  attributeNames = [],
59
59
  listeners = [],
60
- ancestorListeners = [],
61
60
  }) => {
62
61
  rawHref = rawHref.replace( /\s/g, '');
63
62
  name = name.trim();
64
63
  role = role.trim();
65
- const hasListener = Boolean(listeners.length || ancestorListeners.length);
66
64
 
67
65
  if (role.length > 0) return;
68
66
  // Ignore mailto links even if they use one of the failing patterns. See https://github.com/GoogleChrome/lighthouse/issues/11443#issuecomment-694898412
@@ -86,7 +84,8 @@ class CrawlableAnchors extends Audit {
86
84
  !attributeNames.includes('href') &&
87
85
  hrefAssociatedAttributes.every(attribute => !attributeNames.includes(attribute))
88
86
  ) {
89
- return hasListener;
87
+ // If it has an even listener (e.g. onclick) then we can't assume it's a placeholder. Therefore we consider it failing.
88
+ return Boolean(listeners.length);
90
89
  }
91
90
 
92
91
  if (href === '') return true;
@@ -2,20 +2,25 @@ export { LCPBreakdownComputed as LCPBreakdown };
2
2
  declare const LCPBreakdownComputed: typeof LCPBreakdown & {
3
3
  request: (dependencies: import("../../index.js").Artifacts.MetricComputationDataInput, context: LH.Artifacts.ComputedContext) => Promise<{
4
4
  ttfb: number;
5
- loadStart?: number;
6
- loadEnd?: number;
5
+ loadDelay?: number;
6
+ loadDuration?: number;
7
+ renderDelay?: number;
7
8
  }>;
8
9
  };
10
+ /**
11
+ * Note: this omits renderDelay for simulated throttling.
12
+ */
9
13
  declare class LCPBreakdown {
10
14
  /**
11
15
  * @param {LH.Artifacts.MetricComputationDataInput} data
12
16
  * @param {LH.Artifacts.ComputedContext} context
13
- * @return {Promise<{ttfb: number, loadStart?: number, loadEnd?: number}>}
17
+ * @return {Promise<{ttfb: number, loadDelay?: number, loadDuration?: number, renderDelay?: number}>}
14
18
  */
15
19
  static compute_(data: LH.Artifacts.MetricComputationDataInput, context: LH.Artifacts.ComputedContext): Promise<{
16
20
  ttfb: number;
17
- loadStart?: number;
18
- loadEnd?: number;
21
+ loadDelay?: number;
22
+ loadDuration?: number;
23
+ renderDelay?: number;
19
24
  }>;
20
25
  }
21
26
  //# sourceMappingURL=lcp-breakdown.d.ts.map
@@ -10,43 +10,70 @@ import {LargestContentfulPaint} from './largest-contentful-paint.js';
10
10
  import {ProcessedNavigation} from '../processed-navigation.js';
11
11
  import {TimeToFirstByte} from './time-to-first-byte.js';
12
12
  import {LCPImageRecord} from '../lcp-image-record.js';
13
+ import {NavigationInsights} from '../navigation-insights.js';
13
14
 
14
- // TODO(v13): should use LCPBreakdown insight
15
+ /**
16
+ * Note: this omits renderDelay for simulated throttling.
17
+ */
15
18
  class LCPBreakdown {
16
19
  /**
17
20
  * @param {LH.Artifacts.MetricComputationDataInput} data
18
21
  * @param {LH.Artifacts.ComputedContext} context
19
- * @return {Promise<{ttfb: number, loadStart?: number, loadEnd?: number}>}
22
+ * @return {Promise<{ttfb: number, loadDelay?: number, loadDuration?: number, renderDelay?: number}>}
20
23
  */
21
24
  static async compute_(data, context) {
22
- const processedNavigation = await ProcessedNavigation.request(data.trace, context);
23
- const observedLcp = processedNavigation.timings.largestContentfulPaint;
24
- if (observedLcp === undefined) {
25
- throw new LighthouseError(LighthouseError.errors.NO_LCP);
26
- }
27
- const timeOrigin = processedNavigation.timestamps.timeOrigin / 1000;
25
+ if (data.settings.throttlingMethod === 'simulate') {
26
+ const processedNavigation = await ProcessedNavigation.request(data.trace, context);
27
+ const observedLcp = processedNavigation.timings.largestContentfulPaint;
28
+ if (observedLcp === undefined) {
29
+ throw new LighthouseError(LighthouseError.errors.NO_LCP);
30
+ }
31
+ const timeOrigin = processedNavigation.timestamps.timeOrigin / 1000;
28
32
 
29
- const {timing: ttfb} = await TimeToFirstByte.request(data, context);
33
+ const {timing: ttfb} = await TimeToFirstByte.request(data, context);
30
34
 
31
- const lcpRecord = await LCPImageRecord.request(data, context);
32
- if (!lcpRecord) {
33
- return {ttfb};
34
- }
35
+ const lcpRecord = await LCPImageRecord.request(data, context);
36
+ if (!lcpRecord) {
37
+ return {ttfb};
38
+ }
39
+
40
+ // Official LCP^tm. Will be lantern result if simulated, otherwise same as observedLcp.
41
+ const {timing: metricLcp} = await LargestContentfulPaint.request(data, context);
42
+ const throttleRatio = metricLcp / observedLcp;
35
43
 
36
- // Official LCP^tm. Will be lantern result if simulated, otherwise same as observedLcp.
37
- const {timing: metricLcp} = await LargestContentfulPaint.request(data, context);
38
- const throttleRatio = metricLcp / observedLcp;
44
+ const unclampedLoadStart = (lcpRecord.networkRequestTime - timeOrigin) * throttleRatio;
45
+ const loadDelay = Math.max(ttfb, Math.min(unclampedLoadStart, metricLcp));
39
46
 
40
- const unclampedLoadStart = (lcpRecord.networkRequestTime - timeOrigin) * throttleRatio;
41
- const loadStart = Math.max(ttfb, Math.min(unclampedLoadStart, metricLcp));
47
+ const unclampedLoadEnd = (lcpRecord.networkEndTime - timeOrigin) * throttleRatio;
48
+ const loadDuration = Math.max(loadDelay, Math.min(unclampedLoadEnd, metricLcp));
42
49
 
43
- const unclampedLoadEnd = (lcpRecord.networkEndTime - timeOrigin) * throttleRatio;
44
- const loadEnd = Math.max(loadStart, Math.min(unclampedLoadEnd, metricLcp));
50
+ return {
51
+ ttfb,
52
+ loadDelay,
53
+ loadDuration,
54
+ };
55
+ }
56
+
57
+ const {trace, settings, SourceMaps} = data;
58
+ const navInsights = await NavigationInsights.request({trace, settings, SourceMaps}, context);
59
+ const lcpBreakdown = navInsights.model.LCPBreakdown;
60
+ if (lcpBreakdown instanceof Error) {
61
+ throw new LighthouseError(LighthouseError.errors.NO_LCP, {}, {cause: lcpBreakdown});
62
+ }
63
+
64
+ if (!lcpBreakdown.subparts) {
65
+ throw new LighthouseError(LighthouseError.errors.NO_LCP);
66
+ }
45
67
 
46
68
  return {
47
- ttfb,
48
- loadStart,
49
- loadEnd,
69
+ ttfb: lcpBreakdown.subparts.ttfb.range / 1000,
70
+ loadDelay: lcpBreakdown.subparts.loadDelay !== undefined ?
71
+ lcpBreakdown.subparts.loadDelay.range / 1000 :
72
+ undefined,
73
+ loadDuration: lcpBreakdown.subparts.loadDuration !== undefined ?
74
+ lcpBreakdown.subparts.loadDuration.range / 1000 :
75
+ undefined,
76
+ renderDelay: lcpBreakdown.subparts.renderDelay.range / 1000,
50
77
  };
51
78
  }
52
79
  }
@@ -4,10 +4,14 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
 
7
+ import {calculateDocFirstByteTs} from '@paulirish/trace_engine/models/trace/insights/Common.js';
8
+
7
9
  import {makeComputedArtifact} from '../computed-artifact.js';
8
10
  import {NavigationMetric} from './navigation-metric.js';
9
11
  import {MainResource} from '../main-resource.js';
10
12
  import {NetworkAnalysis} from '../network-analysis.js';
13
+ import {NavigationInsights} from '../navigation-insights.js';
14
+ import {TraceEngineResult} from '../trace-engine-result.js';
11
15
 
12
16
  class TimeToFirstByte extends NavigationMetric {
13
17
  /**
@@ -41,18 +45,37 @@ class TimeToFirstByte extends NavigationMetric {
41
45
  * @return {Promise<LH.Artifacts.Metric>}
42
46
  */
43
47
  static async computeObservedMetric(data, context) {
44
- const mainResource = await MainResource.request(data, context);
45
- if (!mainResource.timing) {
46
- throw new Error('missing timing for main resource');
48
+ const {trace, settings, SourceMaps} = data;
49
+ const traceEngineResult =
50
+ await TraceEngineResult.request({trace, settings, SourceMaps}, context);
51
+ const navInsights = await NavigationInsights.request({trace, settings, SourceMaps}, context);
52
+ const lcpBreakdown = navInsights.model.LCPBreakdown;
53
+
54
+ // Defer to LCP breakdown, but if there's no LCP fallback to manual calculation.
55
+ if (!(lcpBreakdown instanceof Error) && lcpBreakdown.subparts) {
56
+ return {
57
+ timing: lcpBreakdown.subparts.ttfb.range / 1000,
58
+ timestamp: lcpBreakdown.subparts.ttfb.max,
59
+ };
60
+ } else if (navInsights.navigation?.args.data?.navigationId) {
61
+ const request = traceEngineResult.data.NetworkRequests.byId.get(
62
+ navInsights.navigation.args.data.navigationId);
63
+ if (!request) {
64
+ throw new Error();
65
+ }
66
+
67
+ const timestamp = calculateDocFirstByteTs(request);
68
+ if (timestamp === null) {
69
+ throw new Error('cannot calculate ttfb');
70
+ }
71
+
72
+ return {
73
+ timing: (timestamp - navInsights.navigation.ts) / 1000,
74
+ timestamp,
75
+ };
47
76
  }
48
77
 
49
- const {processedNavigation} = data;
50
- const timeOriginTs = processedNavigation.timestamps.timeOrigin;
51
- const timestampMs =
52
- mainResource.timing.requestTime * 1000 + mainResource.timing.receiveHeadersStart;
53
- const timestamp = timestampMs * 1000;
54
- const timing = (timestamp - timeOriginTs) / 1000;
55
- return {timing, timestamp};
78
+ throw new Error('cannot determine ttfb');
56
79
  }
57
80
  }
58
81
 
@@ -96,8 +96,9 @@ class TimingSummary {
96
96
  cumulativeLayoutShift,
97
97
  cumulativeLayoutShiftMainFrame,
98
98
 
99
- lcpLoadStart: lcpBreakdown?.loadStart,
100
- lcpLoadEnd: lcpBreakdown?.loadEnd,
99
+ lcpLoadDelay: lcpBreakdown?.loadDelay,
100
+ lcpLoadDuration: lcpBreakdown?.loadDuration,
101
+ lcpRenderDelay: lcpBreakdown?.renderDelay,
101
102
 
102
103
  timeToFirstByte: ttfb?.timing,
103
104
  timeToFirstByteTs: ttfb?.timestamp,
@@ -114,11 +114,9 @@ const defaultConfig = {
114
114
 
115
115
  {id: 'Accessibility', gatherer: 'accessibility'},
116
116
  {id: 'AnchorElements', gatherer: 'anchor-elements'},
117
- {id: 'CacheContents', gatherer: 'cache-contents'},
118
117
  {id: 'ConsoleMessages', gatherer: 'console-messages'},
119
118
  {id: 'CSSUsage', gatherer: 'css-usage'},
120
119
  {id: 'Doctype', gatherer: 'dobetterweb/doctype'},
121
- {id: 'DOMStats', gatherer: 'dobetterweb/domstats'},
122
120
  {id: 'Inputs', gatherer: 'inputs'},
123
121
  {id: 'IFrameElements', gatherer: 'iframe-elements'},
124
122
  {id: 'ImageElements', gatherer: 'image-elements'},
@@ -128,8 +126,6 @@ const defaultConfig = {
128
126
  {id: 'MainDocumentContent', gatherer: 'main-document-content'},
129
127
  {id: 'MetaElements', gatherer: 'meta-elements'},
130
128
  {id: 'NetworkUserAgent', gatherer: 'network-user-agent'},
131
- {id: 'OptimizedImages', gatherer: 'dobetterweb/optimized-images'},
132
- {id: 'ResponseCompression', gatherer: 'dobetterweb/response-compression'},
133
129
  {id: 'RobotsTxt', gatherer: 'seo/robots-txt'},
134
130
  {id: 'Scripts', gatherer: 'scripts'},
135
131
  {id: 'SourceMaps', gatherer: 'source-maps'},
@@ -149,7 +145,6 @@ const defaultConfig = {
149
145
  'redirects-http',
150
146
  'metrics/first-contentful-paint',
151
147
  'metrics/largest-contentful-paint',
152
- 'metrics/first-meaningful-paint',
153
148
  'metrics/speed-index',
154
149
  'screenshot-thumbnails',
155
150
  'final-screenshot',
@@ -260,7 +255,6 @@ const defaultConfig = {
260
255
  'accessibility/manual/use-landmarks',
261
256
  'accessibility/manual/visual-order-follows-dom',
262
257
  'byte-efficiency/total-byte-weight',
263
- 'byte-efficiency/offscreen-images',
264
258
  'byte-efficiency/unminified-css',
265
259
  'byte-efficiency/unminified-javascript',
266
260
  'byte-efficiency/unused-css-rules',
@@ -269,11 +263,9 @@ const defaultConfig = {
269
263
  'dobetterweb/charset',
270
264
  'dobetterweb/geolocation-on-start',
271
265
  'dobetterweb/inspector-issues',
272
- 'dobetterweb/no-document-write',
273
266
  'dobetterweb/js-libraries',
274
267
  'dobetterweb/notification-on-start',
275
268
  'dobetterweb/paste-preventing-inputs',
276
- 'dobetterweb/uses-passive-event-listeners',
277
269
  'seo/meta-description',
278
270
  'seo/http-status-code',
279
271
  'seo/link-text',
@@ -407,9 +399,7 @@ const defaultConfig = {
407
399
  // These are our "invisible" metrics. Not displayed, but still in the LHR.
408
400
  {id: 'interactive', weight: 0, group: 'hidden', acronym: 'TTI'},
409
401
  {id: 'max-potential-fid', weight: 0, group: 'hidden'},
410
- {id: 'first-meaningful-paint', weight: 0, acronym: 'FMP', group: 'hidden'},
411
402
 
412
- {id: 'offscreen-images', weight: 0, group: 'diagnostics'},
413
403
  {id: 'unminified-css', weight: 0, group: 'diagnostics'},
414
404
  {id: 'unminified-javascript', weight: 0, group: 'diagnostics'},
415
405
  {id: 'unused-css-rules', weight: 0, group: 'diagnostics'},
@@ -418,8 +408,6 @@ const defaultConfig = {
418
408
  {id: 'user-timings', weight: 0, group: 'diagnostics'},
419
409
  {id: 'bootup-time', weight: 0, group: 'diagnostics'},
420
410
  {id: 'mainthread-work-breakdown', weight: 0, group: 'diagnostics'},
421
- {id: 'uses-passive-event-listeners', weight: 0, group: 'diagnostics'},
422
- {id: 'no-document-write', weight: 0, group: 'diagnostics'},
423
411
  {id: 'long-tasks', weight: 0, group: 'diagnostics'},
424
412
  {id: 'non-composited-animations', weight: 0, group: 'diagnostics'},
425
413
  {id: 'unsized-images', weight: 0, group: 'diagnostics'},
@@ -157,33 +157,17 @@ class AnchorElements extends BaseGatherer {
157
157
 
158
158
  // DOM.getDocument is necessary for pushNodesByBackendIdsToFrontend to properly retrieve nodeIds if the `DOM` domain was enabled before this gatherer, invoke it to be safe.
159
159
  await session.sendCommand('DOM.getDocument', {depth: -1, pierce: true});
160
- const anchorsWithEventListeners = anchors.map(async anchor => {
161
- const listeners = await getEventListeners(session, anchor.node.devtoolsNodePath);
162
-
163
- /** @type {Set<{type: string}>} */
164
- const ancestorListeners = new Set();
165
- const splitPath = anchor.node.devtoolsNodePath.split(',');
166
- const ancestorListenerPromises = [];
167
- while (splitPath.length >= 2) {
168
- splitPath.length -= 2;
169
- const path = splitPath.join(',');
170
- const promise = getEventListeners(session, path).then(listeners => {
171
- for (const listener of listeners) {
172
- ancestorListeners.add(listener);
173
- }
174
- }).catch(() => {});
175
- ancestorListenerPromises.push(promise);
176
- }
177
-
178
- await Promise.all(ancestorListenerPromises);
179
160
 
180
- return {
181
- ...anchor,
182
- listeners,
183
- ancestorListeners: Array.from(ancestorListeners),
184
- };
161
+ const anchorsWithEventListeners = anchors.map( anchor => {
162
+ return getEventListeners(session, anchor.node.devtoolsNodePath).then(listeners => {
163
+ return {
164
+ ...anchor,
165
+ listeners,
166
+ };
167
+ });
185
168
  });
186
169
 
170
+
187
171
  const result = await Promise.all(anchorsWithEventListeners);
188
172
  await session.sendCommand('DOM.disable');
189
173
  return result;
@@ -61,34 +61,7 @@ class InspectorIssues extends BaseGatherer {
61
61
  const networkRecords = await NetworkRecords.request(devtoolsLog, context);
62
62
 
63
63
  /** @type {LH.Artifacts.InspectorIssues} */
64
- const artifact = {
65
- // TODO(v13): remove empty arrays.
66
- attributionReportingIssue: [],
67
- blockedByResponseIssue: [],
68
- bounceTrackingIssue: [],
69
- clientHintIssue: [],
70
- contentSecurityPolicyIssue: [],
71
- cookieDeprecationMetadataIssue: [],
72
- corsIssue: [],
73
- deprecationIssue: [],
74
- federatedAuthRequestIssue: [],
75
- genericIssue: [],
76
- heavyAdIssue: [],
77
- lowTextContrastIssue: [],
78
- mixedContentIssue: [],
79
- navigatorUserAgentIssue: [],
80
- partitioningBlobURLIssue: [],
81
- propertyRuleIssue: [],
82
- quirksModeIssue: [],
83
- cookieIssue: [],
84
- elementAccessibilityIssue: [],
85
- sharedArrayBufferIssue: [],
86
- sharedDictionaryIssue: [],
87
- stylesheetLoadingIssue: [],
88
- sriMessageSignatureIssue: [],
89
- federatedAuthUserInfoRequestIssue: [],
90
- userReidentificationIssue: [],
91
- };
64
+ const artifact = {};
92
65
 
93
66
  for (const issue of this._issues) {
94
67
  const detailsKey = /** @type {keyof LH.Crdp.Audits.InspectorIssueDetails} */(
@@ -45,15 +45,6 @@ declare class TraceElements extends BaseGatherer {
45
45
  * @return {Promise<TraceElementData|undefined>}
46
46
  */
47
47
  static getResponsivenessElement(trace: LH.Trace, context: LH.Gatherer.Context): Promise<TraceElementData | undefined>;
48
- /**
49
- * @param {LH.Trace} trace
50
- * @param {LH.Gatherer.Context} context
51
- * @return {Promise<{nodeId: number, type: string} | undefined>}
52
- */
53
- static getLcpElement(trace: LH.Trace, context: LH.Gatherer.Context): Promise<{
54
- nodeId: number;
55
- type: string;
56
- } | undefined>;
57
48
  /** @type {LH.Gatherer.GathererMeta<'Trace'|'SourceMaps'>} */
58
49
  meta: LH.Gatherer.GathererMeta<"Trace" | "SourceMaps">;
59
50
  /** @type {Map<string, string>} */
@@ -18,8 +18,6 @@ import {pageFunctions} from '../../lib/page-functions.js';
18
18
  import {Sentry} from '../../lib/sentry.js';
19
19
  import Trace from './trace.js';
20
20
  import {ProcessedTrace} from '../../computed/processed-trace.js';
21
- import {ProcessedNavigation} from '../../computed/processed-navigation.js';
22
- import {LighthouseError} from '../../lib/lh-error.js';
23
21
  import {Responsiveness} from '../../computed/metrics/responsiveness.js';
24
22
  import {CumulativeLayoutShift} from '../../computed/metrics/cumulative-layout-shift.js';
25
23
  import {ExecutionContext} from '../driver/execution-context.js';
@@ -276,35 +274,6 @@ class TraceElements extends BaseGatherer {
276
274
  return animatedElementData;
277
275
  }
278
276
 
279
- /**
280
- * @param {LH.Trace} trace
281
- * @param {LH.Gatherer.Context} context
282
- * @return {Promise<{nodeId: number, type: string} | undefined>}
283
- */
284
- static async getLcpElement(trace, context) {
285
- let processedNavigation;
286
- try {
287
- processedNavigation = await ProcessedNavigation.request(trace, context);
288
- } catch (err) {
289
- // If we were running in timespan mode and there was no paint, treat LCP as missing.
290
- if (context.gatherMode === 'timespan' && err.code === LighthouseError.errors.NO_FCP.code) {
291
- return;
292
- }
293
-
294
- throw err;
295
- }
296
-
297
- // Use main-frame-only LCP to match the metric value.
298
- const lcpData = processedNavigation.largestContentfulPaintEvt?.args?.data;
299
- // These should exist, but trace types are loose.
300
- if (lcpData?.nodeId === undefined || !lcpData.type) return;
301
-
302
- return {
303
- nodeId: lcpData.nodeId,
304
- type: lcpData.type,
305
- };
306
- }
307
-
308
277
  /**
309
278
  * @param {LH.Gatherer.Context} context
310
279
  */
@@ -372,20 +341,15 @@ class TraceElements extends BaseGatherer {
372
341
 
373
342
  const traceEngineData = await TraceElements.getTraceEngineElements(
374
343
  traceEngineResult, navigationId);
375
- const lcpNodeData = await TraceElements.getLcpElement(trace, context);
376
344
  const shiftsData = await TraceElements.getTopLayoutShifts(
377
345
  trace, traceEngineResult, context);
378
346
  const animatedElementData = await this.getAnimatedElements(mainThreadEvents);
379
- const responsivenessElementData = await TraceElements.getResponsivenessElement(trace, context);
380
347
 
381
- // TODO(v13): remove unnecessary entries.
382
348
  /** @type {Map<string, TraceElementData[]>} */
383
349
  const backendNodeDataMap = new Map([
384
350
  ['trace-engine', traceEngineData],
385
- ['largest-contentful-paint', lcpNodeData ? [lcpNodeData] : []],
386
351
  ['layout-shift', shiftsData],
387
352
  ['animation', animatedElementData],
388
- ['responsiveness', responsivenessElementData ? [responsivenessElementData] : []],
389
353
  ]);
390
354
 
391
355
  /** @type {Map<number, LH.Crdp.Runtime.CallFunctionOnResponse | null>} */
@@ -241,13 +241,6 @@ export class NetworkRequest {
241
241
  * LR loses protocol information.
242
242
  */
243
243
  _updateProtocolForLightrider(): void;
244
- /**
245
- * TODO(compat): remove M116.
246
- * `timing.receiveHeadersStart` was added recently, and will be in M116. Until then,
247
- * set it to receiveHeadersEnd, which is close enough, to allow consumers of NetworkRequest
248
- * to use the new field without accounting for this backcompat.
249
- */
250
- _backfillReceiveHeaderStartTiming(): void;
251
244
  /**
252
245
  * LR gets additional, accurate timing information from its underlying fetch infrastructure. This
253
246
  * is passed in via X-Headers similar to 'X-TotalFetchedSize'.
@@ -286,7 +286,6 @@ class NetworkRequest {
286
286
  }
287
287
 
288
288
  this._updateResponseHeadersEndTimeIfNecessary();
289
- this._backfillReceiveHeaderStartTiming();
290
289
  this._updateTransferSizeForLightrider();
291
290
  this._updateTimingsForLightrider();
292
291
  }
@@ -306,7 +305,6 @@ class NetworkRequest {
306
305
  this.localizedFailDescription = data.errorText;
307
306
 
308
307
  this._updateResponseHeadersEndTimeIfNecessary();
309
- this._backfillReceiveHeaderStartTiming();
310
308
  this._updateTransferSizeForLightrider();
311
309
  this._updateTimingsForLightrider();
312
310
  }
@@ -329,7 +327,6 @@ class NetworkRequest {
329
327
  this.networkEndTime = data.timestamp * 1000;
330
328
 
331
329
  this._updateResponseHeadersEndTimeIfNecessary();
332
- this._backfillReceiveHeaderStartTiming();
333
330
  }
334
331
 
335
332
  /**
@@ -463,19 +460,6 @@ class NetworkRequest {
463
460
  }
464
461
  }
465
462
 
466
- /**
467
- * TODO(compat): remove M116.
468
- * `timing.receiveHeadersStart` was added recently, and will be in M116. Until then,
469
- * set it to receiveHeadersEnd, which is close enough, to allow consumers of NetworkRequest
470
- * to use the new field without accounting for this backcompat.
471
- */
472
- _backfillReceiveHeaderStartTiming() {
473
- // Do nothing if a value is already present!
474
- if (!this.timing || this.timing.receiveHeadersStart !== undefined) return;
475
-
476
- this.timing.receiveHeadersStart = this.timing.receiveHeadersEnd;
477
- }
478
-
479
463
  /**
480
464
  * LR gets additional, accurate timing information from its underlying fetch infrastructure. This
481
465
  * is passed in via X-Headers similar to 'X-TotalFetchedSize'.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "lighthouse",
3
3
  "type": "module",
4
- "version": "12.8.2-dev.20251006",
4
+ "version": "12.8.2-dev.20251008",
5
5
  "description": "Automated auditing, performance metrics, and best practices for the web.",
6
6
  "main": "./core/index.js",
7
7
  "bin": {
@@ -181,7 +181,7 @@
181
181
  "webtreemap-cdt": "^3.2.1"
182
182
  },
183
183
  "dependencies": {
184
- "@paulirish/trace_engine": "0.0.60",
184
+ "@paulirish/trace_engine": "0.0.61",
185
185
  "@sentry/node": "^9.28.1",
186
186
  "axe-core": "^4.10.3",
187
187
  "chrome-launcher": "^1.2.1",
@@ -197,9 +197,7 @@
197
197
  "lighthouse-stack-packs": "1.12.3",
198
198
  "lodash-es": "^4.17.21",
199
199
  "lookup-closest-locale": "6.2.0",
200
- "metaviewport-parser": "0.3.0",
201
200
  "open": "^8.4.0",
202
- "parse-cache-control": "1.0.1",
203
201
  "puppeteer-core": "^24.23.0",
204
202
  "robots-parser": "^3.0.1",
205
203
  "speedline-core": "^1.4.3",