lighthouse 13.4.1 → 13.5.0

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 (201) hide show
  1. package/build-tracker.config.js +1 -1
  2. package/cli/test/smokehouse/lighthouse-runners/bundle.js +3 -3
  3. package/cli/test/smokehouse/lighthouse-runners/devtools-mcp.js +2 -1
  4. package/core/audits/accessibility/accesskeys.js +1 -1
  5. package/core/audits/accessibility/aria-allowed-attr.js +1 -1
  6. package/core/audits/accessibility/aria-allowed-role.js +1 -1
  7. package/core/audits/accessibility/aria-command-name.js +1 -1
  8. package/core/audits/accessibility/aria-conditional-attr.js +1 -1
  9. package/core/audits/accessibility/aria-deprecated-role.js +1 -1
  10. package/core/audits/accessibility/aria-dialog-name.js +1 -1
  11. package/core/audits/accessibility/aria-hidden-body.js +1 -1
  12. package/core/audits/accessibility/aria-hidden-focus.js +1 -1
  13. package/core/audits/accessibility/aria-input-field-name.js +1 -1
  14. package/core/audits/accessibility/aria-meter-name.js +1 -1
  15. package/core/audits/accessibility/aria-progressbar-name.js +1 -1
  16. package/core/audits/accessibility/aria-prohibited-attr.js +1 -1
  17. package/core/audits/accessibility/aria-required-attr.js +1 -1
  18. package/core/audits/accessibility/aria-required-children.js +1 -1
  19. package/core/audits/accessibility/aria-required-parent.js +1 -1
  20. package/core/audits/accessibility/aria-roles.js +1 -1
  21. package/core/audits/accessibility/aria-text.js +1 -1
  22. package/core/audits/accessibility/aria-toggle-field-name.js +1 -1
  23. package/core/audits/accessibility/aria-tooltip-name.js +1 -1
  24. package/core/audits/accessibility/aria-treeitem-name.js +1 -1
  25. package/core/audits/accessibility/aria-valid-attr-value.js +1 -1
  26. package/core/audits/accessibility/aria-valid-attr.js +1 -1
  27. package/core/audits/accessibility/autocomplete-valid.js +1 -1
  28. package/core/audits/accessibility/button-name.js +1 -1
  29. package/core/audits/accessibility/bypass.js +1 -1
  30. package/core/audits/accessibility/color-contrast.js +1 -1
  31. package/core/audits/accessibility/definition-list.js +1 -1
  32. package/core/audits/accessibility/dlitem.js +1 -1
  33. package/core/audits/accessibility/document-title.js +1 -1
  34. package/core/audits/accessibility/duplicate-id-aria.js +1 -1
  35. package/core/audits/accessibility/empty-heading.js +1 -1
  36. package/core/audits/accessibility/form-field-multiple-labels.js +1 -1
  37. package/core/audits/accessibility/frame-title.js +1 -1
  38. package/core/audits/accessibility/heading-order.js +1 -1
  39. package/core/audits/accessibility/html-has-lang.js +1 -1
  40. package/core/audits/accessibility/html-lang-valid.js +1 -1
  41. package/core/audits/accessibility/html-xml-lang-mismatch.js +1 -1
  42. package/core/audits/accessibility/identical-links-same-purpose.js +1 -1
  43. package/core/audits/accessibility/image-alt.js +1 -1
  44. package/core/audits/accessibility/image-redundant-alt.js +1 -1
  45. package/core/audits/accessibility/input-button-name.js +1 -1
  46. package/core/audits/accessibility/input-image-alt.js +1 -1
  47. package/core/audits/accessibility/label-content-name-mismatch.js +1 -1
  48. package/core/audits/accessibility/label.js +1 -1
  49. package/core/audits/accessibility/landmark-one-main.js +1 -1
  50. package/core/audits/accessibility/link-in-text-block.js +1 -1
  51. package/core/audits/accessibility/link-name.js +1 -1
  52. package/core/audits/accessibility/list.js +1 -1
  53. package/core/audits/accessibility/listitem.js +1 -1
  54. package/core/audits/accessibility/meta-refresh.js +1 -1
  55. package/core/audits/accessibility/meta-viewport.js +1 -1
  56. package/core/audits/accessibility/object-alt.js +1 -1
  57. package/core/audits/accessibility/presentation-role-conflict.js +1 -1
  58. package/core/audits/accessibility/select-name.js +1 -1
  59. package/core/audits/accessibility/skip-link.js +1 -1
  60. package/core/audits/accessibility/svg-img-alt.js +1 -1
  61. package/core/audits/accessibility/tabindex.js +1 -1
  62. package/core/audits/accessibility/table-duplicate-name.js +1 -1
  63. package/core/audits/accessibility/table-fake-caption.js +1 -1
  64. package/core/audits/accessibility/target-size.js +1 -1
  65. package/core/audits/accessibility/td-has-header.js +1 -1
  66. package/core/audits/accessibility/td-headers-attr.js +1 -1
  67. package/core/audits/accessibility/th-has-data-cells.js +1 -1
  68. package/core/audits/accessibility/valid-lang.js +1 -1
  69. package/core/audits/accessibility/video-caption.js +1 -1
  70. package/core/audits/agentic/ard-schema.d.ts +19 -0
  71. package/core/audits/agentic/ard-schema.js +135 -0
  72. package/core/audits/baseline.d.ts +23 -10
  73. package/core/audits/deprecations.js +1 -2
  74. package/core/audits/webmcp-form-coverage.js +3 -1
  75. package/core/audits/webmcp-registered-tools.d.ts +2 -0
  76. package/core/audits/webmcp-registered-tools.js +16 -1
  77. package/core/audits/webmcp-schema-validity.js +1 -1
  78. package/core/computed/metrics/interactive.js +1 -1
  79. package/core/computed/metrics/lantern-metric.js +1 -1
  80. package/core/computed/trace-engine-result.d.ts +3 -3
  81. package/core/computed/trace-engine-result.js +4 -4
  82. package/core/computed/unused-javascript-summary.d.ts +0 -1
  83. package/core/computed/unused-javascript-summary.js +0 -1
  84. package/core/config/agentic-browsing-config.d.ts +2 -0
  85. package/core/config/agentic-browsing-config.js +13 -1
  86. package/core/config/default-config.js +13 -1
  87. package/core/config/validation.js +6 -0
  88. package/core/gather/driver/execution-context.js +0 -2
  89. package/core/gather/driver/navigation.d.ts +1 -0
  90. package/core/gather/driver/navigation.js +35 -7
  91. package/core/gather/driver/wait-for-condition.d.ts +5 -7
  92. package/core/gather/driver/wait-for-condition.js +15 -9
  93. package/core/gather/fetcher.d.ts +9 -2
  94. package/core/gather/fetcher.js +30 -4
  95. package/core/gather/gatherers/accessibility.js +1 -3
  96. package/core/gather/gatherers/agentic/ard.d.ts +30 -0
  97. package/core/gather/gatherers/agentic/ard.js +125 -0
  98. package/core/gather/gatherers/anchor-elements.js +0 -2
  99. package/core/gather/gatherers/full-page-screenshot.js +0 -4
  100. package/core/gather/gatherers/iframe-elements.js +0 -2
  101. package/core/gather/gatherers/image-elements.js +1 -13
  102. package/core/gather/gatherers/inputs.js +0 -2
  103. package/core/gather/gatherers/link-elements.js +0 -2
  104. package/core/gather/gatherers/meta-elements.js +0 -3
  105. package/core/gather/gatherers/seo/robots-txt.d.ts +1 -0
  106. package/core/gather/gatherers/seo/robots-txt.js +3 -0
  107. package/core/gather/gatherers/stacks.d.ts +1 -1
  108. package/core/gather/gatherers/stacks.js +1 -3
  109. package/core/gather/gatherers/trace-elements.js +1 -3
  110. package/core/gather/gatherers/viewport-dimensions.js +0 -2
  111. package/core/gather/navigation-runner.d.ts +3 -1
  112. package/core/gather/navigation-runner.js +6 -3
  113. package/core/gather/session.js +1 -0
  114. package/core/index.cjs +5 -5
  115. package/core/index.d.cts +5 -5
  116. package/core/lib/arbitrary-equality-map.d.ts +2 -2
  117. package/core/lib/arbitrary-equality-map.js +1 -1
  118. package/core/lib/baseline/web-features-data.json +52 -28
  119. package/core/lib/baseline/web-features-metadata.json +1 -1
  120. package/core/lib/deprecation-description.js +1 -1
  121. package/core/lib/page-functions.d.ts +0 -3
  122. package/core/lib/page-functions.js +0 -3
  123. package/core/lib/rect-helpers.js +0 -8
  124. package/core/runner.js +21 -0
  125. package/core/user-flow.d.ts +3 -0
  126. package/core/user-flow.js +12 -0
  127. package/dist/report/bundle.esm.js +10 -3
  128. package/dist/report/flow.js +20 -13
  129. package/dist/report/standalone.js +10 -3
  130. package/package.json +19 -14
  131. package/report/assets/styles.css +7 -0
  132. package/report/renderer/components.js +1 -1
  133. package/report/renderer/details-renderer.js +7 -5
  134. package/report/renderer/drop-down-menu.d.ts +2 -2
  135. package/report/renderer/drop-down-menu.js +1 -1
  136. package/report/renderer/report-renderer.js +24 -3
  137. package/report/renderer/report-utils.js +1 -1
  138. package/report/renderer/snippet-renderer.d.ts +4 -4
  139. package/report/renderer/snippet-renderer.js +3 -3
  140. package/report/renderer/text-encoding.js +2 -2
  141. package/shared/localization/locales/ar-XB.json +111 -72
  142. package/shared/localization/locales/ar.json +113 -74
  143. package/shared/localization/locales/bg.json +112 -73
  144. package/shared/localization/locales/ca.json +112 -73
  145. package/shared/localization/locales/cs.json +112 -73
  146. package/shared/localization/locales/da.json +112 -73
  147. package/shared/localization/locales/de.json +112 -73
  148. package/shared/localization/locales/el.json +112 -73
  149. package/shared/localization/locales/en-GB.json +111 -72
  150. package/shared/localization/locales/en-US.json +102 -66
  151. package/shared/localization/locales/en-XA.json +111 -72
  152. package/shared/localization/locales/en-XL.json +102 -66
  153. package/shared/localization/locales/es-419.json +112 -73
  154. package/shared/localization/locales/es.json +112 -73
  155. package/shared/localization/locales/fi.json +112 -73
  156. package/shared/localization/locales/fil.json +112 -73
  157. package/shared/localization/locales/fr.json +112 -73
  158. package/shared/localization/locales/he.json +112 -73
  159. package/shared/localization/locales/hi.json +112 -73
  160. package/shared/localization/locales/hr.json +112 -73
  161. package/shared/localization/locales/hu.json +112 -73
  162. package/shared/localization/locales/id.json +112 -73
  163. package/shared/localization/locales/it.json +113 -74
  164. package/shared/localization/locales/ja.json +112 -73
  165. package/shared/localization/locales/ko.json +112 -73
  166. package/shared/localization/locales/lt.json +112 -73
  167. package/shared/localization/locales/lv.json +112 -73
  168. package/shared/localization/locales/nl.json +112 -73
  169. package/shared/localization/locales/no.json +112 -73
  170. package/shared/localization/locales/pl.json +112 -73
  171. package/shared/localization/locales/pt-PT.json +112 -73
  172. package/shared/localization/locales/pt.json +112 -73
  173. package/shared/localization/locales/ro.json +112 -73
  174. package/shared/localization/locales/ru.json +112 -73
  175. package/shared/localization/locales/sk.json +112 -73
  176. package/shared/localization/locales/sl.json +112 -73
  177. package/shared/localization/locales/sr-Latn.json +112 -73
  178. package/shared/localization/locales/sr.json +112 -73
  179. package/shared/localization/locales/sv.json +124 -85
  180. package/shared/localization/locales/ta.json +112 -73
  181. package/shared/localization/locales/te.json +112 -73
  182. package/shared/localization/locales/th.json +112 -73
  183. package/shared/localization/locales/tr.json +113 -74
  184. package/shared/localization/locales/uk.json +112 -73
  185. package/shared/localization/locales/vi.json +112 -73
  186. package/shared/localization/locales/zh-HK.json +112 -73
  187. package/shared/localization/locales/zh-TW.json +113 -74
  188. package/shared/localization/locales/zh.json +113 -74
  189. package/shared/util.d.ts +6 -0
  190. package/shared/util.js +9 -0
  191. package/third-party/ard/LICENSE +201 -0
  192. package/third-party/ard/README.md +27 -0
  193. package/third-party/ard/ard-test.d.ts +2 -0
  194. package/third-party/ard/ard-test.js +358 -0
  195. package/third-party/ard/ard.d.ts +34 -0
  196. package/third-party/ard/ard.js +204 -0
  197. package/third-party/ard/schema-validator.d.ts +20 -0
  198. package/third-party/ard/schema-validator.js +1700 -0
  199. package/third-party/ard/spec/schemas/ai-catalog.schema.json +292 -0
  200. package/tsconfig.json +1 -0
  201. package/types/artifacts.d.ts +14 -0
@@ -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
@@ -189,7 +177,7 @@ function findSizeDeclaration(rule, property) {
189
177
  * Finds the most specific directly matched CSS font-size rule from the list.
190
178
  *
191
179
  * @param {Array<LH.Crdp.CSS.RuleMatch>} matchedCSSRules
192
- * @param {function(LH.Crdp.CSS.CSSStyle):boolean|string|undefined} isDeclarationOfInterest
180
+ * @param {(style: LH.Crdp.CSS.CSSStyle) => boolean|string|undefined} isDeclarationOfInterest
193
181
  */
194
182
  function findMostSpecificMatchedCSSRule(matchedCSSRules = [], isDeclarationOfInterest) {
195
183
  let mostSpecificRule;
@@ -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
 
@@ -13,7 +13,7 @@ export type JSLibraryDetectorTest = {
13
13
  /**
14
14
  * Returns false if library is not present, otherwise returns an object that contains the library version (set to null if the version is not detected).
15
15
  */
16
- test: (arg0: Window) => JSLibraryDetectorTestResult | Promise<JSLibraryDetectorTestResult>;
16
+ test: (w: Window) => JSLibraryDetectorTestResult | Promise<JSLibraryDetectorTestResult>;
17
17
  };
18
18
  export type JSLibrary = {
19
19
  id: string;
@@ -32,7 +32,7 @@ const libDetectorSource = fs.readFileSync(
32
32
  * @property {string} icon
33
33
  * @property {string} url
34
34
  * @property {string|null} npm npm module name, if applicable to library.
35
- * @property {function(Window): JSLibraryDetectorTestResult | Promise<JSLibraryDetectorTestResult>} test Returns false if library is not present, otherwise returns an object that contains the library version (set to null if the version is not detected).
35
+ * @property {(w: Window) => JSLibraryDetectorTestResult | Promise<JSLibraryDetectorTestResult>} test Returns false if library is not present, otherwise returns an object that contains the library version (set to null if the version is not detected).
36
36
  */
37
37
 
38
38
  /**
@@ -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'>} */
@@ -98,7 +96,7 @@ class TraceElements extends BaseGatherer {
98
96
  * @param {Set<object>} seen
99
97
  */
100
98
  function recursiveObjectEnumerate(obj, cb, seen) {
101
- if (seen.has(seen)) {
99
+ if (seen.has(obj)) {
102
100
  return;
103
101
  }
104
102
 
@@ -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} */
@@ -5,17 +5,19 @@ export type NavigationContext = {
5
5
  requestor: LH.NavigationRequestor;
6
6
  baseArtifacts: LH.BaseArtifacts;
7
7
  computedCache: Map<string, LH.ArbitraryEqualityMap>;
8
+ signal?: AbortSignal | undefined;
8
9
  };
9
10
  export type PhaseState = Omit<Parameters<typeof collectPhaseArtifacts>[0], "phase">;
10
11
  /**
11
12
  * @param {LH.Puppeteer.Page|undefined} page
12
13
  * @param {LH.NavigationRequestor|undefined} requestor
13
- * @param {{config?: LH.Config, flags?: LH.Flags}} [options]
14
+ * @param {{config?: LH.Config, flags?: LH.Flags, signal?: AbortSignal }} [options]
14
15
  * @return {Promise<LH.Gatherer.GatherResult>}
15
16
  */
16
17
  export function navigationGather(page: LH.Puppeteer.Page | undefined, requestor: LH.NavigationRequestor | undefined, options?: {
17
18
  config?: LH.Config;
18
19
  flags?: LH.Flags;
20
+ signal?: AbortSignal;
19
21
  }): Promise<LH.Gatherer.GatherResult>;
20
22
  /**
21
23
  * @param {{driver: Driver, resolvedConfig: LH.Config.ResolvedConfig, requestor: LH.NavigationRequestor}} args
@@ -32,6 +32,7 @@ import {NetworkRecords} from '../computed/network-records.js';
32
32
  * @property {LH.NavigationRequestor} requestor
33
33
  * @property {LH.BaseArtifacts} baseArtifacts
34
34
  * @property {Map<string, LH.ArbitraryEqualityMap>} computedCache
35
+ * @property {AbortSignal} [signal]
35
36
  */
36
37
 
37
38
  /** @typedef {Omit<Parameters<typeof collectPhaseArtifacts>[0], 'phase'>} PhaseState */
@@ -79,12 +80,13 @@ async function _cleanupNavigation({driver}) {
79
80
  * @return {Promise<{requestedUrl: string, mainDocumentUrl: string, navigationError: LH.LighthouseError | undefined}>}
80
81
  */
81
82
  async function _navigate(navigationContext) {
82
- const {driver, resolvedConfig, requestor} = navigationContext;
83
+ const {driver, resolvedConfig, requestor, signal} = navigationContext;
83
84
 
84
85
  try {
85
86
  const {requestedUrl, mainDocumentUrl, warnings} = await gotoURL(driver, requestor, {
86
87
  ...resolvedConfig.settings,
87
88
  waitUntil: resolvedConfig.settings.pauseAfterFcpMs ? ['fcp', 'load'] : ['load'],
89
+ signal,
88
90
  });
89
91
 
90
92
  navigationContext.baseArtifacts.LighthouseRunWarnings.push(...warnings);
@@ -251,11 +253,11 @@ async function _cleanup({requestedUrl, driver, resolvedConfig, lhBrowser, lhPage
251
253
  /**
252
254
  * @param {LH.Puppeteer.Page|undefined} page
253
255
  * @param {LH.NavigationRequestor|undefined} requestor
254
- * @param {{config?: LH.Config, flags?: LH.Flags}} [options]
256
+ * @param {{config?: LH.Config, flags?: LH.Flags, signal?: AbortSignal }} [options]
255
257
  * @return {Promise<LH.Gatherer.GatherResult>}
256
258
  */
257
259
  async function navigationGather(page, requestor, options = {}) {
258
- const {flags = {}, config} = options;
260
+ const {flags = {}, config, signal} = options;
259
261
  log.setLevel(flags.logLevel || 'error');
260
262
 
261
263
  const {resolvedConfig} = await initializeConfig('navigation', config, flags);
@@ -291,6 +293,7 @@ async function navigationGather(page, requestor, options = {}) {
291
293
  resolvedConfig,
292
294
  requestor: normalizedRequestor,
293
295
  computedCache,
296
+ signal,
294
297
  };
295
298
  const {baseArtifacts} = await _setup(context);
296
299
 
@@ -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,
package/core/index.cjs CHANGED
@@ -6,13 +6,13 @@
6
6
 
7
7
  /**
8
8
  * @typedef ExportType
9
- * @property {import('./index.js')['startFlow']} startFlow
10
- * @property {import('./index.js')['navigation']} navigation
11
- * @property {import('./index.js')['startTimespan']} startTimespan
12
- * @property {import('./index.js')['snapshot']} snapshot
9
+ * @property {typeof import('./index.js')['startFlow']} startFlow
10
+ * @property {typeof import('./index.js')['navigation']} navigation
11
+ * @property {typeof import('./index.js')['startTimespan']} startTimespan
12
+ * @property {typeof import('./index.js')['snapshot']} snapshot
13
13
  */
14
14
 
15
- /** @type {import('./index.js')['default'] & ExportType} */
15
+ /** @type {typeof import('./index.js')['default'] & ExportType} */
16
16
  const lighthouse = async function lighthouse(...args) {
17
17
  const {default: lighthouse} = await import('./index.js');
18
18
  return lighthouse(...args);
package/core/index.d.cts CHANGED
@@ -6,11 +6,11 @@ export = lighthouse;
6
6
  */
7
7
  /**
8
8
  * @typedef ExportType
9
- * @property {import('./index.js')['startFlow']} startFlow
10
- * @property {import('./index.js')['navigation']} navigation
11
- * @property {import('./index.js')['startTimespan']} startTimespan
12
- * @property {import('./index.js')['snapshot']} snapshot
9
+ * @property {typeof import('./index.js')['startFlow']} startFlow
10
+ * @property {typeof import('./index.js')['navigation']} navigation
11
+ * @property {typeof import('./index.js')['startTimespan']} startTimespan
12
+ * @property {typeof import('./index.js')['snapshot']} snapshot
13
13
  */
14
- /** @type {import('./index.js')['default'] & ExportType} */
14
+ /** @type {typeof import('./index.js')['default'] & ExportType} */
15
15
  declare const lighthouse: typeof import("./index.js")["default"] & ExportType;
16
16
  //# sourceMappingURL=index.d.cts.map
@@ -20,9 +20,9 @@ export class ArbitraryEqualityMap {
20
20
  value: any;
21
21
  }>;
22
22
  /**
23
- * @param {function(*,*):boolean} equalsFn
23
+ * @param {(a: any, b: any) => boolean} equalsFn
24
24
  */
25
- setEqualityFn(equalsFn: (arg0: any, arg1: any) => boolean): void;
25
+ setEqualityFn(equalsFn: (a: any, b: any) => boolean): void;
26
26
  /**
27
27
  * @param {*} key
28
28
  * @return {boolean}
@@ -19,7 +19,7 @@ class ArbitraryEqualityMap {
19
19
  }
20
20
 
21
21
  /**
22
- * @param {function(*,*):boolean} equalsFn
22
+ * @param {(a: any, b: any) => boolean} equalsFn
23
23
  */
24
24
  setEqualityFn(equalsFn) {
25
25
  this._equalsFn = equalsFn;