chrome-devtools-mcp 0.17.2 → 0.18.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 (265) hide show
  1. package/README.md +27 -2
  2. package/build/src/DevToolsConnectionAdapter.js +69 -0
  3. package/build/src/DevtoolsUtils.js +340 -0
  4. package/build/src/McpContext.js +703 -0
  5. package/build/src/McpResponse.js +491 -0
  6. package/build/src/Mutex.js +37 -0
  7. package/build/src/PageCollector.js +309 -0
  8. package/build/src/SlimMcpResponse.js +18 -0
  9. package/build/src/WaitForHelper.js +139 -0
  10. package/build/src/browser.js +202 -0
  11. package/build/src/cli.js +307 -0
  12. package/build/src/daemon/daemon.js +167 -0
  13. package/build/src/daemon/utils.js +67 -0
  14. package/build/src/formatters/ConsoleFormatter.js +241 -0
  15. package/build/src/formatters/IssueFormatter.js +192 -0
  16. package/build/src/formatters/NetworkFormatter.js +218 -0
  17. package/build/src/formatters/SnapshotFormatter.js +134 -0
  18. package/build/src/index.js +21 -0
  19. package/build/src/issue-descriptions.js +39 -0
  20. package/build/src/logger.js +36 -0
  21. package/build/src/main.js +201 -0
  22. package/build/src/polyfill.js +7 -0
  23. package/build/src/telemetry/ClearcutLogger.js +102 -0
  24. package/build/src/telemetry/WatchdogClient.js +60 -0
  25. package/build/src/telemetry/flagUtils.js +45 -0
  26. package/build/src/telemetry/metricUtils.js +14 -0
  27. package/build/src/telemetry/persistence.js +53 -0
  28. package/build/src/telemetry/types.js +33 -0
  29. package/build/src/telemetry/watchdog/ClearcutSender.js +201 -0
  30. package/build/src/telemetry/watchdog/main.js +127 -0
  31. package/build/src/third_party/THIRD_PARTY_NOTICES +2011 -0
  32. package/build/src/third_party/bundled-packages.json +8 -0
  33. package/build/src/third_party/devtools-formatter-worker.js +15449 -0
  34. package/build/src/third_party/index.js +172870 -0
  35. package/build/src/third_party/issue-descriptions/CoepCoopSandboxedIframeCannotNavigateToCoopPage.md +4 -0
  36. package/build/src/third_party/issue-descriptions/CoepCorpNotSameOrigin.md +8 -0
  37. package/build/src/third_party/issue-descriptions/CoepCorpNotSameOriginAfterDefaultedToSameOriginByCoep.md +18 -0
  38. package/build/src/third_party/issue-descriptions/CoepCorpNotSameSite.md +7 -0
  39. package/build/src/third_party/issue-descriptions/CoepFrameResourceNeedsCoepHeader.md +10 -0
  40. package/build/src/third_party/issue-descriptions/CompatibilityModeQuirks.md +5 -0
  41. package/build/src/third_party/issue-descriptions/CookieAttributeValueExceedsMaxSize.md +5 -0
  42. package/build/src/third_party/issue-descriptions/LowTextContrast.md +5 -0
  43. package/build/src/third_party/issue-descriptions/SameSiteExcludeContextDowngradeRead.md +8 -0
  44. package/build/src/third_party/issue-descriptions/SameSiteExcludeContextDowngradeSet.md +8 -0
  45. package/build/src/third_party/issue-descriptions/SameSiteExcludeNavigationContextDowngrade.md +8 -0
  46. package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureErrorRead.md +8 -0
  47. package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureErrorSet.md +8 -0
  48. package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureWarnRead.md +8 -0
  49. package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureWarnSet.md +8 -0
  50. package/build/src/third_party/issue-descriptions/SameSiteUnspecifiedLaxAllowUnsafeRead.md +9 -0
  51. package/build/src/third_party/issue-descriptions/SameSiteUnspecifiedLaxAllowUnsafeSet.md +9 -0
  52. package/build/src/third_party/issue-descriptions/SameSiteWarnCrossDowngradeRead.md +8 -0
  53. package/build/src/third_party/issue-descriptions/SameSiteWarnCrossDowngradeSet.md +8 -0
  54. package/build/src/third_party/issue-descriptions/SameSiteWarnStrictLaxDowngradeStrict.md +8 -0
  55. package/build/src/third_party/issue-descriptions/arInsecureContext.md +7 -0
  56. package/build/src/third_party/issue-descriptions/arInvalidInfoHeader.md +5 -0
  57. package/build/src/third_party/issue-descriptions/arInvalidRegisterOsSourceHeader.md +5 -0
  58. package/build/src/third_party/issue-descriptions/arInvalidRegisterOsTriggerHeader.md +5 -0
  59. package/build/src/third_party/issue-descriptions/arInvalidRegisterSourceHeader.md +5 -0
  60. package/build/src/third_party/issue-descriptions/arInvalidRegisterTriggerHeader.md +5 -0
  61. package/build/src/third_party/issue-descriptions/arNavigationRegistrationUniqueScopeAlreadySet.md +5 -0
  62. package/build/src/third_party/issue-descriptions/arNavigationRegistrationWithoutTransientUserActivation.md +6 -0
  63. package/build/src/third_party/issue-descriptions/arNoRegisterOsSourceHeader.md +5 -0
  64. package/build/src/third_party/issue-descriptions/arNoRegisterOsTriggerHeader.md +5 -0
  65. package/build/src/third_party/issue-descriptions/arNoRegisterSourceHeader.md +5 -0
  66. package/build/src/third_party/issue-descriptions/arNoRegisterTriggerHeader.md +5 -0
  67. package/build/src/third_party/issue-descriptions/arNoWebOrOsSupport.md +4 -0
  68. package/build/src/third_party/issue-descriptions/arOsSourceIgnored.md +18 -0
  69. package/build/src/third_party/issue-descriptions/arOsTriggerIgnored.md +19 -0
  70. package/build/src/third_party/issue-descriptions/arPermissionPolicyDisabled.md +8 -0
  71. package/build/src/third_party/issue-descriptions/arSourceAndTriggerHeaders.md +9 -0
  72. package/build/src/third_party/issue-descriptions/arSourceIgnored.md +13 -0
  73. package/build/src/third_party/issue-descriptions/arTriggerIgnored.md +12 -0
  74. package/build/src/third_party/issue-descriptions/arUntrustworthyReportingOrigin.md +10 -0
  75. package/build/src/third_party/issue-descriptions/arWebAndOsHeaders.md +11 -0
  76. package/build/src/third_party/issue-descriptions/bounceTrackingMitigations.md +3 -0
  77. package/build/src/third_party/issue-descriptions/clientHintMetaTagAllowListInvalidOrigin.md +4 -0
  78. package/build/src/third_party/issue-descriptions/clientHintMetaTagModifiedHTML.md +4 -0
  79. package/build/src/third_party/issue-descriptions/connectionAllowlistInvalidAllowlistItemType.md +12 -0
  80. package/build/src/third_party/issue-descriptions/connectionAllowlistInvalidHeader.md +12 -0
  81. package/build/src/third_party/issue-descriptions/connectionAllowlistInvalidUrlPattern.md +8 -0
  82. package/build/src/third_party/issue-descriptions/connectionAllowlistItemNotInnerList.md +12 -0
  83. package/build/src/third_party/issue-descriptions/connectionAllowlistMoreThanOneList.md +7 -0
  84. package/build/src/third_party/issue-descriptions/connectionAllowlistReportingEndpointNotToken.md +10 -0
  85. package/build/src/third_party/issue-descriptions/cookieCrossSiteRedirectDowngrade.md +12 -0
  86. package/build/src/third_party/issue-descriptions/cookieExcludeBlockedWithinRelatedWebsiteSet.md +4 -0
  87. package/build/src/third_party/issue-descriptions/cookieExcludeDomainNonAscii.md +11 -0
  88. package/build/src/third_party/issue-descriptions/cookieExcludePortMismatch.md +8 -0
  89. package/build/src/third_party/issue-descriptions/cookieExcludeSchemeMismatch.md +7 -0
  90. package/build/src/third_party/issue-descriptions/cookieExcludeThirdPartyPhaseoutRead.md +6 -0
  91. package/build/src/third_party/issue-descriptions/cookieExcludeThirdPartyPhaseoutSet.md +6 -0
  92. package/build/src/third_party/issue-descriptions/cookieWarnDomainNonAscii.md +11 -0
  93. package/build/src/third_party/issue-descriptions/cookieWarnMetadataGrantRead.md +4 -0
  94. package/build/src/third_party/issue-descriptions/cookieWarnMetadataGrantSet.md +4 -0
  95. package/build/src/third_party/issue-descriptions/cookieWarnThirdPartyPhaseoutRead.md +6 -0
  96. package/build/src/third_party/issue-descriptions/cookieWarnThirdPartyPhaseoutSet.md +6 -0
  97. package/build/src/third_party/issue-descriptions/corsAllowCredentialsRequired.md +6 -0
  98. package/build/src/third_party/issue-descriptions/corsDisabledScheme.md +7 -0
  99. package/build/src/third_party/issue-descriptions/corsDisallowedByMode.md +7 -0
  100. package/build/src/third_party/issue-descriptions/corsHeaderDisallowedByPreflightResponse.md +5 -0
  101. package/build/src/third_party/issue-descriptions/corsInvalidHeaderValues.md +7 -0
  102. package/build/src/third_party/issue-descriptions/corsLocalNetworkAccessPermissionDenied.md +19 -0
  103. package/build/src/third_party/issue-descriptions/corsMethodDisallowedByPreflightResponse.md +5 -0
  104. package/build/src/third_party/issue-descriptions/corsNoCorsRedirectModeNotFollow.md +5 -0
  105. package/build/src/third_party/issue-descriptions/corsOriginMismatch.md +6 -0
  106. package/build/src/third_party/issue-descriptions/corsPreflightResponseInvalid.md +5 -0
  107. package/build/src/third_party/issue-descriptions/corsRedirectContainsCredentials.md +5 -0
  108. package/build/src/third_party/issue-descriptions/corsWildcardOriginNotAllowed.md +8 -0
  109. package/build/src/third_party/issue-descriptions/cspEvalViolation.md +9 -0
  110. package/build/src/third_party/issue-descriptions/cspInlineViolation.md +10 -0
  111. package/build/src/third_party/issue-descriptions/cspTrustedTypesPolicyViolation.md +5 -0
  112. package/build/src/third_party/issue-descriptions/cspTrustedTypesSinkViolation.md +8 -0
  113. package/build/src/third_party/issue-descriptions/cspURLViolation.md +10 -0
  114. package/build/src/third_party/issue-descriptions/deprecation.md +3 -0
  115. package/build/src/third_party/issue-descriptions/federatedAuthRequestAccountsHttpNotFound.md +1 -0
  116. package/build/src/third_party/issue-descriptions/federatedAuthRequestAccountsInvalidResponse.md +1 -0
  117. package/build/src/third_party/issue-descriptions/federatedAuthRequestAccountsNoResponse.md +1 -0
  118. package/build/src/third_party/issue-descriptions/federatedAuthRequestApprovalDeclined.md +1 -0
  119. package/build/src/third_party/issue-descriptions/federatedAuthRequestCanceled.md +1 -0
  120. package/build/src/third_party/issue-descriptions/federatedAuthRequestErrorFetchingSignin.md +1 -0
  121. package/build/src/third_party/issue-descriptions/federatedAuthRequestErrorIdToken.md +1 -0
  122. package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenHttpNotFound.md +1 -0
  123. package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenInvalidRequest.md +1 -0
  124. package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenInvalidResponse.md +1 -0
  125. package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenNoResponse.md +1 -0
  126. package/build/src/third_party/issue-descriptions/federatedAuthRequestInvalidSigninResponse.md +1 -0
  127. package/build/src/third_party/issue-descriptions/federatedAuthRequestManifestHttpNotFound.md +1 -0
  128. package/build/src/third_party/issue-descriptions/federatedAuthRequestManifestInvalidResponse.md +1 -0
  129. package/build/src/third_party/issue-descriptions/federatedAuthRequestManifestNoResponse.md +1 -0
  130. package/build/src/third_party/issue-descriptions/federatedAuthRequestTooManyRequests.md +1 -0
  131. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestInvalidAccountsResponse.md +1 -0
  132. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestInvalidConfigOrWellKnown.md +1 -0
  133. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNoAccountSharingPermission.md +1 -0
  134. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNoApiPermission.md +1 -0
  135. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNoReturningUserFromFetchedAccounts.md +1 -0
  136. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotIframe.md +1 -0
  137. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotPotentiallyTrustworthy.md +1 -0
  138. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotSameOrigin.md +1 -0
  139. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotSignedInWithIdp.md +1 -0
  140. package/build/src/third_party/issue-descriptions/fetchingPartitionedBlobURL.md +7 -0
  141. package/build/src/third_party/issue-descriptions/genericFormAriaLabelledByToNonExistingIdError.md +8 -0
  142. package/build/src/third_party/issue-descriptions/genericFormAutocompleteAttributeEmptyError.md +5 -0
  143. package/build/src/third_party/issue-descriptions/genericFormDuplicateIdForInputError.md +5 -0
  144. package/build/src/third_party/issue-descriptions/genericFormEmptyIdAndNameAttributesForInputError.md +5 -0
  145. package/build/src/third_party/issue-descriptions/genericFormInputAssignedAutocompleteValueToIdOrNameAttributeError.md +5 -0
  146. package/build/src/third_party/issue-descriptions/genericFormInputHasWrongButWellIntendedAutocompleteValueError.md +5 -0
  147. package/build/src/third_party/issue-descriptions/genericFormInputWithNoLabelError.md +5 -0
  148. package/build/src/third_party/issue-descriptions/genericFormLabelForMatchesNonExistingIdError.md +5 -0
  149. package/build/src/third_party/issue-descriptions/genericFormLabelForNameError.md +5 -0
  150. package/build/src/third_party/issue-descriptions/genericFormLabelHasNeitherForNorNestedInputError.md +5 -0
  151. package/build/src/third_party/issue-descriptions/genericNavigationEntryMarkedSkippable.md +7 -0
  152. package/build/src/third_party/issue-descriptions/genericResponseWasBlockedByORB.md +4 -0
  153. package/build/src/third_party/issue-descriptions/heavyAd.md +10 -0
  154. package/build/src/third_party/issue-descriptions/mixedContent.md +5 -0
  155. package/build/src/third_party/issue-descriptions/navigatingPartitionedBlobURL.md +5 -0
  156. package/build/src/third_party/issue-descriptions/permissionElementActivationDisabled.md +7 -0
  157. package/build/src/third_party/issue-descriptions/permissionElementActivationDisabledWithOccluder.md +9 -0
  158. package/build/src/third_party/issue-descriptions/permissionElementActivationDisabledWithOccluderParent.md +9 -0
  159. package/build/src/third_party/issue-descriptions/permissionElementCspFrameAncestorsMissing.md +5 -0
  160. package/build/src/third_party/issue-descriptions/permissionElementFencedFrameDisallowed.md +5 -0
  161. package/build/src/third_party/issue-descriptions/permissionElementFontSizeTooLarge.md +5 -0
  162. package/build/src/third_party/issue-descriptions/permissionElementFontSizeTooSmall.md +5 -0
  163. package/build/src/third_party/issue-descriptions/permissionElementGeolocationDeprecated.md +5 -0
  164. package/build/src/third_party/issue-descriptions/permissionElementInsetBoxShadowUnsupported.md +5 -0
  165. package/build/src/third_party/issue-descriptions/permissionElementInvalidDisplayStyle.md +5 -0
  166. package/build/src/third_party/issue-descriptions/permissionElementInvalidSizeValue.md +5 -0
  167. package/build/src/third_party/issue-descriptions/permissionElementInvalidType.md +5 -0
  168. package/build/src/third_party/issue-descriptions/permissionElementInvalidTypeActivation.md +5 -0
  169. package/build/src/third_party/issue-descriptions/permissionElementLowContrast.md +5 -0
  170. package/build/src/third_party/issue-descriptions/permissionElementNonOpaqueColor.md +5 -0
  171. package/build/src/third_party/issue-descriptions/permissionElementPaddingBottomUnsupported.md +6 -0
  172. package/build/src/third_party/issue-descriptions/permissionElementPaddingRightUnsupported.md +6 -0
  173. package/build/src/third_party/issue-descriptions/permissionElementPermissionsPolicyBlocked.md +5 -0
  174. package/build/src/third_party/issue-descriptions/permissionElementRegistrationFailed.md +5 -0
  175. package/build/src/third_party/issue-descriptions/permissionElementRequestInProgress.md +5 -0
  176. package/build/src/third_party/issue-descriptions/permissionElementSecurityChecksFailed.md +5 -0
  177. package/build/src/third_party/issue-descriptions/permissionElementTypeNotSupported.md +5 -0
  178. package/build/src/third_party/issue-descriptions/permissionElementUntrustedEvent.md +7 -0
  179. package/build/src/third_party/issue-descriptions/placeholderDescriptionForInvisibleIssues.md +3 -0
  180. package/build/src/third_party/issue-descriptions/propertyRuleInvalidNameIssue.md +3 -0
  181. package/build/src/third_party/issue-descriptions/propertyRuleIssue.md +7 -0
  182. package/build/src/third_party/issue-descriptions/selectElementAccessibilityDisallowedOptGroupChild.md +7 -0
  183. package/build/src/third_party/issue-descriptions/selectElementAccessibilityDisallowedSelectChild.md +7 -0
  184. package/build/src/third_party/issue-descriptions/selectElementAccessibilityInteractiveContentAttributesSelectDescendant.md +3 -0
  185. package/build/src/third_party/issue-descriptions/selectElementAccessibilityInteractiveContentLegendChild.md +3 -0
  186. package/build/src/third_party/issue-descriptions/selectElementAccessibilityInteractiveContentOptionChild.md +3 -0
  187. package/build/src/third_party/issue-descriptions/selectElementAccessibilityNonPhrasingContentOptionChild.md +3 -0
  188. package/build/src/third_party/issue-descriptions/sharedArrayBuffer.md +7 -0
  189. package/build/src/third_party/issue-descriptions/sharedDictionaryUseErrorCrossOriginNoCorsRequest.md +1 -0
  190. package/build/src/third_party/issue-descriptions/sharedDictionaryUseErrorDictionaryLoadFailure.md +3 -0
  191. package/build/src/third_party/issue-descriptions/sharedDictionaryUseErrorMatchingDictionaryNotUsed.md +3 -0
  192. package/build/src/third_party/issue-descriptions/sharedDictionaryUseErrorUnexpectedContentDictionaryHeader.md +1 -0
  193. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorCossOriginNoCorsRequest.md +1 -0
  194. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorDisallowedBySettings.md +1 -0
  195. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorExpiredResponse.md +3 -0
  196. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorFeatureDisabled.md +3 -0
  197. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorInsufficientResources.md +1 -0
  198. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorInvalidMatchField.md +1 -0
  199. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorInvalidStructuredHeader.md +1 -0
  200. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorInvalidTTLField.md +1 -0
  201. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNavigationRequest.md +3 -0
  202. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNoMatchField.md +1 -0
  203. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonIntegerTTLField.md +1 -0
  204. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonListMatchDestField.md +1 -0
  205. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonSecureContext.md +3 -0
  206. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonStringIdField.md +1 -0
  207. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonStringInMatchDestList.md +1 -0
  208. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonStringMatchField.md +1 -0
  209. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonTokenTypeField.md +1 -0
  210. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorRequestAborted.md +1 -0
  211. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorShuttingDown.md +1 -0
  212. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorTooLongIdField.md +3 -0
  213. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorUnsupportedType.md +3 -0
  214. package/build/src/third_party/issue-descriptions/sriInvalidSignatureHeader.md +14 -0
  215. package/build/src/third_party/issue-descriptions/sriInvalidSignatureInputHeader.md +15 -0
  216. package/build/src/third_party/issue-descriptions/sriMissingSignatureHeader.md +8 -0
  217. package/build/src/third_party/issue-descriptions/sriMissingSignatureInputHeader.md +7 -0
  218. package/build/src/third_party/issue-descriptions/sriSignatureHeaderValueIsIncorrectLength.md +11 -0
  219. package/build/src/third_party/issue-descriptions/sriSignatureHeaderValueIsNotByteSequence.md +14 -0
  220. package/build/src/third_party/issue-descriptions/sriSignatureHeaderValueIsParameterized.md +15 -0
  221. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidComponentName.md +8 -0
  222. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidComponentType.md +13 -0
  223. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidDerivedComponentParameter.md +4 -0
  224. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidHeaderComponentParameter.md +5 -0
  225. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidParameter.md +11 -0
  226. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderKeyIdLength.md +12 -0
  227. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderMissingLabel.md +6 -0
  228. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderMissingRequiredParameters.md +8 -0
  229. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderValueMissingComponents.md +11 -0
  230. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderValueNotInnerList.md +11 -0
  231. package/build/src/third_party/issue-descriptions/sriValidationFailedIntegrityMismatch.md +10 -0
  232. package/build/src/third_party/issue-descriptions/sriValidationFailedInvalidLength.md +5 -0
  233. package/build/src/third_party/issue-descriptions/sriValidationFailedSignatureExpired.md +6 -0
  234. package/build/src/third_party/issue-descriptions/sriValidationFailedSignatureMismatch.md +11 -0
  235. package/build/src/third_party/issue-descriptions/stylesheetLateImport.md +4 -0
  236. package/build/src/third_party/issue-descriptions/stylesheetRequestFailed.md +3 -0
  237. package/build/src/third_party/issue-descriptions/summaryElementAccessibilityInteractiveContentSummaryDescendant.md +3 -0
  238. package/build/src/third_party/issue-descriptions/unencodedDigestIncorrectDigestLength.md +12 -0
  239. package/build/src/third_party/issue-descriptions/unencodedDigestIncorrectDigestType.md +17 -0
  240. package/build/src/third_party/issue-descriptions/unencodedDigestMalformedDictionary.md +14 -0
  241. package/build/src/third_party/issue-descriptions/unencodedDigestUnknownAlgorithm.md +15 -0
  242. package/build/src/tools/ToolDefinition.js +20 -0
  243. package/build/src/tools/categories.js +24 -0
  244. package/build/src/tools/console.js +85 -0
  245. package/build/src/tools/emulation.js +87 -0
  246. package/build/src/tools/extensions.js +79 -0
  247. package/build/src/tools/input.js +374 -0
  248. package/build/src/tools/memory.js +29 -0
  249. package/build/src/tools/network.js +120 -0
  250. package/build/src/tools/pages.js +329 -0
  251. package/build/src/tools/performance.js +188 -0
  252. package/build/src/tools/screencast.js +79 -0
  253. package/build/src/tools/screenshot.js +84 -0
  254. package/build/src/tools/script.js +71 -0
  255. package/build/src/tools/slim/tools.js +81 -0
  256. package/build/src/tools/snapshot.js +55 -0
  257. package/build/src/tools/tools.js +50 -0
  258. package/build/src/trace-processing/parse.js +84 -0
  259. package/build/src/utils/ExtensionRegistry.js +35 -0
  260. package/build/src/utils/keyboard.js +296 -0
  261. package/build/src/utils/pagination.js +49 -0
  262. package/build/src/utils/string.js +36 -0
  263. package/build/src/utils/types.js +6 -0
  264. package/build/src/version.js +9 -0
  265. package/package.json +7 -7
@@ -0,0 +1,71 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import { zod } from '../third_party/index.js';
7
+ import { ToolCategory } from './categories.js';
8
+ import { defineTool } from './ToolDefinition.js';
9
+ export const evaluateScript = defineTool({
10
+ name: 'evaluate_script',
11
+ description: `Evaluate a JavaScript function inside the currently selected page. Returns the response as JSON,
12
+ so returned values have to be JSON-serializable.`,
13
+ annotations: {
14
+ category: ToolCategory.DEBUGGING,
15
+ readOnlyHint: false,
16
+ },
17
+ schema: {
18
+ function: zod.string().describe(`A JavaScript function declaration to be executed by the tool in the currently selected page.
19
+ Example without arguments: \`() => {
20
+ return document.title
21
+ }\` or \`async () => {
22
+ return await fetch("example.com")
23
+ }\`.
24
+ Example with arguments: \`(el) => {
25
+ return el.innerText;
26
+ }\`
27
+ `),
28
+ args: zod
29
+ .array(zod.object({
30
+ uid: zod
31
+ .string()
32
+ .describe('The uid of an element on the page from the page content snapshot'),
33
+ }))
34
+ .optional()
35
+ .describe(`An optional list of arguments to pass to the function.`),
36
+ },
37
+ handler: async (request, response, context) => {
38
+ const args = [];
39
+ try {
40
+ const frames = new Set();
41
+ for (const el of request.params.args ?? []) {
42
+ const handle = await context.getElementByUid(el.uid);
43
+ frames.add(handle.frame);
44
+ args.push(handle);
45
+ }
46
+ let pageOrFrame;
47
+ // We can't evaluate the element handle across frames
48
+ if (frames.size > 1) {
49
+ throw new Error("Elements from different frames can't be evaluated together.");
50
+ }
51
+ else {
52
+ pageOrFrame = [...frames.values()][0] ?? context.getSelectedPage();
53
+ }
54
+ const fn = await pageOrFrame.evaluateHandle(`(${request.params.function})`);
55
+ args.unshift(fn);
56
+ await context.waitForEventsAfterAction(async () => {
57
+ const result = await pageOrFrame.evaluate(async (fn, ...args) => {
58
+ // @ts-expect-error no types.
59
+ return JSON.stringify(await fn(...args));
60
+ }, ...args);
61
+ response.appendResponseLine('Script ran on page and returned:');
62
+ response.appendResponseLine('```json');
63
+ response.appendResponseLine(`${result}`);
64
+ response.appendResponseLine('```');
65
+ });
66
+ }
67
+ finally {
68
+ void Promise.allSettled(args.map(arg => arg.dispose()));
69
+ }
70
+ },
71
+ });
@@ -0,0 +1,81 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import { zod } from '../../third_party/index.js';
7
+ import { ToolCategory } from '../categories.js';
8
+ import { defineTool } from '../ToolDefinition.js';
9
+ export const screenshot = defineTool({
10
+ name: 'screenshot',
11
+ description: `Takes a screenshot`,
12
+ annotations: {
13
+ category: ToolCategory.DEBUGGING,
14
+ // Not read-only due to filePath param.
15
+ readOnlyHint: false,
16
+ },
17
+ schema: {},
18
+ handler: async (request, response, context) => {
19
+ const page = context.getSelectedPage();
20
+ const screenshot = await page.screenshot({
21
+ type: 'png',
22
+ optimizeForSpeed: true,
23
+ });
24
+ const { filename } = await context.saveTemporaryFile(screenshot, `image/png`);
25
+ response.appendResponseLine(filename);
26
+ },
27
+ });
28
+ export const navigate = defineTool({
29
+ name: 'navigate',
30
+ description: `Loads a URL`,
31
+ annotations: {
32
+ category: ToolCategory.NAVIGATION,
33
+ readOnlyHint: false,
34
+ },
35
+ schema: {
36
+ url: zod.string().describe('URL to navigate to'),
37
+ },
38
+ handler: async (request, response, context) => {
39
+ const page = context.getSelectedPage();
40
+ const options = {
41
+ timeout: 30_000,
42
+ };
43
+ const dialogHandler = (dialog) => {
44
+ if (dialog.type() === 'beforeunload') {
45
+ response.appendResponseLine(`Accepted a beforeunload dialog.`);
46
+ void dialog.accept();
47
+ // We are not going to report the dialog like regular dialogs.
48
+ context.clearDialog();
49
+ }
50
+ };
51
+ page.on('dialog', dialogHandler);
52
+ try {
53
+ await page.goto(request.params.url, options);
54
+ response.appendResponseLine(`Navigated to ${page.url()}.`);
55
+ }
56
+ finally {
57
+ page.off('dialog', dialogHandler);
58
+ }
59
+ },
60
+ });
61
+ export const evaluate = defineTool({
62
+ name: 'evaluate',
63
+ description: `Evaluates a JavaScript script`,
64
+ annotations: {
65
+ category: ToolCategory.DEBUGGING,
66
+ readOnlyHint: false,
67
+ },
68
+ schema: {
69
+ script: zod.string().describe(`JS script to run on the page`),
70
+ },
71
+ handler: async (request, response, context) => {
72
+ const page = context.getSelectedPage();
73
+ try {
74
+ const result = await page.evaluate(request.params.script);
75
+ response.appendResponseLine(JSON.stringify(result));
76
+ }
77
+ catch (err) {
78
+ response.appendResponseLine(String(err.message));
79
+ }
80
+ },
81
+ });
@@ -0,0 +1,55 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import { zod } from '../third_party/index.js';
7
+ import { ToolCategory } from './categories.js';
8
+ import { defineTool, timeoutSchema } from './ToolDefinition.js';
9
+ export const takeSnapshot = defineTool({
10
+ name: 'take_snapshot',
11
+ description: `Take a text snapshot of the currently selected page based on the a11y tree. The snapshot lists page elements along with a unique
12
+ identifier (uid). Always use the latest snapshot. Prefer taking a snapshot over taking a screenshot. The snapshot indicates the element selected
13
+ in the DevTools Elements panel (if any).`,
14
+ annotations: {
15
+ category: ToolCategory.DEBUGGING,
16
+ // Not read-only due to filePath param.
17
+ readOnlyHint: false,
18
+ },
19
+ schema: {
20
+ verbose: zod
21
+ .boolean()
22
+ .optional()
23
+ .describe('Whether to include all possible information available in the full a11y tree. Default is false.'),
24
+ filePath: zod
25
+ .string()
26
+ .optional()
27
+ .describe('The absolute path, or a path relative to the current working directory, to save the snapshot to instead of attaching it to the response.'),
28
+ },
29
+ handler: async (request, response) => {
30
+ response.includeSnapshot({
31
+ verbose: request.params.verbose ?? false,
32
+ filePath: request.params.filePath,
33
+ });
34
+ },
35
+ });
36
+ export const waitFor = defineTool({
37
+ name: 'wait_for',
38
+ description: `Wait for the specified text to appear on the selected page.`,
39
+ annotations: {
40
+ category: ToolCategory.NAVIGATION,
41
+ readOnlyHint: true,
42
+ },
43
+ schema: {
44
+ text: zod
45
+ .array(zod.string())
46
+ .min(1)
47
+ .describe('Non-empty list of texts. Resolves when any value appears on the page.'),
48
+ ...timeoutSchema,
49
+ },
50
+ handler: async (request, response, context) => {
51
+ await context.waitForTextOnPage(request.params.text, request.params.timeout);
52
+ response.appendResponseLine(`Element matching one of ${JSON.stringify(request.params.text)} found.`);
53
+ response.includeSnapshot();
54
+ },
55
+ });
@@ -0,0 +1,50 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import * as consoleTools from './console.js';
7
+ import * as emulationTools from './emulation.js';
8
+ import * as extensionTools from './extensions.js';
9
+ import * as inputTools from './input.js';
10
+ import * as memoryTools from './memory.js';
11
+ import * as networkTools from './network.js';
12
+ import * as pagesTools from './pages.js';
13
+ import * as performanceTools from './performance.js';
14
+ import * as screencastTools from './screencast.js';
15
+ import * as screenshotTools from './screenshot.js';
16
+ import * as scriptTools from './script.js';
17
+ import * as slimTools from './slim/tools.js';
18
+ import * as snapshotTools from './snapshot.js';
19
+ export const createTools = (args) => {
20
+ const rawTools = args.slim
21
+ ? Object.values(slimTools)
22
+ : [
23
+ ...Object.values(consoleTools),
24
+ ...Object.values(emulationTools),
25
+ ...Object.values(extensionTools),
26
+ ...Object.values(inputTools),
27
+ ...Object.values(memoryTools),
28
+ ...Object.values(networkTools),
29
+ ...Object.values(pagesTools),
30
+ ...Object.values(performanceTools),
31
+ ...Object.values(screencastTools),
32
+ ...Object.values(screenshotTools),
33
+ ...Object.values(scriptTools),
34
+ ...Object.values(snapshotTools),
35
+ ];
36
+ const tools = [];
37
+ for (const tool of rawTools) {
38
+ if (typeof tool === 'function') {
39
+ // @ts-expect-error none of the tools for now implement the function type tool has type "never"
40
+ tools.push(tool(args));
41
+ }
42
+ else {
43
+ tools.push(tool);
44
+ }
45
+ }
46
+ tools.sort((a, b) => {
47
+ return a.name.localeCompare(b.name);
48
+ });
49
+ return tools;
50
+ };
@@ -0,0 +1,84 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import { logger } from '../logger.js';
7
+ import { DevTools } from '../third_party/index.js';
8
+ const engine = DevTools.TraceEngine.TraceModel.Model.createWithAllHandlers();
9
+ export function traceResultIsSuccess(x) {
10
+ return 'parsedTrace' in x;
11
+ }
12
+ export async function parseRawTraceBuffer(buffer) {
13
+ engine.resetProcessor();
14
+ if (!buffer) {
15
+ return {
16
+ error: 'No buffer was provided.',
17
+ };
18
+ }
19
+ const asString = new TextDecoder().decode(buffer);
20
+ if (!asString) {
21
+ return {
22
+ error: 'Decoding the trace buffer returned an empty string.',
23
+ };
24
+ }
25
+ try {
26
+ const data = JSON.parse(asString);
27
+ const events = Array.isArray(data) ? data : data.traceEvents;
28
+ await engine.parse(events);
29
+ const parsedTrace = engine.parsedTrace();
30
+ if (!parsedTrace) {
31
+ return {
32
+ error: 'No parsed trace was returned from the trace engine.',
33
+ };
34
+ }
35
+ const insights = parsedTrace?.insights ?? null;
36
+ return {
37
+ parsedTrace,
38
+ insights,
39
+ };
40
+ }
41
+ catch (e) {
42
+ const errorText = e instanceof Error ? e.message : JSON.stringify(e);
43
+ logger(`Unexpected error parsing trace: ${errorText}`);
44
+ return {
45
+ error: errorText,
46
+ };
47
+ }
48
+ }
49
+ const extraFormatDescriptions = `Information on performance traces may contain main thread activity represented as call frames and network requests.
50
+
51
+ ${DevTools.PerformanceTraceFormatter.callFrameDataFormatDescription}
52
+
53
+ ${DevTools.PerformanceTraceFormatter.networkDataFormatDescription}`;
54
+ export function getTraceSummary(result) {
55
+ const focus = DevTools.AgentFocus.fromParsedTrace(result.parsedTrace);
56
+ const formatter = new DevTools.PerformanceTraceFormatter(focus);
57
+ const summaryText = formatter.formatTraceSummary();
58
+ return `## Summary of Performance trace findings:
59
+ ${summaryText}
60
+
61
+ ## Details on call tree & network request formats:
62
+ ${extraFormatDescriptions}`;
63
+ }
64
+ export function getInsightOutput(result, insightSetId, insightName) {
65
+ if (!result.insights) {
66
+ return {
67
+ error: 'No Performance insights are available for this trace.',
68
+ };
69
+ }
70
+ const insightSet = result.insights.get(insightSetId);
71
+ if (!insightSet) {
72
+ return {
73
+ error: 'No Performance Insights for the given insight set id. Only use ids given in the "Available insight sets" list.',
74
+ };
75
+ }
76
+ const matchingInsight = insightName in insightSet.model ? insightSet.model[insightName] : null;
77
+ if (!matchingInsight) {
78
+ return {
79
+ error: `No Insight with the name ${insightName} found. Double check the name you provided is accurate and try again.`,
80
+ };
81
+ }
82
+ const formatter = new DevTools.PerformanceInsightFormatter(DevTools.AgentFocus.fromParsedTrace(result.parsedTrace), matchingInsight);
83
+ return { output: formatter.formatInsight() };
84
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import fs from 'node:fs/promises';
7
+ import path from 'node:path';
8
+ export class ExtensionRegistry {
9
+ #extensions = new Map();
10
+ async registerExtension(id, extensionPath) {
11
+ const manifestPath = path.join(extensionPath, 'manifest.json');
12
+ const manifestContent = await fs.readFile(manifestPath, 'utf-8');
13
+ const manifest = JSON.parse(manifestContent);
14
+ const name = manifest.name ?? 'Unknown';
15
+ const version = manifest.version ?? 'Unknown';
16
+ const extension = {
17
+ id,
18
+ name,
19
+ version,
20
+ isEnabled: true,
21
+ path: extensionPath,
22
+ };
23
+ this.#extensions.set(extension.id, extension);
24
+ return extension;
25
+ }
26
+ remove(id) {
27
+ this.#extensions.delete(id);
28
+ }
29
+ list() {
30
+ return Array.from(this.#extensions.values());
31
+ }
32
+ getById(id) {
33
+ return this.#extensions.get(id);
34
+ }
35
+ }
@@ -0,0 +1,296 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ // See the KeyInput type for the list of supported keys.
7
+ const validKeys = new Set([
8
+ '0',
9
+ '1',
10
+ '2',
11
+ '3',
12
+ '4',
13
+ '5',
14
+ '6',
15
+ '7',
16
+ '8',
17
+ '9',
18
+ 'Power',
19
+ 'Eject',
20
+ 'Abort',
21
+ 'Help',
22
+ 'Backspace',
23
+ 'Tab',
24
+ 'Numpad5',
25
+ 'NumpadEnter',
26
+ 'Enter',
27
+ '\r',
28
+ '\n',
29
+ 'ShiftLeft',
30
+ 'ShiftRight',
31
+ 'ControlLeft',
32
+ 'ControlRight',
33
+ 'AltLeft',
34
+ 'AltRight',
35
+ 'Pause',
36
+ 'CapsLock',
37
+ 'Escape',
38
+ 'Convert',
39
+ 'NonConvert',
40
+ 'Space',
41
+ 'Numpad9',
42
+ 'PageUp',
43
+ 'Numpad3',
44
+ 'PageDown',
45
+ 'End',
46
+ 'Numpad1',
47
+ 'Home',
48
+ 'Numpad7',
49
+ 'ArrowLeft',
50
+ 'Numpad4',
51
+ 'Numpad8',
52
+ 'ArrowUp',
53
+ 'ArrowRight',
54
+ 'Numpad6',
55
+ 'Numpad2',
56
+ 'ArrowDown',
57
+ 'Select',
58
+ 'Open',
59
+ 'PrintScreen',
60
+ 'Insert',
61
+ 'Numpad0',
62
+ 'Delete',
63
+ 'NumpadDecimal',
64
+ 'Digit0',
65
+ 'Digit1',
66
+ 'Digit2',
67
+ 'Digit3',
68
+ 'Digit4',
69
+ 'Digit5',
70
+ 'Digit6',
71
+ 'Digit7',
72
+ 'Digit8',
73
+ 'Digit9',
74
+ 'KeyA',
75
+ 'KeyB',
76
+ 'KeyC',
77
+ 'KeyD',
78
+ 'KeyE',
79
+ 'KeyF',
80
+ 'KeyG',
81
+ 'KeyH',
82
+ 'KeyI',
83
+ 'KeyJ',
84
+ 'KeyK',
85
+ 'KeyL',
86
+ 'KeyM',
87
+ 'KeyN',
88
+ 'KeyO',
89
+ 'KeyP',
90
+ 'KeyQ',
91
+ 'KeyR',
92
+ 'KeyS',
93
+ 'KeyT',
94
+ 'KeyU',
95
+ 'KeyV',
96
+ 'KeyW',
97
+ 'KeyX',
98
+ 'KeyY',
99
+ 'KeyZ',
100
+ 'MetaLeft',
101
+ 'MetaRight',
102
+ 'ContextMenu',
103
+ 'NumpadMultiply',
104
+ 'NumpadAdd',
105
+ 'NumpadSubtract',
106
+ 'NumpadDivide',
107
+ 'F1',
108
+ 'F2',
109
+ 'F3',
110
+ 'F4',
111
+ 'F5',
112
+ 'F6',
113
+ 'F7',
114
+ 'F8',
115
+ 'F9',
116
+ 'F10',
117
+ 'F11',
118
+ 'F12',
119
+ 'F13',
120
+ 'F14',
121
+ 'F15',
122
+ 'F16',
123
+ 'F17',
124
+ 'F18',
125
+ 'F19',
126
+ 'F20',
127
+ 'F21',
128
+ 'F22',
129
+ 'F23',
130
+ 'F24',
131
+ 'NumLock',
132
+ 'ScrollLock',
133
+ 'AudioVolumeMute',
134
+ 'AudioVolumeDown',
135
+ 'AudioVolumeUp',
136
+ 'MediaTrackNext',
137
+ 'MediaTrackPrevious',
138
+ 'MediaStop',
139
+ 'MediaPlayPause',
140
+ 'Semicolon',
141
+ 'Equal',
142
+ 'NumpadEqual',
143
+ 'Comma',
144
+ 'Minus',
145
+ 'Period',
146
+ 'Slash',
147
+ 'Backquote',
148
+ 'BracketLeft',
149
+ 'Backslash',
150
+ 'BracketRight',
151
+ 'Quote',
152
+ 'AltGraph',
153
+ 'Props',
154
+ 'Cancel',
155
+ 'Clear',
156
+ 'Shift',
157
+ 'Control',
158
+ 'Alt',
159
+ 'Accept',
160
+ 'ModeChange',
161
+ ' ',
162
+ 'Print',
163
+ 'Execute',
164
+ '\u0000',
165
+ 'a',
166
+ 'b',
167
+ 'c',
168
+ 'd',
169
+ 'e',
170
+ 'f',
171
+ 'g',
172
+ 'h',
173
+ 'i',
174
+ 'j',
175
+ 'k',
176
+ 'l',
177
+ 'm',
178
+ 'n',
179
+ 'o',
180
+ 'p',
181
+ 'q',
182
+ 'r',
183
+ 's',
184
+ 't',
185
+ 'u',
186
+ 'v',
187
+ 'w',
188
+ 'x',
189
+ 'y',
190
+ 'z',
191
+ 'Meta',
192
+ '*',
193
+ '+',
194
+ '-',
195
+ '/',
196
+ ';',
197
+ '=',
198
+ ',',
199
+ '.',
200
+ '`',
201
+ '[',
202
+ '\\',
203
+ ']',
204
+ "'",
205
+ 'Attn',
206
+ 'CrSel',
207
+ 'ExSel',
208
+ 'EraseEof',
209
+ 'Play',
210
+ 'ZoomOut',
211
+ ')',
212
+ '!',
213
+ '@',
214
+ '#',
215
+ '$',
216
+ '%',
217
+ '^',
218
+ '&',
219
+ '(',
220
+ 'A',
221
+ 'B',
222
+ 'C',
223
+ 'D',
224
+ 'E',
225
+ 'F',
226
+ 'G',
227
+ 'H',
228
+ 'I',
229
+ 'J',
230
+ 'K',
231
+ 'L',
232
+ 'M',
233
+ 'N',
234
+ 'O',
235
+ 'P',
236
+ 'Q',
237
+ 'R',
238
+ 'S',
239
+ 'T',
240
+ 'U',
241
+ 'V',
242
+ 'W',
243
+ 'X',
244
+ 'Y',
245
+ 'Z',
246
+ ':',
247
+ '<',
248
+ '_',
249
+ '>',
250
+ '?',
251
+ '~',
252
+ '{',
253
+ '|',
254
+ '}',
255
+ '"',
256
+ 'SoftLeft',
257
+ 'SoftRight',
258
+ 'Camera',
259
+ 'Call',
260
+ 'EndCall',
261
+ 'VolumeDown',
262
+ 'VolumeUp',
263
+ ]);
264
+ function throwIfInvalidKey(key) {
265
+ if (validKeys.has(key)) {
266
+ return key;
267
+ }
268
+ throw new Error(`${key} is invalid. Valid keys are: ${Array.from(validKeys.values()).join(',')}.`);
269
+ }
270
+ /**
271
+ * Returns the primary key, followed by modifiers in original order.
272
+ */
273
+ export function parseKey(keyInput) {
274
+ let key = '';
275
+ const result = [];
276
+ for (const ch of keyInput) {
277
+ // Handle cases like Shift++.
278
+ if (ch === '+' && key) {
279
+ result.push(throwIfInvalidKey(key));
280
+ key = '';
281
+ }
282
+ else {
283
+ key += ch;
284
+ }
285
+ }
286
+ if (key) {
287
+ result.push(throwIfInvalidKey(key));
288
+ }
289
+ if (result.length === 0) {
290
+ throw new Error(`Key ${keyInput} could not be parsed.`);
291
+ }
292
+ if (new Set(result).size !== result.length) {
293
+ throw new Error(`Key ${keyInput} contains duplicate keys.`);
294
+ }
295
+ return [result.at(-1), ...result.slice(0, -1)];
296
+ }