lighthouse 11.6.0-dev.20240303 → 11.6.0-dev.20240305

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 (70) hide show
  1. package/core/audits/byte-efficiency/byte-efficiency-audit.d.ts +2 -2
  2. package/core/audits/byte-efficiency/byte-efficiency-audit.js +7 -7
  3. package/core/audits/byte-efficiency/render-blocking-resources.d.ts +4 -3
  4. package/core/audits/byte-efficiency/render-blocking-resources.js +6 -6
  5. package/core/audits/dobetterweb/uses-http2.d.ts +3 -3
  6. package/core/audits/dobetterweb/uses-http2.js +4 -4
  7. package/core/audits/installable-manifest.d.ts +2 -0
  8. package/core/audits/installable-manifest.js +2 -0
  9. package/core/audits/prioritize-lcp-image.d.ts +1 -1
  10. package/core/computed/document-urls.js +1 -1
  11. package/core/computed/load-simulator.d.ts +4 -2
  12. package/core/computed/load-simulator.js +4 -2
  13. package/core/computed/main-resource.js +1 -1
  14. package/core/computed/metrics/lantern-first-contentful-paint.d.ts +6 -6
  15. package/core/computed/metrics/lantern-first-contentful-paint.js +4 -4
  16. package/core/computed/metrics/lantern-first-meaningful-paint.d.ts +2 -2
  17. package/core/computed/metrics/lantern-first-meaningful-paint.js +1 -1
  18. package/core/computed/metrics/lantern-interactive.d.ts +2 -6
  19. package/core/computed/metrics/lantern-interactive.js +2 -2
  20. package/core/computed/metrics/lantern-largest-contentful-paint.d.ts +2 -2
  21. package/core/computed/metrics/lantern-largest-contentful-paint.js +1 -1
  22. package/core/computed/metrics/lantern-max-potential-fid.d.ts +2 -2
  23. package/core/computed/metrics/lantern-max-potential-fid.js +2 -2
  24. package/core/computed/metrics/lantern-metric.d.ts +8 -7
  25. package/core/computed/metrics/lantern-metric.js +4 -4
  26. package/core/computed/metrics/lantern-speed-index.d.ts +2 -2
  27. package/core/computed/metrics/lantern-speed-index.js +2 -2
  28. package/core/computed/metrics/lantern-total-blocking-time.d.ts +2 -2
  29. package/core/computed/metrics/lantern-total-blocking-time.js +2 -2
  30. package/core/computed/network-analysis.js +1 -1
  31. package/core/computed/page-dependency-graph.d.ts +4 -57
  32. package/core/computed/page-dependency-graph.js +4 -451
  33. package/core/lib/asset-saver.js +1 -1
  34. package/core/lib/{dependency-graph → lantern}/base-node.d.ts +12 -8
  35. package/core/lib/{dependency-graph → lantern}/base-node.js +8 -3
  36. package/core/lib/{dependency-graph → lantern}/cpu-node.d.ts +5 -1
  37. package/core/lib/{dependency-graph → lantern}/cpu-node.js +4 -0
  38. package/core/lib/lantern/lantern.d.ts +8 -0
  39. package/core/lib/lantern/lantern.js +31 -0
  40. package/core/lib/{dependency-graph → lantern}/network-node.d.ts +17 -10
  41. package/core/lib/lantern/network-node.js +115 -0
  42. package/core/lib/lantern/page-dependency-graph.d.ts +58 -0
  43. package/core/lib/lantern/page-dependency-graph.js +463 -0
  44. package/core/lib/{dependency-graph → lantern}/simulator/connection-pool.d.ts +15 -14
  45. package/core/lib/{dependency-graph → lantern}/simulator/connection-pool.js +9 -7
  46. package/core/lib/{dependency-graph → lantern}/simulator/dns-cache.d.ts +11 -5
  47. package/core/lib/{dependency-graph → lantern}/simulator/dns-cache.js +3 -3
  48. package/core/lib/{dependency-graph → lantern}/simulator/network-analyzer.d.ts +36 -32
  49. package/core/lib/{dependency-graph → lantern}/simulator/network-analyzer.js +22 -17
  50. package/core/lib/{dependency-graph → lantern}/simulator/simulator.d.ts +21 -15
  51. package/core/lib/{dependency-graph → lantern}/simulator/simulator.js +24 -15
  52. package/core/lib/lantern-trace-saver.d.ts +4 -4
  53. package/core/lib/lantern-trace-saver.js +6 -4
  54. package/core/lib/navigation-error.js +1 -1
  55. package/core/lib/network-recorder.js +1 -1
  56. package/core/lib/network-request.d.ts +7 -0
  57. package/core/lib/network-request.js +13 -0
  58. package/package.json +1 -1
  59. package/shared/localization/locales/en-US.json +3 -0
  60. package/shared/localization/locales/en-XL.json +3 -0
  61. package/third-party/chromium-synchronization/installability-errors-test.js +1 -1
  62. package/tsconfig.json +6 -5
  63. package/types/artifacts.d.ts +1 -1
  64. package/types/gatherer.d.ts +8 -35
  65. package/types/internal/lantern.d.ts +124 -0
  66. package/core/lib/dependency-graph/network-node.js +0 -99
  67. /package/core/lib/{dependency-graph → lantern}/simulator/simulator-timing-map.d.ts +0 -0
  68. /package/core/lib/{dependency-graph → lantern}/simulator/simulator-timing-map.js +0 -0
  69. /package/core/lib/{dependency-graph → lantern}/simulator/tcp-connection.d.ts +0 -0
  70. /package/core/lib/{dependency-graph → lantern}/simulator/tcp-connection.js +0 -0
@@ -1,12 +1,5 @@
1
1
  export { PageDependencyGraphComputed as PageDependencyGraph };
2
- export type Node = import('../lib/dependency-graph/base-node.js').Node;
3
- export type URLArtifact = Omit<LH.Artifacts['URL'], 'finalDisplayedUrl'>;
4
- export type NetworkNodeOutput = {
5
- nodes: Array<NetworkNode>;
6
- idToNodeMap: Map<string, NetworkNode>;
7
- urlToNodeMap: Map<string, Array<NetworkNode>>;
8
- frameIdToNodeMap: Map<string, NetworkNode | null>;
9
- };
2
+ export type Node = import('../lib/lantern/base-node.js').Node<LH.Artifacts.NetworkRequest>;
10
3
  declare const PageDependencyGraphComputed: typeof PageDependencyGraph & {
11
4
  request: (dependencies: {
12
5
  trace: LH.Trace;
@@ -14,54 +7,11 @@ declare const PageDependencyGraphComputed: typeof PageDependencyGraph & {
14
7
  URL: LH.Artifacts['URL'];
15
8
  }, context: import("../../types/utility-types.js").default.ImmutableObject<{
16
9
  computedCache: Map<string, import("../lib/arbitrary-equality-map.js").ArbitraryEqualityMap>;
17
- }>) => Promise<import("../lib/dependency-graph/base-node.js").Node>;
10
+ }>) => Promise<Node>;
18
11
  };
19
- import { NetworkNode } from '../lib/dependency-graph/network-node.js';
12
+ import { NetworkRequest } from '../lib/network-request.js';
13
+ /** @typedef {import('../lib/lantern/base-node.js').Node<LH.Artifacts.NetworkRequest>} Node */
20
14
  declare class PageDependencyGraph {
21
- /**
22
- * @param {LH.Artifacts.NetworkRequest} record
23
- * @return {Array<string>}
24
- */
25
- static getNetworkInitiators(record: LH.Artifacts.NetworkRequest): Array<string>;
26
- /**
27
- * @param {Array<LH.Artifacts.NetworkRequest>} networkRecords
28
- * @return {NetworkNodeOutput}
29
- */
30
- static getNetworkNodeOutput(networkRecords: Array<LH.Artifacts.NetworkRequest>): NetworkNodeOutput;
31
- /**
32
- * @param {LH.Artifacts.ProcessedTrace} processedTrace
33
- * @return {Array<CPUNode>}
34
- */
35
- static getCPUNodes({ mainThreadEvents }: LH.Artifacts.ProcessedTrace): Array<CPUNode>;
36
- /**
37
- * @param {NetworkNode} rootNode
38
- * @param {NetworkNodeOutput} networkNodeOutput
39
- */
40
- static linkNetworkNodes(rootNode: NetworkNode, networkNodeOutput: NetworkNodeOutput): void;
41
- /**
42
- * @param {Node} rootNode
43
- * @param {NetworkNodeOutput} networkNodeOutput
44
- * @param {Array<CPUNode>} cpuNodes
45
- */
46
- static linkCPUNodes(rootNode: Node, networkNodeOutput: NetworkNodeOutput, cpuNodes: Array<CPUNode>): void;
47
- /**
48
- * Removes the given node from the graph, but retains all paths between its dependencies and
49
- * dependents.
50
- * @param {Node} node
51
- */
52
- static _pruneNode(node: Node): void;
53
- /**
54
- * @param {LH.Artifacts.ProcessedTrace} processedTrace
55
- * @param {Array<LH.Artifacts.NetworkRequest>} networkRecords
56
- * @param {URLArtifact} URL
57
- * @return {Node}
58
- */
59
- static createGraph(processedTrace: LH.Artifacts.ProcessedTrace, networkRecords: Array<LH.Artifacts.NetworkRequest>, URL: URLArtifact): Node;
60
- /**
61
- *
62
- * @param {Node} rootNode
63
- */
64
- static printGraph(rootNode: Node, widthInCharacters?: number): void;
65
15
  /**
66
16
  * @param {{trace: LH.Trace, devtoolsLog: LH.DevtoolsLog, URL: LH.Artifacts['URL']}} data
67
17
  * @param {LH.Artifacts.ComputedContext} context
@@ -73,7 +23,4 @@ declare class PageDependencyGraph {
73
23
  URL: LH.Artifacts['URL'];
74
24
  }, context: LH.Artifacts.ComputedContext): Promise<Node>;
75
25
  }
76
- import { NetworkRequest } from '../lib/network-request.js';
77
- import { ProcessedTrace } from './processed-trace.js';
78
- import { CPUNode } from '../lib/dependency-graph/cpu-node.js';
79
26
  //# sourceMappingURL=page-dependency-graph.d.ts.map
@@ -5,463 +5,15 @@
5
5
  */
6
6
 
7
7
  import {makeComputedArtifact} from './computed-artifact.js';
8
- import {NetworkNode} from '../lib/dependency-graph/network-node.js';
9
- import {CPUNode} from '../lib/dependency-graph/cpu-node.js';
10
- import {TraceProcessor} from '../lib/tracehouse/trace-processor.js';
8
+ import {PageDependencyGraph as LanternPageDependencyGraph} from '../lib/lantern/page-dependency-graph.js';
11
9
  import {NetworkRequest} from '../lib/network-request.js';
12
10
  import {ProcessedTrace} from './processed-trace.js';
13
11
  import {NetworkRecords} from './network-records.js';
14
- import {NetworkAnalyzer} from '../lib/dependency-graph/simulator/network-analyzer.js';
15
12
  import {DocumentUrls} from './document-urls.js';
16
13
 
17
- /** @typedef {import('../lib/dependency-graph/base-node.js').Node} Node */
18
- /** @typedef {Omit<LH.Artifacts['URL'], 'finalDisplayedUrl'>} URLArtifact */
19
-
20
- /**
21
- * @typedef {Object} NetworkNodeOutput
22
- * @property {Array<NetworkNode>} nodes
23
- * @property {Map<string, NetworkNode>} idToNodeMap
24
- * @property {Map<string, Array<NetworkNode>>} urlToNodeMap
25
- * @property {Map<string, NetworkNode|null>} frameIdToNodeMap
26
- */
27
-
28
- // Shorter tasks have negligible impact on simulation results.
29
- const SIGNIFICANT_DUR_THRESHOLD_MS = 10;
30
-
31
- // TODO: video files tend to be enormous and throw off all graph traversals, move this ignore
32
- // into estimation logic when we use the dependency graph for other purposes.
33
- const IGNORED_MIME_TYPES_REGEX = /^video/;
14
+ /** @typedef {import('../lib/lantern/base-node.js').Node<LH.Artifacts.NetworkRequest>} Node */
34
15
 
35
16
  class PageDependencyGraph {
36
- /**
37
- * @param {LH.Artifacts.NetworkRequest} record
38
- * @return {Array<string>}
39
- */
40
- static getNetworkInitiators(record) {
41
- if (!record.initiator) return [];
42
- if (record.initiator.url) return [record.initiator.url];
43
- if (record.initiator.type === 'script') {
44
- // Script initiators have the stack of callFrames from all functions that led to this request.
45
- // If async stacks are enabled, then the stack will also have the parent functions that asynchronously
46
- // led to this request chained in the `parent` property.
47
- /** @type {Set<string>} */
48
- const scriptURLs = new Set();
49
- let stack = record.initiator.stack;
50
- while (stack) {
51
- const callFrames = stack.callFrames || [];
52
- for (const frame of callFrames) {
53
- if (frame.url) scriptURLs.add(frame.url);
54
- }
55
-
56
- stack = stack.parent;
57
- }
58
-
59
- return Array.from(scriptURLs);
60
- }
61
-
62
- return [];
63
- }
64
-
65
- /**
66
- * @param {Array<LH.Artifacts.NetworkRequest>} networkRecords
67
- * @return {NetworkNodeOutput}
68
- */
69
- static getNetworkNodeOutput(networkRecords) {
70
- /** @type {Array<NetworkNode>} */
71
- const nodes = [];
72
- /** @type {Map<string, NetworkNode>} */
73
- const idToNodeMap = new Map();
74
- /** @type {Map<string, Array<NetworkNode>>} */
75
- const urlToNodeMap = new Map();
76
- /** @type {Map<string, NetworkNode|null>} */
77
- const frameIdToNodeMap = new Map();
78
-
79
- networkRecords.forEach(record => {
80
- if (IGNORED_MIME_TYPES_REGEX.test(record.mimeType)) return;
81
- if (record.sessionTargetType === 'worker') return;
82
-
83
- // Network record requestIds can be duplicated for an unknown reason
84
- // Suffix all subsequent records with `:duplicate` until it's unique
85
- // NOTE: This should never happen with modern NetworkRequest library, but old fixtures
86
- // might still have this issue.
87
- while (idToNodeMap.has(record.requestId)) {
88
- record.requestId += ':duplicate';
89
- }
90
-
91
- const node = new NetworkNode(record);
92
- nodes.push(node);
93
-
94
- const urlList = urlToNodeMap.get(record.url) || [];
95
- urlList.push(node);
96
-
97
- idToNodeMap.set(record.requestId, node);
98
- urlToNodeMap.set(record.url, urlList);
99
-
100
- // If the request was for the root document of an iframe, save an entry in our
101
- // map so we can link up the task `args.data.frame` dependencies later in graph creation.
102
- if (record.frameId &&
103
- record.resourceType === NetworkRequest.TYPES.Document &&
104
- record.documentURL === record.url) {
105
- // If there's ever any ambiguity, permanently set the value to `false` to avoid loops in the graph.
106
- const value = frameIdToNodeMap.has(record.frameId) ? null : node;
107
- frameIdToNodeMap.set(record.frameId, value);
108
- }
109
- });
110
-
111
- return {nodes, idToNodeMap, urlToNodeMap, frameIdToNodeMap};
112
- }
113
-
114
- /**
115
- * @param {LH.Artifacts.ProcessedTrace} processedTrace
116
- * @return {Array<CPUNode>}
117
- */
118
- static getCPUNodes({mainThreadEvents}) {
119
- /** @type {Array<CPUNode>} */
120
- const nodes = [];
121
- let i = 0;
122
-
123
- TraceProcessor.assertHasToplevelEvents(mainThreadEvents);
124
-
125
- while (i < mainThreadEvents.length) {
126
- const evt = mainThreadEvents[i];
127
- i++;
128
-
129
- // Skip all trace events that aren't schedulable tasks with sizable duration
130
- if (!TraceProcessor.isScheduleableTask(evt) || !evt.dur) {
131
- continue;
132
- }
133
-
134
- // Capture all events that occurred within the task
135
- /** @type {Array<LH.TraceEvent>} */
136
- const children = [];
137
- for (
138
- const endTime = evt.ts + evt.dur;
139
- i < mainThreadEvents.length && mainThreadEvents[i].ts < endTime;
140
- i++
141
- ) {
142
- children.push(mainThreadEvents[i]);
143
- }
144
-
145
- nodes.push(new CPUNode(evt, children));
146
- }
147
-
148
- return nodes;
149
- }
150
-
151
- /**
152
- * @param {NetworkNode} rootNode
153
- * @param {NetworkNodeOutput} networkNodeOutput
154
- */
155
- static linkNetworkNodes(rootNode, networkNodeOutput) {
156
- networkNodeOutput.nodes.forEach(node => {
157
- const directInitiatorRequest = node.record.initiatorRequest || rootNode.record;
158
- const directInitiatorNode =
159
- networkNodeOutput.idToNodeMap.get(directInitiatorRequest.requestId) || rootNode;
160
- const canDependOnInitiator =
161
- !directInitiatorNode.isDependentOn(node) &&
162
- node.canDependOn(directInitiatorNode);
163
- const initiators = PageDependencyGraph.getNetworkInitiators(node.record);
164
- if (initiators.length) {
165
- initiators.forEach(initiator => {
166
- const parentCandidates = networkNodeOutput.urlToNodeMap.get(initiator) || [];
167
- // Only add the edge if the parent is unambiguous with valid timing and isn't circular.
168
- if (parentCandidates.length === 1 &&
169
- parentCandidates[0].startTime <= node.startTime &&
170
- !parentCandidates[0].isDependentOn(node)) {
171
- node.addDependency(parentCandidates[0]);
172
- } else if (canDependOnInitiator) {
173
- directInitiatorNode.addDependent(node);
174
- }
175
- });
176
- } else if (canDependOnInitiator) {
177
- directInitiatorNode.addDependent(node);
178
- }
179
-
180
- // Make sure the nodes are attached to the graph if the initiator information was invalid.
181
- if (node !== rootNode && node.getDependencies().length === 0 && node.canDependOn(rootNode)) {
182
- node.addDependency(rootNode);
183
- }
184
-
185
- if (!node.record.redirects) return;
186
-
187
- const redirects = [...node.record.redirects, node.record];
188
- for (let i = 1; i < redirects.length; i++) {
189
- const redirectNode = networkNodeOutput.idToNodeMap.get(redirects[i - 1].requestId);
190
- const actualNode = networkNodeOutput.idToNodeMap.get(redirects[i].requestId);
191
- if (actualNode && redirectNode) {
192
- actualNode.addDependency(redirectNode);
193
- }
194
- }
195
- });
196
- }
197
-
198
- /**
199
- * @param {Node} rootNode
200
- * @param {NetworkNodeOutput} networkNodeOutput
201
- * @param {Array<CPUNode>} cpuNodes
202
- */
203
- static linkCPUNodes(rootNode, networkNodeOutput, cpuNodes) {
204
- /** @type {Set<LH.Crdp.Network.ResourceType|undefined>} */
205
- const linkableResourceTypes = new Set([
206
- NetworkRequest.TYPES.XHR, NetworkRequest.TYPES.Fetch, NetworkRequest.TYPES.Script,
207
- ]);
208
-
209
- /** @param {CPUNode} cpuNode @param {string} reqId */
210
- function addDependentNetworkRequest(cpuNode, reqId) {
211
- const networkNode = networkNodeOutput.idToNodeMap.get(reqId);
212
- if (!networkNode ||
213
- // Ignore all network nodes that started before this CPU task started
214
- // A network request that started earlier could not possibly have been started by this task
215
- networkNode.startTime <= cpuNode.startTime) return;
216
- const {record} = networkNode;
217
- const resourceType = record.resourceType ||
218
- record.redirectDestination?.resourceType;
219
- if (!linkableResourceTypes.has(resourceType)) {
220
- // We only link some resources to CPU nodes because we observe LCP simulation
221
- // regressions when including images, etc.
222
- return;
223
- }
224
- cpuNode.addDependent(networkNode);
225
- }
226
-
227
- /**
228
- * If the node has an associated frameId, then create a dependency on the root document request
229
- * for the frame. The task obviously couldn't have started before the frame was even downloaded.
230
- *
231
- * @param {CPUNode} cpuNode
232
- * @param {string|undefined} frameId
233
- */
234
- function addDependencyOnFrame(cpuNode, frameId) {
235
- if (!frameId) return;
236
- const networkNode = networkNodeOutput.frameIdToNodeMap.get(frameId);
237
- if (!networkNode) return;
238
- // Ignore all network nodes that started after this CPU task started
239
- // A network request that started after could not possibly be required this task
240
- if (networkNode.startTime >= cpuNode.startTime) return;
241
- cpuNode.addDependency(networkNode);
242
- }
243
-
244
- /** @param {CPUNode} cpuNode @param {string} url */
245
- function addDependencyOnUrl(cpuNode, url) {
246
- if (!url) return;
247
- // Allow network requests that end up to 100ms before the task started
248
- // Some script evaluations can start before the script finishes downloading
249
- const minimumAllowableTimeSinceNetworkNodeEnd = -100 * 1000;
250
- const candidates = networkNodeOutput.urlToNodeMap.get(url) || [];
251
-
252
- let minCandidate = null;
253
- let minDistance = Infinity;
254
- // Find the closest request that finished before this CPU task started
255
- for (const candidate of candidates) {
256
- // Explicitly ignore all requests that started after this CPU node
257
- // A network request that started after this task started cannot possibly be a dependency
258
- if (cpuNode.startTime <= candidate.startTime) return;
259
-
260
- const distance = cpuNode.startTime - candidate.endTime;
261
- if (distance >= minimumAllowableTimeSinceNetworkNodeEnd && distance < minDistance) {
262
- minCandidate = candidate;
263
- minDistance = distance;
264
- }
265
- }
266
-
267
- if (!minCandidate) return;
268
- cpuNode.addDependency(minCandidate);
269
- }
270
-
271
- /** @type {Map<string, CPUNode>} */
272
- const timers = new Map();
273
- for (const node of cpuNodes) {
274
- for (const evt of node.childEvents) {
275
- if (!evt.args.data) continue;
276
-
277
- const argsUrl = evt.args.data.url;
278
- const stackTraceUrls = (evt.args.data.stackTrace || []).map(l => l.url).filter(Boolean);
279
-
280
- switch (evt.name) {
281
- case 'TimerInstall':
282
- // @ts-expect-error - 'TimerInstall' event means timerId exists.
283
- timers.set(evt.args.data.timerId, node);
284
- stackTraceUrls.forEach(url => addDependencyOnUrl(node, url));
285
- break;
286
- case 'TimerFire': {
287
- // @ts-expect-error - 'TimerFire' event means timerId exists.
288
- const installer = timers.get(evt.args.data.timerId);
289
- if (!installer || installer.endTime > node.startTime) break;
290
- installer.addDependent(node);
291
- break;
292
- }
293
-
294
- case 'InvalidateLayout':
295
- case 'ScheduleStyleRecalculation':
296
- addDependencyOnFrame(node, evt.args.data.frame);
297
- stackTraceUrls.forEach(url => addDependencyOnUrl(node, url));
298
- break;
299
-
300
- case 'EvaluateScript':
301
- addDependencyOnFrame(node, evt.args.data.frame);
302
- // @ts-expect-error - 'EvaluateScript' event means argsUrl is defined.
303
- addDependencyOnUrl(node, argsUrl);
304
- stackTraceUrls.forEach(url => addDependencyOnUrl(node, url));
305
- break;
306
-
307
- case 'XHRReadyStateChange':
308
- // Only create the dependency if the request was completed
309
- // 'XHRReadyStateChange' event means readyState is defined.
310
- if (evt.args.data.readyState !== 4) break;
311
-
312
- // @ts-expect-error - 'XHRReadyStateChange' event means argsUrl is defined.
313
- addDependencyOnUrl(node, argsUrl);
314
- stackTraceUrls.forEach(url => addDependencyOnUrl(node, url));
315
- break;
316
-
317
- case 'FunctionCall':
318
- case 'v8.compile':
319
- addDependencyOnFrame(node, evt.args.data.frame);
320
- // @ts-expect-error - events mean argsUrl is defined.
321
- addDependencyOnUrl(node, argsUrl);
322
- break;
323
-
324
- case 'ParseAuthorStyleSheet':
325
- addDependencyOnFrame(node, evt.args.data.frame);
326
- // @ts-expect-error - 'ParseAuthorStyleSheet' event means styleSheetUrl is defined.
327
- addDependencyOnUrl(node, evt.args.data.styleSheetUrl);
328
- break;
329
-
330
- case 'ResourceSendRequest':
331
- addDependencyOnFrame(node, evt.args.data.frame);
332
- // @ts-expect-error - 'ResourceSendRequest' event means requestId is defined.
333
- addDependentNetworkRequest(node, evt.args.data.requestId);
334
- stackTraceUrls.forEach(url => addDependencyOnUrl(node, url));
335
- break;
336
- }
337
- }
338
-
339
- // Nodes starting before the root node cannot depend on it.
340
- if (node.getNumberOfDependencies() === 0 && node.canDependOn(rootNode)) {
341
- node.addDependency(rootNode);
342
- }
343
- }
344
-
345
- // Second pass to prune the graph of short tasks.
346
- const minimumEvtDur = SIGNIFICANT_DUR_THRESHOLD_MS * 1000;
347
- let foundFirstLayout = false;
348
- let foundFirstPaint = false;
349
- let foundFirstParse = false;
350
-
351
- for (const node of cpuNodes) {
352
- // Don't prune if event is the first ParseHTML/Layout/Paint.
353
- // See https://github.com/GoogleChrome/lighthouse/issues/9627#issuecomment-526699524 for more.
354
- let isFirst = false;
355
- if (!foundFirstLayout && node.childEvents.some(evt => evt.name === 'Layout')) {
356
- isFirst = foundFirstLayout = true;
357
- }
358
- if (!foundFirstPaint && node.childEvents.some(evt => evt.name === 'Paint')) {
359
- isFirst = foundFirstPaint = true;
360
- }
361
- if (!foundFirstParse && node.childEvents.some(evt => evt.name === 'ParseHTML')) {
362
- isFirst = foundFirstParse = true;
363
- }
364
-
365
- if (isFirst || node.event.dur >= minimumEvtDur) {
366
- // Don't prune this node. The task is long / important so it will impact simulation.
367
- continue;
368
- }
369
-
370
- // Prune the node if it isn't highly connected to minimize graph size. Rewiring the graph
371
- // here replaces O(M + N) edges with (M * N) edges, which is fine if either M or N is at
372
- // most 1.
373
- if (node.getNumberOfDependencies() === 1 || node.getNumberOfDependents() <= 1) {
374
- PageDependencyGraph._pruneNode(node);
375
- }
376
- }
377
- }
378
-
379
- /**
380
- * Removes the given node from the graph, but retains all paths between its dependencies and
381
- * dependents.
382
- * @param {Node} node
383
- */
384
- static _pruneNode(node) {
385
- const dependencies = node.getDependencies();
386
- const dependents = node.getDependents();
387
- for (const dependency of dependencies) {
388
- node.removeDependency(dependency);
389
- for (const dependent of dependents) {
390
- dependency.addDependent(dependent);
391
- }
392
- }
393
- for (const dependent of dependents) {
394
- node.removeDependent(dependent);
395
- }
396
- }
397
-
398
- /**
399
- * @param {LH.Artifacts.ProcessedTrace} processedTrace
400
- * @param {Array<LH.Artifacts.NetworkRequest>} networkRecords
401
- * @param {URLArtifact} URL
402
- * @return {Node}
403
- */
404
- static createGraph(processedTrace, networkRecords, URL) {
405
- const networkNodeOutput = PageDependencyGraph.getNetworkNodeOutput(networkRecords);
406
- const cpuNodes = PageDependencyGraph.getCPUNodes(processedTrace);
407
- const {requestedUrl, mainDocumentUrl} = URL;
408
- if (!requestedUrl) throw new Error('requestedUrl is required to get the root request');
409
- if (!mainDocumentUrl) throw new Error('mainDocumentUrl is required to get the main resource');
410
-
411
- const rootRequest = NetworkAnalyzer.findResourceForUrl(networkRecords, requestedUrl);
412
- if (!rootRequest) throw new Error('rootRequest not found');
413
- const rootNode = networkNodeOutput.idToNodeMap.get(rootRequest.requestId);
414
- if (!rootNode) throw new Error('rootNode not found');
415
-
416
- const mainDocumentRequest =
417
- NetworkAnalyzer.findLastDocumentForUrl(networkRecords, mainDocumentUrl);
418
- if (!mainDocumentRequest) throw new Error('mainDocumentRequest not found');
419
- const mainDocumentNode = networkNodeOutput.idToNodeMap.get(mainDocumentRequest.requestId);
420
- if (!mainDocumentNode) throw new Error('mainDocumentNode not found');
421
-
422
- PageDependencyGraph.linkNetworkNodes(rootNode, networkNodeOutput);
423
- PageDependencyGraph.linkCPUNodes(rootNode, networkNodeOutput, cpuNodes);
424
- mainDocumentNode.setIsMainDocument(true);
425
-
426
- if (NetworkNode.hasCycle(rootNode)) {
427
- throw new Error('Invalid dependency graph created, cycle detected');
428
- }
429
-
430
- return rootNode;
431
- }
432
-
433
- /**
434
- *
435
- * @param {Node} rootNode
436
- */
437
- static printGraph(rootNode, widthInCharacters = 100) {
438
- /** @param {string} str @param {number} target */
439
- function padRight(str, target, padChar = ' ') {
440
- return str + padChar.repeat(Math.max(target - str.length, 0));
441
- }
442
-
443
- /** @type {Array<Node>} */
444
- const nodes = [];
445
- rootNode.traverse(node => nodes.push(node));
446
- nodes.sort((a, b) => a.startTime - b.startTime);
447
-
448
- const min = nodes[0].startTime;
449
- const max = nodes.reduce((max, node) => Math.max(max, node.endTime), 0);
450
-
451
- const totalTime = max - min;
452
- const timePerCharacter = totalTime / widthInCharacters;
453
- nodes.forEach(node => {
454
- const offset = Math.round((node.startTime - min) / timePerCharacter);
455
- const length = Math.ceil((node.endTime - node.startTime) / timePerCharacter);
456
- const bar = padRight('', offset) + padRight('', length, '=');
457
-
458
- // @ts-expect-error -- disambiguate displayName from across possible Node types.
459
- const displayName = node.record ? node.record.url : node.type;
460
- // eslint-disable-next-line
461
- console.log(padRight(bar, widthInCharacters), `| ${displayName.slice(0, 30)}`);
462
- });
463
- }
464
-
465
17
  /**
466
18
  * @param {{trace: LH.Trace, devtoolsLog: LH.DevtoolsLog, URL: LH.Artifacts['URL']}} data
467
19
  * @param {LH.Artifacts.ComputedContext} context
@@ -478,7 +30,8 @@ class PageDependencyGraph {
478
30
  // Calculates the URL artifact from the processed trace and DT log.
479
31
  const URL = data.URL || await DocumentUrls.request(data, context);
480
32
 
481
- return PageDependencyGraph.createGraph(processedTrace, networkRecords, URL);
33
+ const lanternRequests = networkRecords.map(NetworkRequest.asLanternNetworkRequest);
34
+ return LanternPageDependencyGraph.createGraph(processedTrace, lanternRequests, URL);
482
35
  }
483
36
  }
484
37
 
@@ -11,7 +11,7 @@ import url from 'url';
11
11
 
12
12
  import log from 'lighthouse-logger';
13
13
 
14
- import {Simulator} from './dependency-graph/simulator/simulator.js';
14
+ import {Simulator} from './lantern/simulator/simulator.js';
15
15
  import lanternTraceSaver from './lantern-trace-saver.js';
16
16
  import {MetricTraceEvents} from './traces/metric-trace-events.js';
17
17
  import {NetworkAnalysis} from '../computed/network-analysis.js';
@@ -2,7 +2,7 @@
2
2
  * A union of all types derived from BaseNode, allowing type check discrimination
3
3
  * based on `node.type`. If a new node type is created, it should be added here.
4
4
  */
5
- export type Node = import('./cpu-node.js').CPUNode | import('./network-node.js').NetworkNode;
5
+ export type Node<T = any> = import('./cpu-node.js').CPUNode<T> | import('./network-node.js').NetworkNode<T>;
6
6
  /**
7
7
  * @license
8
8
  * Copyright 2017 Google LLC
@@ -11,7 +11,8 @@ export type Node = import('./cpu-node.js').CPUNode | import('./network-node.js')
11
11
  /**
12
12
  * A union of all types derived from BaseNode, allowing type check discrimination
13
13
  * based on `node.type`. If a new node type is created, it should be added here.
14
- * @typedef {import('./cpu-node.js').CPUNode | import('./network-node.js').NetworkNode} Node
14
+ * @template [T=any]
15
+ * @typedef {import('./cpu-node.js').CPUNode<T> | import('./network-node.js').NetworkNode<T>} Node
15
16
  */
16
17
  /**
17
18
  * @fileoverview This class encapsulates logic for handling resources and tasks used to model the
@@ -25,7 +26,10 @@ export type Node = import('./cpu-node.js').CPUNode | import('./network-node.js')
25
26
  * This allows particular optimizations in this class so that we do no need to check for cycles as
26
27
  * these methods are called and we can always start traversal at the root node.
27
28
  */
28
- export class BaseNode {
29
+ /**
30
+ * @template [T=any]
31
+ */
32
+ export class BaseNode<T = any> {
29
33
  /**
30
34
  * Returns whether the given node has a cycle in its dependent graph by performing a DFS.
31
35
  * @param {Node} node
@@ -134,17 +138,17 @@ export class BaseNode {
134
138
  *
135
139
  * The `getNextNodes` function takes a visited node and returns which nodes to
136
140
  * visit next. It defaults to returning the node's dependents.
137
- * @param {(node: Node, traversalPath: Node[]) => void} callback
138
- * @param {function(Node): Node[]} [getNextNodes]
141
+ * @param {(node: Node<T>, traversalPath: Node<T>[]) => void} callback
142
+ * @param {function(Node<T>): Node<T>[]} [getNextNodes]
139
143
  */
140
- traverse(callback: (node: Node, traversalPath: Node[]) => void, getNextNodes?: ((arg0: Node) => Node[]) | undefined): void;
144
+ traverse(callback: (node: Node<T>, traversalPath: Node<T>[]) => void, getNextNodes?: ((arg0: Node<T>) => Node<T>[]) | undefined): void;
141
145
  /**
142
146
  * @see BaseNode.traverse
143
147
  * @param {function(Node): Node[]} [getNextNodes]
144
148
  */
145
149
  traverseGenerator(getNextNodes?: ((arg0: Node) => Node[]) | undefined): Generator<{
146
- node: Node;
147
- traversalPath: Node[];
150
+ node: Node<any>;
151
+ traversalPath: Node<any>[];
148
152
  }, void, unknown>;
149
153
  /**
150
154
  * @param {Node} node
@@ -7,7 +7,8 @@
7
7
  /**
8
8
  * A union of all types derived from BaseNode, allowing type check discrimination
9
9
  * based on `node.type`. If a new node type is created, it should be added here.
10
- * @typedef {import('./cpu-node.js').CPUNode | import('./network-node.js').NetworkNode} Node
10
+ * @template [T=any]
11
+ * @typedef {import('./cpu-node.js').CPUNode<T> | import('./network-node.js').NetworkNode<T>} Node
11
12
  */
12
13
 
13
14
  /**
@@ -22,6 +23,10 @@
22
23
  * This allows particular optimizations in this class so that we do no need to check for cycles as
23
24
  * these methods are called and we can always start traversal at the root node.
24
25
  */
26
+
27
+ /**
28
+ * @template [T=any]
29
+ */
25
30
  class BaseNode {
26
31
  /**
27
32
  * @param {string} id
@@ -256,8 +261,8 @@ class BaseNode {
256
261
  *
257
262
  * The `getNextNodes` function takes a visited node and returns which nodes to
258
263
  * visit next. It defaults to returning the node's dependents.
259
- * @param {(node: Node, traversalPath: Node[]) => void} callback
260
- * @param {function(Node): Node[]} [getNextNodes]
264
+ * @param {(node: Node<T>, traversalPath: Node<T>[]) => void} callback
265
+ * @param {function(Node<T>): Node<T>[]} [getNextNodes]
261
266
  */
262
267
  traverse(callback, getNextNodes) {
263
268
  for (const {node, traversalPath} of this.traverseGenerator(getNextNodes)) {
@@ -1,4 +1,8 @@
1
- export class CPUNode extends BaseNode {
1
+ /**
2
+ * @template [T=any]
3
+ * @extends {BaseNode<T>}
4
+ */
5
+ export class CPUNode<T = any> extends BaseNode<T> {
2
6
  /**
3
7
  * @param {LH.TraceEvent} parentEvent
4
8
  * @param {LH.TraceEvent[]=} childEvents
@@ -7,6 +7,10 @@
7
7
  import * as LH from '../../../types/lh.js';
8
8
  import {BaseNode} from './base-node.js';
9
9
 
10
+ /**
11
+ * @template [T=any]
12
+ * @extends {BaseNode<T>}
13
+ */
10
14
  class CPUNode extends BaseNode {
11
15
  /**
12
16
  * @param {LH.TraceEvent} parentEvent
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2024 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ /** @type {LH.Util.SelfMap<LH.Crdp.Network.ResourceType>} */
7
+ export const NetworkRequestTypes: LH.Util.SelfMap<LH.Crdp.Network.ResourceType>;
8
+ //# sourceMappingURL=lantern.d.ts.map