lighthouse 12.0.0 → 12.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (219) hide show
  1. package/cli/run.js +5 -38
  2. package/cli/test/smokehouse/lighthouse-runners/bundle.js +1 -1
  3. package/cli/test/smokehouse/smokehouse.js +11 -1
  4. package/core/audits/accessibility/aria-allowed-role.js +7 -9
  5. package/core/audits/byte-efficiency/byte-efficiency-audit.d.ts +2 -2
  6. package/core/audits/byte-efficiency/byte-efficiency-audit.js +3 -3
  7. package/core/audits/byte-efficiency/offscreen-images.js +1 -1
  8. package/core/audits/byte-efficiency/render-blocking-resources.d.ts +3 -3
  9. package/core/audits/byte-efficiency/render-blocking-resources.js +10 -10
  10. package/core/audits/dobetterweb/uses-http2.d.ts +2 -2
  11. package/core/audits/dobetterweb/uses-http2.js +5 -5
  12. package/core/audits/long-tasks.d.ts +7 -6
  13. package/core/audits/long-tasks.js +5 -4
  14. package/core/audits/metrics/first-meaningful-paint.d.ts +1 -17
  15. package/core/audits/metrics/first-meaningful-paint.js +5 -47
  16. package/core/audits/metrics/interactive.d.ts +1 -1
  17. package/core/audits/metrics/interactive.js +1 -1
  18. package/core/audits/predictive-perf.js +0 -6
  19. package/core/audits/prioritize-lcp-image.d.ts +1 -1
  20. package/core/audits/prioritize-lcp-image.js +2 -2
  21. package/core/audits/redirects.js +2 -3
  22. package/core/audits/third-party-facades.js +1 -1
  23. package/core/audits/third-party-summary.js +1 -1
  24. package/core/audits/uses-rel-preconnect.js +2 -2
  25. package/core/audits/uses-rel-preload.js +9 -9
  26. package/core/computed/critical-request-chains.js +3 -3
  27. package/core/computed/document-urls.js +3 -2
  28. package/core/computed/load-simulator.d.ts +4 -4
  29. package/core/computed/load-simulator.js +3 -54
  30. package/core/computed/main-resource.js +3 -2
  31. package/core/computed/metrics/interactive.d.ts +6 -0
  32. package/core/computed/metrics/interactive.js +2 -2
  33. package/core/computed/metrics/lantern-first-contentful-paint.d.ts +4 -4
  34. package/core/computed/metrics/lantern-first-contentful-paint.js +3 -3
  35. package/core/computed/metrics/lantern-interactive.d.ts +4 -4
  36. package/core/computed/metrics/lantern-interactive.js +6 -6
  37. package/core/computed/metrics/lantern-largest-contentful-paint.d.ts +4 -4
  38. package/core/computed/metrics/lantern-largest-contentful-paint.js +3 -3
  39. package/core/computed/metrics/lantern-max-potential-fid.d.ts +4 -4
  40. package/core/computed/metrics/lantern-max-potential-fid.js +3 -3
  41. package/core/computed/metrics/lantern-metric.d.ts +21 -2
  42. package/core/computed/metrics/lantern-metric.js +39 -4
  43. package/core/computed/metrics/lantern-speed-index.d.ts +4 -4
  44. package/core/computed/metrics/lantern-speed-index.js +4 -4
  45. package/core/computed/metrics/lantern-total-blocking-time.d.ts +4 -4
  46. package/core/computed/metrics/lantern-total-blocking-time.js +3 -3
  47. package/core/computed/metrics/timing-summary.js +0 -6
  48. package/core/computed/metrics/total-blocking-time.js +3 -1
  49. package/core/computed/navigation-insights.d.ts +2 -20
  50. package/core/computed/network-analysis.d.ts +0 -5
  51. package/core/computed/network-analysis.js +2 -42
  52. package/core/computed/page-dependency-graph.d.ts +5 -3
  53. package/core/computed/page-dependency-graph.js +17 -7
  54. package/core/computed/processed-navigation.d.ts +1 -1
  55. package/core/computed/tbt-impact-tasks.js +4 -2
  56. package/core/computed/trace-engine-result.d.ts +0 -11
  57. package/core/computed/trace-engine-result.js +5 -19
  58. package/core/config/constants.d.ts +28 -53
  59. package/core/config/constants.js +2 -43
  60. package/core/gather/driver/navigation.js +1 -1
  61. package/core/gather/driver/network-monitor.d.ts +2 -1
  62. package/core/gather/driver/network-monitor.js +4 -4
  63. package/core/gather/driver/target-manager.d.ts +1 -1
  64. package/core/gather/driver.d.ts +0 -12
  65. package/core/gather/driver.js +1 -26
  66. package/core/gather/gatherers/inspector-issues.js +1 -0
  67. package/core/gather/gatherers/root-causes.js +0 -1
  68. package/core/gather/session.d.ts +5 -3
  69. package/core/gather/session.js +22 -1
  70. package/core/lib/asset-saver.d.ts +13 -9
  71. package/core/lib/asset-saver.js +77 -30
  72. package/core/lib/bf-cache-strings.js +2 -2
  73. package/core/lib/lantern/{base-node.d.ts → BaseNode.d.ts} +7 -9
  74. package/core/lib/lantern/{base-node.js → BaseNode.js} +6 -6
  75. package/core/lib/lantern/BaseNode.test.js +385 -0
  76. package/core/lib/lantern/CpuNode.d.ts +43 -0
  77. package/core/lib/lantern/{cpu-node.js → CpuNode.js} +8 -9
  78. package/core/lib/lantern/{lantern-error.d.ts → LanternError.d.ts} +1 -1
  79. package/core/lib/lantern/{metric.d.ts → Metric.d.ts} +23 -25
  80. package/core/lib/lantern/{metric.js → Metric.js} +14 -16
  81. package/core/lib/lantern/{network-node.d.ts → NetworkNode.d.ts} +6 -7
  82. package/core/lib/lantern/{network-node.js → NetworkNode.js} +7 -9
  83. package/core/lib/lantern/PageDependencyGraph.d.ts +82 -0
  84. package/core/lib/lantern/{page-dependency-graph.js → PageDependencyGraph.js} +185 -50
  85. package/core/lib/lantern/PageDependencyGraph.test.js +654 -0
  86. package/core/{computed/metrics/tbt-utils.d.ts → lib/lantern/TBTUtils.d.ts} +1 -1
  87. package/core/lib/lantern/TBTUtils.test.d.ts +2 -0
  88. package/core/lib/lantern/TBTUtils.test.js +130 -0
  89. package/core/lib/lantern/TraceEngineComputationData.d.ts +25 -0
  90. package/core/lib/lantern/TraceEngineComputationData.js +466 -0
  91. package/core/lib/lantern/lantern.d.ts +19 -7
  92. package/core/lib/lantern/lantern.js +21 -1
  93. package/core/lib/lantern/metrics/{first-contentful-paint.d.ts → FirstContentfulPaint.d.ts} +11 -11
  94. package/core/lib/lantern/metrics/{first-contentful-paint.js → FirstContentfulPaint.js} +11 -13
  95. package/core/lib/lantern/metrics/FirstContentfulPaint.test.js +54 -0
  96. package/core/lib/lantern/metrics/Interactive.d.ts +20 -0
  97. package/core/lib/lantern/metrics/{interactive.js → Interactive.js} +23 -26
  98. package/core/lib/lantern/metrics/Interactive.test.js +56 -0
  99. package/core/lib/lantern/metrics/LargestContentfulPaint.d.ts +19 -0
  100. package/core/lib/lantern/metrics/{largest-contentful-paint.js → LargestContentfulPaint.js} +16 -19
  101. package/core/lib/lantern/metrics/LargestContentfulPaint.test.js +42 -0
  102. package/core/lib/lantern/metrics/MaxPotentialFID.d.ts +24 -0
  103. package/core/lib/lantern/metrics/{max-potential-fid.js → MaxPotentialFID.js} +12 -13
  104. package/core/lib/lantern/metrics/MetricTestUtils.d.ts +19 -0
  105. package/core/lib/lantern/metrics/MetricTestUtils.js +48 -0
  106. package/core/lib/lantern/metrics/{speed-index.d.ts → SpeedIndex.d.ts} +7 -13
  107. package/core/lib/lantern/metrics/{speed-index.js → SpeedIndex.js} +14 -16
  108. package/core/lib/lantern/metrics/SpeedIndex.test.js +83 -0
  109. package/core/lib/lantern/metrics/TotalBlockingTime.d.ts +25 -0
  110. package/core/lib/lantern/metrics/{total-blocking-time.js → TotalBlockingTime.js} +13 -16
  111. package/core/lib/lantern/metrics/__snapshots__/FirstContentfulPaint.test.js.snap +11 -0
  112. package/core/lib/lantern/metrics/__snapshots__/Interactive.test.js.snap +17 -0
  113. package/core/lib/lantern/metrics/metrics.d.ts +9 -0
  114. package/core/lib/lantern/metrics/metrics.js +16 -0
  115. package/core/lib/lantern/{simulator/connection-pool.d.ts → simulation/ConnectionPool.d.ts} +15 -15
  116. package/core/lib/lantern/{simulator/connection-pool.js → simulation/ConnectionPool.js} +25 -25
  117. package/core/lib/lantern/simulation/ConnectionPool.test.js +195 -0
  118. package/core/lib/lantern/simulation/Constants.d.ts +52 -0
  119. package/core/lib/lantern/simulation/Constants.js +48 -0
  120. package/core/lib/lantern/{simulator/dns-cache.d.ts → simulation/DNSCache.d.ts} +3 -3
  121. package/core/lib/lantern/{simulator/dns-cache.js → simulation/DNSCache.js} +2 -2
  122. package/core/lib/lantern/simulation/DNSCache.test.js +72 -0
  123. package/core/lib/lantern/{simulator/network-analyzer.d.ts → simulation/NetworkAnalyzer.d.ts} +34 -21
  124. package/core/lib/lantern/{simulator/network-analyzer.js → simulation/NetworkAnalyzer.js} +106 -57
  125. package/core/lib/lantern/simulation/NetworkAnalyzer.test.js +475 -0
  126. package/core/lib/lantern/{simulator/simulator-timing-map.d.ts → simulation/SimulationTimingMap.d.ts} +7 -7
  127. package/core/lib/lantern/{simulator/simulator-timing-map.js → simulation/SimulationTimingMap.js} +5 -6
  128. package/core/lib/lantern/{simulator/simulator.d.ts → simulation/Simulator.d.ts} +21 -19
  129. package/core/lib/lantern/{simulator/simulator.js → simulation/Simulator.js} +100 -45
  130. package/core/lib/lantern/simulation/Simulator.test.js +434 -0
  131. package/core/lib/lantern/simulation/TCPConnection.test.d.ts +2 -0
  132. package/core/lib/lantern/simulation/TCPConnection.test.js +374 -0
  133. package/core/lib/lantern/{simulator/tcp-connection.d.ts → simulation/TcpConnection.d.ts} +2 -2
  134. package/core/lib/lantern/{simulator/tcp-connection.js → simulation/TcpConnection.js} +1 -1
  135. package/core/lib/lantern/simulation/simulation.d.ts +21 -0
  136. package/core/lib/lantern/simulation/simulation.js +28 -0
  137. package/core/lib/lantern/types/lantern.d.ts +145 -20
  138. package/core/lib/lantern-trace-saver.d.ts +4 -4
  139. package/core/lib/lantern-trace-saver.js +4 -4
  140. package/core/lib/navigation-error.js +3 -3
  141. package/core/lib/network-recorder.js +2 -2
  142. package/core/lib/network-request.d.ts +29 -3
  143. package/core/lib/network-request.js +20 -13
  144. package/core/lib/tracehouse/trace-processor.d.ts +0 -2
  145. package/core/lib/tracehouse/trace-processor.js +1 -31
  146. package/core/lib/traces/metric-trace-events.js +0 -5
  147. package/dist/report/bundle.esm.js +1 -1
  148. package/dist/report/flow.js +1 -1
  149. package/dist/report/standalone.js +1 -1
  150. package/package.json +14 -13
  151. package/report/renderer/performance-category-renderer.js +1 -1
  152. package/shared/localization/locales/ar-XB.json +43 -4
  153. package/shared/localization/locales/ar.json +43 -4
  154. package/shared/localization/locales/bg.json +43 -4
  155. package/shared/localization/locales/ca.json +44 -5
  156. package/shared/localization/locales/cs.json +43 -4
  157. package/shared/localization/locales/da.json +43 -4
  158. package/shared/localization/locales/de.json +43 -4
  159. package/shared/localization/locales/el.json +43 -4
  160. package/shared/localization/locales/en-GB.json +42 -3
  161. package/shared/localization/locales/en-US.json +4 -4
  162. package/shared/localization/locales/en-XA.json +43 -4
  163. package/shared/localization/locales/en-XL.json +4 -4
  164. package/shared/localization/locales/es-419.json +43 -4
  165. package/shared/localization/locales/es.json +42 -3
  166. package/shared/localization/locales/fi.json +43 -4
  167. package/shared/localization/locales/fil.json +42 -3
  168. package/shared/localization/locales/fr.json +42 -3
  169. package/shared/localization/locales/he.json +48 -9
  170. package/shared/localization/locales/hi.json +43 -4
  171. package/shared/localization/locales/hr.json +43 -4
  172. package/shared/localization/locales/hu.json +42 -3
  173. package/shared/localization/locales/id.json +42 -3
  174. package/shared/localization/locales/it.json +43 -4
  175. package/shared/localization/locales/ja.json +42 -3
  176. package/shared/localization/locales/ko.json +43 -4
  177. package/shared/localization/locales/lt.json +42 -3
  178. package/shared/localization/locales/lv.json +43 -4
  179. package/shared/localization/locales/nl.json +44 -5
  180. package/shared/localization/locales/no.json +42 -3
  181. package/shared/localization/locales/pl.json +43 -4
  182. package/shared/localization/locales/pt-PT.json +43 -4
  183. package/shared/localization/locales/pt.json +43 -4
  184. package/shared/localization/locales/ro.json +43 -4
  185. package/shared/localization/locales/ru.json +43 -4
  186. package/shared/localization/locales/sk.json +43 -4
  187. package/shared/localization/locales/sl.json +42 -3
  188. package/shared/localization/locales/sr-Latn.json +43 -4
  189. package/shared/localization/locales/sr.json +43 -4
  190. package/shared/localization/locales/sv.json +42 -3
  191. package/shared/localization/locales/ta.json +42 -3
  192. package/shared/localization/locales/te.json +43 -4
  193. package/shared/localization/locales/th.json +44 -5
  194. package/shared/localization/locales/tr.json +43 -4
  195. package/shared/localization/locales/uk.json +43 -4
  196. package/shared/localization/locales/vi.json +43 -4
  197. package/shared/localization/locales/zh-HK.json +43 -4
  198. package/shared/localization/locales/zh-TW.json +43 -4
  199. package/shared/localization/locales/zh.json +43 -4
  200. package/third-party/chromium-synchronization/inspector-issueAdded-types-test.js +1 -0
  201. package/tsconfig-all.json +1 -0
  202. package/tsconfig.json +11 -7
  203. package/types/artifacts.d.ts +23 -25
  204. package/types/gatherer.d.ts +2 -3
  205. package/types/lhr/lhr.d.ts +2 -0
  206. package/core/computed/metrics/first-meaningful-paint.d.ts +0 -21
  207. package/core/computed/metrics/first-meaningful-paint.js +0 -44
  208. package/core/computed/metrics/lantern-first-meaningful-paint.d.ts +0 -25
  209. package/core/computed/metrics/lantern-first-meaningful-paint.js +0 -43
  210. package/core/lib/lantern/cpu-node.d.ts +0 -44
  211. package/core/lib/lantern/metrics/first-meaningful-paint.d.ts +0 -6
  212. package/core/lib/lantern/metrics/first-meaningful-paint.js +0 -64
  213. package/core/lib/lantern/metrics/interactive.d.ts +0 -26
  214. package/core/lib/lantern/metrics/largest-contentful-paint.d.ts +0 -19
  215. package/core/lib/lantern/metrics/max-potential-fid.d.ts +0 -30
  216. package/core/lib/lantern/metrics/total-blocking-time.d.ts +0 -31
  217. package/core/lib/lantern/page-dependency-graph.d.ts +0 -58
  218. /package/core/lib/lantern/{lantern-error.js → LanternError.js} +0 -0
  219. /package/core/{computed/metrics/tbt-utils.js → lib/lantern/TBTUtils.js} +0 -0
@@ -4,7 +4,7 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
 
7
- import * as Lantern from '../types/lantern.js';
7
+ import * as Lantern from '../lantern.js';
8
8
  import UrlUtils from '../../url-utils.js';
9
9
 
10
10
  const INITIAL_CWD = 14 * 1024;
@@ -15,7 +15,7 @@ const DEFAULT_SERVER_RESPONSE_PERCENTAGE = 0.4;
15
15
  /**
16
16
  * For certain resource types, server response time takes up a greater percentage of TTFB (dynamic
17
17
  * assets like HTML documents, XHR/API calls, etc)
18
- * @type {Partial<Record<LH.Crdp.Network.ResourceType, number>>}
18
+ * @type {Partial<Record<Lantern.ResourceType, number>>}
19
19
  */
20
20
  const SERVER_RESPONSE_PERCENTAGE_OF_TTFB = {
21
21
  Document: 0.9,
@@ -88,30 +88,30 @@ class NetworkAnalyzer {
88
88
  return summaryByKey;
89
89
  }
90
90
 
91
- /** @typedef {{record: Lantern.NetworkRequest, timing: LH.Crdp.Network.ResourceTiming, connectionReused?: boolean}} RequestInfo */
91
+ /** @typedef {{request: Lantern.NetworkRequest, timing: Lantern.ResourceTiming, connectionReused?: boolean}} RequestInfo */
92
92
 
93
93
  /**
94
- * @param {Lantern.NetworkRequest[]} records
94
+ * @param {Lantern.NetworkRequest[]} requests
95
95
  * @param {(e: RequestInfo) => number | number[] | undefined} iteratee
96
96
  * @return {Map<string, number[]>}
97
97
  */
98
- static _estimateValueByOrigin(records, iteratee) {
99
- const connectionWasReused = NetworkAnalyzer.estimateIfConnectionWasReused(records);
100
- const groupedByOrigin = NetworkAnalyzer.groupByOrigin(records);
98
+ static _estimateValueByOrigin(requests, iteratee) {
99
+ const connectionWasReused = NetworkAnalyzer.estimateIfConnectionWasReused(requests);
100
+ const groupedByOrigin = NetworkAnalyzer.groupByOrigin(requests);
101
101
 
102
102
  const estimates = new Map();
103
- for (const [origin, originRecords] of groupedByOrigin.entries()) {
103
+ for (const [origin, originRequests] of groupedByOrigin.entries()) {
104
104
  /** @type {number[]} */
105
105
  let originEstimates = [];
106
106
 
107
- for (const record of originRecords) {
108
- const timing = record.timing;
107
+ for (const request of originRequests) {
108
+ const timing = request.timing;
109
109
  if (!timing) continue;
110
110
 
111
111
  const value = iteratee({
112
- record,
112
+ request,
113
113
  timing,
114
- connectionReused: connectionWasReused.get(record.requestId),
114
+ connectionReused: connectionWasReused.get(request.requestId),
115
115
  });
116
116
  if (typeof value !== 'undefined') {
117
117
  originEstimates = originEstimates.concat(value);
@@ -137,11 +137,11 @@ class NetworkAnalyzer {
137
137
  * @return {number[]|number|undefined}
138
138
  */
139
139
  static _estimateRTTViaConnectionTiming(info) {
140
- const {timing, connectionReused, record} = info;
140
+ const {timing, connectionReused, request} = info;
141
141
  if (connectionReused) return;
142
142
 
143
143
  const {connectStart, sslStart, sslEnd, connectEnd} = timing;
144
- if (connectEnd >= 0 && connectStart >= 0 && record.protocol.startsWith('h3')) {
144
+ if (connectEnd >= 0 && connectStart >= 0 && request.protocol.startsWith('h3')) {
145
145
  // These values are equal to sslStart and sslEnd for h3.
146
146
  return connectEnd - connectStart;
147
147
  } else if (sslStart >= 0 && sslEnd >= 0 && sslStart !== connectStart) {
@@ -161,17 +161,17 @@ class NetworkAnalyzer {
161
161
  * @return {number|undefined}
162
162
  */
163
163
  static _estimateRTTViaDownloadTiming(info) {
164
- const {timing, connectionReused, record} = info;
164
+ const {timing, connectionReused, request} = info;
165
165
  if (connectionReused) return;
166
166
 
167
167
  // Only look at downloads that went past the initial congestion window
168
- if (record.transferSize <= INITIAL_CWD) return;
168
+ if (request.transferSize <= INITIAL_CWD) return;
169
169
  if (!Number.isFinite(timing.receiveHeadersEnd) || timing.receiveHeadersEnd < 0) return;
170
170
 
171
171
  // Compute the amount of time downloading everything after the first congestion window took
172
- const totalTime = record.networkEndTime - record.networkRequestTime;
172
+ const totalTime = request.networkEndTime - request.networkRequestTime;
173
173
  const downloadTimeAfterFirstByte = totalTime - timing.receiveHeadersEnd;
174
- const numberOfRoundTrips = Math.log2(record.transferSize / INITIAL_CWD);
174
+ const numberOfRoundTrips = Math.log2(request.transferSize / INITIAL_CWD);
175
175
 
176
176
  // Ignore requests that required a high number of round trips since bandwidth starts to play
177
177
  // a larger role than latency
@@ -190,7 +190,7 @@ class NetworkAnalyzer {
190
190
  * @return {number|undefined}
191
191
  */
192
192
  static _estimateRTTViaSendStartTiming(info) {
193
- const {timing, connectionReused, record} = info;
193
+ const {timing, connectionReused, request} = info;
194
194
  if (connectionReused) return;
195
195
 
196
196
  if (!Number.isFinite(timing.sendStart) || timing.sendStart < 0) return;
@@ -198,8 +198,8 @@ class NetworkAnalyzer {
198
198
  // Assume everything before sendStart was just DNS + (SSL)? + TCP handshake
199
199
  // 1 RT for DNS, 1 RT (maybe) for SSL, 1 RT for TCP
200
200
  let roundTrips = 1;
201
- if (!record.protocol.startsWith('h3')) roundTrips += 1; // TCP
202
- if (record.parsedURL.scheme === 'https') roundTrips += 1;
201
+ if (!request.protocol.startsWith('h3')) roundTrips += 1; // TCP
202
+ if (request.parsedURL.scheme === 'https') roundTrips += 1;
203
203
  return timing.sendStart / roundTrips;
204
204
  }
205
205
 
@@ -213,12 +213,12 @@ class NetworkAnalyzer {
213
213
  * @return {number|undefined}
214
214
  */
215
215
  static _estimateRTTViaHeadersEndTiming(info) {
216
- const {timing, connectionReused, record} = info;
216
+ const {timing, connectionReused, request} = info;
217
217
  if (!Number.isFinite(timing.receiveHeadersEnd) || timing.receiveHeadersEnd < 0) return;
218
- if (!record.resourceType) return;
218
+ if (!request.resourceType) return;
219
219
 
220
220
  const serverResponseTimePercentage =
221
- SERVER_RESPONSE_PERCENTAGE_OF_TTFB[record.resourceType] ||
221
+ SERVER_RESPONSE_PERCENTAGE_OF_TTFB[request.resourceType] ||
222
222
  DEFAULT_SERVER_RESPONSE_PERCENTAGE;
223
223
  const estimatedServerResponseTime = timing.receiveHeadersEnd * serverResponseTimePercentage;
224
224
 
@@ -230,8 +230,8 @@ class NetworkAnalyzer {
230
230
  // TTFB = DNS + (SSL)? + TCP handshake + 1 RT for request + server response time
231
231
  if (!connectionReused) {
232
232
  roundTrips += 1; // DNS
233
- if (!record.protocol.startsWith('h3')) roundTrips += 1; // TCP
234
- if (record.parsedURL.scheme === 'https') roundTrips += 1; // SSL
233
+ if (!request.protocol.startsWith('h3')) roundTrips += 1; // TCP
234
+ if (request.parsedURL.scheme === 'https') roundTrips += 1; // SSL
235
235
  }
236
236
 
237
237
  // subtract out our estimated server response time
@@ -246,28 +246,28 @@ class NetworkAnalyzer {
246
246
  * @return {Map<string, number[]>}
247
247
  */
248
248
  static _estimateResponseTimeByOrigin(records, rttByOrigin) {
249
- return NetworkAnalyzer._estimateValueByOrigin(records, ({record, timing}) => {
250
- if (record.serverResponseTime !== undefined) return record.serverResponseTime;
249
+ return NetworkAnalyzer._estimateValueByOrigin(records, ({request, timing}) => {
250
+ if (request.serverResponseTime !== undefined) return request.serverResponseTime;
251
251
 
252
252
  if (!Number.isFinite(timing.receiveHeadersEnd) || timing.receiveHeadersEnd < 0) return;
253
253
  if (!Number.isFinite(timing.sendEnd) || timing.sendEnd < 0) return;
254
254
 
255
255
  const ttfb = timing.receiveHeadersEnd - timing.sendEnd;
256
- const origin = record.parsedURL.securityOrigin;
256
+ const origin = request.parsedURL.securityOrigin;
257
257
  const rtt = rttByOrigin.get(origin) || rttByOrigin.get(NetworkAnalyzer.SUMMARY) || 0;
258
258
  return Math.max(ttfb - rtt, 0);
259
259
  });
260
260
  }
261
261
 
262
262
  /**
263
- * @param {Lantern.NetworkRequest[]} records
263
+ * @param {Lantern.NetworkRequest[]} requests
264
264
  * @return {boolean}
265
265
  */
266
- static canTrustConnectionInformation(records) {
266
+ static canTrustConnectionInformation(requests) {
267
267
  const connectionIdWasStarted = new Map();
268
- for (const record of records) {
269
- const started = connectionIdWasStarted.get(record.connectionId) || !record.connectionReused;
270
- connectionIdWasStarted.set(record.connectionId, started);
268
+ for (const request of requests) {
269
+ const started = connectionIdWasStarted.get(request.connectionId) || !request.connectionReused;
270
+ connectionIdWasStarted.set(request.connectionId, started);
271
271
  }
272
272
 
273
273
  // We probably can't trust the network information if all the connection IDs were the same
@@ -289,10 +289,10 @@ class NetworkAnalyzer {
289
289
 
290
290
  // Check if we can trust the connection information coming from the protocol
291
291
  if (!forceCoarseEstimates && NetworkAnalyzer.canTrustConnectionInformation(records)) {
292
- return new Map(records.map(record => [record.requestId, !!record.connectionReused]));
292
+ return new Map(records.map(request => [request.requestId, !!request.connectionReused]));
293
293
  }
294
294
 
295
- // Otherwise we're on our own, a record may not have needed a fresh connection if...
295
+ // Otherwise we're on our own, a request may not have needed a fresh connection if...
296
296
  // - It was not the first request to the domain
297
297
  // - It was H2
298
298
  // - It was after the first request to the domain ended
@@ -300,13 +300,13 @@ class NetworkAnalyzer {
300
300
  const groupedByOrigin = NetworkAnalyzer.groupByOrigin(records);
301
301
  for (const [_, originRecords] of groupedByOrigin.entries()) {
302
302
  const earliestReusePossible = originRecords
303
- .map(record => record.networkEndTime)
303
+ .map(request => request.networkEndTime)
304
304
  .reduce((a, b) => Math.min(a, b), Infinity);
305
305
 
306
- for (const record of originRecords) {
306
+ for (const request of originRecords) {
307
307
  connectionWasReused.set(
308
- record.requestId,
309
- record.networkRequestTime >= earliestReusePossible || record.protocol === 'h2'
308
+ request.requestId,
309
+ request.networkRequestTime >= earliestReusePossible || request.protocol === 'h2'
310
310
  );
311
311
  }
312
312
 
@@ -343,7 +343,7 @@ class NetworkAnalyzer {
343
343
  const groupedByOrigin = NetworkAnalyzer.groupByOrigin(records);
344
344
 
345
345
  const estimatesByOrigin = new Map();
346
- for (const [origin, originRecords] of groupedByOrigin.entries()) {
346
+ for (const [origin, originRequests] of groupedByOrigin.entries()) {
347
347
  /** @type {number[]} */
348
348
  const originEstimates = [];
349
349
 
@@ -352,14 +352,14 @@ class NetworkAnalyzer {
352
352
  */
353
353
  // eslint-disable-next-line no-inner-declarations
354
354
  function collectEstimates(estimator, multiplier = 1) {
355
- for (const record of originRecords) {
356
- const timing = record.timing;
355
+ for (const request of originRequests) {
356
+ const timing = request.timing;
357
357
  if (!timing) continue;
358
358
 
359
359
  const estimates = estimator({
360
- record,
360
+ request,
361
361
  timing,
362
- connectionReused: connectionWasReused.get(record.requestId),
362
+ connectionReused: connectionWasReused.get(request.requestId),
363
363
  });
364
364
  if (estimates === undefined) continue;
365
365
 
@@ -427,32 +427,32 @@ class NetworkAnalyzer {
427
427
 
428
428
 
429
429
  /**
430
- * Computes the average throughput for the given records in bits/second.
430
+ * Computes the average throughput for the given requests in bits/second.
431
431
  * Excludes data URI, failed or otherwise incomplete, and cached requests.
432
- * Returns Infinity if there were no analyzable network records.
432
+ * Returns Infinity if there were no analyzable network requests.
433
433
  *
434
- * @param {Array<Lantern.NetworkRequest>} networkRecords
434
+ * @param {Lantern.NetworkRequest[]} records
435
435
  * @return {number}
436
436
  */
437
- static estimateThroughput(networkRecords) {
437
+ static estimateThroughput(records) {
438
438
  let totalBytes = 0;
439
439
 
440
440
  // We will measure throughput by summing the total bytes downloaded by the total time spent
441
- // downloading those bytes. We slice up all the network records into start/end boundaries, so
441
+ // downloading those bytes. We slice up all the network requests into start/end boundaries, so
442
442
  // it's easier to deal with the gaps in downloading.
443
- const timeBoundaries = networkRecords.reduce((boundaries, record) => {
444
- const scheme = record.parsedURL?.scheme;
443
+ const timeBoundaries = records.reduce((boundaries, request) => {
444
+ const scheme = request.parsedURL?.scheme;
445
445
  // Requests whose bodies didn't come over the network or didn't completely finish will mess
446
446
  // with the computation, just skip over them.
447
- if (scheme === 'data' || record.failed || !record.finished ||
448
- record.statusCode > 300 || !record.transferSize) {
447
+ if (scheme === 'data' || request.failed || !request.finished ||
448
+ request.statusCode > 300 || !request.transferSize) {
449
449
  return boundaries;
450
450
  }
451
451
 
452
452
  // If we've made it this far, all the times we need should be valid (i.e. not undefined/-1).
453
- totalBytes += record.transferSize;
454
- boundaries.push({time: record.responseHeadersEndTime / 1000, isStart: true});
455
- boundaries.push({time: record.networkEndTime / 1000, isStart: false});
453
+ totalBytes += request.transferSize;
454
+ boundaries.push({time: request.responseHeadersEndTime / 1000, isStart: true});
455
+ boundaries.push({time: request.networkEndTime / 1000, isStart: false});
456
456
  return boundaries;
457
457
  }, /** @type {Array<{time: number, isStart: boolean}>} */([])).sort((a, b) => a.time - b.time);
458
458
 
@@ -483,6 +483,55 @@ class NetworkAnalyzer {
483
483
  return totalBytes * 8 / totalDuration;
484
484
  }
485
485
 
486
+ /**
487
+ * @param {Lantern.NetworkRequest[]} records
488
+ */
489
+ static computeRTTAndServerResponseTime(records) {
490
+ // First pass compute the estimated observed RTT to each origin's servers.
491
+ /** @type {Map<string, number>} */
492
+ const rttByOrigin = new Map();
493
+ for (const [origin, summary] of NetworkAnalyzer.estimateRTTByOrigin(records).entries()) {
494
+ rttByOrigin.set(origin, summary.min);
495
+ }
496
+
497
+ // We'll use the minimum RTT as the assumed connection latency since we care about how much addt'l
498
+ // latency each origin introduces as Lantern will be simulating with its own connection latency.
499
+ const minimumRtt = Math.min(...Array.from(rttByOrigin.values()));
500
+ // We'll use the observed RTT information to help estimate the server response time
501
+ const responseTimeSummaries = NetworkAnalyzer.estimateServerResponseTimeByOrigin(records, {
502
+ rttByOrigin,
503
+ });
504
+
505
+ /** @type {Map<string, number>} */
506
+ const additionalRttByOrigin = new Map();
507
+ /** @type {Map<string, number>} */
508
+ const serverResponseTimeByOrigin = new Map();
509
+ for (const [origin, summary] of responseTimeSummaries.entries()) {
510
+ // Not all origins have usable timing data, we'll default to using no additional latency.
511
+ const rttForOrigin = rttByOrigin.get(origin) || minimumRtt;
512
+ additionalRttByOrigin.set(origin, rttForOrigin - minimumRtt);
513
+ serverResponseTimeByOrigin.set(origin, summary.median);
514
+ }
515
+
516
+ return {
517
+ rtt: minimumRtt,
518
+ additionalRttByOrigin,
519
+ serverResponseTimeByOrigin,
520
+ };
521
+ }
522
+
523
+ /**
524
+ * @param {Lantern.NetworkRequest[]} records
525
+ * @return {Lantern.Simulation.Settings['networkAnalysis']}
526
+ */
527
+ static analyze(records) {
528
+ const throughput = NetworkAnalyzer.estimateThroughput(records);
529
+ return {
530
+ throughput,
531
+ ...NetworkAnalyzer.computeRTTAndServerResponseTime(records),
532
+ };
533
+ }
534
+
486
535
  /**
487
536
  * @template {Lantern.NetworkRequest} T
488
537
  * @param {Array<T>} records