lighthouse 12.0.0-dev.20240610 → 12.0.0-dev.20240612

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 (118) 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 +2 -2
  3. package/core/audits/byte-efficiency/render-blocking-resources.d.ts +3 -3
  4. package/core/audits/byte-efficiency/render-blocking-resources.js +4 -4
  5. package/core/audits/dobetterweb/uses-http2.d.ts +2 -2
  6. package/core/audits/dobetterweb/uses-http2.js +2 -2
  7. package/core/audits/long-tasks.d.ts +7 -6
  8. package/core/audits/long-tasks.js +5 -4
  9. package/core/audits/prioritize-lcp-image.d.ts +1 -1
  10. package/core/computed/document-urls.js +3 -2
  11. package/core/computed/load-simulator.d.ts +4 -4
  12. package/core/computed/load-simulator.js +3 -3
  13. package/core/computed/main-resource.js +3 -2
  14. package/core/computed/metrics/lantern-first-contentful-paint.d.ts +4 -4
  15. package/core/computed/metrics/lantern-first-contentful-paint.js +3 -3
  16. package/core/computed/metrics/lantern-interactive.d.ts +4 -4
  17. package/core/computed/metrics/lantern-interactive.js +3 -3
  18. package/core/computed/metrics/lantern-largest-contentful-paint.d.ts +4 -4
  19. package/core/computed/metrics/lantern-largest-contentful-paint.js +3 -3
  20. package/core/computed/metrics/lantern-max-potential-fid.d.ts +4 -4
  21. package/core/computed/metrics/lantern-max-potential-fid.js +3 -3
  22. package/core/computed/metrics/lantern-metric.d.ts +4 -3
  23. package/core/computed/metrics/lantern-metric.js +4 -4
  24. package/core/computed/metrics/lantern-speed-index.d.ts +4 -4
  25. package/core/computed/metrics/lantern-speed-index.js +3 -3
  26. package/core/computed/metrics/lantern-total-blocking-time.d.ts +4 -4
  27. package/core/computed/metrics/lantern-total-blocking-time.js +3 -3
  28. package/core/computed/metrics/total-blocking-time.js +3 -1
  29. package/core/computed/network-analysis.js +2 -2
  30. package/core/computed/page-dependency-graph.d.ts +2 -2
  31. package/core/computed/page-dependency-graph.js +6 -6
  32. package/core/computed/processed-navigation.d.ts +1 -1
  33. package/core/computed/tbt-impact-tasks.js +4 -2
  34. package/core/config/constants.d.ts +28 -53
  35. package/core/config/constants.js +2 -43
  36. package/core/lib/asset-saver.js +2 -2
  37. package/core/lib/lantern/{base-node.d.ts → BaseNode.d.ts} +7 -9
  38. package/core/lib/lantern/{base-node.js → BaseNode.js} +6 -6
  39. package/core/lib/lantern/BaseNode.test.js +385 -0
  40. package/core/lib/lantern/CpuNode.d.ts +43 -0
  41. package/core/lib/lantern/{cpu-node.js → CpuNode.js} +8 -9
  42. package/core/lib/lantern/{lantern-error.d.ts → LanternError.d.ts} +1 -1
  43. package/core/lib/lantern/{metric.d.ts → Metric.d.ts} +17 -17
  44. package/core/lib/lantern/{metric.js → Metric.js} +10 -12
  45. package/core/lib/lantern/{network-node.d.ts → NetworkNode.d.ts} +4 -5
  46. package/core/lib/lantern/{network-node.js → NetworkNode.js} +4 -5
  47. package/core/lib/lantern/{page-dependency-graph.d.ts → PageDependencyGraph.d.ts} +22 -11
  48. package/core/lib/lantern/{page-dependency-graph.js → PageDependencyGraph.js} +56 -22
  49. package/core/lib/lantern/PageDependencyGraph.test.js +654 -0
  50. package/core/lib/lantern/{tbt-utils.d.ts → TBTUtils.d.ts} +1 -1
  51. package/core/lib/lantern/TBTUtils.test.d.ts +2 -0
  52. package/core/lib/lantern/TBTUtils.test.js +130 -0
  53. package/core/lib/lantern/{trace-engine-computation-data.d.ts → TraceEngineComputationData.d.ts} +7 -7
  54. package/core/lib/lantern/{trace-engine-computation-data.js → TraceEngineComputationData.js} +18 -16
  55. package/core/lib/lantern/lantern.d.ts +19 -7
  56. package/core/lib/lantern/lantern.js +21 -1
  57. package/core/lib/lantern/metrics/{first-contentful-paint.d.ts → FirstContentfulPaint.d.ts} +11 -11
  58. package/core/lib/lantern/metrics/{first-contentful-paint.js → FirstContentfulPaint.js} +8 -10
  59. package/core/lib/lantern/metrics/FirstContentfulPaint.test.js +54 -0
  60. package/core/lib/lantern/metrics/Interactive.d.ts +20 -0
  61. package/core/lib/lantern/metrics/{interactive.js → Interactive.js} +14 -17
  62. package/core/lib/lantern/metrics/Interactive.test.js +56 -0
  63. package/core/lib/lantern/metrics/LargestContentfulPaint.d.ts +19 -0
  64. package/core/lib/lantern/metrics/{largest-contentful-paint.js → LargestContentfulPaint.js} +12 -15
  65. package/core/lib/lantern/metrics/LargestContentfulPaint.test.js +42 -0
  66. package/core/lib/lantern/metrics/MaxPotentialFID.d.ts +24 -0
  67. package/core/lib/lantern/metrics/{max-potential-fid.js → MaxPotentialFID.js} +12 -13
  68. package/core/lib/lantern/metrics/MetricTestUtils.d.ts +19 -0
  69. package/core/lib/lantern/metrics/MetricTestUtils.js +48 -0
  70. package/core/lib/lantern/metrics/{speed-index.d.ts → SpeedIndex.d.ts} +7 -13
  71. package/core/lib/lantern/metrics/{speed-index.js → SpeedIndex.js} +12 -14
  72. package/core/lib/lantern/metrics/SpeedIndex.test.js +83 -0
  73. package/core/lib/lantern/metrics/TotalBlockingTime.d.ts +25 -0
  74. package/core/lib/lantern/metrics/{total-blocking-time.js → TotalBlockingTime.js} +13 -16
  75. package/core/lib/lantern/metrics/__snapshots__/first-contentful-paint-test.js.snap +11 -0
  76. package/core/lib/lantern/metrics/__snapshots__/interactive-test.js.snap +17 -0
  77. package/core/lib/lantern/metrics/metrics.d.ts +9 -0
  78. package/core/lib/lantern/metrics/metrics.js +16 -0
  79. package/core/lib/lantern/{simulator/connection-pool.d.ts → simulation/ConnectionPool.d.ts} +5 -5
  80. package/core/lib/lantern/{simulator/connection-pool.js → simulation/ConnectionPool.js} +3 -3
  81. package/core/lib/lantern/simulation/ConnectionPool.test.js +195 -0
  82. package/core/lib/lantern/simulation/Constants.d.ts +52 -0
  83. package/core/lib/lantern/simulation/Constants.js +48 -0
  84. package/core/lib/lantern/{simulator/dns-cache.d.ts → simulation/DNSCache.d.ts} +2 -2
  85. package/core/lib/lantern/{simulator/dns-cache.js → simulation/DNSCache.js} +1 -1
  86. package/core/lib/lantern/simulation/DNSCache.test.js +72 -0
  87. package/core/lib/lantern/{simulator/network-analyzer.d.ts → simulation/NetworkAnalyzer.d.ts} +8 -8
  88. package/core/lib/lantern/{simulator/network-analyzer.js → simulation/NetworkAnalyzer.js} +3 -3
  89. package/core/lib/lantern/simulation/NetworkAnalyzer.test.js +475 -0
  90. package/core/lib/lantern/{simulator/simulator-timing-map.d.ts → simulation/SimulationTimingMap.d.ts} +7 -7
  91. package/core/lib/lantern/{simulator/simulator-timing-map.js → simulation/SimulationTimingMap.js} +5 -6
  92. package/core/lib/lantern/{simulator/simulator.d.ts → simulation/Simulator.d.ts} +13 -15
  93. package/core/lib/lantern/{simulator/simulator.js → simulation/Simulator.js} +29 -30
  94. package/core/lib/lantern/simulation/Simulator.test.js +434 -0
  95. package/core/lib/lantern/simulation/TCPConnection.test.d.ts +2 -0
  96. package/core/lib/lantern/simulation/TCPConnection.test.js +374 -0
  97. package/core/lib/lantern/{simulator/tcp-connection.d.ts → simulation/TcpConnection.d.ts} +2 -2
  98. package/core/lib/lantern/{simulator/tcp-connection.js → simulation/TcpConnection.js} +1 -1
  99. package/core/lib/lantern/simulation/simulation.d.ts +21 -0
  100. package/core/lib/lantern/simulation/simulation.js +28 -0
  101. package/core/lib/lantern/types/lantern.d.ts +81 -9
  102. package/core/lib/lantern-trace-saver.d.ts +4 -4
  103. package/core/lib/lantern-trace-saver.js +3 -3
  104. package/core/lib/navigation-error.js +3 -3
  105. package/core/lib/network-recorder.js +2 -2
  106. package/core/lib/network-request.d.ts +1 -1
  107. package/core/lib/network-request.js +1 -1
  108. package/package.json +5 -5
  109. package/tsconfig.json +10 -8
  110. package/types/artifacts.d.ts +3 -10
  111. package/types/gatherer.d.ts +1 -1
  112. package/core/lib/lantern/cpu-node.d.ts +0 -44
  113. package/core/lib/lantern/metrics/interactive.d.ts +0 -26
  114. package/core/lib/lantern/metrics/largest-contentful-paint.d.ts +0 -19
  115. package/core/lib/lantern/metrics/max-potential-fid.d.ts +0 -30
  116. package/core/lib/lantern/metrics/total-blocking-time.d.ts +0 -31
  117. /package/core/lib/lantern/{lantern-error.js → LanternError.js} +0 -0
  118. /package/core/lib/lantern/{tbt-utils.js → TBTUtils.js} +0 -0
@@ -0,0 +1,19 @@
1
+ export type Node = import('../BaseNode.js').Node;
2
+ /** @typedef {import('../BaseNode.js').Node} Node */
3
+ export class LargestContentfulPaint extends Lantern.Metric {
4
+ /**
5
+ * Low priority image nodes are usually offscreen and very unlikely to be the
6
+ * resource that is required for LCP. Our LCP graphs include everything except for these images.
7
+ *
8
+ * @param {Node} node
9
+ * @return {boolean}
10
+ */
11
+ static isNotLowPriorityImageNode(node: Node): boolean;
12
+ /**
13
+ * @param {Lantern.Simulation.Result} simulationResult
14
+ * @return {Lantern.Simulation.Result}
15
+ */
16
+ static getEstimateFromSimulation(simulationResult: Lantern.Simulation.Result): Lantern.Simulation.Result;
17
+ }
18
+ import * as Lantern from '../lantern.js';
19
+ //# sourceMappingURL=LargestContentfulPaint.d.ts.map
@@ -4,16 +4,13 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
 
7
- import * as Lantern from '../types/lantern.js';
8
- import {Metric} from '../metric.js';
9
- import {FirstContentfulPaint} from './first-contentful-paint.js';
10
- import {LanternError} from '../lantern-error.js';
7
+ import * as Lantern from '../lantern.js';
11
8
 
12
- /** @typedef {import('../base-node.js').Node} Node */
9
+ /** @typedef {import('../BaseNode.js').Node} Node */
13
10
 
14
- class LargestContentfulPaint extends Metric {
11
+ class LargestContentfulPaint extends Lantern.Metric {
15
12
  /**
16
- * @return {LH.Gatherer.Simulation.MetricCoefficients}
13
+ * @return {Lantern.Simulation.MetricCoefficients}
17
14
  */
18
15
  static get COEFFICIENTS() {
19
16
  return {
@@ -45,10 +42,10 @@ class LargestContentfulPaint extends Metric {
45
42
  static getOptimisticGraph(dependencyGraph, processedNavigation) {
46
43
  const lcp = processedNavigation.timestamps.largestContentfulPaint;
47
44
  if (!lcp) {
48
- throw new LanternError('NO_LCP');
45
+ throw new Lantern.Error('NO_LCP');
49
46
  }
50
47
 
51
- return FirstContentfulPaint.getFirstPaintBasedGraph(dependencyGraph, {
48
+ return Lantern.Metrics.FirstContentfulPaint.getFirstPaintBasedGraph(dependencyGraph, {
52
49
  cutoffTimestamp: lcp,
53
50
  treatNodeAsRenderBlocking: LargestContentfulPaint.isNotLowPriorityImageNode,
54
51
  });
@@ -62,10 +59,10 @@ class LargestContentfulPaint extends Metric {
62
59
  static getPessimisticGraph(dependencyGraph, processedNavigation) {
63
60
  const lcp = processedNavigation.timestamps.largestContentfulPaint;
64
61
  if (!lcp) {
65
- throw new LanternError('NO_LCP');
62
+ throw new Lantern.Error('NO_LCP');
66
63
  }
67
64
 
68
- return FirstContentfulPaint.getFirstPaintBasedGraph(dependencyGraph, {
65
+ return Lantern.Metrics.FirstContentfulPaint.getFirstPaintBasedGraph(dependencyGraph, {
69
66
  cutoffTimestamp: lcp,
70
67
  treatNodeAsRenderBlocking: _ => true,
71
68
  // For pessimistic LCP we'll include *all* layout nodes
@@ -74,8 +71,8 @@ class LargestContentfulPaint extends Metric {
74
71
  }
75
72
 
76
73
  /**
77
- * @param {LH.Gatherer.Simulation.Result} simulationResult
78
- * @return {LH.Gatherer.Simulation.Result}
74
+ * @param {Lantern.Simulation.Result} simulationResult
75
+ * @return {Lantern.Simulation.Result}
79
76
  */
80
77
  static getEstimateFromSimulation(simulationResult) {
81
78
  const nodeTimesNotOffscreenImages = Array.from(simulationResult.nodeTimings.entries())
@@ -90,8 +87,8 @@ class LargestContentfulPaint extends Metric {
90
87
 
91
88
  /**
92
89
  * @param {Lantern.Simulation.MetricComputationDataInput} data
93
- * @param {Omit<import('../metric.js').Extras, 'optimistic'>=} extras
94
- * @return {Promise<LH.Artifacts.LanternMetric>}
90
+ * @param {Omit<import('../Metric.js').Extras, 'optimistic'>=} extras
91
+ * @return {Promise<Lantern.Metrics.Result>}
95
92
  */
96
93
  static async compute(data, extras) {
97
94
  const fcpResult = extras?.fcpResult;
@@ -0,0 +1,42 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2024 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import assert from 'assert/strict';
8
+
9
+ import * as Lantern from '../lantern.js';
10
+ import {getComputationDataFromFixture} from './MetricTestUtils.js';
11
+ import {readJson} from '../../../test/test-utils.js';
12
+
13
+ const {FirstContentfulPaint, LargestContentfulPaint} = Lantern.Metrics;
14
+
15
+ const trace = readJson('../../../fixtures/artifacts/paul/trace.json', import.meta);
16
+
17
+ describe('Metrics: Lantern LCP', () => {
18
+ it('should compute predicted value', async () => {
19
+ const data = await getComputationDataFromFixture({trace});
20
+ const result = await LargestContentfulPaint.compute(data, {
21
+ fcpResult: await FirstContentfulPaint.compute(data),
22
+ });
23
+
24
+ expect({
25
+ timing: Math.round(result.timing),
26
+ optimistic: Math.round(result.optimisticEstimate.timeInMs),
27
+ pessimistic: Math.round(result.pessimisticEstimate.timeInMs),
28
+ optimisticNodeTimings: result.optimisticEstimate.nodeTimings.size,
29
+ pessimisticNodeTimings: result.pessimisticEstimate.nodeTimings.size}).
30
+ toMatchInlineSnapshot(`
31
+ Object {
32
+ "optimistic": 1457,
33
+ "optimisticNodeTimings": 8,
34
+ "pessimistic": 1616,
35
+ "pessimisticNodeTimings": 9,
36
+ "timing": 1536,
37
+ }
38
+ `);
39
+ assert.ok(result.optimisticGraph, 'should have created optimistic graph');
40
+ assert.ok(result.pessimisticGraph, 'should have created pessimistic graph');
41
+ });
42
+ });
@@ -0,0 +1,24 @@
1
+ export type Node = import('../BaseNode.js').Node;
2
+ /** @typedef {import('../BaseNode.js').Node} Node */
3
+ export class MaxPotentialFID extends Lantern.Metric {
4
+ /**
5
+ * @param {Node} dependencyGraph
6
+ * @return {Node}
7
+ */
8
+ static getOptimisticGraph(dependencyGraph: Node): Node;
9
+ /**
10
+ * @param {Node} dependencyGraph
11
+ * @return {Node}
12
+ */
13
+ static getPessimisticGraph(dependencyGraph: Node): Node;
14
+ /**
15
+ * @param {Lantern.Simulation.Result['nodeTimings']} nodeTimings
16
+ * @param {number} fcpTimeInMs
17
+ * @return {Array<{duration: number}>}
18
+ */
19
+ static getTimingsAfterFCP(nodeTimings: Lantern.Simulation.Result['nodeTimings'], fcpTimeInMs: number): Array<{
20
+ duration: number;
21
+ }>;
22
+ }
23
+ import * as Lantern from '../lantern.js';
24
+ //# sourceMappingURL=MaxPotentialFID.d.ts.map
@@ -4,15 +4,13 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
 
7
- import * as Lantern from '../types/lantern.js';
8
- import {Metric} from '../metric.js';
9
- import {BaseNode} from '../base-node.js';
7
+ import * as Lantern from '../lantern.js';
10
8
 
11
- /** @typedef {import('../base-node.js').Node} Node */
9
+ /** @typedef {import('../BaseNode.js').Node} Node */
12
10
 
13
- class MaxPotentialFID extends Metric {
11
+ class MaxPotentialFID extends Lantern.Metric {
14
12
  /**
15
- * @return {LH.Gatherer.Simulation.MetricCoefficients}
13
+ * @return {Lantern.Simulation.MetricCoefficients}
16
14
  */
17
15
  static get COEFFICIENTS() {
18
16
  return {
@@ -39,9 +37,9 @@ class MaxPotentialFID extends Metric {
39
37
  }
40
38
 
41
39
  /**
42
- * @param {LH.Gatherer.Simulation.Result} simulation
43
- * @param {import('../metric.js').Extras} extras
44
- * @return {LH.Gatherer.Simulation.Result}
40
+ * @param {Lantern.Simulation.Result} simulation
41
+ * @param {import('../Metric.js').Extras} extras
42
+ * @return {Lantern.Simulation.Result}
45
43
  */
46
44
  static getEstimateFromSimulation(simulation, extras) {
47
45
  if (!extras.fcpResult) throw new Error('missing fcpResult');
@@ -65,8 +63,8 @@ class MaxPotentialFID extends Metric {
65
63
 
66
64
  /**
67
65
  * @param {Lantern.Simulation.MetricComputationDataInput} data
68
- * @param {Omit<import('../metric.js').Extras, 'optimistic'>=} extras
69
- * @return {Promise<LH.Artifacts.LanternMetric>}
66
+ * @param {Omit<import('../Metric.js').Extras, 'optimistic'>=} extras
67
+ * @return {Promise<Lantern.Metrics.Result>}
70
68
  */
71
69
  static compute(data, extras) {
72
70
  const fcpResult = extras?.fcpResult;
@@ -78,13 +76,14 @@ class MaxPotentialFID extends Metric {
78
76
  }
79
77
 
80
78
  /**
81
- * @param {LH.Gatherer.Simulation.Result['nodeTimings']} nodeTimings
79
+ * @param {Lantern.Simulation.Result['nodeTimings']} nodeTimings
82
80
  * @param {number} fcpTimeInMs
83
81
  * @return {Array<{duration: number}>}
84
82
  */
85
83
  static getTimingsAfterFCP(nodeTimings, fcpTimeInMs) {
86
84
  return Array.from(nodeTimings.entries())
87
- .filter(([node, timing]) => node.type === BaseNode.TYPES.CPU && timing.endTime > fcpTimeInMs)
85
+ .filter(([node, timing]) =>
86
+ node.type === Lantern.BaseNode.TYPES.CPU && timing.endTime > fcpTimeInMs)
88
87
  .map(([_, timing]) => timing);
89
88
  }
90
89
  }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @param {TraceEngine.Types.TraceEvents.TraceEventData[]} traceEvents
3
+ */
4
+ export function runTraceEngine(traceEvents: TraceEngine.Types.TraceEvents.TraceEventData[]): Promise<TraceEngine.Handlers.Types.EnabledHandlerDataWithMeta<typeof TraceEngine.Handlers.ModelHandlers>>;
5
+ /**
6
+ * @param {{trace: Lantern.Trace, settings?: Lantern.Simulation.Settings, URL?: Lantern.Simulation.URL}} opts
7
+ */
8
+ export function getComputationDataFromFixture({ trace, settings, URL }: {
9
+ trace: Lantern.Trace;
10
+ settings?: Lantern.Simulation.Settings;
11
+ URL?: Lantern.Simulation.URL;
12
+ }): Promise<{
13
+ simulator: Lantern.Simulation.Simulator<any>;
14
+ graph: import("../PageDependencyGraph.js").Node;
15
+ processedNavigation: import("../types/lantern.js").Simulation.ProcessedNavigation;
16
+ }>;
17
+ import * as TraceEngine from '@paulirish/trace_engine';
18
+ import * as Lantern from '../lantern.js';
19
+ //# sourceMappingURL=MetricTestUtils.d.ts.map
@@ -0,0 +1,48 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2024 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import * as TraceEngine from '@paulirish/trace_engine';
8
+
9
+ import * as Lantern from '../lantern.js';
10
+ import {polyfillDOMRect} from '../../polyfill-dom-rect.js';
11
+
12
+ polyfillDOMRect();
13
+
14
+ /**
15
+ * @param {TraceEngine.Types.TraceEvents.TraceEventData[]} traceEvents
16
+ */
17
+ async function runTraceEngine(traceEvents) {
18
+ const processor = TraceEngine.Processor.TraceProcessor.createWithAllHandlers();
19
+ await processor.parse(traceEvents);
20
+ if (!processor.traceParsedData) throw new Error('No data');
21
+ return processor.traceParsedData;
22
+ }
23
+
24
+ /**
25
+ * @param {{trace: Lantern.Trace, settings?: Lantern.Simulation.Settings, URL?: Lantern.Simulation.URL}} opts
26
+ */
27
+ async function getComputationDataFromFixture({trace, settings, URL}) {
28
+ settings = settings ?? /** @type {Lantern.Simulation.Settings} */({});
29
+ if (!settings.throttlingMethod) settings.throttlingMethod = 'simulate';
30
+ const traceEngineData = await runTraceEngine(
31
+ /** @type {TraceEngine.Types.TraceEvents.TraceEventData[]} */ (trace.traceEvents)
32
+ );
33
+ const requests =
34
+ Lantern.TraceEngineComputationData.createNetworkRequests(trace, traceEngineData);
35
+ const networkAnalysis = Lantern.Simulation.NetworkAnalyzer.analyze(requests);
36
+
37
+ return {
38
+ simulator: Lantern.Simulation.Simulator.createSimulator({...settings, networkAnalysis}),
39
+ graph: Lantern.TraceEngineComputationData.createGraph(requests, trace, traceEngineData, URL),
40
+ processedNavigation:
41
+ Lantern.TraceEngineComputationData.createProcessedNavigation(traceEngineData),
42
+ };
43
+ }
44
+
45
+ export {
46
+ runTraceEngine,
47
+ getComputationDataFromFixture,
48
+ };
@@ -1,6 +1,6 @@
1
- export type Node = import('../base-node.js').Node;
2
- /** @typedef {import('../base-node.js').Node} Node */
3
- export class SpeedIndex extends Metric {
1
+ export type Node = import('../BaseNode.js').Node;
2
+ /** @typedef {import('../BaseNode.js').Node} Node */
3
+ export class SpeedIndex extends Lantern.Metric {
4
4
  /**
5
5
  * @param {Node} dependencyGraph
6
6
  * @return {Node}
@@ -11,12 +11,6 @@ export class SpeedIndex extends Metric {
11
11
  * @return {Node}
12
12
  */
13
13
  static getPessimisticGraph(dependencyGraph: Node): Node;
14
- /**
15
- * @param {LH.Gatherer.Simulation.Result} simulationResult
16
- * @param {import('../metric.js').Extras} extras
17
- * @return {LH.Gatherer.Simulation.Result}
18
- */
19
- static getEstimateFromSimulation(simulationResult: LH.Gatherer.Simulation.Result, extras: import('../metric.js').Extras): LH.Gatherer.Simulation.Result;
20
14
  /**
21
15
  * Approximate speed index using layout events from the simulated node timings.
22
16
  * The layout-based speed index is the weighted average of the endTime of CPU nodes that contained
@@ -28,11 +22,11 @@ export class SpeedIndex extends Metric {
28
22
  * different methods. Read more in the evaluation doc.
29
23
  *
30
24
  * @see https://docs.google.com/document/d/1qJWXwxoyVLVadezIp_Tgdk867G3tDNkkVRvUJSH3K1E/edit#
31
- * @param {LH.Gatherer.Simulation.Result['nodeTimings']} nodeTimings
25
+ * @param {Lantern.Simulation.Result['nodeTimings']} nodeTimings
32
26
  * @param {number} fcpTimeInMs
33
27
  * @return {number}
34
28
  */
35
- static computeLayoutBasedSpeedIndex(nodeTimings: LH.Gatherer.Simulation.Result['nodeTimings'], fcpTimeInMs: number): number;
29
+ static computeLayoutBasedSpeedIndex(nodeTimings: Lantern.Simulation.Result['nodeTimings'], fcpTimeInMs: number): number;
36
30
  }
37
- import { Metric } from '../metric.js';
38
- //# sourceMappingURL=speed-index.d.ts.map
31
+ import * as Lantern from '../lantern.js';
32
+ //# sourceMappingURL=SpeedIndex.d.ts.map
@@ -4,17 +4,15 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
 
7
- import * as Lantern from '../types/lantern.js';
8
- import {Metric} from '../metric.js';
9
- import {BaseNode} from '../base-node.js';
7
+ import * as Lantern from '../lantern.js';
10
8
 
11
9
  const mobileSlow4GRtt = 150;
12
10
 
13
- /** @typedef {import('../base-node.js').Node} Node */
11
+ /** @typedef {import('../BaseNode.js').Node} Node */
14
12
 
15
- class SpeedIndex extends Metric {
13
+ class SpeedIndex extends Lantern.Metric {
16
14
  /**
17
- * @return {LH.Gatherer.Simulation.MetricCoefficients}
15
+ * @return {Lantern.Simulation.MetricCoefficients}
18
16
  */
19
17
  static get COEFFICIENTS() {
20
18
  return {
@@ -28,7 +26,7 @@ class SpeedIndex extends Metric {
28
26
 
29
27
  /**
30
28
  * @param {number} rttMs
31
- * @return {LH.Gatherer.Simulation.MetricCoefficients}
29
+ * @return {Lantern.Simulation.MetricCoefficients}
32
30
  */
33
31
  static getScaledCoefficients(rttMs) { // eslint-disable-line no-unused-vars
34
32
  // We want to scale our default coefficients based on the speed of the connection.
@@ -70,9 +68,9 @@ class SpeedIndex extends Metric {
70
68
  }
71
69
 
72
70
  /**
73
- * @param {LH.Gatherer.Simulation.Result} simulationResult
74
- * @param {import('../metric.js').Extras} extras
75
- * @return {LH.Gatherer.Simulation.Result}
71
+ * @param {Lantern.Simulation.Result} simulationResult
72
+ * @param {import('../Metric.js').Extras} extras
73
+ * @return {Lantern.Simulation.Result}
76
74
  */
77
75
  static getEstimateFromSimulation(simulationResult, extras) {
78
76
  if (!extras.fcpResult) throw new Error('missing fcpResult');
@@ -90,8 +88,8 @@ class SpeedIndex extends Metric {
90
88
 
91
89
  /**
92
90
  * @param {Lantern.Simulation.MetricComputationDataInput} data
93
- * @param {Omit<import('../metric.js').Extras, 'optimistic'>=} extras
94
- * @return {Promise<LH.Artifacts.LanternMetric>}
91
+ * @param {Omit<import('../Metric.js').Extras, 'optimistic'>=} extras
92
+ * @return {Promise<Lantern.Metrics.Result>}
95
93
  */
96
94
  static async compute(data, extras) {
97
95
  const fcpResult = extras?.fcpResult;
@@ -115,7 +113,7 @@ class SpeedIndex extends Metric {
115
113
  * different methods. Read more in the evaluation doc.
116
114
  *
117
115
  * @see https://docs.google.com/document/d/1qJWXwxoyVLVadezIp_Tgdk867G3tDNkkVRvUJSH3K1E/edit#
118
- * @param {LH.Gatherer.Simulation.Result['nodeTimings']} nodeTimings
116
+ * @param {Lantern.Simulation.Result['nodeTimings']} nodeTimings
119
117
  * @param {number} fcpTimeInMs
120
118
  * @return {number}
121
119
  */
@@ -123,7 +121,7 @@ class SpeedIndex extends Metric {
123
121
  /** @type {Array<{time: number, weight: number}>} */
124
122
  const layoutWeights = [];
125
123
  for (const [node, timing] of nodeTimings.entries()) {
126
- if (node.type !== BaseNode.TYPES.CPU) continue;
124
+ if (node.type !== Lantern.BaseNode.TYPES.CPU) continue;
127
125
 
128
126
  if (node.childEvents.some(x => x.name === 'Layout')) {
129
127
  const timingWeight = Math.max(Math.log2(timing.endTime - timing.startTime), 0);
@@ -0,0 +1,83 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2024 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import * as Lantern from '../lantern.js';
8
+ import {readJson} from '../../../test/test-utils.js';
9
+ import {getComputationDataFromFixture} from './MetricTestUtils.js';
10
+
11
+ const {SpeedIndex, FirstContentfulPaint} = Lantern.Metrics;
12
+
13
+ const trace = readJson('../../../fixtures/artifacts/progressive-app/trace.json', import.meta);
14
+
15
+ const defaultThrottling = Lantern.Simulation.Constants.throttling.mobileSlow4G;
16
+
17
+ describe('Metrics: Lantern Speed Index', () => {
18
+ it('should compute predicted value', async () => {
19
+ const data = await getComputationDataFromFixture({trace});
20
+ // TODO: observedSpeedIndex is from the Speedline library, and is used for optimistic
21
+ // mode. At the moment callers must pass the result into Lantern.
22
+ const observedSpeedIndex = 379.04474997520487;
23
+ const result = await SpeedIndex.compute(data, {
24
+ fcpResult: await FirstContentfulPaint.compute(data),
25
+ observedSpeedIndex,
26
+ });
27
+
28
+ expect({
29
+ timing: Math.round(result.timing),
30
+ optimistic: Math.round(result.optimisticEstimate.timeInMs),
31
+ pessimistic: Math.round(result.pessimisticEstimate.timeInMs)}).
32
+ toMatchInlineSnapshot(`
33
+ Object {
34
+ "optimistic": 379,
35
+ "pessimistic": 1122,
36
+ "timing": 1107,
37
+ }
38
+ `);
39
+ });
40
+
41
+ it('should compute predicted value for different settings', async () => {
42
+ const settings = {throttlingMethod: 'simulate', throttling: {...defaultThrottling, rttMs: 300}};
43
+ const data = await getComputationDataFromFixture({trace, settings});
44
+ const observedSpeedIndex = 379.04474997520487;
45
+ const result = await SpeedIndex.compute(data, {
46
+ fcpResult: await FirstContentfulPaint.compute(data),
47
+ observedSpeedIndex,
48
+ });
49
+
50
+ expect({
51
+ timing: Math.round(result.timing),
52
+ optimistic: Math.round(result.optimisticEstimate.timeInMs),
53
+ pessimistic: Math.round(result.pessimisticEstimate.timeInMs)}).
54
+ toMatchInlineSnapshot(`
55
+ Object {
56
+ "optimistic": 379,
57
+ "pessimistic": 2022,
58
+ "timing": 2007,
59
+ }
60
+ `);
61
+ });
62
+
63
+ it('should not scale coefficients at default', async () => {
64
+ const result = SpeedIndex.getScaledCoefficients(defaultThrottling.rttMs);
65
+ expect(result).toEqual(SpeedIndex.COEFFICIENTS);
66
+ });
67
+
68
+ it('should scale coefficients back', async () => {
69
+ const result = SpeedIndex.getScaledCoefficients(5);
70
+ expect(result).toEqual({intercept: 0, pessimistic: 0.5, optimistic: 0.5});
71
+ });
72
+
73
+ it('should scale coefficients forward', async () => {
74
+ const result = SpeedIndex.getScaledCoefficients(300);
75
+ expect(result).toMatchInlineSnapshot(`
76
+ Object {
77
+ "intercept": 0,
78
+ "optimistic": 2.525,
79
+ "pessimistic": 0.275,
80
+ }
81
+ `);
82
+ });
83
+ });
@@ -0,0 +1,25 @@
1
+ export type Node = import('../BaseNode.js').Node;
2
+ /** @typedef {import('../BaseNode.js').Node} Node */
3
+ export class TotalBlockingTime extends Lantern.Metric {
4
+ /**
5
+ * @param {Node} dependencyGraph
6
+ * @return {Node}
7
+ */
8
+ static getOptimisticGraph(dependencyGraph: Node): Node;
9
+ /**
10
+ * @param {Node} dependencyGraph
11
+ * @return {Node}
12
+ */
13
+ static getPessimisticGraph(dependencyGraph: Node): Node;
14
+ /**
15
+ * @param {Lantern.Simulation.Result['nodeTimings']} nodeTimings
16
+ * @param {number} minDurationMs
17
+ */
18
+ static getTopLevelEvents(nodeTimings: Lantern.Simulation.Result['nodeTimings'], minDurationMs: number): {
19
+ start: number;
20
+ end: number;
21
+ duration: number;
22
+ }[];
23
+ }
24
+ import * as Lantern from '../lantern.js';
25
+ //# sourceMappingURL=TotalBlockingTime.d.ts.map
@@ -4,16 +4,13 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
 
7
- import * as Lantern from '../types/lantern.js';
8
- import {Metric} from '../metric.js';
9
- import {BaseNode} from '../base-node.js';
10
- import {BLOCKING_TIME_THRESHOLD, calculateSumOfBlockingTime} from '../tbt-utils.js';
7
+ import * as Lantern from '../lantern.js';
11
8
 
12
- /** @typedef {import('../base-node.js').Node} Node */
9
+ /** @typedef {import('../BaseNode.js').Node} Node */
13
10
 
14
- class TotalBlockingTime extends Metric {
11
+ class TotalBlockingTime extends Lantern.Metric {
15
12
  /**
16
- * @return {LH.Gatherer.Simulation.MetricCoefficients}
13
+ * @return {Lantern.Simulation.MetricCoefficients}
17
14
  */
18
15
  static get COEFFICIENTS() {
19
16
  return {
@@ -40,9 +37,9 @@ class TotalBlockingTime extends Metric {
40
37
  }
41
38
 
42
39
  /**
43
- * @param {LH.Gatherer.Simulation.Result} simulation
44
- * @param {import('../metric.js').Extras} extras
45
- * @return {LH.Gatherer.Simulation.Result}
40
+ * @param {Lantern.Simulation.Result} simulation
41
+ * @param {import('../Metric.js').Extras} extras
42
+ * @return {Lantern.Simulation.Result}
46
43
  */
47
44
  static getEstimateFromSimulation(simulation, extras) {
48
45
  if (!extras.fcpResult) throw new Error('missing fcpResult');
@@ -64,7 +61,7 @@ class TotalBlockingTime extends Metric {
64
61
  ? extras.interactiveResult.optimisticEstimate.timeInMs
65
62
  : extras.interactiveResult.pessimisticEstimate.timeInMs;
66
63
 
67
- const minDurationMs = BLOCKING_TIME_THRESHOLD;
64
+ const minDurationMs = Lantern.TBTUtils.BLOCKING_TIME_THRESHOLD;
68
65
 
69
66
  const events = TotalBlockingTime.getTopLevelEvents(
70
67
  simulation.nodeTimings,
@@ -72,7 +69,7 @@ class TotalBlockingTime extends Metric {
72
69
  );
73
70
 
74
71
  return {
75
- timeInMs: calculateSumOfBlockingTime(
72
+ timeInMs: Lantern.TBTUtils.calculateSumOfBlockingTime(
76
73
  events,
77
74
  fcpTimeInMs,
78
75
  interactiveTimeMs
@@ -83,8 +80,8 @@ class TotalBlockingTime extends Metric {
83
80
 
84
81
  /**
85
82
  * @param {Lantern.Simulation.MetricComputationDataInput} data
86
- * @param {Omit<import('../metric.js').Extras, 'optimistic'>=} extras
87
- * @return {Promise<LH.Artifacts.LanternMetric>}
83
+ * @param {Omit<import('../Metric.js').Extras, 'optimistic'>=} extras
84
+ * @return {Promise<Lantern.Metrics.Result>}
88
85
  */
89
86
  static async compute(data, extras) {
90
87
  const fcpResult = extras?.fcpResult;
@@ -101,7 +98,7 @@ class TotalBlockingTime extends Metric {
101
98
  }
102
99
 
103
100
  /**
104
- * @param {LH.Gatherer.Simulation.Result['nodeTimings']} nodeTimings
101
+ * @param {Lantern.Simulation.Result['nodeTimings']} nodeTimings
105
102
  * @param {number} minDurationMs
106
103
  */
107
104
  static getTopLevelEvents(nodeTimings, minDurationMs) {
@@ -110,7 +107,7 @@ class TotalBlockingTime extends Metric {
110
107
  const events = [];
111
108
 
112
109
  for (const [node, timing] of nodeTimings.entries()) {
113
- if (node.type !== BaseNode.TYPES.CPU) continue;
110
+ if (node.type !== Lantern.BaseNode.TYPES.CPU) continue;
114
111
  // Filtering out events below minimum duration.
115
112
  if (timing.duration < minDurationMs) continue;
116
113
 
@@ -0,0 +1,11 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`Metrics: Lantern FCP should compute predicted value 1`] = `
4
+ Object {
5
+ "optimistic": 1107,
6
+ "optimisticNodeTimings": 4,
7
+ "pessimistic": 1107,
8
+ "pessimisticNodeTimings": 4,
9
+ "timing": 1107,
10
+ }
11
+ `;
@@ -0,0 +1,17 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`Metrics: Lantern TTI should compute predicted value 1`] = `
4
+ Object {
5
+ "optimistic": 1107,
6
+ "pessimistic": 1134,
7
+ "timing": 1122,
8
+ }
9
+ `;
10
+
11
+ exports[`Metrics: Lantern TTI should compute predicted value on iframes with substantial layout 1`] = `
12
+ Object {
13
+ "optimistic": 2372,
14
+ "pessimistic": 2386,
15
+ "timing": 2379,
16
+ }
17
+ `;
@@ -0,0 +1,9 @@
1
+ export { FirstContentfulPaint } from "./FirstContentfulPaint.js";
2
+ export { Interactive } from "./Interactive.js";
3
+ export { LargestContentfulPaint } from "./LargestContentfulPaint.js";
4
+ export { MaxPotentialFID } from "./MaxPotentialFID.js";
5
+ export { SpeedIndex } from "./SpeedIndex.js";
6
+ export { TotalBlockingTime } from "./TotalBlockingTime.js";
7
+ export type Result<T = any> = Lantern.MetricResult<T>;
8
+ import * as Lantern from '../types/lantern.js';
9
+ //# sourceMappingURL=metrics.d.ts.map
@@ -0,0 +1,16 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2024 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import * as Lantern from '../types/lantern.js';
8
+
9
+ export {FirstContentfulPaint} from './FirstContentfulPaint.js';
10
+ export {Interactive} from './Interactive.js';
11
+ export {LargestContentfulPaint} from './LargestContentfulPaint.js';
12
+ export {MaxPotentialFID} from './MaxPotentialFID.js';
13
+ export {SpeedIndex} from './SpeedIndex.js';
14
+ export {TotalBlockingTime} from './TotalBlockingTime.js';
15
+
16
+ /** @template [T=any] @typedef {Lantern.MetricResult<T>} Result */