lighthouse 12.3.0-dev.20250223 → 12.3.0-dev.20250225

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 (97) hide show
  1. package/core/audits/audit.js +1 -0
  2. package/core/audits/insights/cls-culprits-insight.d.ts +14 -0
  3. package/core/audits/insights/cls-culprits-insight.js +97 -12
  4. package/core/audits/insights/document-latency-insight.js +1 -0
  5. package/core/audits/insights/dom-size-insight.js +1 -0
  6. package/core/audits/insights/font-display-insight.js +1 -0
  7. package/core/audits/insights/image-delivery-insight.js +6 -0
  8. package/core/audits/insights/interaction-to-next-paint-insight.js +1 -0
  9. package/core/audits/insights/lcp-discovery-insight.js +1 -0
  10. package/core/audits/insights/lcp-phases-insight.js +1 -0
  11. package/core/audits/insights/long-critical-network-tree-insight.js +1 -0
  12. package/core/audits/insights/render-blocking-insight.js +1 -0
  13. package/core/audits/insights/third-parties-insight.js +1 -0
  14. package/core/audits/insights/viewport-insight.js +1 -0
  15. package/core/audits/layout-shifts.d.ts +0 -1
  16. package/core/audits/layout-shifts.js +18 -21
  17. package/core/config/default-config.js +0 -1
  18. package/core/gather/gatherers/trace-elements.d.ts +2 -2
  19. package/core/gather/gatherers/trace-elements.js +27 -20
  20. package/core/lib/bf-cache-strings.d.ts +7 -4
  21. package/core/lib/bf-cache-strings.js +174 -140
  22. package/core/lib/cdt/generated/ParsedURL.d.ts +1 -0
  23. package/core/lib/cdt/generated/ParsedURL.js +16 -4
  24. package/core/lib/cdt/generated/SourceMap.d.ts +32 -5
  25. package/core/lib/cdt/generated/SourceMap.js +192 -100
  26. package/core/lib/deprecations-strings.d.ts +78 -98
  27. package/core/lib/deprecations-strings.js +23 -41
  28. package/core/lib/i18n/i18n.d.ts +1 -0
  29. package/core/lib/i18n/i18n.js +2 -0
  30. package/dist/report/bundle.esm.js +164 -15
  31. package/dist/report/flow.js +159 -10
  32. package/dist/report/standalone.js +158 -9
  33. package/package.json +3 -3
  34. package/readme.md +1 -0
  35. package/report/assets/styles.css +141 -5
  36. package/report/assets/templates.html +13 -0
  37. package/report/renderer/components.js +2 -2
  38. package/report/renderer/details-renderer.js +3 -0
  39. package/report/renderer/dom.d.ts +12 -1
  40. package/report/renderer/dom.js +26 -1
  41. package/report/renderer/performance-category-renderer.d.ts +2 -2
  42. package/report/renderer/performance-category-renderer.js +58 -23
  43. package/report/renderer/topbar-features.js +4 -5
  44. package/shared/localization/locales/ar-XB.json +0 -24
  45. package/shared/localization/locales/ar.json +0 -24
  46. package/shared/localization/locales/bg.json +0 -24
  47. package/shared/localization/locales/ca.json +0 -24
  48. package/shared/localization/locales/cs.json +0 -24
  49. package/shared/localization/locales/da.json +0 -24
  50. package/shared/localization/locales/de.json +0 -24
  51. package/shared/localization/locales/el.json +0 -24
  52. package/shared/localization/locales/en-GB.json +0 -24
  53. package/shared/localization/locales/en-US.json +33 -27
  54. package/shared/localization/locales/en-XA.json +0 -24
  55. package/shared/localization/locales/en-XL.json +33 -27
  56. package/shared/localization/locales/es-419.json +0 -24
  57. package/shared/localization/locales/es.json +0 -24
  58. package/shared/localization/locales/fi.json +0 -24
  59. package/shared/localization/locales/fil.json +0 -24
  60. package/shared/localization/locales/fr.json +0 -24
  61. package/shared/localization/locales/he.json +0 -24
  62. package/shared/localization/locales/hi.json +0 -24
  63. package/shared/localization/locales/hr.json +0 -24
  64. package/shared/localization/locales/hu.json +0 -24
  65. package/shared/localization/locales/id.json +0 -24
  66. package/shared/localization/locales/it.json +0 -24
  67. package/shared/localization/locales/ja.json +0 -24
  68. package/shared/localization/locales/ko.json +0 -24
  69. package/shared/localization/locales/lt.json +0 -24
  70. package/shared/localization/locales/lv.json +0 -24
  71. package/shared/localization/locales/nl.json +0 -24
  72. package/shared/localization/locales/no.json +0 -24
  73. package/shared/localization/locales/pl.json +0 -24
  74. package/shared/localization/locales/pt-PT.json +0 -24
  75. package/shared/localization/locales/pt.json +0 -24
  76. package/shared/localization/locales/ro.json +0 -24
  77. package/shared/localization/locales/ru.json +0 -24
  78. package/shared/localization/locales/sk.json +0 -24
  79. package/shared/localization/locales/sl.json +0 -24
  80. package/shared/localization/locales/sr-Latn.json +0 -24
  81. package/shared/localization/locales/sr.json +0 -24
  82. package/shared/localization/locales/sv.json +0 -24
  83. package/shared/localization/locales/ta.json +0 -24
  84. package/shared/localization/locales/te.json +0 -24
  85. package/shared/localization/locales/th.json +0 -24
  86. package/shared/localization/locales/tr.json +0 -24
  87. package/shared/localization/locales/uk.json +0 -24
  88. package/shared/localization/locales/vi.json +0 -24
  89. package/shared/localization/locales/zh-HK.json +0 -24
  90. package/shared/localization/locales/zh-TW.json +0 -24
  91. package/shared/localization/locales/zh.json +0 -24
  92. package/types/artifacts.d.ts +1 -2
  93. package/types/audit.d.ts +2 -0
  94. package/types/lhr/audit-details.d.ts +5 -1
  95. package/types/lhr/audit-result.d.ts +2 -0
  96. package/core/gather/gatherers/root-causes.d.ts +0 -19
  97. package/core/gather/gatherers/root-causes.js +0 -144
@@ -478,6 +478,7 @@ class Audit {
478
478
 
479
479
  details: product.details,
480
480
  guidanceLevel: audit.meta.guidanceLevel,
481
+ replacesAudits: audit.meta.replacesAudits,
481
482
  };
482
483
  }
483
484
 
@@ -1,5 +1,16 @@
1
1
  export default CLSCulpritsInsight;
2
+ export type SubItem = {
3
+ extra?: LH.Audit.Details.NodeValue | LH.Audit.Details.UrlValue;
4
+ cause: LH.IcuMessage;
5
+ };
2
6
  declare class CLSCulpritsInsight extends Audit {
7
+ /**
8
+ * @param {import('@paulirish/trace_engine/models/trace/insights/CLSCulprits.js').CLSCulpritsInsightModel} insight
9
+ * @param {import('../../lib/trace-engine.js').SaneSyntheticLayoutShift} event
10
+ * @param {LH.Artifacts.TraceElement[]} TraceElements
11
+ * @return {LH.Audit.Details.TableSubItems|undefined}
12
+ */
13
+ static getCulpritSubItems(insight: import("@paulirish/trace_engine/models/trace/insights/CLSCulprits.js").CLSCulpritsInsightModel, event: import("../../lib/trace-engine.js").SaneSyntheticLayoutShift, TraceElements: LH.Artifacts.TraceElement[]): LH.Audit.Details.TableSubItems | undefined;
3
14
  /**
4
15
  * @param {LH.Artifacts} artifacts
5
16
  * @param {LH.Audit.Context} context
@@ -7,5 +18,8 @@ declare class CLSCulpritsInsight extends Audit {
7
18
  */
8
19
  static audit(artifacts: LH.Artifacts, context: LH.Audit.Context): Promise<LH.Audit.Product>;
9
20
  }
21
+ export namespace UIStrings {
22
+ let columnScore: string;
23
+ }
10
24
  import { Audit } from '../audit.js';
11
25
  //# sourceMappingURL=cls-culprits-insight.d.ts.map
@@ -1,19 +1,32 @@
1
- /* eslint-disable no-unused-vars */ // TODO: remove once implemented.
2
-
3
1
  /**
4
2
  * @license
5
3
  * Copyright 2025 Google LLC
6
4
  * SPDX-License-Identifier: Apache-2.0
7
5
  */
8
6
 
9
- import {UIStrings} from '@paulirish/trace_engine/models/trace/insights/CLSCulprits.js';
7
+ import {UIStrings as InsightUIStrings} from '@paulirish/trace_engine/models/trace/insights/CLSCulprits.js';
10
8
 
11
9
  import {Audit} from '../audit.js';
12
10
  import * as i18n from '../../lib/i18n/i18n.js';
13
11
  import {adaptInsightToAuditProduct, makeNodeItemForNodeId} from './insight-audit.js';
12
+ import TraceElements from '../../gather/gatherers/trace-elements.js';
13
+ import {CumulativeLayoutShift} from '../../computed/metrics/cumulative-layout-shift.js';
14
+
15
+ const MAX_LAYOUT_SHIFTS_PER_CLUSTER = 5;
16
+
17
+ /** @typedef {{extra?: LH.Audit.Details.NodeValue | LH.Audit.Details.UrlValue, cause: LH.IcuMessage}} SubItem */
14
18
 
15
19
  // eslint-disable-next-line max-len
16
- const str_ = i18n.createIcuMessageFn('node_modules/@paulirish/trace_engine/models/trace/insights/CLSCulprits.js', UIStrings);
20
+ const insightStr_ = i18n.createIcuMessageFn('node_modules/@paulirish/trace_engine/models/trace/insights/CLSCulprits.js', InsightUIStrings);
21
+
22
+ /* eslint-disable max-len */
23
+ const UIStrings = {
24
+ /** Label for a column in a data table; entries in this column will be a number representing how large the layout shift was. */
25
+ columnScore: 'Layout shift score',
26
+ };
27
+ /* eslint-enable max-len */
28
+
29
+ const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
17
30
 
18
31
  class CLSCulpritsInsight extends Audit {
19
32
  /**
@@ -22,31 +35,103 @@ class CLSCulpritsInsight extends Audit {
22
35
  static get meta() {
23
36
  return {
24
37
  id: 'cls-culprits-insight',
25
- title: str_(UIStrings.title),
26
- failureTitle: str_(UIStrings.title),
27
- description: str_(UIStrings.description),
38
+ title: insightStr_(InsightUIStrings.title),
39
+ failureTitle: insightStr_(InsightUIStrings.title),
40
+ description: insightStr_(InsightUIStrings.description),
28
41
  guidanceLevel: 3,
29
42
  requiredArtifacts: ['traces', 'TraceElements'],
43
+ replacesAudits: ['layout-shifts', 'non-composited-animations', 'unsized-images'],
30
44
  };
31
45
  }
32
46
 
47
+ /**
48
+ * @param {import('@paulirish/trace_engine/models/trace/insights/CLSCulprits.js').CLSCulpritsInsightModel} insight
49
+ * @param {import('../../lib/trace-engine.js').SaneSyntheticLayoutShift} event
50
+ * @param {LH.Artifacts.TraceElement[]} TraceElements
51
+ * @return {LH.Audit.Details.TableSubItems|undefined}
52
+ */
53
+ static getCulpritSubItems(insight, event, TraceElements) {
54
+ const culprits = insight.shifts.get(event);
55
+ if (!culprits) {
56
+ return;
57
+ }
58
+
59
+ /** @type {SubItem[]} */
60
+ const subItems = [];
61
+ for (const backendNodeId of culprits.unsizedImages) {
62
+ subItems.push({
63
+ extra: makeNodeItemForNodeId(TraceElements, backendNodeId),
64
+ cause: insightStr_(InsightUIStrings.unsizedImages),
65
+ });
66
+ }
67
+ for (const request of culprits.fontRequests) {
68
+ const url = request.args.data.url;
69
+ subItems.push({
70
+ extra: {type: 'url', value: url},
71
+ cause: insightStr_(InsightUIStrings.fontRequest),
72
+ });
73
+ }
74
+ if (culprits.iframeIds.length) {
75
+ subItems.push({
76
+ cause: insightStr_(InsightUIStrings.injectedIframe),
77
+ });
78
+ }
79
+
80
+ if (subItems.length) {
81
+ return {type: 'subitems', items: subItems};
82
+ }
83
+ }
84
+
33
85
  /**
34
86
  * @param {LH.Artifacts} artifacts
35
87
  * @param {LH.Audit.Context} context
36
88
  * @return {Promise<LH.Audit.Product>}
37
89
  */
38
90
  static async audit(artifacts, context) {
39
- // TODO: implement.
40
91
  return adaptInsightToAuditProduct(artifacts, context, 'CLSCulprits', (insight) => {
41
92
  /** @type {LH.Audit.Details.Table['headings']} */
42
93
  const headings = [
94
+ /* eslint-disable max-len */
95
+ {key: 'node', valueType: 'node', subItemsHeading: {key: 'extra'}, label: insightStr_(i18n.UIStrings.columnElement)},
96
+ {key: 'score', valueType: 'numeric', subItemsHeading: {key: 'cause', valueType: 'text'}, granularity: 0.001, label: str_(UIStrings.columnScore)},
97
+ /* eslint-enable max-len */
43
98
  ];
44
- /** @type {LH.Audit.Details.Table['items']} */
45
- const items = [
46
- ];
47
- return Audit.makeTableDetails(headings, items);
99
+
100
+ const tables = insight.clusters.map(cluster => {
101
+ const events =
102
+ /** @type {import('../../lib/trace-engine.js').SaneSyntheticLayoutShift[]} */ (
103
+ cluster.events.filter(e => !!e.args.data)
104
+ ).sort((a, b) => b.args.data.weighted_score_delta - a.args.data.weighted_score_delta)
105
+ .slice(0, MAX_LAYOUT_SHIFTS_PER_CLUSTER);
106
+ const impactByNodeId = CumulativeLayoutShift.getImpactByNodeId(events.map(e => ({
107
+ impactedNodes: e.args.data.impacted_nodes,
108
+ ts: e.ts,
109
+ isMainFrame: e.args.data.is_main_frame,
110
+ weightedScore: e.args.data.weighted_score_delta,
111
+ event: /** @type {any} */ (e),
112
+ })));
113
+
114
+ /** @type {LH.Audit.Details.Table['items']} */
115
+ const items = events.map(event => {
116
+ const biggestImpactNodeId = TraceElements.getBiggestImpactNodeForShiftEvent(
117
+ event.args.data.impacted_nodes || [], impactByNodeId, event);
118
+ return {
119
+ node: makeNodeItemForNodeId(artifacts.TraceElements, biggestImpactNodeId),
120
+ score: event.args.data?.weighted_score_delta,
121
+ subItems: this.getCulpritSubItems(insight, event, artifacts.TraceElements),
122
+ };
123
+ });
124
+ items.unshift({
125
+ node: {type: 'text', value: insightStr_(i18n.UIStrings.total)},
126
+ score: cluster.clusterCumulativeScore,
127
+ });
128
+ return Audit.makeTableDetails(headings, items);
129
+ });
130
+
131
+ return Audit.makeListDetails(tables);
48
132
  });
49
133
  }
50
134
  }
51
135
 
52
136
  export default CLSCulpritsInsight;
137
+ export {UIStrings};
@@ -25,6 +25,7 @@ class DocumentLatencyInsight extends Audit {
25
25
  description: str_(UIStrings.description),
26
26
  guidanceLevel: 3,
27
27
  requiredArtifacts: ['traces', 'TraceElements'],
28
+ replacesAudits: ['redirects', 'server-response-time', 'uses-text-compression'],
28
29
  };
29
30
  }
30
31
 
@@ -25,6 +25,7 @@ class DOMSizeInsight extends Audit {
25
25
  description: str_(UIStrings.description),
26
26
  guidanceLevel: 3,
27
27
  requiredArtifacts: ['traces', 'TraceElements'],
28
+ replacesAudits: ['dom-size'],
28
29
  };
29
30
  }
30
31
 
@@ -27,6 +27,7 @@ class FontDisplayInsight extends Audit {
27
27
  description: str_(UIStrings.description),
28
28
  guidanceLevel: 3,
29
29
  requiredArtifacts: ['traces', 'TraceElements'],
30
+ replacesAudits: ['font-display'],
30
31
  };
31
32
  }
32
33
 
@@ -25,6 +25,12 @@ class ImageDeliveryInsight extends Audit {
25
25
  description: str_(UIStrings.description),
26
26
  guidanceLevel: 3,
27
27
  requiredArtifacts: ['traces', 'TraceElements'],
28
+ replacesAudits: [
29
+ 'modern-image-formats',
30
+ 'uses-optimized-images',
31
+ 'efficient-animated-content',
32
+ 'uses-responsive-images',
33
+ ],
28
34
  };
29
35
  }
30
36
 
@@ -25,6 +25,7 @@ class InteractionToNextPaintInsight extends Audit {
25
25
  description: str_(UIStrings.description),
26
26
  guidanceLevel: 3,
27
27
  requiredArtifacts: ['traces', 'TraceElements'],
28
+ replacesAudits: ['work-during-interaction'],
28
29
  };
29
30
  }
30
31
 
@@ -25,6 +25,7 @@ class LCPDiscoveryInsight extends Audit {
25
25
  description: str_(UIStrings.description),
26
26
  guidanceLevel: 3,
27
27
  requiredArtifacts: ['traces', 'TraceElements'],
28
+ replacesAudits: ['prioritize-lcp-image', 'lcp-lazy-loaded'],
28
29
  };
29
30
  }
30
31
 
@@ -25,6 +25,7 @@ class LCPPhasesInsight extends Audit {
25
25
  description: str_(UIStrings.description),
26
26
  guidanceLevel: 3,
27
27
  requiredArtifacts: ['traces', 'TraceElements'],
28
+ replacesAudits: ['largest-contentful-paint-element'],
28
29
  };
29
30
  }
30
31
 
@@ -27,6 +27,7 @@ class LongCriticalNetworkTreeInsight extends Audit {
27
27
  description: str_(UIStrings.description),
28
28
  guidanceLevel: 3,
29
29
  requiredArtifacts: ['traces', 'TraceElements'],
30
+ replacesAudits: ['critical-request-chains'],
30
31
  };
31
32
  }
32
33
 
@@ -25,6 +25,7 @@ class RenderBlockingInsight extends Audit {
25
25
  description: str_(UIStrings.description),
26
26
  guidanceLevel: 3,
27
27
  requiredArtifacts: ['traces', 'TraceElements'],
28
+ replacesAudits: ['render-blocking-resources'],
28
29
  };
29
30
  }
30
31
 
@@ -32,6 +32,7 @@ class ThirdPartiesInsight extends Audit {
32
32
  description: str_(UIStrings.description),
33
33
  guidanceLevel: 3,
34
34
  requiredArtifacts: ['traces', 'TraceElements'],
35
+ replacesAudits: ['third-party-summary'],
35
36
  };
36
37
  }
37
38
 
@@ -25,6 +25,7 @@ class ViewportInsight extends Audit {
25
25
  description: str_(UIStrings.description),
26
26
  guidanceLevel: 3,
27
27
  requiredArtifacts: ['traces', 'TraceElements'],
28
+ replacesAudits: ['viewport'],
28
29
  };
29
30
  }
30
31
 
@@ -26,7 +26,6 @@ export namespace UIStrings {
26
26
  let rootCauseUnsizedMedia: string;
27
27
  let rootCauseFontChanges: string;
28
28
  let rootCauseInjectedIframe: string;
29
- let rootCauseRenderBlockingRequest: string;
30
29
  let displayValueShiftsFound: string;
31
30
  }
32
31
  import { Audit } from './audit.js';
@@ -29,8 +29,6 @@ const UIStrings = {
29
29
  rootCauseFontChanges: 'Web font loaded',
30
30
  /** A possible reason why that the layout shift occured. */
31
31
  rootCauseInjectedIframe: 'Injected iframe',
32
- /** A possible reason why that the layout shift occured. */
33
- rootCauseRenderBlockingRequest: 'A late network request adjusted the page layout',
34
32
  /** Label shown per-audit to show how many layout shifts are present. The `{# shifts found}` placeholder will be replaced with the number of layout shifts. */
35
33
  displayValueShiftsFound: `{shiftCount, plural, =1 {1 layout shift found} other {# layout shifts found}}`,
36
34
  };
@@ -49,7 +47,7 @@ class LayoutShifts extends Audit {
49
47
  description: str_(UIStrings.description),
50
48
  scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS,
51
49
  guidanceLevel: 2,
52
- requiredArtifacts: ['traces', 'RootCauses', 'TraceElements'],
50
+ requiredArtifacts: ['traces', 'TraceElements'],
53
51
  };
54
52
  }
55
53
 
@@ -67,11 +65,21 @@ class LayoutShifts extends Audit {
67
65
  const traceElements = artifacts.TraceElements
68
66
  .filter(element => element.traceEventType === 'layout-shift');
69
67
 
68
+ /** @type {LH.Artifacts.TraceEngineRootCauses} */
69
+ const allRootCauses = {
70
+ layoutShifts: new Map(),
71
+ };
72
+ for (const insightSet of traceEngineResult.insights.values()) {
73
+ for (const [shift, reasons] of insightSet.model.CLSCulprits.shifts) {
74
+ allRootCauses.layoutShifts.set(shift, reasons);
75
+ }
76
+ }
77
+
70
78
  /** @type {Item[]} */
71
79
  const items = [];
72
80
  const layoutShiftEvents =
73
81
  /** @type {import('../lib/trace-engine.js').SaneSyntheticLayoutShift[]} */(
74
- clusters.flatMap(c => c.events)
82
+ clusters.flatMap(c => c.events).filter(e => !!e.args.data)
75
83
  );
76
84
  const topLayoutShiftEvents = layoutShiftEvents
77
85
  .sort((a, b) => b.args.data.weighted_score_delta - a.args.data.weighted_score_delta)
@@ -82,41 +90,30 @@ class LayoutShifts extends Audit {
82
90
  const biggestImpactElement = traceElements.find(t => t.nodeId === biggestImpactNodeId);
83
91
 
84
92
  // Turn root causes into sub-items.
85
- const index = layoutShiftEvents.indexOf(event);
86
- const rootCauses = artifacts.RootCauses.layoutShifts[index];
93
+ const rootCauses = allRootCauses.layoutShifts.get(event);
87
94
  /** @type {SubItem[]} */
88
95
  const subItems = [];
89
96
  if (rootCauses) {
90
- for (const cause of rootCauses.unsizedMedia) {
97
+ for (const backendNodeId of rootCauses.unsizedImages) {
91
98
  const element = artifacts.TraceElements.find(
92
- t => t.traceEventType === 'layout-shift' && t.nodeId === cause.node.backendNodeId);
99
+ t => t.traceEventType === 'trace-engine' && t.nodeId === backendNodeId);
93
100
  subItems.push({
94
101
  extra: element ? Audit.makeNodeItem(element.node) : undefined,
95
102
  cause: str_(UIStrings.rootCauseUnsizedMedia),
96
103
  });
97
104
  }
98
- for (const cause of rootCauses.fontChanges) {
99
- const url = cause.request.args.data.url;
105
+ for (const request of rootCauses.fontRequests) {
106
+ const url = request.args.data.url;
100
107
  subItems.push({
101
108
  extra: {type: 'url', value: url},
102
109
  cause: str_(UIStrings.rootCauseFontChanges),
103
110
  });
104
111
  }
105
- for (const cause of rootCauses.iframes) {
106
- const element = artifacts.TraceElements.find(
107
- t => t.traceEventType === 'layout-shift' && t.nodeId === cause.iframe.backendNodeId);
112
+ if (rootCauses.iframeIds.length) {
108
113
  subItems.push({
109
- extra: element ? Audit.makeNodeItem(element.node) : undefined,
110
114
  cause: str_(UIStrings.rootCauseInjectedIframe),
111
115
  });
112
116
  }
113
- for (const cause of rootCauses.renderBlockingRequests) {
114
- const url = cause.request.args.data.url;
115
- subItems.push({
116
- extra: {type: 'url', value: url},
117
- cause: str_(UIStrings.rootCauseRenderBlockingRequest),
118
- });
119
- }
120
117
  }
121
118
 
122
119
  items.push({
@@ -109,7 +109,6 @@ const defaultConfig = {
109
109
  // Artifacts which can be depended on come first.
110
110
  {id: 'DevtoolsLog', gatherer: 'devtools-log'},
111
111
  {id: 'Trace', gatherer: 'trace'},
112
- {id: 'RootCauses', gatherer: 'root-causes'},
113
112
 
114
113
  {id: 'Accessibility', gatherer: 'accessibility'},
115
114
  {id: 'AnchorElements', gatherer: 'anchor-elements'},
@@ -56,8 +56,8 @@ declare class TraceElements extends BaseGatherer {
56
56
  nodeId: number;
57
57
  type: string;
58
58
  } | undefined>;
59
- /** @type {LH.Gatherer.GathererMeta<'Trace'|'RootCauses'>} */
60
- meta: LH.Gatherer.GathererMeta<"Trace" | "RootCauses">;
59
+ /** @type {LH.Gatherer.GathererMeta<'Trace'>} */
60
+ meta: LH.Gatherer.GathererMeta<"Trace">;
61
61
  /** @type {Map<string, string>} */
62
62
  animationIdToName: Map<string, string>;
63
63
  /** @param {LH.Crdp.Animation.AnimationStartedEvent} args */
@@ -23,7 +23,6 @@ import {LighthouseError} from '../../lib/lh-error.js';
23
23
  import {Responsiveness} from '../../computed/metrics/responsiveness.js';
24
24
  import {CumulativeLayoutShift} from '../../computed/metrics/cumulative-layout-shift.js';
25
25
  import {ExecutionContext} from '../driver/execution-context.js';
26
- import RootCauses from './root-causes.js';
27
26
  import {TraceEngineResult} from '../../computed/trace-engine-result.js';
28
27
 
29
28
  /** @typedef {{nodeId: number, animations?: {name?: string, failureReasonsMask?: number, unsupportedProperties?: string[]}[], type?: string}} TraceElementData */
@@ -46,10 +45,10 @@ function getNodeDetailsData() {
46
45
  /* c8 ignore stop */
47
46
 
48
47
  class TraceElements extends BaseGatherer {
49
- /** @type {LH.Gatherer.GathererMeta<'Trace'|'RootCauses'>} */
48
+ /** @type {LH.Gatherer.GathererMeta<'Trace'>} */
50
49
  meta = {
51
50
  supportedModes: ['timespan', 'navigation'],
52
- dependencies: {Trace: Trace.symbol, RootCauses: RootCauses.symbol},
51
+ dependencies: {Trace: Trace.symbol},
53
52
  };
54
53
 
55
54
  /** @type {Map<string, string>} */
@@ -103,13 +102,23 @@ class TraceElements extends BaseGatherer {
103
102
  seen.add(obj);
104
103
 
105
104
  if (obj && typeof obj === 'object' && !Array.isArray(obj)) {
106
- Object.keys(obj).forEach(key => {
107
- if (typeof obj[key] === 'object') {
108
- recursiveObjectEnumerate(obj[key], cb, seen);
109
- } else {
110
- cb(obj, key);
105
+ if (obj instanceof Map) {
106
+ for (const [key, val] of obj) {
107
+ if (typeof val === 'object') {
108
+ recursiveObjectEnumerate(val, cb, seen);
109
+ } else {
110
+ cb(val, key);
111
+ }
111
112
  }
112
- });
113
+ } else {
114
+ Object.keys(obj).forEach(key => {
115
+ if (typeof obj[key] === 'object') {
116
+ recursiveObjectEnumerate(obj[key], cb, seen);
117
+ } else {
118
+ cb(obj[key], key);
119
+ }
120
+ });
121
+ }
113
122
  } else if (Array.isArray(obj)) {
114
123
  obj.forEach(item => {
115
124
  if (typeof item === 'object' || Array.isArray(item)) {
@@ -121,12 +130,18 @@ class TraceElements extends BaseGatherer {
121
130
 
122
131
  /** @type {number[]} */
123
132
  const nodeIds = [];
124
- recursiveObjectEnumerate(insightSet.model, (obj, key) => {
125
- if (typeof obj[key] === 'number' && (key === 'nodeId' || key === 'node_id')) {
126
- nodeIds.push(obj[key]);
133
+ recursiveObjectEnumerate(insightSet.model, (val, key) => {
134
+ const keys = ['nodeId', 'node_id'];
135
+ if (typeof val === 'number' && keys.includes(key)) {
136
+ nodeIds.push(val);
127
137
  }
128
138
  }, new Set());
129
139
 
140
+ // TODO: would be better if unsizedImages was `Array<{nodeId}>`.
141
+ for (const shift of insightSet.model.CLSCulprits.shifts.values()) {
142
+ nodeIds.push(...shift.unsizedImages);
143
+ }
144
+
130
145
  return [...new Set(nodeIds)].map(id => ({nodeId: id}));
131
146
  }
132
147
 
@@ -212,14 +227,6 @@ class TraceElements extends BaseGatherer {
212
227
  nodeIds.push(biggestImpactedNodeId);
213
228
  }
214
229
 
215
- const index = layoutShiftEvents.indexOf(event);
216
- const shiftRootCauses = rootCauses.layoutShifts[index];
217
- if (shiftRootCauses) {
218
- for (const cause of shiftRootCauses.unsizedMedia) {
219
- nodeIds.push(cause.node.backendNodeId);
220
- }
221
- }
222
-
223
230
  return nodeIds.map(nodeId => ({nodeId}));
224
231
  });
225
232
  }
@@ -1,6 +1,6 @@
1
- /** @type {Record<string, {name: LH.IcuMessage} | undefined>} */
1
+ /** @type {Record<string, {name: LH.IcuMessage|string} | undefined>} */
2
2
  export const NotRestoredReasonDescription: Record<string, {
3
- name: LH.IcuMessage;
3
+ name: LH.IcuMessage | string;
4
4
  } | undefined>;
5
5
  export namespace UIStrings {
6
6
  let notMainFrame: string;
@@ -77,7 +77,6 @@ export namespace UIStrings {
77
77
  let printing: string;
78
78
  let webDatabase: string;
79
79
  let pictureInPicture: string;
80
- let portal: string;
81
80
  let speechRecognizer: string;
82
81
  let idleManager: string;
83
82
  let paymentManager: string;
@@ -87,6 +86,7 @@ export namespace UIStrings {
87
86
  let outstandingNetworkRequestDirectSocket: string;
88
87
  let injectedJavascript: string;
89
88
  let injectedStyleSheet: string;
89
+ let contentDiscarded: string;
90
90
  let contentSecurityHandler: string;
91
91
  let contentWebAuthenticationAPI: string;
92
92
  let contentFileChooser: string;
@@ -117,8 +117,11 @@ export namespace UIStrings {
117
117
  let errorDocument: string;
118
118
  let fencedFramesEmbedder: string;
119
119
  let keepaliveRequest: string;
120
- let authorizationHeader: string;
120
+ let jsNetworkRequestReceivedCacheControlNoStoreResource: string;
121
121
  let indexedDBEvent: string;
122
122
  let cookieDisabled: string;
123
+ let webRTCSticky: string;
124
+ let webTransportSticky: string;
125
+ let webSocketSticky: string;
123
126
  }
124
127
  //# sourceMappingURL=bf-cache-strings.d.ts.map