lighthouse 13.4.1-dev.20260825 → 13.4.1-dev.20260831

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 (111) hide show
  1. package/core/audits/accessibility/accesskeys.js +1 -1
  2. package/core/audits/accessibility/aria-allowed-attr.js +1 -1
  3. package/core/audits/accessibility/aria-allowed-role.js +1 -1
  4. package/core/audits/accessibility/aria-command-name.js +1 -1
  5. package/core/audits/accessibility/aria-conditional-attr.js +1 -1
  6. package/core/audits/accessibility/aria-deprecated-role.js +1 -1
  7. package/core/audits/accessibility/aria-dialog-name.js +1 -1
  8. package/core/audits/accessibility/aria-hidden-body.js +1 -1
  9. package/core/audits/accessibility/aria-hidden-focus.js +1 -1
  10. package/core/audits/accessibility/aria-input-field-name.js +1 -1
  11. package/core/audits/accessibility/aria-meter-name.js +1 -1
  12. package/core/audits/accessibility/aria-progressbar-name.js +1 -1
  13. package/core/audits/accessibility/aria-prohibited-attr.js +1 -1
  14. package/core/audits/accessibility/aria-required-attr.js +1 -1
  15. package/core/audits/accessibility/aria-required-children.js +1 -1
  16. package/core/audits/accessibility/aria-required-parent.js +1 -1
  17. package/core/audits/accessibility/aria-roles.js +1 -1
  18. package/core/audits/accessibility/aria-text.js +1 -1
  19. package/core/audits/accessibility/aria-toggle-field-name.js +1 -1
  20. package/core/audits/accessibility/aria-tooltip-name.js +1 -1
  21. package/core/audits/accessibility/aria-treeitem-name.js +1 -1
  22. package/core/audits/accessibility/aria-valid-attr-value.js +1 -1
  23. package/core/audits/accessibility/aria-valid-attr.js +1 -1
  24. package/core/audits/accessibility/autocomplete-valid.js +1 -1
  25. package/core/audits/accessibility/button-name.js +1 -1
  26. package/core/audits/accessibility/bypass.js +1 -1
  27. package/core/audits/accessibility/color-contrast.js +1 -1
  28. package/core/audits/accessibility/definition-list.js +1 -1
  29. package/core/audits/accessibility/dlitem.js +1 -1
  30. package/core/audits/accessibility/document-title.js +1 -1
  31. package/core/audits/accessibility/duplicate-id-aria.js +1 -1
  32. package/core/audits/accessibility/empty-heading.js +1 -1
  33. package/core/audits/accessibility/form-field-multiple-labels.js +1 -1
  34. package/core/audits/accessibility/frame-title.js +1 -1
  35. package/core/audits/accessibility/heading-order.js +1 -1
  36. package/core/audits/accessibility/html-has-lang.js +1 -1
  37. package/core/audits/accessibility/html-lang-valid.js +1 -1
  38. package/core/audits/accessibility/html-xml-lang-mismatch.js +1 -1
  39. package/core/audits/accessibility/identical-links-same-purpose.js +1 -1
  40. package/core/audits/accessibility/image-alt.js +1 -1
  41. package/core/audits/accessibility/image-redundant-alt.js +1 -1
  42. package/core/audits/accessibility/input-button-name.js +1 -1
  43. package/core/audits/accessibility/input-image-alt.js +1 -1
  44. package/core/audits/accessibility/label-content-name-mismatch.js +1 -1
  45. package/core/audits/accessibility/label.js +1 -1
  46. package/core/audits/accessibility/landmark-one-main.js +1 -1
  47. package/core/audits/accessibility/link-in-text-block.js +1 -1
  48. package/core/audits/accessibility/link-name.js +1 -1
  49. package/core/audits/accessibility/list.js +1 -1
  50. package/core/audits/accessibility/listitem.js +1 -1
  51. package/core/audits/accessibility/meta-refresh.js +1 -1
  52. package/core/audits/accessibility/meta-viewport.js +1 -1
  53. package/core/audits/accessibility/object-alt.js +1 -1
  54. package/core/audits/accessibility/presentation-role-conflict.js +1 -1
  55. package/core/audits/accessibility/select-name.js +1 -1
  56. package/core/audits/accessibility/skip-link.js +1 -1
  57. package/core/audits/accessibility/svg-img-alt.js +1 -1
  58. package/core/audits/accessibility/tabindex.js +1 -1
  59. package/core/audits/accessibility/table-duplicate-name.js +1 -1
  60. package/core/audits/accessibility/table-fake-caption.js +1 -1
  61. package/core/audits/accessibility/target-size.js +1 -1
  62. package/core/audits/accessibility/td-has-header.js +1 -1
  63. package/core/audits/accessibility/td-headers-attr.js +1 -1
  64. package/core/audits/accessibility/th-has-data-cells.js +1 -1
  65. package/core/audits/accessibility/valid-lang.js +1 -1
  66. package/core/audits/accessibility/video-caption.js +1 -1
  67. package/core/audits/agentic/ard-schema.d.ts +19 -0
  68. package/core/audits/agentic/ard-schema.js +135 -0
  69. package/core/audits/baseline.d.ts +10 -5
  70. package/core/config/agentic-browsing-config.d.ts +2 -0
  71. package/core/config/agentic-browsing-config.js +12 -0
  72. package/core/config/default-config.js +12 -0
  73. package/core/gather/driver/execution-context.js +0 -2
  74. package/core/gather/driver/wait-for-condition.js +0 -6
  75. package/core/gather/fetcher.d.ts +9 -2
  76. package/core/gather/fetcher.js +30 -4
  77. package/core/gather/gatherers/accessibility.js +0 -2
  78. package/core/gather/gatherers/agentic/ard.d.ts +30 -0
  79. package/core/gather/gatherers/agentic/ard.js +125 -0
  80. package/core/gather/gatherers/anchor-elements.js +0 -2
  81. package/core/gather/gatherers/full-page-screenshot.js +0 -4
  82. package/core/gather/gatherers/iframe-elements.js +0 -2
  83. package/core/gather/gatherers/image-elements.js +0 -12
  84. package/core/gather/gatherers/inputs.js +0 -2
  85. package/core/gather/gatherers/link-elements.js +0 -2
  86. package/core/gather/gatherers/meta-elements.js +0 -3
  87. package/core/gather/gatherers/seo/robots-txt.d.ts +1 -0
  88. package/core/gather/gatherers/seo/robots-txt.js +3 -0
  89. package/core/gather/gatherers/stacks.js +0 -2
  90. package/core/gather/gatherers/trace-elements.js +0 -2
  91. package/core/gather/gatherers/viewport-dimensions.js +0 -2
  92. package/core/gather/session.js +1 -0
  93. package/core/lib/baseline/web-features-data.json +26 -13
  94. package/core/lib/baseline/web-features-metadata.json +1 -1
  95. package/core/lib/page-functions.d.ts +0 -3
  96. package/core/lib/page-functions.js +0 -3
  97. package/core/lib/rect-helpers.js +0 -8
  98. package/package.json +16 -11
  99. package/shared/localization/locales/en-US.json +99 -66
  100. package/shared/localization/locales/en-XL.json +99 -66
  101. package/third-party/ard/LICENSE +201 -0
  102. package/third-party/ard/README.md +27 -0
  103. package/third-party/ard/ard-test.d.ts +2 -0
  104. package/third-party/ard/ard-test.js +358 -0
  105. package/third-party/ard/ard.d.ts +34 -0
  106. package/third-party/ard/ard.js +204 -0
  107. package/third-party/ard/schema-validator.d.ts +20 -0
  108. package/third-party/ard/schema-validator.js +1700 -0
  109. package/third-party/ard/spec/schemas/ai-catalog.schema.json +292 -0
  110. package/tsconfig.json +1 -0
  111. package/types/artifacts.d.ts +14 -0
@@ -10,7 +10,7 @@ import * as LH from '../../types/lh.js';
10
10
  * ignoring normal browser constraints such as CORS.
11
11
  */
12
12
 
13
- /** @typedef {{content: string|null, status: number|null}} FetchResponse */
13
+ /** @typedef {{content: string|null, status: number|null, headers?: Record<string, string>|null}} FetchResponse */
14
14
 
15
15
  class Fetcher {
16
16
  /**
@@ -37,6 +37,26 @@ class Fetcher {
37
37
  return this._fetchResourceOverProtocol(url, options);
38
38
  }
39
39
 
40
+ /**
41
+ * @param {Headers|Record<string, string|number|boolean>|null|undefined} rawHeaders
42
+ * @return {Record<string, string>|null}
43
+ */
44
+ _normalizeHeaders(rawHeaders) {
45
+ if (!rawHeaders) return null;
46
+
47
+ /** @type {Record<string, string>} */
48
+ const headers = {};
49
+ const entries = typeof /** @type {Headers} */ (rawHeaders).entries === 'function' ?
50
+ /** @type {Headers} */ (rawHeaders).entries() :
51
+ Object.entries(rawHeaders);
52
+
53
+ for (const [key, value] of entries) {
54
+ headers[key.toLowerCase()] = String(value);
55
+ }
56
+
57
+ return headers;
58
+ }
59
+
40
60
  /**
41
61
  * @param {string} url
42
62
  * @return {Promise<FetchResponse>}
@@ -49,9 +69,12 @@ class Fetcher {
49
69
  content = await response.text();
50
70
  } catch {}
51
71
 
72
+ const headers = this._normalizeHeaders(response.headers);
73
+
52
74
  return {
53
75
  content,
54
76
  status: response.status,
77
+ headers,
55
78
  };
56
79
  }
57
80
 
@@ -82,7 +105,7 @@ class Fetcher {
82
105
 
83
106
  /**
84
107
  * @param {string} url
85
- * @return {Promise<{stream: LH.Crdp.IO.StreamHandle|null, status: number|null}>}
108
+ * @return {Promise<{stream: LH.Crdp.IO.StreamHandle|null, status: number|null, headers?: Record<string, string>|null}>}
86
109
  */
87
110
  async _loadNetworkResource(url) {
88
111
  const frameTreeResponse = await this.session.sendCommand('Page.getFrameTree');
@@ -98,6 +121,7 @@ class Fetcher {
98
121
  return {
99
122
  stream: networkResponse.resource.success ? (networkResponse.resource.stream || null) : null,
100
123
  status: networkResponse.resource.httpStatusCode || null,
124
+ headers: networkResponse.resource.headers || null,
101
125
  };
102
126
  }
103
127
 
@@ -110,12 +134,14 @@ class Fetcher {
110
134
  const startTime = Date.now();
111
135
  const response = await this._wrapWithTimeout(this._loadNetworkResource(url), options.timeout);
112
136
 
137
+ const headers = this._normalizeHeaders(response.headers);
138
+
113
139
  const isOk = response.status && response.status >= 200 && response.status <= 299;
114
- if (!response.stream || !isOk) return {status: response.status, content: null};
140
+ if (!response.stream || !isOk) return {status: response.status, content: null, headers};
115
141
 
116
142
  const timeout = options.timeout - (Date.now() - startTime);
117
143
  const content = await this._readIOStream(response.stream, {timeout});
118
- return {status: response.status, content};
144
+ return {status: response.status, content, headers};
119
145
  }
120
146
 
121
147
  /**
@@ -13,7 +13,6 @@ import {pageFunctions} from '../../lib/page-functions.js';
13
13
  /**
14
14
  * @return {Promise<LH.Artifacts.Accessibility>}
15
15
  */
16
- /* c8 ignore start */
17
16
  async function runA11yChecks() {
18
17
  /** @type {typeof import('axe-core/axe')} */
19
18
  // @ts-expect-error - axe defined by axeLibSource
@@ -183,7 +182,6 @@ function createAxeRuleResultArtifact(result) {
183
182
  description: result.description,
184
183
  };
185
184
  }
186
- /* c8 ignore stop */
187
185
 
188
186
  class Accessibility extends BaseGatherer {
189
187
  /** @type {LH.Gatherer.GathererMeta} */
@@ -0,0 +1,30 @@
1
+ export default AgentResourceDiscovery;
2
+ declare class AgentResourceDiscovery extends BaseGatherer {
3
+ /**
4
+ * @param {LH.Artifacts['RobotsTxt']|null|undefined} robotsTxt
5
+ * @param {string} finalDisplayedUrl
6
+ * @return {string|null}
7
+ */
8
+ static getRobotsTxtAgentmap(robotsTxt: LH.Artifacts["RobotsTxt"] | null | undefined, finalDisplayedUrl: string): string | null;
9
+ /**
10
+ * @param {LH.Gatherer.Context} context
11
+ * @param {string} finalDisplayedUrl
12
+ * @return {Promise<string|null>}
13
+ */
14
+ static getHtmlLinkFromDom(context: LH.Gatherer.Context, finalDisplayedUrl: string): Promise<string | null>;
15
+ /**
16
+ * @param {LH.Gatherer.Context} context
17
+ * @param {string} finalDisplayedUrl
18
+ * @return {Promise<string|null>}
19
+ */
20
+ static getHttpHeaderLink(context: LH.Gatherer.Context, finalDisplayedUrl: string): Promise<string | null>;
21
+ /** @type {LH.Gatherer.GathererMeta<'RobotsTxt'>} */
22
+ meta: LH.Gatherer.GathererMeta<"RobotsTxt">;
23
+ /**
24
+ * @param {LH.Gatherer.Context<'RobotsTxt'>} context
25
+ * @return {Promise<LH.Artifacts['AgentResourceDiscovery']>}
26
+ */
27
+ getArtifact(context: LH.Gatherer.Context<"RobotsTxt">): Promise<LH.Artifacts["AgentResourceDiscovery"]>;
28
+ }
29
+ import BaseGatherer from '../../base-gatherer.js';
30
+ //# sourceMappingURL=ard.d.ts.map
@@ -0,0 +1,125 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import log from 'lighthouse-logger';
8
+ import LinkHeader from 'http-link-header';
9
+
10
+ import BaseGatherer from '../../base-gatherer.js';
11
+ import RobotsTxt from '../seo/robots-txt.js';
12
+ import {pageFunctions} from '../../../lib/page-functions.js';
13
+ function getAiCatalogLinkInDOM() {
14
+ const link = document.querySelector('link[rel~="ai-catalog" i]');
15
+ return link instanceof HTMLLinkElement ? link.href : null;
16
+ }
17
+
18
+ class AgentResourceDiscovery extends BaseGatherer {
19
+ /** @type {LH.Gatherer.GathererMeta<'RobotsTxt'>} */
20
+ meta = {
21
+ supportedModes: ['snapshot', 'navigation'],
22
+ dependencies: {RobotsTxt: RobotsTxt.symbol},
23
+ };
24
+
25
+ /**
26
+ * @param {LH.Artifacts['RobotsTxt']|null|undefined} robotsTxt
27
+ * @param {string} finalDisplayedUrl
28
+ * @return {string|null}
29
+ */
30
+ static getRobotsTxtAgentmap(robotsTxt, finalDisplayedUrl) {
31
+ if (!robotsTxt?.content) return null;
32
+ const match = robotsTxt.content.match(/^\s*Agentmap:\s*(\S+)/im);
33
+ if (!match) return null;
34
+ try {
35
+ return new URL(match[1], finalDisplayedUrl).href;
36
+ } catch {
37
+ return null;
38
+ }
39
+ }
40
+
41
+ /**
42
+ * @param {LH.Gatherer.Context} context
43
+ * @param {string} finalDisplayedUrl
44
+ * @return {Promise<string|null>}
45
+ */
46
+ static async getHtmlLinkFromDom(context, finalDisplayedUrl) {
47
+ try {
48
+ const href = await context.driver.executionContext.evaluate(getAiCatalogLinkInDOM, {
49
+ args: [],
50
+ useIsolation: true,
51
+ deps: [pageFunctions.getNodeDetails],
52
+ });
53
+ if (!href) return null;
54
+ return new URL(href, finalDisplayedUrl).href;
55
+ } catch {
56
+ return null;
57
+ }
58
+ }
59
+
60
+ /**
61
+ * @param {LH.Gatherer.Context} context
62
+ * @param {string} finalDisplayedUrl
63
+ * @return {Promise<string|null>}
64
+ */
65
+ static async getHttpHeaderLink(context, finalDisplayedUrl) {
66
+ // Only check in navigation mode
67
+ if (context.gatherMode !== 'navigation') return null;
68
+
69
+ try {
70
+ const mainResourceResponse = await context.driver.fetcher.fetchResource(finalDisplayedUrl);
71
+ const linkHeader = mainResourceResponse.headers?.['link'];
72
+ if (!linkHeader) return null;
73
+
74
+ const parsed = LinkHeader.parse(linkHeader);
75
+ const aiCatalogRef = parsed.get('rel', 'ai-catalog')[0];
76
+ if (!aiCatalogRef?.uri) return null;
77
+
78
+ return new URL(aiCatalogRef.uri, finalDisplayedUrl).href;
79
+ } catch {
80
+ return null;
81
+ }
82
+ }
83
+
84
+ /**
85
+ * @param {LH.Gatherer.Context<'RobotsTxt'>} context
86
+ * @return {Promise<LH.Artifacts['AgentResourceDiscovery']>}
87
+ */
88
+ async getArtifact(context) {
89
+ const {finalDisplayedUrl} = context.baseArtifacts.URL;
90
+ const robotsTxt = context.dependencies.RobotsTxt;
91
+ const robotsTxtAgentmap = AgentResourceDiscovery.getRobotsTxtAgentmap(
92
+ robotsTxt, finalDisplayedUrl);
93
+ const [htmlLink, httpHeaderLink] = await Promise.all([
94
+ AgentResourceDiscovery.getHtmlLinkFromDom(context, finalDisplayedUrl),
95
+ AgentResourceDiscovery.getHttpHeaderLink(context, finalDisplayedUrl),
96
+ ]);
97
+ const wellKnown = new URL('/.well-known/ai-catalog.json', finalDisplayedUrl).href;
98
+ const catalogUrl = robotsTxtAgentmap || htmlLink || httpHeaderLink || wellKnown;
99
+
100
+ /** @type {string|undefined} */
101
+ let errorMessage;
102
+ const fetchResult = await context.driver.fetcher.fetchResource(catalogUrl)
103
+ .catch(err => {
104
+ log.error('AgentResourceDiscovery', err);
105
+ errorMessage = err.message;
106
+ return {status: null, content: null, headers: null};
107
+ });
108
+
109
+ return {
110
+ status: fetchResult.status,
111
+ content: fetchResult.content,
112
+ headers: fetchResult.headers || null,
113
+ catalogUrl,
114
+ discoverySignals: {
115
+ robotsTxtAgentmap,
116
+ htmlLink,
117
+ httpHeaderLink,
118
+ wellKnown,
119
+ },
120
+ errorMessage,
121
+ };
122
+ }
123
+ }
124
+
125
+ export default AgentResourceDiscovery;
@@ -19,7 +19,6 @@ import {resolveDevtoolsNodePathToObjectId} from '../driver/dom.js';
19
19
  *
20
20
  * @return {LH.Artifacts['AnchorElements']}
21
21
  */
22
- /* c8 ignore start */
23
22
  function collectAnchorElements() {
24
23
  /** @param {string} url */
25
24
  const resolveURLOrEmpty = url => {
@@ -114,7 +113,6 @@ function collectAnchorElements() {
114
113
  };
115
114
  });
116
115
  }
117
- /* c8 ignore stop */
118
116
 
119
117
  /**
120
118
  * @param {LH.Gatherer.ProtocolSession} session
@@ -31,8 +31,6 @@ function kebabCaseToCamelCase(str) {
31
31
  );
32
32
  }
33
33
 
34
- /* c8 ignore start */
35
-
36
34
  function getObservedDeviceMetrics() {
37
35
  // Convert the Web API's kebab case (landscape-primary) to camel case (landscapePrimary).
38
36
  const screenOrientationType = kebabCaseToCamelCase(window.screen.orientation.type);
@@ -53,8 +51,6 @@ function waitForDoubleRaf() {
53
51
  });
54
52
  }
55
53
 
56
- /* c8 ignore stop */
57
-
58
54
  class FullPageScreenshot extends BaseGatherer {
59
55
  /** @type {LH.Gatherer.GathererMeta} */
60
56
  meta = {
@@ -14,7 +14,6 @@ import {pageFunctions} from '../../lib/page-functions.js';
14
14
  /**
15
15
  * @return {LH.Artifacts['IFrameElements']}
16
16
  */
17
- /* c8 ignore start */
18
17
  function collectIFrameElements() {
19
18
  const realBoundingClientRect = window.__HTMLElementBoundingClientRect ||
20
19
  window.HTMLElement.prototype.getBoundingClientRect;
@@ -35,7 +34,6 @@ function collectIFrameElements() {
35
34
  };
36
35
  });
37
36
  }
38
- /* c8 ignore stop */
39
37
 
40
38
  class IFrameElements extends BaseGatherer {
41
39
  /** @type {LH.Gatherer.GathererMeta} */
@@ -17,7 +17,6 @@ import {pageFunctions} from '../../lib/page-functions.js';
17
17
  /* global getElementsInDocument, getNodeDetails */
18
18
 
19
19
  /** @param {Element} element */
20
- /* c8 ignore start */
21
20
  function getClientRect(element) {
22
21
  const clientRect = element.getBoundingClientRect();
23
22
  return {
@@ -28,7 +27,6 @@ function getClientRect(element) {
28
27
  right: clientRect.right,
29
28
  };
30
29
  }
31
- /* c8 ignore stop */
32
30
 
33
31
  /**
34
32
  * If an image is within `picture`, the `picture` element's css position
@@ -36,7 +34,6 @@ function getClientRect(element) {
36
34
  * @param {Element} element
37
35
  * @param {CSSStyleDeclaration} computedStyle
38
36
  */
39
- /* c8 ignore start */
40
37
  function getPosition(element, computedStyle) {
41
38
  if (element.parentElement && element.parentElement.tagName === 'PICTURE') {
42
39
  const parentStyle = window.getComputedStyle(element.parentElement);
@@ -44,13 +41,11 @@ function getPosition(element, computedStyle) {
44
41
  }
45
42
  return computedStyle.getPropertyValue('position');
46
43
  }
47
- /* c8 ignore stop */
48
44
 
49
45
  /**
50
46
  * @param {Array<Element>} allElements
51
47
  * @return {Array<LH.Artifacts.ImageElement>}
52
48
  */
53
- /* c8 ignore start */
54
49
  function getHTMLImages(allElements) {
55
50
  const allImageElements = /** @type {Array<HTMLImageElement>} */ (allElements.filter(element => {
56
51
  return element.localName === 'img';
@@ -89,13 +84,11 @@ function getHTMLImages(allElements) {
89
84
  };
90
85
  });
91
86
  }
92
- /* c8 ignore stop */
93
87
 
94
88
  /**
95
89
  * @param {Array<Element>} allElements
96
90
  * @return {Array<LH.Artifacts.ImageElement>}
97
91
  */
98
- /* c8 ignore start */
99
92
  function getCSSImages(allElements) {
100
93
  // Chrome normalizes background image style from getComputedStyle to be an absolute URL in quotes.
101
94
  // Only match basic background-image: url("http://host/image.jpeg") declarations
@@ -138,23 +131,19 @@ function getCSSImages(allElements) {
138
131
 
139
132
  return images;
140
133
  }
141
- /* c8 ignore stop */
142
134
 
143
135
  /** @return {Array<LH.Artifacts.ImageElement>} */
144
- /* c8 ignore start */
145
136
  function collectImageElementInfo() {
146
137
  /** @type {Array<Element>} */
147
138
  // @ts-expect-error - added by getElementsInDocumentFnString
148
139
  const allElements = getElementsInDocument();
149
140
  return getHTMLImages(allElements).concat(getCSSImages(allElements));
150
141
  }
151
- /* c8 ignore stop */
152
142
 
153
143
  /**
154
144
  * @param {string} url
155
145
  * @return {Promise<{naturalWidth: number, naturalHeight: number}>}
156
146
  */
157
- /* c8 ignore start */
158
147
  function determineNaturalSize(url) {
159
148
  return new Promise((resolve, reject) => {
160
149
  const img = new Image();
@@ -169,7 +158,6 @@ function determineNaturalSize(url) {
169
158
  img.src = url;
170
159
  });
171
160
  }
172
- /* c8 ignore stop */
173
161
 
174
162
  /**
175
163
  * @param {Partial<Pick<LH.Crdp.CSS.CSSStyle, 'cssProperties'>>|undefined} rule
@@ -14,7 +14,6 @@ import {pageFunctions} from '../../lib/page-functions.js';
14
14
  /**
15
15
  * @return {LH.Artifacts['Inputs']}
16
16
  */
17
- /* c8 ignore start */
18
17
  function collectElements() {
19
18
  /** @type {LH.Artifacts.InputElement[]} */
20
19
  const inputArtifacts = [];
@@ -93,7 +92,6 @@ function collectElements() {
93
92
  labels: [...labelElToArtifact.values()],
94
93
  };
95
94
  }
96
- /* c8 ignore stop */
97
95
 
98
96
  class Inputs extends BaseGatherer {
99
97
  /** @type {LH.Gatherer.GathererMeta} */
@@ -59,7 +59,6 @@ function getCrossoriginFromHeader(value) {
59
59
  /**
60
60
  * @return {LH.Artifacts['LinkElements']}
61
61
  */
62
- /* c8 ignore start */
63
62
  function getLinkElementsInDOM() {
64
63
  /** @type {Array<HTMLOrSVGElement>} */
65
64
  // @ts-expect-error - getElementsInDocument put into scope via stringification
@@ -91,7 +90,6 @@ function getLinkElementsInDOM() {
91
90
 
92
91
  return linkElements;
93
92
  }
94
- /* c8 ignore stop */
95
93
 
96
94
  class LinkElements extends BaseGatherer {
97
95
  /** @type {LH.Gatherer.GathererMeta<'DevtoolsLog'>} */
@@ -8,8 +8,6 @@ import BaseGatherer from '../base-gatherer.js';
8
8
  import {pageFunctions} from '../../lib/page-functions.js';
9
9
 
10
10
  /* globals getElementsInDocument getNodeDetails */
11
-
12
- /* c8 ignore start */
13
11
  function collectMetaElements() {
14
12
  const functions = /** @type {typeof pageFunctions} */({
15
13
  // @ts-expect-error - getElementsInDocument put into scope via stringification
@@ -36,7 +34,6 @@ function collectMetaElements() {
36
34
  };
37
35
  });
38
36
  }
39
- /* c8 ignore stop */
40
37
 
41
38
  class MetaElements extends BaseGatherer {
42
39
  /** @type {LH.Gatherer.GathererMeta} */
@@ -1,5 +1,6 @@
1
1
  export default RobotsTxt;
2
2
  declare class RobotsTxt extends BaseGatherer {
3
+ static symbol: symbol;
3
4
  /**
4
5
  * @param {LH.Gatherer.Context} passContext
5
6
  * @return {Promise<LH.Artifacts['RobotsTxt']>}
@@ -9,8 +9,11 @@ import log from 'lighthouse-logger';
9
9
  import BaseGatherer from '../../base-gatherer.js';
10
10
 
11
11
  class RobotsTxt extends BaseGatherer {
12
+ static symbol = Symbol('RobotsTxt');
13
+
12
14
  /** @type {LH.Gatherer.GathererMeta} */
13
15
  meta = {
16
+ symbol: RobotsTxt.symbol,
14
17
  supportedModes: ['snapshot', 'navigation'],
15
18
  };
16
19
 
@@ -46,7 +46,6 @@ const libDetectorSource = fs.readFileSync(
46
46
  /**
47
47
  * Obtains a list of detected JS libraries and their versions.
48
48
  */
49
- /* c8 ignore start */
50
49
  async function detectLibraries() {
51
50
  /** @type {JSLibrary[]} */
52
51
  const libraries = [];
@@ -81,7 +80,6 @@ async function detectLibraries() {
81
80
 
82
81
  return libraries;
83
82
  }
84
- /* c8 ignore stop */
85
83
 
86
84
 
87
85
  /** @implements {LH.Gatherer.GathererInstance} */
@@ -30,7 +30,6 @@ const MAX_LAYOUT_SHIFTS = 15;
30
30
  /**
31
31
  * @param {Element | ShadowRoot | Text} node
32
32
  */
33
- /* c8 ignore start */
34
33
  function getNodeDetailsData(node) {
35
34
  /** @type {Element | ShadowRoot | Text | null} */
36
35
  let elem = node.nodeType === document.ELEMENT_NODE ? node : node.parentElement;
@@ -45,7 +44,6 @@ function getNodeDetailsData(node) {
45
44
  }
46
45
  return traceElement;
47
46
  }
48
- /* c8 ignore stop */
49
47
 
50
48
  class TraceElements extends BaseGatherer {
51
49
  /** @type {LH.Gatherer.GathererMeta<'Trace'|'SourceMaps'>} */
@@ -9,7 +9,6 @@ import BaseGatherer from '../base-gatherer.js';
9
9
  /**
10
10
  * @return {LH.Artifacts.ViewportDimensions}
11
11
  */
12
- /* c8 ignore start */
13
12
  function getViewportDimensions() {
14
13
  // window.innerWidth to get the scrollable size of the window (irrespective of zoom)
15
14
  // window.outerWidth to get the size of the visible area
@@ -22,7 +21,6 @@ function getViewportDimensions() {
22
21
  devicePixelRatio: window.devicePixelRatio,
23
22
  };
24
23
  }
25
- /* c8 ignore stop */
26
24
 
27
25
  class ViewportDimensions extends BaseGatherer {
28
26
  /** @type {LH.Gatherer.GathererMeta} */
@@ -123,6 +123,7 @@ class ProtocolSession extends CrdpEventEmitter {
123
123
  }));
124
124
  });
125
125
 
126
+ // @ts-ignore-error CdpSession.send expects a method key of ProtocolMapping.Commands, but we use LH.CrdpCommands
126
127
  const resultPromise = this._cdpSession.send(method, ...params, {
127
128
  // Add 50ms to the Puppeteer timeout to ensure the Lighthouse timeout finishes first.
128
129
  timeout: timeoutMs + PPTR_BUFFER,