lighthouse 11.7.0-dev.20240418 → 11.7.0-dev.20240419
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.
- package/cli/test/smokehouse/__snapshots__/report-assert-test.js.snap +14 -14
- package/core/audits/dobetterweb/uses-http2.js +2 -4
- package/core/audits/prioritize-lcp-image.js +2 -2
- package/core/audits/uses-rel-preload.js +4 -4
- package/core/lib/lantern/metric.js +2 -6
- package/core/lib/lantern/metrics/interactive.js +2 -2
- package/core/lib/lantern/metrics/speed-index.js +4 -5
- package/core/lib/lantern/simulator/connection-pool.d.ts +2 -12
- package/core/lib/lantern/simulator/connection-pool.js +3 -22
- package/core/lib/lantern/simulator/simulator.d.ts +2 -4
- package/core/lib/lantern/simulator/simulator.js +6 -13
- package/core/lib/lantern-trace-saver.js +0 -1
- package/dist/report/bundle.esm.js +1 -1
- package/dist/report/flow.js +1 -1
- package/dist/report/standalone.js +1 -1
- package/package.json +1 -1
- package/report/renderer/category-renderer.js +1 -1
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
exports[`getAssertionReport works (multiple failing) 1`] = `
|
|
4
4
|
"X difference at cumulative-layout-shift audit.details.items.length
|
|
5
5
|
expected: []
|
|
6
|
-
found: [{\\"cumulativeLayoutShiftMainFrame\\":0.
|
|
6
|
+
found: [{\\"cumulativeLayoutShiftMainFrame\\":0.09957584751767748,\\"newEngineResult\\":{\\"cumulativeLayoutShift\\":0.09957584751767748,\\"cumulativeLayoutShiftMainFrame\\":0.09957584751767748},\\"newEngineResultDiffered\\":false}]
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
X difference at cumulative-layout-shift audit.details.blah
|
|
@@ -15,11 +15,11 @@ exports[`getAssertionReport works (multiple failing) 1`] = `
|
|
|
15
15
|
\\"id\\": \\"cumulative-layout-shift\\",
|
|
16
16
|
\\"title\\": \\"Cumulative Layout Shift\\",
|
|
17
17
|
\\"description\\": \\"Cumulative Layout Shift measures the movement of visible elements within the viewport. [Learn more about the Cumulative Layout Shift metric](https://web.dev/articles/cls).\\",
|
|
18
|
-
\\"score\\": 0.
|
|
18
|
+
\\"score\\": 0.9,
|
|
19
19
|
\\"scoreDisplayMode\\": \\"numeric\\",
|
|
20
|
-
\\"numericValue\\": 0.
|
|
20
|
+
\\"numericValue\\": 0.09957584751767748,
|
|
21
21
|
\\"numericUnit\\": \\"unitless\\",
|
|
22
|
-
\\"displayValue\\": \\"0.
|
|
22
|
+
\\"displayValue\\": \\"0.1\\",
|
|
23
23
|
\\"scoringOptions\\": {
|
|
24
24
|
\\"p10\\": 0.1,
|
|
25
25
|
\\"median\\": 0.25
|
|
@@ -28,10 +28,10 @@ exports[`getAssertionReport works (multiple failing) 1`] = `
|
|
|
28
28
|
\\"type\\": \\"debugdata\\",
|
|
29
29
|
\\"items\\": [
|
|
30
30
|
{
|
|
31
|
-
\\"cumulativeLayoutShiftMainFrame\\": 0.
|
|
31
|
+
\\"cumulativeLayoutShiftMainFrame\\": 0.09957584751767748,
|
|
32
32
|
\\"newEngineResult\\": {
|
|
33
|
-
\\"cumulativeLayoutShift\\": 0.
|
|
34
|
-
\\"cumulativeLayoutShiftMainFrame\\": 0.
|
|
33
|
+
\\"cumulativeLayoutShift\\": 0.09957584751767748,
|
|
34
|
+
\\"cumulativeLayoutShiftMainFrame\\": 0.09957584751767748
|
|
35
35
|
},
|
|
36
36
|
\\"newEngineResultDiffered\\": false
|
|
37
37
|
}
|
|
@@ -43,18 +43,18 @@ exports[`getAssertionReport works (multiple failing) 1`] = `
|
|
|
43
43
|
exports[`getAssertionReport works (trivial failing) 1`] = `
|
|
44
44
|
"X difference at cumulative-layout-shift audit.details.items.length
|
|
45
45
|
expected: []
|
|
46
|
-
found: [{\\"cumulativeLayoutShiftMainFrame\\":0.
|
|
46
|
+
found: [{\\"cumulativeLayoutShiftMainFrame\\":0.09957584751767748,\\"newEngineResult\\":{\\"cumulativeLayoutShift\\":0.09957584751767748,\\"cumulativeLayoutShiftMainFrame\\":0.09957584751767748},\\"newEngineResultDiffered\\":false}]
|
|
47
47
|
|
|
48
48
|
found result:
|
|
49
49
|
{
|
|
50
50
|
\\"id\\": \\"cumulative-layout-shift\\",
|
|
51
51
|
\\"title\\": \\"Cumulative Layout Shift\\",
|
|
52
52
|
\\"description\\": \\"Cumulative Layout Shift measures the movement of visible elements within the viewport. [Learn more about the Cumulative Layout Shift metric](https://web.dev/articles/cls).\\",
|
|
53
|
-
\\"score\\": 0.
|
|
53
|
+
\\"score\\": 0.9,
|
|
54
54
|
\\"scoreDisplayMode\\": \\"numeric\\",
|
|
55
|
-
\\"numericValue\\": 0.
|
|
55
|
+
\\"numericValue\\": 0.09957584751767748,
|
|
56
56
|
\\"numericUnit\\": \\"unitless\\",
|
|
57
|
-
\\"displayValue\\": \\"0.
|
|
57
|
+
\\"displayValue\\": \\"0.1\\",
|
|
58
58
|
\\"scoringOptions\\": {
|
|
59
59
|
\\"p10\\": 0.1,
|
|
60
60
|
\\"median\\": 0.25
|
|
@@ -63,10 +63,10 @@ exports[`getAssertionReport works (trivial failing) 1`] = `
|
|
|
63
63
|
\\"type\\": \\"debugdata\\",
|
|
64
64
|
\\"items\\": [
|
|
65
65
|
{
|
|
66
|
-
\\"cumulativeLayoutShiftMainFrame\\": 0.
|
|
66
|
+
\\"cumulativeLayoutShiftMainFrame\\": 0.09957584751767748,
|
|
67
67
|
\\"newEngineResult\\": {
|
|
68
|
-
\\"cumulativeLayoutShift\\": 0.
|
|
69
|
-
\\"cumulativeLayoutShiftMainFrame\\": 0.
|
|
68
|
+
\\"cumulativeLayoutShift\\": 0.09957584751767748,
|
|
69
|
+
\\"cumulativeLayoutShiftMainFrame\\": 0.09957584751767748
|
|
70
70
|
},
|
|
71
71
|
\\"newEngineResultDiffered\\": false
|
|
72
72
|
}
|
|
@@ -78,11 +78,9 @@ class UsesHTTP2Audit extends Audit {
|
|
|
78
78
|
options = Object.assign({label: ''}, options);
|
|
79
79
|
const beforeLabel = `${this.meta.id}-${options.label}-before`;
|
|
80
80
|
const afterLabel = `${this.meta.id}-${options.label}-after`;
|
|
81
|
-
const flexibleOrdering = true;
|
|
82
81
|
|
|
83
82
|
const urlsToChange = new Set(results.map(result => result.url));
|
|
84
|
-
const simulationBefore =
|
|
85
|
-
simulator.simulate(graph, {label: beforeLabel, flexibleOrdering});
|
|
83
|
+
const simulationBefore = simulator.simulate(graph, {label: beforeLabel});
|
|
86
84
|
|
|
87
85
|
// Update all the protocols to reflect implementing our recommendations
|
|
88
86
|
/** @type {Map<string, string>} */
|
|
@@ -95,7 +93,7 @@ class UsesHTTP2Audit extends Audit {
|
|
|
95
93
|
node.request.protocol = 'h2';
|
|
96
94
|
});
|
|
97
95
|
|
|
98
|
-
const simulationAfter = simulator.simulate(graph, {label: afterLabel
|
|
96
|
+
const simulationAfter = simulator.simulate(graph, {label: afterLabel});
|
|
99
97
|
|
|
100
98
|
// Restore the original protocol after we've done our simulation
|
|
101
99
|
graph.traverse(node => {
|
|
@@ -194,8 +194,8 @@ class PrioritizeLcpImage extends Audit {
|
|
|
194
194
|
modifiedLCPNode.removeAllDependencies();
|
|
195
195
|
modifiedLCPNode.addDependency(mainDocumentNode);
|
|
196
196
|
|
|
197
|
-
const simulationBeforeChanges = simulator.simulate(graph
|
|
198
|
-
const simulationAfterChanges = simulator.simulate(modifiedGraph
|
|
197
|
+
const simulationBeforeChanges = simulator.simulate(graph);
|
|
198
|
+
const simulationAfterChanges = simulator.simulate(modifiedGraph);
|
|
199
199
|
const lcpTimingsBefore = simulationBeforeChanges.nodeTimings.get(lcpNode);
|
|
200
200
|
if (!lcpTimingsBefore) throw new Error('Impossible - node timings should never be undefined');
|
|
201
201
|
const lcpTimingsAfter = simulationAfterChanges.nodeTimings.get(modifiedLCPNode);
|
|
@@ -143,9 +143,9 @@ class UsesRelPreloadAudit extends Audit {
|
|
|
143
143
|
return {wastedMs: 0, results: []};
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
-
// Preload changes the ordering of requests, simulate the original graph
|
|
146
|
+
// Preload changes the ordering of requests, simulate the original graph
|
|
147
147
|
// to have a reasonable baseline for comparison.
|
|
148
|
-
const simulationBeforeChanges = simulator.simulate(graph
|
|
148
|
+
const simulationBeforeChanges = simulator.simulate(graph);
|
|
149
149
|
const modifiedGraph = graph.cloneWithRelationships();
|
|
150
150
|
|
|
151
151
|
/** @type {Array<LH.Gatherer.Simulation.GraphNetworkNode>} */
|
|
@@ -174,8 +174,8 @@ class UsesRelPreloadAudit extends Audit {
|
|
|
174
174
|
node.addDependency(mainDocumentNode);
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
-
// Once we've modified the dependencies, simulate the new graph
|
|
178
|
-
const simulationAfterChanges = simulator.simulate(modifiedGraph
|
|
177
|
+
// Once we've modified the dependencies, simulate the new graph.
|
|
178
|
+
const simulationAfterChanges = simulator.simulate(modifiedGraph);
|
|
179
179
|
const originalNodesByRecord = Array.from(simulationBeforeChanges.nodeTimings.keys())
|
|
180
180
|
// @ts-expect-error we don't care if all nodes without a record collect on `undefined`
|
|
181
181
|
.reduce((map, node) => map.set(node.record, node), new Map());
|
|
@@ -101,19 +101,15 @@ class Metric {
|
|
|
101
101
|
const optimisticGraph = this.getOptimisticGraph(graph, processedNavigation);
|
|
102
102
|
const pessimisticGraph = this.getPessimisticGraph(graph, processedNavigation);
|
|
103
103
|
|
|
104
|
-
/** @type {{flexibleOrdering?: boolean, label?: string}} */
|
|
105
104
|
let simulateOptions = {label: `optimistic${metricName}`};
|
|
106
105
|
const optimisticSimulation = simulator.simulate(optimisticGraph, simulateOptions);
|
|
107
106
|
|
|
108
|
-
simulateOptions = {label: `optimisticFlex${metricName}`, flexibleOrdering: true};
|
|
109
|
-
const optimisticFlexSimulation = simulator.simulate(optimisticGraph, simulateOptions);
|
|
110
|
-
|
|
111
107
|
simulateOptions = {label: `pessimistic${metricName}`};
|
|
112
108
|
const pessimisticSimulation = simulator.simulate(pessimisticGraph, simulateOptions);
|
|
113
109
|
|
|
114
110
|
const optimisticEstimate = this.getEstimateFromSimulation(
|
|
115
|
-
optimisticSimulation
|
|
116
|
-
|
|
111
|
+
optimisticSimulation,
|
|
112
|
+
{...extras, optimistic: true}
|
|
117
113
|
);
|
|
118
114
|
|
|
119
115
|
const pessimisticEstimate = this.getEstimateFromSimulation(
|
|
@@ -18,12 +18,11 @@ class SpeedIndex extends Metric {
|
|
|
18
18
|
*/
|
|
19
19
|
static get COEFFICIENTS() {
|
|
20
20
|
return {
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
|
|
24
|
-
intercept: -250,
|
|
21
|
+
// Note that the optimistic estimate is based on the real observed speed index rather than a
|
|
22
|
+
// real lantern graph (and the final estimate will be Math.max(FCP, Speed Index)).
|
|
23
|
+
intercept: 0,
|
|
25
24
|
optimistic: 1.4,
|
|
26
|
-
pessimistic: 0.
|
|
25
|
+
pessimistic: 0.4,
|
|
27
26
|
};
|
|
28
27
|
}
|
|
29
28
|
|
|
@@ -19,27 +19,17 @@ export class ConnectionPool {
|
|
|
19
19
|
_initializeConnections(): void;
|
|
20
20
|
/**
|
|
21
21
|
* @param {Array<TcpConnection>} connections
|
|
22
|
-
* @param {{ignoreConnectionReused?: boolean, observedConnectionWasReused: boolean}} options
|
|
23
22
|
*/
|
|
24
|
-
_findAvailableConnectionWithLargestCongestionWindow(connections: Array<TcpConnection
|
|
25
|
-
ignoreConnectionReused?: boolean | undefined;
|
|
26
|
-
observedConnectionWasReused: boolean;
|
|
27
|
-
}): TcpConnection | null;
|
|
23
|
+
_findAvailableConnectionWithLargestCongestionWindow(connections: Array<TcpConnection>): TcpConnection | null;
|
|
28
24
|
/**
|
|
29
25
|
* This method finds an available connection to the origin specified by the network record or null
|
|
30
26
|
* if no connection was available. If returned, connection will not be available for other network
|
|
31
27
|
* records until release is called.
|
|
32
28
|
*
|
|
33
|
-
* If ignoreConnectionReused is true, acquire will consider all connections not in use as available.
|
|
34
|
-
* Otherwise, only connections that have matching "warmth" are considered available.
|
|
35
|
-
*
|
|
36
29
|
* @param {Lantern.NetworkRequest} record
|
|
37
|
-
* @param {{ignoreConnectionReused?: boolean}} options
|
|
38
30
|
* @return {?TcpConnection}
|
|
39
31
|
*/
|
|
40
|
-
acquire(record: Lantern.NetworkRequest
|
|
41
|
-
ignoreConnectionReused?: boolean;
|
|
42
|
-
}): TcpConnection | null;
|
|
32
|
+
acquire(record: Lantern.NetworkRequest): TcpConnection | null;
|
|
43
33
|
/**
|
|
44
34
|
* Return the connection currently being used to fetch a record. If no connection
|
|
45
35
|
* currently being used for this record, an error will be thrown.
|
|
@@ -85,24 +85,13 @@ export class ConnectionPool {
|
|
|
85
85
|
|
|
86
86
|
/**
|
|
87
87
|
* @param {Array<TcpConnection>} connections
|
|
88
|
-
* @param {{ignoreConnectionReused?: boolean, observedConnectionWasReused: boolean}} options
|
|
89
88
|
*/
|
|
90
|
-
_findAvailableConnectionWithLargestCongestionWindow(connections
|
|
91
|
-
const {ignoreConnectionReused, observedConnectionWasReused} = options;
|
|
92
|
-
|
|
89
|
+
_findAvailableConnectionWithLargestCongestionWindow(connections) {
|
|
93
90
|
/** @type {TcpConnection|null} */
|
|
94
91
|
let maxConnection = null;
|
|
95
92
|
for (let i = 0; i < connections.length; i++) {
|
|
96
93
|
const connection = connections[i];
|
|
97
94
|
|
|
98
|
-
// Normally, we want to make sure the connection warmth matches the state of the record
|
|
99
|
-
// we're acquiring for. Do this check first since it's the common case and cheaper than our
|
|
100
|
-
// "in use" check below.
|
|
101
|
-
// Use the _warmed property instead of the getter because this is a surprisingly hot code path.
|
|
102
|
-
if (!ignoreConnectionReused && connection._warmed !== observedConnectionWasReused) {
|
|
103
|
-
continue;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
95
|
// Connections that are in use are never available.
|
|
107
96
|
if (this._connectionsInUse.has(connection)) {
|
|
108
97
|
continue;
|
|
@@ -121,23 +110,15 @@ export class ConnectionPool {
|
|
|
121
110
|
* if no connection was available. If returned, connection will not be available for other network
|
|
122
111
|
* records until release is called.
|
|
123
112
|
*
|
|
124
|
-
* If ignoreConnectionReused is true, acquire will consider all connections not in use as available.
|
|
125
|
-
* Otherwise, only connections that have matching "warmth" are considered available.
|
|
126
|
-
*
|
|
127
113
|
* @param {Lantern.NetworkRequest} record
|
|
128
|
-
* @param {{ignoreConnectionReused?: boolean}} options
|
|
129
114
|
* @return {?TcpConnection}
|
|
130
115
|
*/
|
|
131
|
-
acquire(record
|
|
116
|
+
acquire(record) {
|
|
132
117
|
if (this._connectionsByRecord.has(record)) throw new Error('Record already has a connection');
|
|
133
118
|
|
|
134
119
|
const origin = record.parsedURL.securityOrigin;
|
|
135
|
-
const observedConnectionWasReused = !!this._connectionReusedByRequestId.get(record.requestId);
|
|
136
120
|
const connections = this._connectionsByOrigin.get(origin) || [];
|
|
137
|
-
const connectionToUse = this._findAvailableConnectionWithLargestCongestionWindow(connections
|
|
138
|
-
ignoreConnectionReused: options.ignoreConnectionReused,
|
|
139
|
-
observedConnectionWasReused,
|
|
140
|
-
});
|
|
121
|
+
const connectionToUse = this._findAvailableConnectionWithLargestCongestionWindow(connections);
|
|
141
122
|
|
|
142
123
|
if (!connectionToUse) return null;
|
|
143
124
|
|
|
@@ -29,7 +29,6 @@ export class Simulator<T = any> {
|
|
|
29
29
|
_layoutTaskMultiplier: number;
|
|
30
30
|
/** @type {Array<Node>} */
|
|
31
31
|
_cachedNodeListByStartPosition: Array<Node>;
|
|
32
|
-
_flexibleOrdering: boolean;
|
|
33
32
|
_nodeTimings: SimulatorTimingMap;
|
|
34
33
|
/** @type {Map<string, number>} */
|
|
35
34
|
_numberInProgressByType: Map<string, number>;
|
|
@@ -131,17 +130,16 @@ export class Simulator<T = any> {
|
|
|
131
130
|
* Estimates the time taken to process all of the graph's nodes, returns the overall time along with
|
|
132
131
|
* each node annotated by start/end times.
|
|
133
132
|
*
|
|
134
|
-
*
|
|
133
|
+
* Simulator/connection pool are allowed to deviate from what was
|
|
135
134
|
* observed in the trace/devtoolsLog and start requests as soon as they are queued (i.e. do not
|
|
136
135
|
* wait around for a warm connection to be available if the original record was fetched on a warm
|
|
137
136
|
* connection).
|
|
138
137
|
*
|
|
139
138
|
* @param {Node} graph
|
|
140
|
-
* @param {{
|
|
139
|
+
* @param {{label?: string}=} options
|
|
141
140
|
* @return {Lantern.Simulation.Result<T>}
|
|
142
141
|
*/
|
|
143
142
|
simulate(graph: Node, options?: {
|
|
144
|
-
flexibleOrdering?: boolean;
|
|
145
143
|
label?: string;
|
|
146
144
|
} | undefined): Lantern.Simulation.Result<T>;
|
|
147
145
|
/**
|
|
@@ -80,7 +80,6 @@ class Simulator {
|
|
|
80
80
|
this._cachedNodeListByStartPosition = [];
|
|
81
81
|
|
|
82
82
|
// Properties reset on every `.simulate` call but duplicated here for type checking
|
|
83
|
-
this._flexibleOrdering = false;
|
|
84
83
|
this._nodeTimings = new SimulatorTimingMap();
|
|
85
84
|
/** @type {Map<string, number>} */
|
|
86
85
|
this._numberInProgressByType = new Map();
|
|
@@ -197,9 +196,7 @@ class Simulator {
|
|
|
197
196
|
* @return {?TcpConnection}
|
|
198
197
|
*/
|
|
199
198
|
_acquireConnection(record) {
|
|
200
|
-
return this._connectionPool.acquire(record
|
|
201
|
-
ignoreConnectionReused: this._flexibleOrdering,
|
|
202
|
-
});
|
|
199
|
+
return this._connectionPool.acquire(record);
|
|
203
200
|
}
|
|
204
201
|
|
|
205
202
|
/**
|
|
@@ -425,13 +422,13 @@ class Simulator {
|
|
|
425
422
|
* Estimates the time taken to process all of the graph's nodes, returns the overall time along with
|
|
426
423
|
* each node annotated by start/end times.
|
|
427
424
|
*
|
|
428
|
-
*
|
|
425
|
+
* Simulator/connection pool are allowed to deviate from what was
|
|
429
426
|
* observed in the trace/devtoolsLog and start requests as soon as they are queued (i.e. do not
|
|
430
427
|
* wait around for a warm connection to be available if the original record was fetched on a warm
|
|
431
428
|
* connection).
|
|
432
429
|
*
|
|
433
430
|
* @param {Node} graph
|
|
434
|
-
* @param {{
|
|
431
|
+
* @param {{label?: string}=} options
|
|
435
432
|
* @return {Lantern.Simulation.Result<T>}
|
|
436
433
|
*/
|
|
437
434
|
simulate(graph, options) {
|
|
@@ -441,11 +438,9 @@ class Simulator {
|
|
|
441
438
|
|
|
442
439
|
options = Object.assign({
|
|
443
440
|
label: undefined,
|
|
444
|
-
flexibleOrdering: false,
|
|
445
441
|
}, options);
|
|
446
442
|
|
|
447
443
|
// initialize the necessary data containers
|
|
448
|
-
this._flexibleOrdering = !!options.flexibleOrdering;
|
|
449
444
|
this._dns = new DNSCache({rtt: this._rtt});
|
|
450
445
|
this._initializeConnectionPool(graph);
|
|
451
446
|
this._initializeAuxiliaryData();
|
|
@@ -470,11 +465,9 @@ class Simulator {
|
|
|
470
465
|
}
|
|
471
466
|
|
|
472
467
|
if (!nodesInProgress.size) {
|
|
473
|
-
//
|
|
474
|
-
//
|
|
475
|
-
|
|
476
|
-
this._flexibleOrdering = true;
|
|
477
|
-
continue;
|
|
468
|
+
// Interplay between fromDiskCache and connectionReused can be incorrect,
|
|
469
|
+
// have to give up.
|
|
470
|
+
throw new Error('Failed to start a node');
|
|
478
471
|
}
|
|
479
472
|
|
|
480
473
|
// set the available throughput for all connections based on # inflight
|