lighthouse 13.6.0-dev.20261105 → 13.6.0-dev.20261110

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.
@@ -11,26 +11,6 @@ import {ProcessedTrace} from './processed-trace.js';
11
11
  import {NetworkRecords} from './network-records.js';
12
12
  import {TraceEngineResult} from './trace-engine-result.js';
13
13
 
14
- /**
15
- * ResourceSendRequest URLs in the trace retain #hash fragments, whereas CDP
16
- * Network.requestWillBeSent, Page.frameNavigated, initiator.url, documentURL,
17
- * and V8 script events strip #hash fragments, and Lantern's NetworkAnalyzer
18
- * requires request.url not to have a fragment.
19
- * TODO: Remove once @paulirish/trace_engine includes the fix in LanternComputationData.
20
- * @param {string} urlStr
21
- * @return {string}
22
- */
23
- function stripUrlHash(urlStr) {
24
- if (!urlStr.includes('#')) return urlStr;
25
- try {
26
- const url = new URL(urlStr);
27
- url.hash = '';
28
- return url.href;
29
- } catch {
30
- return urlStr;
31
- }
32
- }
33
-
34
14
  class PageDependencyGraph {
35
15
  /**
36
16
  * @param {{trace: LH.Trace, devtoolsLog: LH.DevtoolsLog, settings: LH.Audit.Context['settings'], URL: LH.Artifacts['URL'], SourceMaps: LH.Artifacts['SourceMaps'], HostDPR: LH.Artifacts['HostDPR'], fromTrace: boolean}} data
@@ -47,41 +27,7 @@ class PageDependencyGraph {
47
27
  if (data.fromTrace) {
48
28
  const traceEngineResult =
49
29
  await TraceEngineResult.request({trace, settings, SourceMaps, HostDPR}, context);
50
- let parsedTrace = traceEngineResult.data;
51
- if (parsedTrace.NetworkRequests.byTime.some(
52
- r => r.args.data.url.includes('#') ||
53
- r.args.data.redirects.some(red => red.url.includes('#'))
54
- )) {
55
- const byTime = parsedTrace.NetworkRequests.byTime.map(request => {
56
- const url = stripUrlHash(request.args.data.url);
57
- const redirects = request.args.data.redirects.map(redirect => ({
58
- ...redirect,
59
- url: stripUrlHash(redirect.url),
60
- }));
61
- if (url === request.args.data.url &&
62
- redirects.every((red, i) => red.url === request.args.data.redirects[i].url)) {
63
- return request;
64
- }
65
- return {
66
- ...request,
67
- args: {
68
- ...request.args,
69
- data: {
70
- ...request.args.data,
71
- url,
72
- redirects,
73
- },
74
- },
75
- };
76
- });
77
- parsedTrace = {
78
- ...parsedTrace,
79
- NetworkRequests: {
80
- ...parsedTrace.NetworkRequests,
81
- byTime,
82
- },
83
- };
84
- }
30
+ const parsedTrace = traceEngineResult.data;
85
31
  const requests =
86
32
  Lantern.TraceEngineComputationData.createNetworkRequests(trace, parsedTrace);
87
33
  const graph =
@@ -128,13 +128,15 @@ const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
128
128
  const defaultConfig = {
129
129
  settings: constants.defaultSettings,
130
130
  artifacts: [
131
- {id: 'WebMCP', gatherer: 'webmcp'},
132
- {id: 'WebMcpSchemaIssues', gatherer: 'webmcp-schema'},
133
- {id: 'LlmsTxt', gatherer: 'agentic/llms-txt'},
134
131
  // Artifacts which can be depended on come first.
135
132
  {id: 'DevtoolsLog', gatherer: 'devtools-log'},
136
133
  {id: 'Trace', gatherer: 'trace'},
134
+ {id: 'RobotsTxt', gatherer: 'seo/robots-txt'},
137
135
 
136
+ {id: 'WebMCP', gatherer: 'webmcp'},
137
+ {id: 'WebMcpSchemaIssues', gatherer: 'webmcp-schema'},
138
+ {id: 'LlmsTxt', gatherer: 'agentic/llms-txt'},
139
+ {id: 'AgentResourceDiscovery', gatherer: 'agentic/ard'},
138
140
  {id: 'Accessibility', gatherer: 'accessibility'},
139
141
  {id: 'AnchorElements', gatherer: 'anchor-elements'},
140
142
  {id: 'ConsoleMessages', gatherer: 'console-messages'},
@@ -149,8 +151,6 @@ const defaultConfig = {
149
151
  {id: 'MainDocumentContent', gatherer: 'main-document-content'},
150
152
  {id: 'MetaElements', gatherer: 'meta-elements'},
151
153
  {id: 'NetworkUserAgent', gatherer: 'network-user-agent'},
152
- {id: 'RobotsTxt', gatherer: 'seo/robots-txt'},
153
- {id: 'AgentResourceDiscovery', gatherer: 'agentic/ard'},
154
154
  {id: 'Scripts', gatherer: 'scripts'},
155
155
  {id: 'SourceMaps', gatherer: 'source-maps'},
156
156
  {id: 'Stacks', gatherer: 'stacks'},
@@ -45,6 +45,10 @@ export class NetworkMonitor extends NetworkMonitor_base {
45
45
  requestedUrl?: string;
46
46
  mainDocumentUrl?: string;
47
47
  }>;
48
+ /**
49
+ * @return {Array<NetworkRequest>}
50
+ */
51
+ getRawRecords(): Array<NetworkRequest>;
48
52
  /**
49
53
  * @return {Array<NetworkRequest>}
50
54
  */
@@ -120,9 +120,16 @@ class NetworkMonitor extends NetworkMonitorEventEmitter {
120
120
  /**
121
121
  * @return {Array<NetworkRequest>}
122
122
  */
123
- getInflightRequests() {
123
+ getRawRecords() {
124
124
  if (!this._networkRecorder) return [];
125
- return this._networkRecorder.getRawRecords().filter(request => !request.finished);
125
+ return this._networkRecorder.getRawRecords();
126
+ }
127
+
128
+ /**
129
+ * @return {Array<NetworkRequest>}
130
+ */
131
+ getInflightRequests() {
132
+ return this.getRawRecords().filter(request => !request.finished);
126
133
  }
127
134
 
128
135
  /**
@@ -16,6 +16,7 @@ import LinkHeader from 'http-link-header';
16
16
 
17
17
  import BaseGatherer from '../../base-gatherer.js';
18
18
  import RobotsTxt from '../seo/robots-txt.js';
19
+ import * as Lantern from '../../../lib/lantern/lantern.js';
19
20
 
20
21
  /**
21
22
  * Link relations for ARD entry sources. `ai-catalog` is the predecessor relation, which
@@ -161,15 +162,26 @@ class AgentResourceDiscovery extends BaseGatherer {
161
162
  static async getHttpHeaderLinks(context, finalDisplayedUrl) {
162
163
  if (context.gatherMode !== 'navigation') return NO_LINKS;
163
164
 
164
- try {
165
- const mainResourceResponse = await context.driver.fetcher.fetchResource(finalDisplayedUrl);
166
- const linkHeader = mainResourceResponse.headers?.link;
167
- if (!linkHeader) return NO_LINKS;
165
+ const {mainDocumentUrl} = context.baseArtifacts.URL;
166
+ if (!mainDocumentUrl) return NO_LINKS;
168
167
 
169
- const parsed = LinkHeader.parse(linkHeader.replace(/\n/g, ','));
168
+ try {
169
+ // Read the headers from the network monitor rather than a DevtoolsLog dependency, since
170
+ // this gatherer also supports snapshot mode and so cannot depend on DevtoolsLog.
171
+ const records = context.driver.networkMonitor.getRawRecords();
172
+ const mainResource = Lantern.Core.NetworkAnalyzer.findLastDocumentForUrl(
173
+ records,
174
+ mainDocumentUrl
175
+ );
176
+ if (!mainResource) return NO_LINKS;
177
+
178
+ // NetworkRequest already splits multi-value headers into separate entries.
179
+ const refs = mainResource.responseHeaders
180
+ .filter(header => header.name.toLowerCase() === 'link')
181
+ .flatMap(header => LinkHeader.parse(header.value).refs);
170
182
  /** @param {string} rel */
171
183
  const getUri = rel =>
172
- resolveUrl(parsed.refs.find(ref => ref.rel?.toLowerCase() === rel)?.uri, finalDisplayedUrl);
184
+ resolveUrl(refs.find(ref => ref.rel?.toLowerCase() === rel)?.uri, finalDisplayedUrl);
173
185
  return {ard: getUri(REL_ARD), legacy: getUri(REL_LEGACY)};
174
186
  } catch (err) {
175
187
  log.verbose('AgentResourceDiscovery', `Could not read Link header: ${err.message}`);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "lighthouse",
3
3
  "type": "module",
4
- "version": "13.6.0-dev.20261105",
4
+ "version": "13.6.0-dev.20261110",
5
5
  "description": "Automated auditing, performance metrics, and best practices for the web.",
6
6
  "main": "./core/index.js",
7
7
  "bin": {