lighthouse 11.7.0 → 12.0.0-dev.20240422

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 (334) hide show
  1. package/CONTRIBUTING.md +2 -2
  2. package/cli/bin.js +0 -7
  3. package/cli/cli-flags.d.ts +1 -5
  4. package/cli/cli-flags.js +4 -8
  5. package/cli/test/smokehouse/__snapshots__/report-assert-test.js.snap +14 -14
  6. package/cli/test/smokehouse/config/exclusions.js +3 -0
  7. package/cli/test/smokehouse/core-tests.js +6 -20
  8. package/cli/test/smokehouse/frontends/smokehouse-bin.js +2 -2
  9. package/core/audits/accessibility/accesskeys.js +1 -1
  10. package/core/audits/accessibility/aria-allowed-attr.js +1 -1
  11. package/core/audits/accessibility/aria-allowed-role.js +1 -1
  12. package/core/audits/accessibility/aria-command-name.js +1 -1
  13. package/core/audits/accessibility/aria-conditional-attr.d.ts +10 -0
  14. package/core/audits/accessibility/aria-conditional-attr.js +43 -0
  15. package/core/audits/accessibility/{duplicate-id-active.d.ts → aria-deprecated-role.d.ts} +3 -3
  16. package/core/audits/accessibility/aria-deprecated-role.js +43 -0
  17. package/core/audits/accessibility/aria-dialog-name.js +1 -1
  18. package/core/audits/accessibility/aria-hidden-body.js +1 -1
  19. package/core/audits/accessibility/aria-hidden-focus.js +1 -1
  20. package/core/audits/accessibility/aria-input-field-name.js +1 -1
  21. package/core/audits/accessibility/aria-meter-name.js +1 -1
  22. package/core/audits/accessibility/aria-progressbar-name.js +1 -1
  23. package/core/audits/accessibility/aria-prohibited-attr.d.ts +10 -0
  24. package/core/audits/accessibility/aria-prohibited-attr.js +44 -0
  25. package/core/audits/accessibility/aria-required-attr.js +1 -1
  26. package/core/audits/accessibility/aria-required-children.js +1 -1
  27. package/core/audits/accessibility/aria-required-parent.js +1 -1
  28. package/core/audits/accessibility/aria-roles.js +1 -1
  29. package/core/audits/accessibility/aria-text.js +1 -1
  30. package/core/audits/accessibility/aria-toggle-field-name.js +1 -1
  31. package/core/audits/accessibility/aria-tooltip-name.js +1 -1
  32. package/core/audits/accessibility/aria-treeitem-name.js +1 -1
  33. package/core/audits/accessibility/aria-valid-attr-value.js +1 -1
  34. package/core/audits/accessibility/aria-valid-attr.js +1 -1
  35. package/core/audits/accessibility/button-name.js +1 -1
  36. package/core/audits/accessibility/bypass.js +1 -1
  37. package/core/audits/accessibility/color-contrast.js +1 -1
  38. package/core/audits/accessibility/definition-list.js +1 -1
  39. package/core/audits/accessibility/dlitem.js +1 -1
  40. package/core/audits/accessibility/document-title.js +1 -1
  41. package/core/audits/accessibility/duplicate-id-aria.js +1 -1
  42. package/core/audits/accessibility/empty-heading.js +1 -1
  43. package/core/audits/accessibility/form-field-multiple-labels.js +1 -1
  44. package/core/audits/accessibility/frame-title.js +1 -1
  45. package/core/audits/accessibility/heading-order.js +1 -1
  46. package/core/audits/accessibility/html-has-lang.js +1 -1
  47. package/core/audits/accessibility/html-lang-valid.js +1 -1
  48. package/core/audits/accessibility/html-xml-lang-mismatch.js +1 -1
  49. package/core/audits/accessibility/identical-links-same-purpose.js +1 -1
  50. package/core/audits/accessibility/image-alt.js +1 -1
  51. package/core/audits/accessibility/image-redundant-alt.js +1 -1
  52. package/core/audits/accessibility/input-button-name.js +1 -1
  53. package/core/audits/accessibility/input-image-alt.js +1 -1
  54. package/core/audits/accessibility/label-content-name-mismatch.js +1 -1
  55. package/core/audits/accessibility/label.js +1 -1
  56. package/core/audits/accessibility/landmark-one-main.js +1 -1
  57. package/core/audits/accessibility/link-in-text-block.js +1 -1
  58. package/core/audits/accessibility/link-name.js +1 -1
  59. package/core/audits/accessibility/list.js +1 -1
  60. package/core/audits/accessibility/listitem.js +1 -1
  61. package/core/audits/accessibility/meta-refresh.js +1 -1
  62. package/core/audits/accessibility/meta-viewport.js +1 -1
  63. package/core/audits/accessibility/object-alt.js +1 -1
  64. package/core/audits/accessibility/select-name.js +1 -1
  65. package/core/audits/accessibility/skip-link.js +1 -1
  66. package/core/audits/accessibility/tabindex.js +1 -1
  67. package/core/audits/accessibility/table-duplicate-name.js +1 -1
  68. package/core/audits/accessibility/table-fake-caption.js +1 -1
  69. package/core/audits/accessibility/target-size.js +1 -2
  70. package/core/audits/accessibility/td-has-header.js +1 -1
  71. package/core/audits/accessibility/td-headers-attr.js +1 -1
  72. package/core/audits/accessibility/th-has-data-cells.js +1 -1
  73. package/core/audits/accessibility/valid-lang.js +1 -1
  74. package/core/audits/accessibility/video-caption.js +1 -1
  75. package/core/audits/byte-efficiency/byte-efficiency-audit.d.ts +0 -16
  76. package/core/audits/byte-efficiency/byte-efficiency-audit.js +1 -38
  77. package/core/audits/byte-efficiency/offscreen-images.d.ts +0 -11
  78. package/core/audits/byte-efficiency/offscreen-images.js +0 -15
  79. package/core/audits/byte-efficiency/render-blocking-resources.js +27 -26
  80. package/core/audits/byte-efficiency/unminified-css.js +2 -2
  81. package/core/audits/byte-efficiency/unused-css-rules.js +3 -1
  82. package/core/audits/dobetterweb/uses-http2.d.ts +0 -13
  83. package/core/audits/dobetterweb/uses-http2.js +4 -38
  84. package/core/audits/font-display.js +2 -2
  85. package/core/audits/layout-shifts.js +2 -2
  86. package/core/audits/preload-fonts.d.ts +1 -5
  87. package/core/audits/preload-fonts.js +1 -10
  88. package/core/audits/prioritize-lcp-image.js +2 -2
  89. package/core/audits/redirects-http.d.ts +19 -0
  90. package/core/audits/redirects-http.js +75 -0
  91. package/core/audits/resource-summary.d.ts +2 -0
  92. package/core/audits/resource-summary.js +5 -4
  93. package/core/audits/seo/is-crawlable.d.ts +2 -1
  94. package/core/audits/seo/link-text.js +12 -0
  95. package/core/audits/uses-rel-preload.d.ts +1 -5
  96. package/core/audits/uses-rel-preload.js +5 -15
  97. package/core/audits/work-during-interaction.d.ts +2 -2
  98. package/core/audits/work-during-interaction.js +2 -2
  99. package/core/computed/js-bundles.d.ts +1 -1
  100. package/core/computed/metrics/cumulative-layout-shift.js +2 -2
  101. package/core/computed/metrics/lantern-first-contentful-paint.d.ts +13 -82
  102. package/core/computed/metrics/lantern-first-contentful-paint.js +16 -178
  103. package/core/computed/metrics/lantern-first-meaningful-paint.d.ts +13 -12
  104. package/core/computed/metrics/lantern-first-meaningful-paint.js +11 -49
  105. package/core/computed/metrics/lantern-interactive.d.ts +13 -21
  106. package/core/computed/metrics/lantern-interactive.js +12 -84
  107. package/core/computed/metrics/lantern-largest-contentful-paint.d.ts +13 -25
  108. package/core/computed/metrics/lantern-largest-contentful-paint.js +13 -80
  109. package/core/computed/metrics/lantern-max-potential-fid.d.ts +13 -24
  110. package/core/computed/metrics/lantern-max-potential-fid.js +12 -64
  111. package/core/computed/metrics/lantern-metric.d.ts +14 -18
  112. package/core/computed/metrics/lantern-metric.js +34 -30
  113. package/core/computed/metrics/lantern-speed-index.d.ts +13 -32
  114. package/core/computed/metrics/lantern-speed-index.js +13 -116
  115. package/core/computed/metrics/lantern-total-blocking-time.d.ts +13 -25
  116. package/core/computed/metrics/lantern-total-blocking-time.js +10 -94
  117. package/core/computed/metrics/metric.js +1 -3
  118. package/core/computed/module-duplication.d.ts +1 -1
  119. package/core/computed/navigation-insights.d.ts +36 -0
  120. package/core/computed/navigation-insights.js +35 -0
  121. package/core/computed/page-dependency-graph.js +3 -7
  122. package/core/computed/resource-summary.d.ts +10 -12
  123. package/core/computed/resource-summary.js +12 -19
  124. package/core/computed/trace-engine-result.d.ts +16 -3
  125. package/core/computed/trace-engine-result.js +19 -14
  126. package/core/computed/unused-css.d.ts +3 -1
  127. package/core/computed/unused-css.js +6 -5
  128. package/core/config/config-helpers.js +0 -4
  129. package/core/config/constants.js +0 -1
  130. package/core/config/default-config.js +63 -142
  131. package/core/config/experimental-config.js +11 -0
  132. package/core/config/filters.js +0 -1
  133. package/core/gather/driver/navigation.js +4 -1
  134. package/core/gather/driver/prepare.js +1 -1
  135. package/core/gather/driver/target-manager.js +4 -3
  136. package/core/gather/driver/wait-for-condition.js +2 -3
  137. package/core/gather/driver.d.ts +12 -0
  138. package/core/gather/driver.js +27 -0
  139. package/core/gather/gatherers/accessibility.js +4 -6
  140. package/core/gather/gatherers/css-usage.d.ts +0 -21
  141. package/core/gather/gatherers/css-usage.js +9 -119
  142. package/core/gather/gatherers/root-causes.d.ts +2 -2
  143. package/core/gather/gatherers/root-causes.js +6 -5
  144. package/core/gather/gatherers/stylesheets.d.ts +26 -0
  145. package/core/gather/gatherers/stylesheets.js +138 -0
  146. package/core/gather/gatherers/trace-elements.d.ts +4 -13
  147. package/core/gather/gatherers/trace-elements.js +42 -31
  148. package/core/gather/navigation-runner.js +3 -1
  149. package/core/gather/session.d.ts +8 -0
  150. package/core/gather/session.js +14 -1
  151. package/core/lib/emulation.js +2 -2
  152. package/core/lib/i18n/i18n.d.ts +0 -1
  153. package/core/lib/i18n/i18n.js +0 -2
  154. package/core/lib/lantern/cpu-node.d.ts +7 -1
  155. package/core/lib/lantern/cpu-node.js +12 -2
  156. package/core/lib/lantern/lantern-error.d.ts +8 -0
  157. package/core/lib/lantern/lantern-error.js +9 -0
  158. package/core/lib/lantern/metric.js +2 -6
  159. package/core/lib/lantern/metrics/first-contentful-paint.d.ts +79 -0
  160. package/core/lib/lantern/metrics/first-contentful-paint.js +200 -0
  161. package/core/lib/lantern/metrics/first-meaningful-paint.d.ts +6 -0
  162. package/core/lib/lantern/metrics/first-meaningful-paint.js +64 -0
  163. package/core/lib/lantern/metrics/interactive.d.ts +26 -0
  164. package/core/lib/lantern/metrics/interactive.js +112 -0
  165. package/core/lib/lantern/metrics/largest-contentful-paint.d.ts +19 -0
  166. package/core/lib/lantern/metrics/largest-contentful-paint.js +108 -0
  167. package/core/lib/lantern/metrics/max-potential-fid.d.ts +30 -0
  168. package/core/lib/lantern/metrics/max-potential-fid.js +92 -0
  169. package/core/lib/lantern/metrics/speed-index.d.ts +38 -0
  170. package/core/lib/lantern/metrics/speed-index.js +144 -0
  171. package/core/lib/lantern/metrics/total-blocking-time.d.ts +31 -0
  172. package/core/lib/lantern/metrics/total-blocking-time.js +128 -0
  173. package/core/lib/lantern/network-node.js +28 -4
  174. package/core/lib/lantern/page-dependency-graph.d.ts +4 -4
  175. package/core/lib/lantern/page-dependency-graph.js +19 -7
  176. package/core/lib/lantern/simulator/connection-pool.d.ts +2 -12
  177. package/core/lib/lantern/simulator/connection-pool.js +3 -22
  178. package/core/lib/lantern/simulator/network-analyzer.js +1 -12
  179. package/core/lib/lantern/simulator/simulator.d.ts +2 -4
  180. package/core/lib/lantern/simulator/simulator.js +7 -14
  181. package/core/lib/lantern/types/lantern.d.ts +5 -3
  182. package/core/lib/lantern-trace-saver.js +0 -1
  183. package/core/lib/lh-error.d.ts +9 -0
  184. package/core/lib/lh-error.js +13 -3
  185. package/core/lib/lighthouse-compatibility.js +53 -10
  186. package/core/lib/navigation-error.js +3 -0
  187. package/core/lib/network-request.d.ts +1 -0
  188. package/core/lib/network-request.js +29 -1
  189. package/core/lib/proto-preprocessor.js +24 -8
  190. package/core/lib/tracehouse/trace-processor.js +15 -2
  191. package/core/runner.js +18 -25
  192. package/dist/report/bundle.esm.js +11 -112
  193. package/dist/report/flow.js +16 -117
  194. package/dist/report/standalone.js +12 -113
  195. package/flow-report/src/help-dialog.tsx +0 -1
  196. package/flow-report/src/i18n/i18n.d.ts +0 -4
  197. package/flow-report/src/i18n/ui-strings.d.ts +0 -1
  198. package/flow-report/src/i18n/ui-strings.js +0 -2
  199. package/flow-report/src/summary/category.tsx +10 -12
  200. package/package.json +7 -7
  201. package/readme.md +4 -4
  202. package/report/assets/styles.css +1 -60
  203. package/report/assets/templates.html +0 -95
  204. package/report/renderer/category-renderer.js +1 -1
  205. package/report/renderer/components.d.ts +2 -2
  206. package/report/renderer/components.js +2 -103
  207. package/report/renderer/dom.d.ts +1 -1
  208. package/report/renderer/dom.js +1 -1
  209. package/report/renderer/performance-category-renderer.d.ts +0 -7
  210. package/report/renderer/performance-category-renderer.js +1 -32
  211. package/report/renderer/report-renderer.js +3 -13
  212. package/report/renderer/report-ui-features.js +1 -1
  213. package/report/renderer/report-utils.d.ts +0 -1
  214. package/report/renderer/report-utils.js +13 -13
  215. package/shared/localization/locales/ar-XB.json +62 -329
  216. package/shared/localization/locales/ar.json +62 -329
  217. package/shared/localization/locales/bg.json +62 -329
  218. package/shared/localization/locales/ca.json +62 -329
  219. package/shared/localization/locales/cs.json +62 -329
  220. package/shared/localization/locales/da.json +62 -329
  221. package/shared/localization/locales/de.json +62 -329
  222. package/shared/localization/locales/el.json +62 -329
  223. package/shared/localization/locales/en-GB.json +62 -329
  224. package/shared/localization/locales/en-US.json +101 -332
  225. package/shared/localization/locales/en-XA.json +62 -329
  226. package/shared/localization/locales/en-XL.json +101 -332
  227. package/shared/localization/locales/es-419.json +62 -329
  228. package/shared/localization/locales/es.json +62 -329
  229. package/shared/localization/locales/fi.json +62 -329
  230. package/shared/localization/locales/fil.json +61 -328
  231. package/shared/localization/locales/fr.json +62 -329
  232. package/shared/localization/locales/he.json +62 -329
  233. package/shared/localization/locales/hi.json +62 -329
  234. package/shared/localization/locales/hr.json +63 -330
  235. package/shared/localization/locales/hu.json +62 -329
  236. package/shared/localization/locales/id.json +62 -329
  237. package/shared/localization/locales/it.json +62 -329
  238. package/shared/localization/locales/ja.json +62 -329
  239. package/shared/localization/locales/ko.json +62 -329
  240. package/shared/localization/locales/lt.json +62 -329
  241. package/shared/localization/locales/lv.json +62 -329
  242. package/shared/localization/locales/nl.json +62 -329
  243. package/shared/localization/locales/no.json +62 -329
  244. package/shared/localization/locales/pl.json +61 -328
  245. package/shared/localization/locales/pt-PT.json +62 -329
  246. package/shared/localization/locales/pt.json +64 -331
  247. package/shared/localization/locales/ro.json +62 -329
  248. package/shared/localization/locales/ru.json +62 -329
  249. package/shared/localization/locales/sk.json +62 -329
  250. package/shared/localization/locales/sl.json +62 -329
  251. package/shared/localization/locales/sr-Latn.json +62 -329
  252. package/shared/localization/locales/sr.json +62 -329
  253. package/shared/localization/locales/sv.json +62 -329
  254. package/shared/localization/locales/ta.json +61 -328
  255. package/shared/localization/locales/te.json +62 -329
  256. package/shared/localization/locales/th.json +62 -329
  257. package/shared/localization/locales/tr.json +62 -329
  258. package/shared/localization/locales/uk.json +62 -329
  259. package/shared/localization/locales/vi.json +61 -328
  260. package/shared/localization/locales/zh-HK.json +62 -329
  261. package/shared/localization/locales/zh-TW.json +61 -328
  262. package/shared/localization/locales/zh.json +62 -329
  263. package/tsconfig.json +1 -3
  264. package/types/artifacts.d.ts +21 -110
  265. package/types/audit.d.ts +0 -10
  266. package/types/config.d.ts +0 -1
  267. package/types/externs.d.ts +0 -2
  268. package/types/gatherer.d.ts +3 -0
  269. package/types/internal/smokehouse.d.ts +1 -1
  270. package/types/lh.d.ts +0 -2
  271. package/types/lhr/audit-details.d.ts +9 -4
  272. package/types/lhr/lhr.d.ts +0 -4
  273. package/types/lhr/settings.d.ts +0 -4
  274. package/core/audits/accessibility/duplicate-id-active.js +0 -42
  275. package/core/audits/content-width.d.ts +0 -17
  276. package/core/audits/content-width.js +0 -76
  277. package/core/audits/installable-manifest.d.ts +0 -111
  278. package/core/audits/installable-manifest.js +0 -248
  279. package/core/audits/layout-shift-elements.d.ts +0 -16
  280. package/core/audits/layout-shift-elements.js +0 -101
  281. package/core/audits/manual/pwa-cross-browser.d.ts +0 -12
  282. package/core/audits/manual/pwa-cross-browser.js +0 -40
  283. package/core/audits/manual/pwa-each-page-has-url.d.ts +0 -12
  284. package/core/audits/manual/pwa-each-page-has-url.js +0 -38
  285. package/core/audits/manual/pwa-page-transitions.d.ts +0 -12
  286. package/core/audits/manual/pwa-page-transitions.js +0 -38
  287. package/core/audits/maskable-icon.d.ts +0 -25
  288. package/core/audits/maskable-icon.js +0 -70
  289. package/core/audits/multi-check-audit.d.ts +0 -28
  290. package/core/audits/multi-check-audit.js +0 -82
  291. package/core/audits/no-unload-listeners.d.ts +0 -16
  292. package/core/audits/no-unload-listeners.js +0 -86
  293. package/core/audits/performance-budget.d.ts +0 -39
  294. package/core/audits/performance-budget.js +0 -154
  295. package/core/audits/script-elements-test-audit.d.ts +0 -14
  296. package/core/audits/script-elements-test-audit.js +0 -29
  297. package/core/audits/seo/plugins.d.ts +0 -15
  298. package/core/audits/seo/plugins.js +0 -150
  299. package/core/audits/seo/tap-targets.d.ts +0 -50
  300. package/core/audits/seo/tap-targets.js +0 -352
  301. package/core/audits/splash-screen.d.ts +0 -37
  302. package/core/audits/splash-screen.js +0 -98
  303. package/core/audits/themed-omnibox.d.ts +0 -42
  304. package/core/audits/themed-omnibox.js +0 -102
  305. package/core/audits/timing-budget.d.ts +0 -43
  306. package/core/audits/timing-budget.js +0 -175
  307. package/core/computed/manifest-values.d.ts +0 -24
  308. package/core/computed/manifest-values.js +0 -136
  309. package/core/config/budget.d.ts +0 -74
  310. package/core/config/budget.js +0 -340
  311. package/core/config/metrics-to-audits.d.ts +0 -19
  312. package/core/config/metrics-to-audits.js +0 -62
  313. package/core/gather/driver/service-workers.d.ts +0 -16
  314. package/core/gather/driver/service-workers.js +0 -52
  315. package/core/gather/gatherers/dobetterweb/tags-blocking-first-paint.d.ts +0 -47
  316. package/core/gather/gatherers/dobetterweb/tags-blocking-first-paint.js +0 -233
  317. package/core/gather/gatherers/global-listeners.d.ts +0 -27
  318. package/core/gather/gatherers/global-listeners.js +0 -108
  319. package/core/gather/gatherers/installability-errors.d.ts +0 -17
  320. package/core/gather/gatherers/installability-errors.js +0 -56
  321. package/core/gather/gatherers/script-elements.d.ts +0 -21
  322. package/core/gather/gatherers/script-elements.js +0 -100
  323. package/core/gather/gatherers/seo/embedded-content.d.ts +0 -10
  324. package/core/gather/gatherers/seo/embedded-content.js +0 -63
  325. package/core/gather/gatherers/seo/tap-targets.d.ts +0 -21
  326. package/core/gather/gatherers/seo/tap-targets.js +0 -389
  327. package/core/gather/gatherers/service-worker.d.ts +0 -10
  328. package/core/gather/gatherers/service-worker.js +0 -32
  329. package/core/gather/gatherers/web-app-manifest.d.ts +0 -32
  330. package/core/gather/gatherers/web-app-manifest.js +0 -106
  331. package/report/renderer/pwa-category-renderer.d.ts +0 -55
  332. package/report/renderer/pwa-category-renderer.js +0 -184
  333. package/third-party/chromium-synchronization/installability-errors-test.js +0 -79
  334. package/types/lhr/budget.d.ts +0 -60
@@ -0,0 +1,64 @@
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
+ import {Metric} from '../metric.js';
9
+ import {FirstContentfulPaint} from './first-contentful-paint.js';
10
+
11
+ /** @typedef {import('../base-node.js').Node} Node */
12
+
13
+ class FirstMeaningfulPaint extends Metric {
14
+ /**
15
+ * @return {Lantern.Simulation.MetricCoefficients}
16
+ */
17
+ static get COEFFICIENTS() {
18
+ return {
19
+ intercept: 0,
20
+ optimistic: 0.5,
21
+ pessimistic: 0.5,
22
+ };
23
+ }
24
+
25
+ /**
26
+ * @param {Node} dependencyGraph
27
+ * @param {LH.Artifacts.ProcessedNavigation} processedNavigation
28
+ * @return {Node}
29
+ */
30
+ static getOptimisticGraph(dependencyGraph, processedNavigation) {
31
+ const fmp = processedNavigation.timestamps.firstMeaningfulPaint;
32
+ if (!fmp) {
33
+ throw new Error('NO_FMP');
34
+ }
35
+ return FirstContentfulPaint.getFirstPaintBasedGraph(dependencyGraph, {
36
+ cutoffTimestamp: fmp,
37
+ // See FirstContentfulPaint's getOptimisticGraph implementation for a longer description
38
+ // of why we exclude script initiated resources here.
39
+ treatNodeAsRenderBlocking: node =>
40
+ node.hasRenderBlockingPriority() && node.initiatorType !== 'script',
41
+ });
42
+ }
43
+
44
+ /**
45
+ * @param {Node} dependencyGraph
46
+ * @param {LH.Artifacts.ProcessedNavigation} processedNavigation
47
+ * @return {Node}
48
+ */
49
+ static getPessimisticGraph(dependencyGraph, processedNavigation) {
50
+ const fmp = processedNavigation.timestamps.firstMeaningfulPaint;
51
+ if (!fmp) {
52
+ throw new Error('NO_FMP');
53
+ }
54
+
55
+ return FirstContentfulPaint.getFirstPaintBasedGraph(dependencyGraph, {
56
+ cutoffTimestamp: fmp,
57
+ treatNodeAsRenderBlocking: node => node.hasRenderBlockingPriority(),
58
+ // For pessimistic FMP we'll include *all* layout nodes
59
+ additionalCpuNodesToTreatAsRenderBlocking: node => node.didPerformLayout(),
60
+ });
61
+ }
62
+ }
63
+
64
+ export {FirstMeaningfulPaint};
@@ -0,0 +1,26 @@
1
+ export type Node = import('../base-node.js').Node;
2
+ export class Interactive extends Metric {
3
+ /**
4
+ * @param {Node} dependencyGraph
5
+ * @return {Node}
6
+ */
7
+ static getOptimisticGraph(dependencyGraph: Node): Node;
8
+ /**
9
+ * @param {Node} dependencyGraph
10
+ * @return {Node}
11
+ */
12
+ static getPessimisticGraph(dependencyGraph: Node): Node;
13
+ /**
14
+ * @param {LH.Gatherer.Simulation.Result} simulationResult
15
+ * @param {import('../metric.js').Extras} extras
16
+ * @return {LH.Gatherer.Simulation.Result}
17
+ */
18
+ static getEstimateFromSimulation(simulationResult: LH.Gatherer.Simulation.Result, extras: import('../metric.js').Extras): LH.Gatherer.Simulation.Result;
19
+ /**
20
+ * @param {LH.Gatherer.Simulation.Result['nodeTimings']} nodeTimings
21
+ * @return {number}
22
+ */
23
+ static getLastLongTaskEndTime(nodeTimings: LH.Gatherer.Simulation.Result['nodeTimings'], duration?: number): number;
24
+ }
25
+ import { Metric } from '../metric.js';
26
+ //# sourceMappingURL=interactive.d.ts.map
@@ -0,0 +1,112 @@
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
+ import {Metric} from '../metric.js';
9
+ import {BaseNode} from '../base-node.js';
10
+ import {NetworkRequestTypes} from '../lantern.js';
11
+
12
+ /** @typedef {import('../base-node.js').Node} Node */
13
+
14
+ // Any CPU task of 20 ms or more will end up being a critical long task on mobile
15
+ const CRITICAL_LONG_TASK_THRESHOLD = 20;
16
+
17
+ class Interactive extends Metric {
18
+ /**
19
+ * @return {LH.Gatherer.Simulation.MetricCoefficients}
20
+ */
21
+ static get COEFFICIENTS() {
22
+ return {
23
+ intercept: 0,
24
+ optimistic: 0.45,
25
+ pessimistic: 0.55,
26
+ };
27
+ }
28
+
29
+ /**
30
+ * @param {Node} dependencyGraph
31
+ * @return {Node}
32
+ */
33
+ static getOptimisticGraph(dependencyGraph) {
34
+ // Adjust the critical long task threshold for microseconds
35
+ const minimumCpuTaskDuration = CRITICAL_LONG_TASK_THRESHOLD * 1000;
36
+
37
+ return dependencyGraph.cloneWithRelationships(node => {
38
+ // Include everything that might be a long task
39
+ if (node.type === BaseNode.TYPES.CPU) {
40
+ return node.duration > minimumCpuTaskDuration;
41
+ }
42
+
43
+ // Include all scripts and high priority requests, exclude all images
44
+ const isImage = node.record.resourceType === NetworkRequestTypes.Image;
45
+ const isScript = node.record.resourceType === NetworkRequestTypes.Script;
46
+ return (
47
+ !isImage &&
48
+ (isScript ||
49
+ node.record.priority === 'High' ||
50
+ node.record.priority === 'VeryHigh')
51
+ );
52
+ });
53
+ }
54
+
55
+ /**
56
+ * @param {Node} dependencyGraph
57
+ * @return {Node}
58
+ */
59
+ static getPessimisticGraph(dependencyGraph) {
60
+ return dependencyGraph;
61
+ }
62
+
63
+ /**
64
+ * @param {LH.Gatherer.Simulation.Result} simulationResult
65
+ * @param {import('../metric.js').Extras} extras
66
+ * @return {LH.Gatherer.Simulation.Result}
67
+ */
68
+ static getEstimateFromSimulation(simulationResult, extras) {
69
+ if (!extras.fmpResult) throw new Error('missing fmpResult');
70
+
71
+ const lastTaskAt = Interactive.getLastLongTaskEndTime(simulationResult.nodeTimings);
72
+ const minimumTime = extras.optimistic
73
+ ? extras.fmpResult.optimisticEstimate.timeInMs
74
+ : extras.fmpResult.pessimisticEstimate.timeInMs;
75
+ return {
76
+ timeInMs: Math.max(minimumTime, lastTaskAt),
77
+ nodeTimings: simulationResult.nodeTimings,
78
+ };
79
+ }
80
+
81
+ /**
82
+ * @param {Lantern.Simulation.MetricComputationDataInput} data
83
+ * @param {Omit<import('../metric.js').Extras, 'optimistic'>=} extras
84
+ * @return {Promise<LH.Artifacts.LanternMetric>}
85
+ */
86
+ static async compute(data, extras) {
87
+ const fmpResult = extras?.fmpResult;
88
+ if (!fmpResult) {
89
+ throw new Error('FMP is required to calculate the Interactive metric');
90
+ }
91
+
92
+ const metricResult = await super.compute(data, extras);
93
+ metricResult.timing = Math.max(metricResult.timing, fmpResult.timing);
94
+ return metricResult;
95
+ }
96
+
97
+ /**
98
+ * @param {LH.Gatherer.Simulation.Result['nodeTimings']} nodeTimings
99
+ * @return {number}
100
+ */
101
+ static getLastLongTaskEndTime(nodeTimings, duration = 50) {
102
+ return Array.from(nodeTimings.entries())
103
+ .filter(([node, timing]) => {
104
+ if (node.type !== BaseNode.TYPES.CPU) return false;
105
+ return timing.duration > duration;
106
+ })
107
+ .map(([_, timing]) => timing.endTime)
108
+ .reduce((max, x) => Math.max(max || 0, x || 0), 0);
109
+ }
110
+ }
111
+
112
+ export {Interactive};
@@ -0,0 +1,19 @@
1
+ export type Node = import('../base-node.js').Node;
2
+ /** @typedef {import('../base-node.js').Node} Node */
3
+ export class LargestContentfulPaint extends 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 {LH.Gatherer.Simulation.Result} simulationResult
14
+ * @return {LH.Gatherer.Simulation.Result}
15
+ */
16
+ static getEstimateFromSimulation(simulationResult: LH.Gatherer.Simulation.Result): LH.Gatherer.Simulation.Result;
17
+ }
18
+ import { Metric } from '../metric.js';
19
+ //# sourceMappingURL=largest-contentful-paint.d.ts.map
@@ -0,0 +1,108 @@
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
+ import {Metric} from '../metric.js';
9
+ import {FirstContentfulPaint} from './first-contentful-paint.js';
10
+ import {LanternError} from '../lantern-error.js';
11
+
12
+ /** @typedef {import('../base-node.js').Node} Node */
13
+
14
+ class LargestContentfulPaint extends Metric {
15
+ /**
16
+ * @return {LH.Gatherer.Simulation.MetricCoefficients}
17
+ */
18
+ static get COEFFICIENTS() {
19
+ return {
20
+ intercept: 0,
21
+ optimistic: 0.5,
22
+ pessimistic: 0.5,
23
+ };
24
+ }
25
+
26
+ /**
27
+ * Low priority image nodes are usually offscreen and very unlikely to be the
28
+ * resource that is required for LCP. Our LCP graphs include everything except for these images.
29
+ *
30
+ * @param {Node} node
31
+ * @return {boolean}
32
+ */
33
+ static isNotLowPriorityImageNode(node) {
34
+ if (node.type !== 'network') return true;
35
+ const isImage = node.record.resourceType === 'Image';
36
+ const isLowPriority = node.record.priority === 'Low' || node.record.priority === 'VeryLow';
37
+ return !isImage || !isLowPriority;
38
+ }
39
+
40
+ /**
41
+ * @param {Node} dependencyGraph
42
+ * @param {LH.Artifacts.ProcessedNavigation} processedNavigation
43
+ * @return {Node}
44
+ */
45
+ static getOptimisticGraph(dependencyGraph, processedNavigation) {
46
+ const lcp = processedNavigation.timestamps.largestContentfulPaint;
47
+ if (!lcp) {
48
+ throw new LanternError('NO_LCP');
49
+ }
50
+
51
+ return FirstContentfulPaint.getFirstPaintBasedGraph(dependencyGraph, {
52
+ cutoffTimestamp: lcp,
53
+ treatNodeAsRenderBlocking: LargestContentfulPaint.isNotLowPriorityImageNode,
54
+ });
55
+ }
56
+
57
+ /**
58
+ * @param {Node} dependencyGraph
59
+ * @param {LH.Artifacts.ProcessedNavigation} processedNavigation
60
+ * @return {Node}
61
+ */
62
+ static getPessimisticGraph(dependencyGraph, processedNavigation) {
63
+ const lcp = processedNavigation.timestamps.largestContentfulPaint;
64
+ if (!lcp) {
65
+ throw new LanternError('NO_LCP');
66
+ }
67
+
68
+ return FirstContentfulPaint.getFirstPaintBasedGraph(dependencyGraph, {
69
+ cutoffTimestamp: lcp,
70
+ treatNodeAsRenderBlocking: _ => true,
71
+ // For pessimistic LCP we'll include *all* layout nodes
72
+ additionalCpuNodesToTreatAsRenderBlocking: node => node.didPerformLayout(),
73
+ });
74
+ }
75
+
76
+ /**
77
+ * @param {LH.Gatherer.Simulation.Result} simulationResult
78
+ * @return {LH.Gatherer.Simulation.Result}
79
+ */
80
+ static getEstimateFromSimulation(simulationResult) {
81
+ const nodeTimesNotOffscreenImages = Array.from(simulationResult.nodeTimings.entries())
82
+ .filter(entry => LargestContentfulPaint.isNotLowPriorityImageNode(entry[0]))
83
+ .map(entry => entry[1].endTime);
84
+
85
+ return {
86
+ timeInMs: Math.max(...nodeTimesNotOffscreenImages),
87
+ nodeTimings: simulationResult.nodeTimings,
88
+ };
89
+ }
90
+
91
+ /**
92
+ * @param {Lantern.Simulation.MetricComputationDataInput} data
93
+ * @param {Omit<import('../metric.js').Extras, 'optimistic'>=} extras
94
+ * @return {Promise<LH.Artifacts.LanternMetric>}
95
+ */
96
+ static async compute(data, extras) {
97
+ const fcpResult = extras?.fcpResult;
98
+ if (!fcpResult) {
99
+ throw new Error('FCP is required to calculate the LCP metric');
100
+ }
101
+
102
+ const metricResult = await super.compute(data, extras);
103
+ metricResult.timing = Math.max(metricResult.timing, fcpResult.timing);
104
+ return metricResult;
105
+ }
106
+ }
107
+
108
+ export {LargestContentfulPaint};
@@ -0,0 +1,30 @@
1
+ export type Node = import('../base-node.js').Node;
2
+ /** @typedef {import('../base-node.js').Node} Node */
3
+ export class MaxPotentialFID extends 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 {LH.Gatherer.Simulation.Result} simulation
16
+ * @param {import('../metric.js').Extras} extras
17
+ * @return {LH.Gatherer.Simulation.Result}
18
+ */
19
+ static getEstimateFromSimulation(simulation: LH.Gatherer.Simulation.Result, extras: import('../metric.js').Extras): LH.Gatherer.Simulation.Result;
20
+ /**
21
+ * @param {LH.Gatherer.Simulation.Result['nodeTimings']} nodeTimings
22
+ * @param {number} fcpTimeInMs
23
+ * @return {Array<{duration: number}>}
24
+ */
25
+ static getTimingsAfterFCP(nodeTimings: LH.Gatherer.Simulation.Result['nodeTimings'], fcpTimeInMs: number): Array<{
26
+ duration: number;
27
+ }>;
28
+ }
29
+ import { Metric } from '../metric.js';
30
+ //# sourceMappingURL=max-potential-fid.d.ts.map
@@ -0,0 +1,92 @@
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
+ import {Metric} from '../metric.js';
9
+ import {BaseNode} from '../base-node.js';
10
+
11
+ /** @typedef {import('../base-node.js').Node} Node */
12
+
13
+ class MaxPotentialFID extends Metric {
14
+ /**
15
+ * @return {LH.Gatherer.Simulation.MetricCoefficients}
16
+ */
17
+ static get COEFFICIENTS() {
18
+ return {
19
+ intercept: 0,
20
+ optimistic: 0.5,
21
+ pessimistic: 0.5,
22
+ };
23
+ }
24
+
25
+ /**
26
+ * @param {Node} dependencyGraph
27
+ * @return {Node}
28
+ */
29
+ static getOptimisticGraph(dependencyGraph) {
30
+ return dependencyGraph;
31
+ }
32
+
33
+ /**
34
+ * @param {Node} dependencyGraph
35
+ * @return {Node}
36
+ */
37
+ static getPessimisticGraph(dependencyGraph) {
38
+ return dependencyGraph;
39
+ }
40
+
41
+ /**
42
+ * @param {LH.Gatherer.Simulation.Result} simulation
43
+ * @param {import('../metric.js').Extras} extras
44
+ * @return {LH.Gatherer.Simulation.Result}
45
+ */
46
+ static getEstimateFromSimulation(simulation, extras) {
47
+ if (!extras.fcpResult) throw new Error('missing fcpResult');
48
+
49
+ // Intentionally use the opposite FCP estimate, a more pessimistic FCP means that more tasks
50
+ // are excluded from the FID computation, so a higher FCP means lower FID for same work.
51
+ const fcpTimeInMs = extras.optimistic
52
+ ? extras.fcpResult.pessimisticEstimate.timeInMs
53
+ : extras.fcpResult.optimisticEstimate.timeInMs;
54
+
55
+ const timings = MaxPotentialFID.getTimingsAfterFCP(
56
+ simulation.nodeTimings,
57
+ fcpTimeInMs
58
+ );
59
+
60
+ return {
61
+ timeInMs: Math.max(...timings.map(timing => timing.duration), 16),
62
+ nodeTimings: simulation.nodeTimings,
63
+ };
64
+ }
65
+
66
+ /**
67
+ * @param {Lantern.Simulation.MetricComputationDataInput} data
68
+ * @param {Omit<import('../metric.js').Extras, 'optimistic'>=} extras
69
+ * @return {Promise<LH.Artifacts.LanternMetric>}
70
+ */
71
+ static compute(data, extras) {
72
+ const fcpResult = extras?.fcpResult;
73
+ if (!fcpResult) {
74
+ throw new Error('FCP is required to calculate the Max Potential FID metric');
75
+ }
76
+
77
+ return super.compute(data, extras);
78
+ }
79
+
80
+ /**
81
+ * @param {LH.Gatherer.Simulation.Result['nodeTimings']} nodeTimings
82
+ * @param {number} fcpTimeInMs
83
+ * @return {Array<{duration: number}>}
84
+ */
85
+ static getTimingsAfterFCP(nodeTimings, fcpTimeInMs) {
86
+ return Array.from(nodeTimings.entries())
87
+ .filter(([node, timing]) => node.type === BaseNode.TYPES.CPU && timing.endTime > fcpTimeInMs)
88
+ .map(([_, timing]) => timing);
89
+ }
90
+ }
91
+
92
+ export {MaxPotentialFID};
@@ -0,0 +1,38 @@
1
+ export type Node = import('../base-node.js').Node;
2
+ /** @typedef {import('../base-node.js').Node} Node */
3
+ export class SpeedIndex extends 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 {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
+ /**
21
+ * Approximate speed index using layout events from the simulated node timings.
22
+ * The layout-based speed index is the weighted average of the endTime of CPU nodes that contained
23
+ * a 'Layout' task. log(duration) is used as the weight to stand for "significance" to the page.
24
+ *
25
+ * If no layout events can be found or the endTime of a CPU task is too early, FCP is used instead.
26
+ *
27
+ * This approach was determined after evaluating the accuracy/complexity tradeoff of many
28
+ * different methods. Read more in the evaluation doc.
29
+ *
30
+ * @see https://docs.google.com/document/d/1qJWXwxoyVLVadezIp_Tgdk867G3tDNkkVRvUJSH3K1E/edit#
31
+ * @param {LH.Gatherer.Simulation.Result['nodeTimings']} nodeTimings
32
+ * @param {number} fcpTimeInMs
33
+ * @return {number}
34
+ */
35
+ static computeLayoutBasedSpeedIndex(nodeTimings: LH.Gatherer.Simulation.Result['nodeTimings'], fcpTimeInMs: number): number;
36
+ }
37
+ import { Metric } from '../metric.js';
38
+ //# sourceMappingURL=speed-index.d.ts.map
@@ -0,0 +1,144 @@
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
+ import {Metric} from '../metric.js';
9
+ import {BaseNode} from '../base-node.js';
10
+
11
+ const mobileSlow4GRtt = 150;
12
+
13
+ /** @typedef {import('../base-node.js').Node} Node */
14
+
15
+ class SpeedIndex extends Metric {
16
+ /**
17
+ * @return {LH.Gatherer.Simulation.MetricCoefficients}
18
+ */
19
+ static get COEFFICIENTS() {
20
+ return {
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,
24
+ optimistic: 1.4,
25
+ pessimistic: 0.4,
26
+ };
27
+ }
28
+
29
+ /**
30
+ * @param {number} rttMs
31
+ * @return {LH.Gatherer.Simulation.MetricCoefficients}
32
+ */
33
+ static getScaledCoefficients(rttMs) { // eslint-disable-line no-unused-vars
34
+ // We want to scale our default coefficients based on the speed of the connection.
35
+ // We will linearly interpolate coefficients for the passed-in rttMs based on two pre-determined points:
36
+ // 1. Baseline point of 30 ms RTT where Speed Index should be a ~50/50 blend of optimistic/pessimistic.
37
+ // 30 ms was based on a typical home WiFi connection's actual RTT.
38
+ // Coefficients here follow from the fact that the optimistic estimate should be very close
39
+ // to reality at this connection speed and the pessimistic estimate compensates for minor
40
+ // connection speed differences.
41
+ // 2. Default throttled point of 150 ms RTT where the default coefficients have been determined to be most accurate.
42
+ // Coefficients here were determined through thorough analysis and linear regression on the
43
+ // lantern test data set. See core/scripts/test-lantern.sh for more detail.
44
+ // While the coefficients haven't been analyzed at the interpolated points, it's our current best effort.
45
+ const defaultCoefficients = this.COEFFICIENTS;
46
+ const defaultRttExcess = mobileSlow4GRtt - 30;
47
+ const multiplier = Math.max((rttMs - 30) / defaultRttExcess, 0);
48
+
49
+ return {
50
+ intercept: defaultCoefficients.intercept * multiplier,
51
+ optimistic: 0.5 + (defaultCoefficients.optimistic - 0.5) * multiplier,
52
+ pessimistic: 0.5 + (defaultCoefficients.pessimistic - 0.5) * multiplier,
53
+ };
54
+ }
55
+
56
+ /**
57
+ * @param {Node} dependencyGraph
58
+ * @return {Node}
59
+ */
60
+ static getOptimisticGraph(dependencyGraph) {
61
+ return dependencyGraph;
62
+ }
63
+
64
+ /**
65
+ * @param {Node} dependencyGraph
66
+ * @return {Node}
67
+ */
68
+ static getPessimisticGraph(dependencyGraph) {
69
+ return dependencyGraph;
70
+ }
71
+
72
+ /**
73
+ * @param {LH.Gatherer.Simulation.Result} simulationResult
74
+ * @param {import('../metric.js').Extras} extras
75
+ * @return {LH.Gatherer.Simulation.Result}
76
+ */
77
+ static getEstimateFromSimulation(simulationResult, extras) {
78
+ if (!extras.fcpResult) throw new Error('missing fcpResult');
79
+ if (!extras.speedline) throw new Error('missing speedline');
80
+
81
+ const fcpTimeInMs = extras.fcpResult.pessimisticEstimate.timeInMs;
82
+ const estimate = extras.optimistic
83
+ ? extras.speedline.speedIndex
84
+ : SpeedIndex.computeLayoutBasedSpeedIndex(simulationResult.nodeTimings, fcpTimeInMs);
85
+ return {
86
+ timeInMs: estimate,
87
+ nodeTimings: simulationResult.nodeTimings,
88
+ };
89
+ }
90
+
91
+ /**
92
+ * @param {Lantern.Simulation.MetricComputationDataInput} data
93
+ * @param {Omit<import('../metric.js').Extras, 'optimistic'>=} extras
94
+ * @return {Promise<LH.Artifacts.LanternMetric>}
95
+ */
96
+ static async compute(data, extras) {
97
+ const fcpResult = extras?.fcpResult;
98
+ if (!fcpResult) {
99
+ throw new Error('FCP is required to calculate the SpeedIndex metric');
100
+ }
101
+
102
+ const metricResult = await super.compute(data, extras);
103
+ metricResult.timing = Math.max(metricResult.timing, fcpResult.timing);
104
+ return metricResult;
105
+ }
106
+
107
+ /**
108
+ * Approximate speed index using layout events from the simulated node timings.
109
+ * The layout-based speed index is the weighted average of the endTime of CPU nodes that contained
110
+ * a 'Layout' task. log(duration) is used as the weight to stand for "significance" to the page.
111
+ *
112
+ * If no layout events can be found or the endTime of a CPU task is too early, FCP is used instead.
113
+ *
114
+ * This approach was determined after evaluating the accuracy/complexity tradeoff of many
115
+ * different methods. Read more in the evaluation doc.
116
+ *
117
+ * @see https://docs.google.com/document/d/1qJWXwxoyVLVadezIp_Tgdk867G3tDNkkVRvUJSH3K1E/edit#
118
+ * @param {LH.Gatherer.Simulation.Result['nodeTimings']} nodeTimings
119
+ * @param {number} fcpTimeInMs
120
+ * @return {number}
121
+ */
122
+ static computeLayoutBasedSpeedIndex(nodeTimings, fcpTimeInMs) {
123
+ /** @type {Array<{time: number, weight: number}>} */
124
+ const layoutWeights = [];
125
+ for (const [node, timing] of nodeTimings.entries()) {
126
+ if (node.type !== BaseNode.TYPES.CPU) continue;
127
+
128
+ if (node.childEvents.some(x => x.name === 'Layout')) {
129
+ const timingWeight = Math.max(Math.log2(timing.endTime - timing.startTime), 0);
130
+ layoutWeights.push({time: timing.endTime, weight: timingWeight});
131
+ }
132
+ }
133
+
134
+ const totalWeightedTime = layoutWeights
135
+ .map(evt => evt.weight * Math.max(evt.time, fcpTimeInMs))
136
+ .reduce((a, b) => a + b, 0);
137
+ const totalWeight = layoutWeights.map(evt => evt.weight).reduce((a, b) => a + b, 0);
138
+
139
+ if (!totalWeight) return fcpTimeInMs;
140
+ return totalWeightedTime / totalWeight;
141
+ }
142
+ }
143
+
144
+ export {SpeedIndex};
@@ -0,0 +1,31 @@
1
+ export type Node = import('../base-node.js').Node;
2
+ /** @typedef {import('../base-node.js').Node} Node */
3
+ export class TotalBlockingTime extends 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 {LH.Gatherer.Simulation.Result} simulation
16
+ * @param {import('../metric.js').Extras} extras
17
+ * @return {LH.Gatherer.Simulation.Result}
18
+ */
19
+ static getEstimateFromSimulation(simulation: LH.Gatherer.Simulation.Result, extras: import('../metric.js').Extras): LH.Gatherer.Simulation.Result;
20
+ /**
21
+ * @param {LH.Gatherer.Simulation.Result['nodeTimings']} nodeTimings
22
+ * @param {number} minDurationMs
23
+ */
24
+ static getTopLevelEvents(nodeTimings: LH.Gatherer.Simulation.Result['nodeTimings'], minDurationMs: number): {
25
+ start: number;
26
+ end: number;
27
+ duration: number;
28
+ }[];
29
+ }
30
+ import { Metric } from '../metric.js';
31
+ //# sourceMappingURL=total-blocking-time.d.ts.map