browsertime 19.3.1 → 21.0.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 (225) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/README.md +9 -8
  3. package/bin/browsertime.js +9 -3
  4. package/browserscripts/pageinfo/interactionToNextPaintInfo.js +170 -143
  5. package/browserscripts/timings/interactionToNextPaint.js +49 -137
  6. package/lib/chrome/webdriver/setupChromiumOptions.js +0 -9
  7. package/lib/core/engine/collector.js +11 -0
  8. package/lib/core/engine/command/actions.js +52 -0
  9. package/lib/core/engine/command/addText.js +48 -30
  10. package/lib/core/engine/command/android.js +43 -4
  11. package/lib/core/engine/command/cache.js +39 -2
  12. package/lib/core/engine/command/chromeDevToolsProtocol.js +51 -1
  13. package/lib/core/engine/command/chromeTrace.js +34 -0
  14. package/lib/core/engine/command/click.js +102 -52
  15. package/lib/core/engine/command/debug.js +19 -2
  16. package/lib/core/engine/command/element.js +66 -0
  17. package/lib/core/engine/command/geckoProfiler.js +41 -1
  18. package/lib/core/engine/command/javaScript.js +43 -20
  19. package/lib/core/engine/command/measure.js +151 -19
  20. package/lib/core/engine/command/meta.js +17 -0
  21. package/lib/core/engine/command/mouse/clickAndHold.js +56 -29
  22. package/lib/core/engine/command/mouse/contextClick.js +30 -11
  23. package/lib/core/engine/command/mouse/doubleClick.js +35 -11
  24. package/lib/core/engine/command/mouse/mouseMove.js +38 -16
  25. package/lib/core/engine/command/mouse/singleClick.js +187 -12
  26. package/lib/core/engine/command/navigation.js +31 -3
  27. package/lib/core/engine/command/screenshot.js +33 -0
  28. package/lib/core/engine/command/scroll.js +35 -13
  29. package/lib/core/engine/command/select.js +55 -36
  30. package/lib/core/engine/command/set.js +51 -36
  31. package/lib/core/engine/command/stopWatch.js +30 -9
  32. package/lib/core/engine/command/switch.js +49 -11
  33. package/lib/core/engine/command/wait.js +50 -25
  34. package/lib/core/engine/commands.js +267 -0
  35. package/lib/core/engine/context.js +81 -0
  36. package/lib/core/engine/index.js +6 -2
  37. package/lib/core/engine/iteration.js +20 -102
  38. package/lib/firefox/geckoProfiler.js +49 -1
  39. package/lib/firefox/webdriver/builder.js +35 -12
  40. package/lib/firefox/webdriver/firefox.js +1 -1
  41. package/lib/support/cli.js +35 -15
  42. package/lib/support/engineUtils.js +13 -1
  43. package/lib/support/statistics.js +11 -1
  44. package/lib/support/userTiming.js +2 -2
  45. package/package.json +17 -8
  46. package/types/android/index.d.ts +70 -0
  47. package/types/android/index.d.ts.map +1 -0
  48. package/types/chrome/parseCpuTrace.d.ts +21 -0
  49. package/types/chrome/parseCpuTrace.d.ts.map +1 -0
  50. package/types/chrome/traceCategoriesParser.d.ts +2 -0
  51. package/types/chrome/traceCategoriesParser.d.ts.map +1 -0
  52. package/types/chrome/webdriver/traceUtilities.d.ts +8 -0
  53. package/types/chrome/webdriver/traceUtilities.d.ts.map +1 -0
  54. package/types/core/engine/command/action.d.ts +73 -0
  55. package/types/core/engine/command/action.d.ts.map +1 -0
  56. package/types/core/engine/command/actions.d.ts +38 -0
  57. package/types/core/engine/command/actions.d.ts.map +1 -0
  58. package/types/core/engine/command/addText.d.ts +68 -0
  59. package/types/core/engine/command/addText.d.ts.map +1 -0
  60. package/types/core/engine/command/android.d.ts +39 -0
  61. package/types/core/engine/command/android.d.ts.map +1 -0
  62. package/types/core/engine/command/cache.d.ts +53 -0
  63. package/types/core/engine/command/cache.d.ts.map +1 -0
  64. package/types/core/engine/command/chromeDevToolsProtocol.d.ts +61 -0
  65. package/types/core/engine/command/chromeDevToolsProtocol.d.ts.map +1 -0
  66. package/types/core/engine/command/chromeTrace.d.ts +45 -0
  67. package/types/core/engine/command/chromeTrace.d.ts.map +1 -0
  68. package/types/core/engine/command/click.d.ts +151 -0
  69. package/types/core/engine/command/click.d.ts.map +1 -0
  70. package/types/core/engine/command/debug.d.ts +28 -0
  71. package/types/core/engine/command/debug.d.ts.map +1 -0
  72. package/types/core/engine/command/element.d.ts +49 -0
  73. package/types/core/engine/command/element.d.ts.map +1 -0
  74. package/types/core/engine/command/geckoProfiler.d.ts +46 -0
  75. package/types/core/engine/command/geckoProfiler.d.ts.map +1 -0
  76. package/types/core/engine/command/javaScript.d.ts +63 -0
  77. package/types/core/engine/command/javaScript.d.ts.map +1 -0
  78. package/types/core/engine/command/measure.d.ts +194 -0
  79. package/types/core/engine/command/measure.d.ts.map +1 -0
  80. package/types/core/engine/command/meta.d.ts +23 -0
  81. package/types/core/engine/command/meta.d.ts.map +1 -0
  82. package/types/core/engine/command/mouse/clickAndHold.d.ts +82 -0
  83. package/types/core/engine/command/mouse/clickAndHold.d.ts.map +1 -0
  84. package/types/core/engine/command/mouse/contextClick.d.ts +44 -0
  85. package/types/core/engine/command/mouse/contextClick.d.ts.map +1 -0
  86. package/types/core/engine/command/mouse/doubleClick.d.ts +51 -0
  87. package/types/core/engine/command/mouse/doubleClick.d.ts.map +1 -0
  88. package/types/core/engine/command/mouse/index.d.ts +6 -0
  89. package/types/core/engine/command/mouse/index.d.ts.map +1 -0
  90. package/types/core/engine/command/mouse/mouseMove.d.ts +56 -0
  91. package/types/core/engine/command/mouse/mouseMove.d.ts.map +1 -0
  92. package/types/core/engine/command/mouse/singleClick.d.ts +115 -0
  93. package/types/core/engine/command/mouse/singleClick.d.ts.map +1 -0
  94. package/types/core/engine/command/navigation.d.ts +45 -0
  95. package/types/core/engine/command/navigation.d.ts.map +1 -0
  96. package/types/core/engine/command/screenshot.d.ts +33 -0
  97. package/types/core/engine/command/screenshot.d.ts.map +1 -0
  98. package/types/core/engine/command/scroll.d.ts +52 -0
  99. package/types/core/engine/command/scroll.d.ts.map +1 -0
  100. package/types/core/engine/command/select.d.ts +81 -0
  101. package/types/core/engine/command/select.d.ts.map +1 -0
  102. package/types/core/engine/command/set.d.ts +74 -0
  103. package/types/core/engine/command/set.d.ts.map +1 -0
  104. package/types/core/engine/command/stopWatch.d.ts +49 -0
  105. package/types/core/engine/command/stopWatch.d.ts.map +1 -0
  106. package/types/core/engine/command/switch.d.ts +77 -0
  107. package/types/core/engine/command/switch.d.ts.map +1 -0
  108. package/types/core/engine/command/util/lcpHighlightScript.d.ts +2 -0
  109. package/types/core/engine/command/util/lcpHighlightScript.d.ts.map +1 -0
  110. package/types/core/engine/command/util/lsHighlightScript.d.ts +2 -0
  111. package/types/core/engine/command/util/lsHighlightScript.d.ts.map +1 -0
  112. package/types/core/engine/command/wait.d.ts +74 -0
  113. package/types/core/engine/command/wait.d.ts.map +1 -0
  114. package/types/core/engine/commands.d.ts +161 -0
  115. package/types/core/engine/commands.d.ts.map +1 -0
  116. package/types/core/engine/context.d.ts +87 -0
  117. package/types/core/engine/context.d.ts.map +1 -0
  118. package/types/firefox/geckoProfiler.d.ts +11 -0
  119. package/types/firefox/geckoProfiler.d.ts.map +1 -0
  120. package/types/firefox/settings/geckoProfilerDefaults.d.ts +8 -0
  121. package/types/firefox/settings/geckoProfilerDefaults.d.ts.map +1 -0
  122. package/types/scripting.d.ts +2 -0
  123. package/types/support/engineUtils.d.ts +5 -0
  124. package/types/support/engineUtils.d.ts.map +1 -0
  125. package/types/support/errors.d.ts +15 -0
  126. package/types/support/errors.d.ts.map +1 -0
  127. package/types/support/fileUtil.d.ts +10 -0
  128. package/types/support/fileUtil.d.ts.map +1 -0
  129. package/types/support/filters.d.ts +7 -0
  130. package/types/support/filters.d.ts.map +1 -0
  131. package/types/support/getViewPort.d.ts +2 -0
  132. package/types/support/getViewPort.d.ts.map +1 -0
  133. package/types/support/pathToFolder.d.ts +2 -0
  134. package/types/support/pathToFolder.d.ts.map +1 -0
  135. package/types/support/storageManager.d.ts +17 -0
  136. package/types/support/storageManager.d.ts.map +1 -0
  137. package/types/support/tcpdump.d.ts +10 -0
  138. package/types/support/tcpdump.d.ts.map +1 -0
  139. package/types/support/userTiming.d.ts +2 -0
  140. package/types/support/userTiming.d.ts.map +1 -0
  141. package/types/support/util.d.ts +6 -0
  142. package/types/support/util.d.ts.map +1 -0
  143. package/types/video/defaults.d.ts +7 -0
  144. package/types/video/defaults.d.ts.map +1 -0
  145. package/types/video/postprocessing/finetune/addTextToVideo.d.ts +2 -0
  146. package/types/video/postprocessing/finetune/addTextToVideo.d.ts.map +1 -0
  147. package/types/video/postprocessing/finetune/convertFps.d.ts +2 -0
  148. package/types/video/postprocessing/finetune/convertFps.d.ts.map +1 -0
  149. package/types/video/postprocessing/finetune/getFont.d.ts +2 -0
  150. package/types/video/postprocessing/finetune/getFont.d.ts.map +1 -0
  151. package/types/video/postprocessing/finetune/getTimingMetrics.d.ts +2 -0
  152. package/types/video/postprocessing/finetune/getTimingMetrics.d.ts.map +1 -0
  153. package/types/video/postprocessing/finetune/index.d.ts +2 -0
  154. package/types/video/postprocessing/finetune/index.d.ts.map +1 -0
  155. package/types/video/postprocessing/finetune/removeOrange.d.ts +2 -0
  156. package/types/video/postprocessing/finetune/removeOrange.d.ts.map +1 -0
  157. package/types/video/postprocessing/visualmetrics/extraMetrics.d.ts +5 -0
  158. package/types/video/postprocessing/visualmetrics/extraMetrics.d.ts.map +1 -0
  159. package/types/video/postprocessing/visualmetrics/getVideoMetrics.d.ts +5 -0
  160. package/types/video/postprocessing/visualmetrics/getVideoMetrics.d.ts.map +1 -0
  161. package/types/video/postprocessing/visualmetrics/visualMetrics.d.ts +3 -0
  162. package/types/video/postprocessing/visualmetrics/visualMetrics.d.ts.map +1 -0
  163. package/types/video/screenRecording/android/recorder.d.ts +11 -0
  164. package/types/video/screenRecording/android/recorder.d.ts.map +1 -0
  165. package/types/video/screenRecording/desktop/convert.d.ts +2 -0
  166. package/types/video/screenRecording/desktop/convert.d.ts.map +1 -0
  167. package/types/video/screenRecording/desktop/desktopRecorder.d.ts +28 -0
  168. package/types/video/screenRecording/desktop/desktopRecorder.d.ts.map +1 -0
  169. package/types/video/screenRecording/desktop/ffmpegRecorder.d.ts +17 -0
  170. package/types/video/screenRecording/desktop/ffmpegRecorder.d.ts.map +1 -0
  171. package/types/video/screenRecording/desktop/osx/getSPDisplaysDataType.d.ts +2 -0
  172. package/types/video/screenRecording/desktop/osx/getSPDisplaysDataType.d.ts.map +1 -0
  173. package/types/video/screenRecording/desktop/osx/getScreen.d.ts +2 -0
  174. package/types/video/screenRecording/desktop/osx/getScreen.d.ts.map +1 -0
  175. package/types/video/screenRecording/firefox/firefoxWindowRecorder.d.ts +13 -0
  176. package/types/video/screenRecording/firefox/firefoxWindowRecorder.d.ts.map +1 -0
  177. package/types/video/screenRecording/ios/convertToMp4.d.ts +2 -0
  178. package/types/video/screenRecording/ios/convertToMp4.d.ts.map +1 -0
  179. package/types/video/screenRecording/ios/iosRecorder.d.ts +14 -0
  180. package/types/video/screenRecording/ios/iosRecorder.d.ts.map +1 -0
  181. package/types/video/screenRecording/iosSimulator/convertToMp4.d.ts +2 -0
  182. package/types/video/screenRecording/iosSimulator/convertToMp4.d.ts.map +1 -0
  183. package/types/video/screenRecording/iosSimulator/recorder.d.ts +9 -0
  184. package/types/video/screenRecording/iosSimulator/recorder.d.ts.map +1 -0
  185. package/types/video/screenRecording/recorder.d.ts +7 -0
  186. package/types/video/screenRecording/recorder.d.ts.map +1 -0
  187. package/types/video/screenRecording/setOrangeBackground.d.ts +2 -0
  188. package/types/video/screenRecording/setOrangeBackground.d.ts.map +1 -0
  189. package/types/video/video.d.ts +41 -0
  190. package/types/video/video.d.ts.map +1 -0
  191. package/CONTRIBUTING.md +0 -24
  192. package/browsersupport/firefox-profile/cert9.db +0 -0
  193. package/browsersupport/firefox-profile/chrome/userChrome.css +0 -4
  194. package/browsersupport/firefox-profile/key4.db +0 -0
  195. package/docs/examples/README.md +0 -9
  196. package/docs/examples/advance.md +0 -12
  197. package/docs/examples/android.md +0 -54
  198. package/docs/examples/basic.md +0 -36
  199. package/docs/examples/chrome.md +0 -28
  200. package/docs/examples/firefox.md +0 -10
  201. package/docs/issues.md +0 -18
  202. package/docs/new-style-script-objects.md +0 -38
  203. package/docs/sequence-diagrams/loadAndWait.wsd +0 -24
  204. package/docs/sequence-diagrams/run.wsd +0 -24
  205. package/vendor/mac/x86/qvh +0 -0
  206. package/visualmetrics/__init__.py +0 -1
  207. package/visualmetrics/test_data/ms_000000.png +0 -0
  208. package/visualmetrics/test_data/ms_000920.png +0 -0
  209. package/visualmetrics/test_data/ms_001000.png +0 -0
  210. package/visualmetrics/test_data/ms_001080.png +0 -0
  211. package/visualmetrics/test_data/ms_001200.png +0 -0
  212. package/visualmetrics/test_data/ms_001240.png +0 -0
  213. package/visualmetrics/test_data/ms_001280.png +0 -0
  214. package/visualmetrics/test_data/ms_001360.png +0 -0
  215. package/visualmetrics/test_data/ms_001400.png +0 -0
  216. package/visualmetrics/test_data/ms_001520.png +0 -0
  217. package/visualmetrics/test_data/ms_002040.png +0 -0
  218. package/visualmetrics/test_data/ms_002600.png +0 -0
  219. package/visualmetrics/test_data/ms_003160.png +0 -0
  220. package/visualmetrics/test_data/ms_003720.png +0 -0
  221. package/visualmetrics/test_data/ms_004280.png +0 -0
  222. package/visualmetrics/test_data/ms_004880.png +0 -0
  223. package/visualmetrics/test_data/ms_005440.png +0 -0
  224. package/visualmetrics/test_data/ms_006000.png +0 -0
  225. package/visualmetrics/test_visualmetrics.py +0 -34
@@ -140,7 +140,7 @@ export class Firefox {
140
140
  this.firefoxConfig.geckoProfiler &&
141
141
  this.firefoxConfig.geckoProfilerRecordingType !== 'custom'
142
142
  ) {
143
- await this.geckoProfiler.stop(index, url);
143
+ await this.geckoProfiler.stop(index, url, result);
144
144
  }
145
145
 
146
146
  if (this.firefoxConfig.perfStats) {
@@ -537,6 +537,13 @@ export function parseCommandLine() {
537
537
  'need to specify the logs you wish to gather.',
538
538
  group: 'firefox'
539
539
  })
540
+ .option('firefox.powerConsumption', {
541
+ type: 'boolean',
542
+ default: false,
543
+ describe:
544
+ 'Enable power consumption collection (in Wh). To get the consumption you also need to set firefox.geckoProfilerParams.features to include power.',
545
+ group: 'firefox'
546
+ })
540
547
  .option('firefox.setMozLog', {
541
548
  describe:
542
549
  'Use in conjunction with firefox.collectMozLog to set MOZ_LOG to something ' +
@@ -905,43 +912,50 @@ export function parseCommandLine() {
905
912
  })
906
913
  .option('pageCompleteCheck', {
907
914
  describe:
908
- 'Supply a JavaScript (inline or JavaScript file) that decides when the browser is finished loading the page and can start to collect metrics. The JavaScript snippet is repeatedly queried to see if page has completed loading (indicated by the script returning true). Use it to fetch timings happening after the loadEventEnd. By default the tests ends 2 seconds after loadEventEnd. Also checkout --pageCompleteCheckInactivity and --pageCompleteCheckPollTimeout'
915
+ 'Supply a JavaScript (inline or JavaScript file) that decides when the browser is finished loading the page and can start to collect metrics. The JavaScript snippet is repeatedly queried to see if page has completed loading (indicated by the script returning true). Use it to fetch timings happening after the loadEventEnd. By default the tests ends 2 seconds after loadEventEnd. Also checkout --pageCompleteCheckInactivity and --pageCompleteCheckPollTimeout',
916
+ group: 'PageLoad'
909
917
  })
910
918
  .option('pageCompleteWaitTime', {
911
919
  describe:
912
920
  'How long time you want to wait for your pageComplteteCheck to finish, after it is signaled to closed. Extra parameter passed on to your pageCompleteCheck.',
913
- default: 8000
921
+ default: 8000,
922
+ group: 'PageLoad'
914
923
  })
915
924
  .option('pageCompleteCheckInactivity', {
916
925
  describe:
917
926
  'Alternative way to choose when to end your test. This will wait for 2 seconds of inactivity that happens after loadEventEnd.',
918
927
  type: 'boolean',
919
- default: false
928
+ default: false,
929
+ group: 'PageLoad'
920
930
  })
921
931
  .option('pageCompleteCheckNetworkIdle', {
922
932
  describe:
923
933
  'Alternative way to choose when to end your test that works in Chrome and Firefox. Uses CDP or WebDriver Bidi to look at network traffic instead of running JavaScript in the browser to know when to end the test. By default this will wait 5 seconds of inactivity in the network log (no requets/responses in 5 seconds). Use --timeouts.networkIdle to change the 5 seconds. The test will end after 2 minutes if there is still activity on the network. You can change that timout using --timeouts.pageCompleteCheck ',
924
934
  type: 'boolean',
925
- default: false
935
+ default: false,
936
+ group: 'PageLoad'
926
937
  })
927
938
  .option('pageCompleteCheckPollTimeout', {
928
939
  type: 'number',
929
940
  default: 1500,
930
941
  describe:
931
- 'The time in ms to wait for running the page complete check the next time.'
942
+ 'The time in ms to wait for running the page complete check the next time.',
943
+ group: 'PageLoad'
932
944
  })
933
945
  .option('pageCompleteCheckStartWait', {
934
946
  type: 'number',
935
947
  default: 5000,
936
948
  describe:
937
- 'The time in ms to wait for running the page complete check for the first time. Use this when you have a pageLoadStrategy set to none'
949
+ 'The time in ms to wait for running the page complete check for the first time. Use this when you have a pageLoadStrategy set to none',
950
+ group: 'PageLoad'
938
951
  })
939
952
  .option('pageLoadStrategy', {
940
953
  type: 'string',
941
954
  default: 'none',
942
955
  choices: ['eager', 'none', 'normal'],
943
956
  describe:
944
- 'Set the strategy to waiting for document readiness after a navigation event. After the strategy is ready, your pageCompleteCheck will start runninhg.'
957
+ 'Set the strategy to waiting for document readiness after a navigation event. After the strategy is ready, your pageCompleteCheck will start running.',
958
+ group: 'PageLoad'
945
959
  })
946
960
  .option('iterations', {
947
961
  alias: 'n',
@@ -965,13 +979,15 @@ export function parseCommandLine() {
965
979
  type: 'number',
966
980
  default: 0,
967
981
  describe:
968
- 'Extra time added for the browser to settle before starting to test a URL. This delay happens after the browser was opened and before the navigation to the URL'
982
+ 'Extra time added for the browser to settle before starting to test a URL. This delay happens after the browser was opened and before the navigation to the URL',
983
+ group: 'PageLoad'
969
984
  })
970
985
  .option('webdriverPageload', {
971
986
  type: 'boolean',
972
987
  describe:
973
988
  'Use webdriver.get to initialize the page load instead of window.location.',
974
- default: false
989
+ default: false,
990
+ group: 'PageLoad'
975
991
  })
976
992
  .option('proxy.pac', {
977
993
  type: 'string',
@@ -1106,7 +1122,8 @@ export function parseCommandLine() {
1106
1122
  describe:
1107
1123
  'Use internal browser functionality to clear browser cache between runs instead of only using Selenium.',
1108
1124
  type: 'boolean',
1109
- default: false
1125
+ default: false,
1126
+ group: 'PageLoad'
1110
1127
  })
1111
1128
  .option('basicAuth', {
1112
1129
  describe:
@@ -1211,9 +1228,9 @@ export function parseCommandLine() {
1211
1228
  type: 'integer',
1212
1229
  default: 1500
1213
1230
  })
1214
- .option('userTimingWhitelist', {
1231
+ .option('userTimingAllowList', {
1215
1232
  describe:
1216
- 'All userTimings are captured by default this option takes a regex that will whitelist which userTimings to capture in the results.'
1233
+ 'All userTimings are captured by default this option takes a regex that will allow which userTimings to capture in the results.'
1217
1234
  })
1218
1235
  .option('headless', {
1219
1236
  type: 'boolean',
@@ -1233,7 +1250,8 @@ export function parseCommandLine() {
1233
1250
  type: 'boolean',
1234
1251
  default: false,
1235
1252
  describe:
1236
- 'Flush DNS between runs, works on Mac OS and Linux. Your user needs sudo rights to be able to flush the DNS.'
1253
+ 'Flush DNS between runs, works on Mac OS and Linux. Your user needs sudo rights to be able to flush the DNS.',
1254
+ group: 'PageLoad'
1237
1255
  })
1238
1256
  .option('extension', {
1239
1257
  describe:
@@ -1243,7 +1261,8 @@ export function parseCommandLine() {
1243
1261
  describe:
1244
1262
  'Convenient parameter to use if you test a SPA application: will automatically wait for X seconds after last network activity and use hash in file names. Read more: https://www.sitespeed.io/documentation/sitespeed.io/spa/',
1245
1263
  type: 'boolean',
1246
- default: false
1264
+ default: false,
1265
+ group: 'PageLoad'
1247
1266
  })
1248
1267
  .option('cjs', {
1249
1268
  describe:
@@ -1255,7 +1274,8 @@ export function parseCommandLine() {
1255
1274
  type: 'number',
1256
1275
  default: 3,
1257
1276
  describe:
1258
- 'If the browser fails to start, you can retry to start it this amount of times.'
1277
+ 'If the browser fails to start, you can retry to start it this amount of times.',
1278
+ group: 'PageLoad'
1259
1279
  })
1260
1280
  .option('preWarmServer', {
1261
1281
  type: 'boolean',
@@ -1,12 +1,17 @@
1
1
  import { resolve, dirname, join } from 'node:path';
2
2
  import { promisify } from 'node:util';
3
- import { writeFile as _writeFile, unlink as _unlink } from 'node:fs';
3
+ import {
4
+ writeFile as _writeFile,
5
+ unlink as _unlink,
6
+ readFile as _readFile
7
+ } from 'node:fs';
4
8
  import { pathToFileURL } from 'node:url';
5
9
  import dayjs from 'dayjs';
6
10
  import intel from 'intel';
7
11
  import { toArray } from '../support/util.js';
8
12
  const log = intel.getLogger('browsertime');
9
13
  const writeFile = promisify(_writeFile);
14
+ const readFile = promisify(_readFile);
10
15
  const unlink = promisify(_unlink);
11
16
 
12
17
  const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor;
@@ -82,6 +87,13 @@ export async function loadPrePostScripts(scripts, options) {
82
87
  return readScripts;
83
88
  }
84
89
 
90
+ export async function loadPageCompleteScript(script) {
91
+ if (script && script.endsWith('js')) {
92
+ return readFile(resolve(script), 'utf8');
93
+ }
94
+ return script;
95
+ }
96
+
85
97
  export async function loadScript(script, options, throwError) {
86
98
  if (script) {
87
99
  return loadFile(script, options, throwError);
@@ -10,6 +10,10 @@ function validateType(value, type, message) {
10
10
  }
11
11
  }
12
12
 
13
+ function hasDecimals(num) {
14
+ return num !== Math.floor(num);
15
+ }
16
+
13
17
  function percentileName(percentile) {
14
18
  if (percentile === 0) {
15
19
  return 'min';
@@ -129,7 +133,7 @@ export class Statistics {
129
133
  // https://en.wikipedia.org/wiki/Interquartile_range
130
134
  stats = stats.iqr();
131
135
  }
132
- if (stats.median() < 1 && stats.median() > 0) {
136
+ if (stats.median() < 10 && stats.median() > 0) {
133
137
  decimals = 4;
134
138
  }
135
139
  const node = {
@@ -165,6 +169,12 @@ export class Statistics {
165
169
  }
166
170
  if (stats.median() < 1 && stats.median() > 0) {
167
171
  decimals = 4;
172
+ } else if (
173
+ hasDecimals(stats.median()) &&
174
+ stats.median() < 100 &&
175
+ stats.median() > 1
176
+ ) {
177
+ decimals = 2;
168
178
  }
169
179
  const results = {
170
180
  median: Number.parseFloat(stats.median().toFixed(decimals)),
@@ -1,5 +1,5 @@
1
- export function filterWhitelisted(userTimings, whitelistRegex) {
2
- const allowed = new RegExp(whitelistRegex);
1
+ export function filterAllowlisted(userTimings, allowlistRegex) {
2
+ const allowed = new RegExp(allowlistRegex);
3
3
  userTimings.marks = userTimings.marks.filter(mark => allowed.test(mark.name));
4
4
  userTimings.measures = userTimings.measures.filter(measure =>
5
5
  allowed.test(measure.name)
package/package.json CHANGED
@@ -1,24 +1,25 @@
1
1
  {
2
2
  "name": "browsertime",
3
3
  "description": "Get performance metrics from your web page using Browsertime.",
4
- "version": "19.3.1",
4
+ "version": "21.0.0",
5
5
  "bin": "./bin/browsertime.js",
6
6
  "type": "module",
7
+ "types": "./types/scripting.d.ts",
7
8
  "dependencies": {
8
9
  "@cypress/xvfb": "1.2.4",
9
10
  "@devicefarmer/adbkit": "3.2.5",
10
11
  "@sitespeed.io/chromedriver": "120.0.6099-71",
11
12
  "@sitespeed.io/edgedriver": "120.0.2210-77",
12
- "@sitespeed.io/geckodriver": "0.33.0-c",
13
+ "@sitespeed.io/geckodriver": "0.34.0",
13
14
  "@sitespeed.io/throttle": "5.0.0",
14
15
  "@sitespeed.io/tracium": "0.3.3",
15
- "ff-test-bidi-har-export": "0.0.12",
16
16
  "btoa": "1.2.1",
17
17
  "chrome-har": "0.13.2",
18
18
  "chrome-remote-interface": "0.33.0",
19
19
  "dayjs": "1.11.10",
20
20
  "execa": "8.0.1",
21
21
  "fast-stats": "0.0.6",
22
+ "ff-test-bidi-har-export": "0.0.12",
22
23
  "find-up": "7.0.0",
23
24
  "get-port": "7.0.0",
24
25
  "hasbin": "1.2.3",
@@ -36,14 +37,18 @@
36
37
  "jimp": "0.22.10"
37
38
  },
38
39
  "devDependencies": {
40
+ "@types/selenium-webdriver": "4.1.21",
39
41
  "ava": "6.0.1",
42
+ "clean-jsdoc-theme": "4.2.17",
40
43
  "eslint": "8.55.0",
41
44
  "eslint-config-prettier": "9.1.0",
42
45
  "eslint-plugin-prettier": "5.0.1",
43
46
  "eslint-plugin-unicorn": "49.0.0",
47
+ "jsdoc": "4.0.2",
44
48
  "prettier": "3.1.1",
49
+ "serve": "14.2.1",
45
50
  "serve-handler": "6.1.5",
46
- "serve": "14.2.1"
51
+ "typescript": "5.3.3"
47
52
  },
48
53
  "engines": {
49
54
  "node": ">=18.0.0"
@@ -56,13 +61,14 @@
56
61
  "bin",
57
62
  "browserscripts",
58
63
  "browsersupport",
59
- "visualmetrics",
64
+ "visualmetrics/visualmetrics-portable.py",
65
+ "visualmetrics/visualmetrics.py",
60
66
  "browsertime.png",
61
- "docs",
62
67
  "index.js",
63
68
  "lib",
64
69
  "package.json",
65
- "vendor"
70
+ "vendor",
71
+ "types"
66
72
  ],
67
73
  "exports": "./index.js",
68
74
  "scripts": {
@@ -70,7 +76,10 @@
70
76
  "start-server": "serve test/data/html/",
71
77
  "stop-server": "sudo pkill serve",
72
78
  "lint": "eslint .",
73
- "lint:fix": "eslint . --fix"
79
+ "lint:fix": "eslint . --fix",
80
+ "tsc": "tsc",
81
+ "jsdoc": "jsdoc --configure jsdoc/jsdoc.json",
82
+ "prepublishOnly": "npm run lint && npm run tsc"
74
83
  },
75
84
  "author": "Peter Hedenskog",
76
85
  "contributors": [
@@ -0,0 +1,70 @@
1
+ export function isAndroidConfigured(options: any): boolean;
2
+ export class Android {
3
+ constructor(options: any);
4
+ client: any;
5
+ id: any;
6
+ port: any;
7
+ screenBrightnessMode: number;
8
+ screenBrightness: number;
9
+ tmpDir: string;
10
+ _init(): Promise<void>;
11
+ device: any;
12
+ sdcard: any;
13
+ _runCommand(command: any): Promise<any>;
14
+ _runCommandAndGet(command: any): Promise<any>;
15
+ _runAsRootAndGet(command: any): Promise<any>;
16
+ _runAsRoot(command: any): Promise<boolean>;
17
+ _downloadFile(sourcePath: any, destinationPath: any): Promise<any>;
18
+ _downloadDir(sourcePath: any, destinationPath: any): Promise<void>;
19
+ getFullPathOnSdCard(path: any): string;
20
+ mkDirOnSdCard(dirName: any): Promise<any>;
21
+ removeFileOnSdCard(file: any): Promise<any>;
22
+ removePathOnSdCard(path: any): Promise<any>;
23
+ reboot(): Promise<any>;
24
+ getTemperature(): Promise<number>;
25
+ getMeta(): Promise<{
26
+ model: any;
27
+ name: any;
28
+ device: any;
29
+ androidVersion: any;
30
+ id: any;
31
+ wifi: any;
32
+ }>;
33
+ pullNetLog(destination: any): Promise<any>;
34
+ addDevtoolsFw(): Promise<any>;
35
+ removeDevtoolsFw(): Promise<({
36
+ all?: string;
37
+ isCanceled: boolean;
38
+ } & import("execa").ExecaReturnBase<string>)[]>;
39
+ startVideo(): Promise<any>;
40
+ ping(address: any): Promise<boolean>;
41
+ clickPowerButton(): Promise<any>;
42
+ getWifi(): Promise<any>;
43
+ closeAppNotRespondingPopup(): Promise<any>;
44
+ pressHomeButton(): Promise<any>;
45
+ stopVideo(): Promise<any>;
46
+ getPhoneState(): Promise<string>;
47
+ pullVideo(destinationPath: any): Promise<any>;
48
+ removeVideo(): Promise<any>;
49
+ pidof(packageName: any): Promise<any>;
50
+ _pidofWithPidof(packageName: any): Promise<number>;
51
+ _pidofWithPs(packageName: any): Promise<number>;
52
+ _processStartTime(pid: any): Promise<{
53
+ dateInMs: number;
54
+ systemUptimeInSeconds: any;
55
+ utilStartTimeAfterSystemStartTimeInJiffies: any;
56
+ processStartTimeAfterSystemStartTimeInJiffies: any;
57
+ jiffesPerSeconds: number;
58
+ processStartTimeInMs: number;
59
+ }>;
60
+ processStartTime(pid: any, count?: number): Promise<number>;
61
+ startPowerTesting(): Promise<void>;
62
+ stopPowerTesting(): Promise<void>;
63
+ resetPowerUsage(): Promise<void>;
64
+ measurePowerUsage(packageName: any): Promise<{
65
+ 'full-screen': number;
66
+ 'full-wifi': number;
67
+ total: number;
68
+ }>;
69
+ }
70
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../lib/android/index.js"],"names":[],"mappings":"AAiiBA,2DAWC;AA/hBD;IACE,0BAwBC;IAfC,YAAgC;IAEhC,QAIC;IACD,UAAgC;IAGhC,6BAA6B;IAC7B,yBAA2B;IAC3B,eAAgC;IAKlC,uBAgBC;IALC,YAA4C;IAG1C,YAAoE;IAIxE,wCAEC;IAED,8CAIC;IAED,6CAIC;IAED,2CAUC;IAED,mEAYC;IAED,mEAcC;IAED,uCAEC;IAED,0CAGC;IAED,4CAMC;IAED,4CAMC;IAED,uBAGC;IAED,kCAOC;IAED;;;;;;;OAuBC;IAED,2CAKC;IAED,8BAKC;IAED;;;oDAuBC;IAED,2BAIC;IAED,qCAGC;IAED,iCAGC;IAED,wBASC;IAED,2CAOC;IAED,gCAEC;IAED,0BAIC;IAED,iCAGC;IAED,8CAIC;IAED,4BAEC;IAED,sCAYC;IAED,mDAsBC;IAED,gDA+CC;IAED;;;;;;;OAqDC;IAED,4DAQC;IAED,mCAcC;IAED,kCAQC;IAED,iCAIC;IAED;;;;OAGC;CACF"}
@@ -0,0 +1,21 @@
1
+ export function parseCPUTrace(tracelog: any, url: any): Promise<{
2
+ categories: {
3
+ parseHTML: number;
4
+ styleLayout: number;
5
+ paintCompositeRender: number;
6
+ scriptParseCompile: number;
7
+ scriptEvaluation: number;
8
+ garbageCollection: number;
9
+ other: number;
10
+ };
11
+ events: {};
12
+ urls: {
13
+ url: string;
14
+ value: number;
15
+ }[];
16
+ } | {
17
+ categories?: undefined;
18
+ events?: undefined;
19
+ urls?: undefined;
20
+ }>;
21
+ //# sourceMappingURL=parseCpuTrace.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parseCpuTrace.d.ts","sourceRoot":"","sources":["../../lib/chrome/parseCpuTrace.js"],"names":[],"mappings":"AAaA;;;;;;;;;;;;;;;;;;;GA4EC"}
@@ -0,0 +1,2 @@
1
+ export function parse(events: any, url: any): any;
2
+ //# sourceMappingURL=traceCategoriesParser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"traceCategoriesParser.d.ts","sourceRoot":"","sources":["../../lib/chrome/traceCategoriesParser.js"],"names":[],"mappings":"AA2JA,kDAQC"}
@@ -0,0 +1,8 @@
1
+ export function getRenderBlocking(trace: any): Promise<{
2
+ renderBlockingInfo: {};
3
+ renderBlocking: {
4
+ recalculateStyle: {};
5
+ requests: {};
6
+ };
7
+ }>;
8
+ //# sourceMappingURL=traceUtilities.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"traceUtilities.d.ts","sourceRoot":"","sources":["../../../lib/chrome/webdriver/traceUtilities.js"],"names":[],"mappings":"AA8DA;;;;;;GAoCC"}
@@ -0,0 +1,73 @@
1
+ /**
2
+ * This class provides an abstraction layer for Selenium's action sequence functionality.
3
+ * It allows for easy interaction with web elements using different locating strategies
4
+ * and simulating complex user gestures like mouse movements, key presses, etc.
5
+ *
6
+ * @class
7
+ * @hideconstructor
8
+ * @see https://www.selenium.dev/documentation/webdriver/actions_api/
9
+ */
10
+ export class Action {
11
+ constructor(browser: any);
12
+ /**
13
+ * @private
14
+ */
15
+ private driver;
16
+ /**
17
+ * @private
18
+ */
19
+ private actions;
20
+ clear(): Promise<any>;
21
+ /**
22
+ * Retrieves the current action sequence builder.
23
+ * The actions builder can be used to chain multiple browser actions.
24
+ * @returns {SeleniumActions} The current Selenium Actions builder object for chaining browser actions.
25
+ * @example
26
+ * // Example of using the actions builder to perform a drag-and-drop operation:
27
+ * const elementToDrag = await commands.action.getElementByCss('.draggable');
28
+ * const dropTarget = await commands.action.getElementByCss('.drop-target');
29
+ * await commands.action.getAction()
30
+ * .dragAndDrop(elementToDrag, dropTarget)
31
+ * .perform();
32
+ *
33
+ */
34
+ getAction(): SeleniumActions;
35
+ /**
36
+ * Finds an element by its CSS selector.
37
+ *
38
+ * @param {string} name - The CSS selector of the element.
39
+ * @returns {Promise<WebElement>} A promise that resolves to the WebElement found.
40
+ */
41
+ getElementByCss(name: string): Promise<WebElement>;
42
+ /**
43
+ * Finds an element by its ID.
44
+ *
45
+ * @param {string} id - The ID of the element.
46
+ * @returns {Promise<WebElement>} A promise that resolves to the WebElement found.
47
+ */
48
+ getElementById(id: string): Promise<WebElement>;
49
+ /**
50
+ * Finds an element by its XPath.
51
+ *
52
+ * @param {string} xpath - The XPath query of the element.
53
+ * @returns {Promise<WebElement>} A promise that resolves to the WebElement found.
54
+ */
55
+ getElementByXpath(xpath: string): Promise<WebElement>;
56
+ /**
57
+ * Finds an element by its class name.
58
+ *
59
+ * @param {string} className - The class name of the element.
60
+ * @returns {Promise<WebElement>} A promise that resolves to the WebElement found.
61
+ */
62
+ getElementByClassName(className: string): Promise<WebElement>;
63
+ /**
64
+ * Finds an element by its name attribute.
65
+ *
66
+ * @param {string} name - The name attribute of the element.
67
+ * @returns {Promise<WebElement>} A promise that resolves to the WebElement found.
68
+ */
69
+ getElementByName(name: string): Promise<WebElement>;
70
+ }
71
+ import { Actions as SeleniumActions } from 'selenium-webdriver';
72
+ import { WebElement } from 'selenium-webdriver';
73
+ //# sourceMappingURL=action.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"action.d.ts","sourceRoot":"","sources":["../../../../lib/core/engine/command/action.js"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AACH;IACE,0BASC;IARC;;OAEG;IACH,eAAiC;IACjC;;OAEG;IACH,gBAAmD;IASrD,sBAEC;IAED;;;;;;;;;;;;OAYG;IACH,aAVa,eAAe,CAY3B;IAED;;;;;OAKG;IACH,sBAHW,MAAM,GACJ,QAAQ,UAAU,CAAC,CAI/B;IAED;;;;;OAKG;IACH,mBAHW,MAAM,GACJ,QAAQ,UAAU,CAAC,CAI/B;IAED;;;;;OAKG;IACH,yBAHW,MAAM,GACJ,QAAQ,UAAU,CAAC,CAI/B;IAED;;;;;OAKG;IACH,iCAHW,MAAM,GACJ,QAAQ,UAAU,CAAC,CAI/B;IAED;;;;;OAKG;IACH,uBAHW,MAAM,GACJ,QAAQ,UAAU,CAAC,CAI/B;CACF;2CAnG0D,oBAAoB;2BAApB,oBAAoB"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * This class provides an abstraction layer for Selenium's action sequence functionality.
3
+ * It allows for easy interaction with web elements using different locating strategies
4
+ * and simulating complex user gestures like mouse movements, key presses, etc.
5
+ *
6
+ * @class
7
+ * @hideconstructor
8
+ * @see https://www.selenium.dev/documentation/webdriver/actions_api/
9
+ * @see https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/lib/input_exports_Actions.html
10
+ */
11
+ export class Actions {
12
+ constructor(browser: any);
13
+ /**
14
+ * @private
15
+ */
16
+ private driver;
17
+ /**
18
+ * @private
19
+ */
20
+ private actions;
21
+ clear(): Promise<any>;
22
+ /**
23
+ * Retrieves the current action sequence builder.
24
+ * The actions builder can be used to chain multiple browser actions.
25
+ * @returns {SeleniumActions} The current Selenium Actions builder object for chaining browser actions.
26
+ * @example
27
+ * // Example of using the actions builder to perform a drag-and-drop operation:
28
+ * const elementToDrag = await commands.action.getElementByCss('.draggable');
29
+ * const dropTarget = await commands.action.getElementByCss('.drop-target');
30
+ * await commands.action.getAction()
31
+ * .dragAndDrop(elementToDrag, dropTarget)
32
+ * .perform();
33
+ *
34
+ */
35
+ getActions(): SeleniumActions;
36
+ }
37
+ import { Actions as SeleniumActions } from 'selenium-webdriver/lib/input.js';
38
+ //# sourceMappingURL=actions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../../../../lib/core/engine/command/actions.js"],"names":[],"mappings":"AAGA;;;;;;;;;GASG;AACH;IACE,0BASC;IARC;;OAEG;IACH,eAAiC;IACjC;;OAEG;IACH,gBAAmD;IASrD,sBAEC;IAED;;;;;;;;;;;;OAYG;IACH,cAVa,eAAe,CAY3B;CACF;2CAjD0C,iCAAiC"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Provides functionality to add text to elements on a web page using various selectors.
3
+ * @class
4
+ * @hideconstructor
5
+ */
6
+ export class AddText {
7
+ constructor(browser: any);
8
+ /**
9
+ * @private
10
+ */
11
+ private browser;
12
+ /**
13
+ * Adds text to an element identified by its ID.
14
+ *
15
+ * @async
16
+ * @example commands.addText.byId('mytext', 'id');
17
+ * @param {string} text - The text string to add.
18
+ * @param {string} id - The ID of the element.
19
+ * @returns {Promise<void>} A promise that resolves when the text has been added.
20
+ * @throws {Error} Throws an error if the element is not found.
21
+ */
22
+ byId(text: string, id: string): Promise<void>;
23
+ /**
24
+ * Adds text to an element identified by its XPath.
25
+ *
26
+ * @async
27
+ * @example commands.addText.byXpath('mytext', 'xpath');
28
+ * @param {string} text - The text string to add.
29
+ * @param {string} xpath - The XPath of the element.
30
+ * @returns {Promise<void>} A promise that resolves when the text has been added.
31
+ * @throws {Error} Throws an error if the element is not found.
32
+ */
33
+ byXpath(text: string, xpath: string): Promise<void>;
34
+ /**
35
+ * Adds text to an element identified by its CSS selector.
36
+ *
37
+ * @async
38
+ * @example commands.addText.bySelector('mytext', 'selector');
39
+ * @param {string} text - The text string to add.
40
+ * @param {string} selector - The CSS selector of the element.
41
+ * @returns {Promise<void>} A promise that resolves when the text has been added.
42
+ * @throws {Error} Throws an error if the element is not found.
43
+ */
44
+ bySelector(text: string, selector: string): Promise<void>;
45
+ /**
46
+ * Adds text to an element identified by its class name.
47
+ *
48
+ * @async
49
+ * @example commands.addText.byClassName('mytext', 'className');
50
+ * @param {string} text - The text string to add.
51
+ * @param {string} className - The class name of the element.
52
+ * @returns {Promise<void>} A promise that resolves when the text has been added.
53
+ * @throws {Error} Throws an error if the element is not found.
54
+ */
55
+ byClassName(text: string, className: string): Promise<void>;
56
+ /**
57
+ * Adds text to an element identified by its name attribute.
58
+ *
59
+ * @async
60
+ * @example commands.addText.byName('mytext', 'name');
61
+ * @param {string} text - The text string to add.
62
+ * @param {string} name - The name attribute of the element.
63
+ * @returns {Promise<void>} A promise that resolves when the text has been added.
64
+ * @throws {Error} Throws an error if the element is not found.
65
+ */
66
+ byName(text: string, name: string): Promise<void>;
67
+ }
68
+ //# sourceMappingURL=addText.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"addText.d.ts","sourceRoot":"","sources":["../../../../lib/core/engine/command/addText.js"],"names":[],"mappings":"AAIA;;;;GAIG;AACH;IACE,0BAKC;IAJC;;OAEG;IACH,gBAAsB;IAGxB;;;;;;;;;OASG;IACH,WALW,MAAM,MACN,MAAM,GACJ,QAAQ,IAAI,CAAC,CAezB;IAED;;;;;;;;;OASG;IACH,cALW,MAAM,SACN,MAAM,GACJ,QAAQ,IAAI,CAAC,CAezB;IAED;;;;;;;;;OASG;IACH,iBALW,MAAM,YACN,MAAM,GACJ,QAAQ,IAAI,CAAC,CAezB;IAED;;;;;;;;;OASG;IACH,kBALW,MAAM,aACN,MAAM,GACJ,QAAQ,IAAI,CAAC,CAezB;IAED;;;;;;;;;OASG;IACH,aALW,MAAM,QACN,MAAM,GACJ,QAAQ,IAAI,CAAC,CAezB;CACF"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Provides functionality to interact with an Android device through shell commands.
3
+ * @class
4
+ * @see https://www.sitespeed.io/documentation/sitespeed.io/mobile-phones/#test-on-android
5
+ * @hideconstructor
6
+ */
7
+ export class AndroidCommand {
8
+ constructor(options: any);
9
+ /**
10
+ * @private
11
+ */
12
+ private options;
13
+ /**
14
+ * Runs a shell command on the connected Android device.
15
+ * This method requires the Android device to be properly configured.
16
+ *
17
+ * @async
18
+ * @example await commands.android.shell('');
19
+ * @param {string} command - The shell command to run on the Android device.
20
+ * @returns {Promise<string>} A promise that resolves with the result of the command or rejects if there's an error.
21
+ * @throws {Error} Throws an error if Android is not configured or if the command fails.
22
+ */
23
+ shell(command: string): Promise<string>;
24
+ a: Android;
25
+ /**
26
+ * Runs a shell command on the connected Android device as the root user.
27
+ * This method requires the Android device to be properly configured and that you
28
+ * rooted the device.
29
+ *
30
+ * @async
31
+ * @example await commands.android.shellAsRoot('');
32
+ * @param {string} command - The shell command to run on the Android device as root.
33
+ * @returns {Promise<string>} A promise that resolves with the result of the command or rejects if there's an error.
34
+ * @throws {Error} Throws an error if Android is not configured or if the command fails.
35
+ */
36
+ shellAsRoot(command: string): Promise<string>;
37
+ }
38
+ import { Android } from '../../../android/index.js';
39
+ //# sourceMappingURL=android.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"android.d.ts","sourceRoot":"","sources":["../../../../lib/core/engine/command/android.js"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH;IACE,0BAKC;IAJC;;OAEG;IACH,gBAAsB;IAGxB;;;;;;;;;OASG;IACH,eAJW,MAAM,GACJ,QAAQ,MAAM,CAAC,CAc3B;IAPK,WAAkC;IASxC;;;;;;;;;;OAUG;IACH,qBAJW,MAAM,GACJ,QAAQ,MAAM,CAAC,CAgB3B;CACF;wBAhE4C,2BAA2B"}