@vibebrowser/chrome-devtools-mcp 0.26.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 (294) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +873 -0
  3. package/build/src/DevToolsConnectionAdapter.js +70 -0
  4. package/build/src/DevtoolsUtils.js +295 -0
  5. package/build/src/HeapSnapshotManager.js +110 -0
  6. package/build/src/McpContext.js +605 -0
  7. package/build/src/McpPage.js +315 -0
  8. package/build/src/McpResponse.js +858 -0
  9. package/build/src/Mutex.js +38 -0
  10. package/build/src/PageCollector.js +297 -0
  11. package/build/src/SlimMcpResponse.js +19 -0
  12. package/build/src/TextSnapshot.js +236 -0
  13. package/build/src/ToolHandler.js +217 -0
  14. package/build/src/WaitForHelper.js +190 -0
  15. package/build/src/bin/check-latest-version.js +50 -0
  16. package/build/src/bin/chrome-devtools-cli-options.js +840 -0
  17. package/build/src/bin/chrome-devtools-mcp-cli-options.js +350 -0
  18. package/build/src/bin/chrome-devtools-mcp-main.js +94 -0
  19. package/build/src/bin/chrome-devtools-mcp.js +31 -0
  20. package/build/src/bin/chrome-devtools.js +189 -0
  21. package/build/src/bin/install-service.js +246 -0
  22. package/build/src/bin/service/chrome-devtools-mcp.service.template +17 -0
  23. package/build/src/bin/service/com.vibebrowser.chrome-devtools-mcp.plist.template +37 -0
  24. package/build/src/browser.js +204 -0
  25. package/build/src/daemon/client.js +154 -0
  26. package/build/src/daemon/daemon.js +204 -0
  27. package/build/src/daemon/types.js +7 -0
  28. package/build/src/daemon/utils.js +115 -0
  29. package/build/src/formatters/ConsoleFormatter.js +288 -0
  30. package/build/src/formatters/HeapSnapshotFormatter.js +54 -0
  31. package/build/src/formatters/IssueFormatter.js +193 -0
  32. package/build/src/formatters/NetworkFormatter.js +236 -0
  33. package/build/src/formatters/SnapshotFormatter.js +135 -0
  34. package/build/src/index.js +140 -0
  35. package/build/src/issue-descriptions.js +40 -0
  36. package/build/src/logger.js +37 -0
  37. package/build/src/polyfill.js +8 -0
  38. package/build/src/telemetry/ClearcutLogger.js +169 -0
  39. package/build/src/telemetry/WatchdogClient.js +61 -0
  40. package/build/src/telemetry/errors.js +18 -0
  41. package/build/src/telemetry/flagUtils.js +89 -0
  42. package/build/src/telemetry/metricsRegistry.js +89 -0
  43. package/build/src/telemetry/persistence.js +72 -0
  44. package/build/src/telemetry/transformation.js +134 -0
  45. package/build/src/telemetry/types.js +31 -0
  46. package/build/src/telemetry/watchdog/ClearcutSender.js +205 -0
  47. package/build/src/telemetry/watchdog/main.js +128 -0
  48. package/build/src/third_party/devtools-formatter-worker.js +8 -0
  49. package/build/src/third_party/devtools-heap-snapshot-worker.js +8 -0
  50. package/build/src/third_party/index.js +32 -0
  51. package/build/src/third_party/issue-descriptions/CoepCoopSandboxedIframeCannotNavigateToCoopPage.md +4 -0
  52. package/build/src/third_party/issue-descriptions/CoepCorpNotSameOrigin.md +8 -0
  53. package/build/src/third_party/issue-descriptions/CoepCorpNotSameOriginAfterDefaultedToSameOriginByCoep.md +18 -0
  54. package/build/src/third_party/issue-descriptions/CoepCorpNotSameSite.md +7 -0
  55. package/build/src/third_party/issue-descriptions/CoepFrameResourceNeedsCoepHeader.md +10 -0
  56. package/build/src/third_party/issue-descriptions/CompatibilityModeQuirks.md +5 -0
  57. package/build/src/third_party/issue-descriptions/CookieAttributeValueExceedsMaxSize.md +5 -0
  58. package/build/src/third_party/issue-descriptions/LowTextContrast.md +5 -0
  59. package/build/src/third_party/issue-descriptions/SameSiteExcludeContextDowngradeRead.md +8 -0
  60. package/build/src/third_party/issue-descriptions/SameSiteExcludeContextDowngradeSet.md +8 -0
  61. package/build/src/third_party/issue-descriptions/SameSiteExcludeNavigationContextDowngrade.md +8 -0
  62. package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureErrorRead.md +8 -0
  63. package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureErrorSet.md +8 -0
  64. package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureWarnRead.md +8 -0
  65. package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureWarnSet.md +8 -0
  66. package/build/src/third_party/issue-descriptions/SameSiteUnspecifiedLaxAllowUnsafeRead.md +9 -0
  67. package/build/src/third_party/issue-descriptions/SameSiteUnspecifiedLaxAllowUnsafeSet.md +9 -0
  68. package/build/src/third_party/issue-descriptions/SameSiteWarnCrossDowngradeRead.md +8 -0
  69. package/build/src/third_party/issue-descriptions/SameSiteWarnCrossDowngradeSet.md +8 -0
  70. package/build/src/third_party/issue-descriptions/SameSiteWarnStrictLaxDowngradeStrict.md +8 -0
  71. package/build/src/third_party/issue-descriptions/arInsecureContext.md +7 -0
  72. package/build/src/third_party/issue-descriptions/arInvalidInfoHeader.md +5 -0
  73. package/build/src/third_party/issue-descriptions/arInvalidRegisterOsSourceHeader.md +5 -0
  74. package/build/src/third_party/issue-descriptions/arInvalidRegisterOsTriggerHeader.md +5 -0
  75. package/build/src/third_party/issue-descriptions/arInvalidRegisterSourceHeader.md +5 -0
  76. package/build/src/third_party/issue-descriptions/arInvalidRegisterTriggerHeader.md +5 -0
  77. package/build/src/third_party/issue-descriptions/arNavigationRegistrationUniqueScopeAlreadySet.md +5 -0
  78. package/build/src/third_party/issue-descriptions/arNavigationRegistrationWithoutTransientUserActivation.md +6 -0
  79. package/build/src/third_party/issue-descriptions/arNoRegisterOsSourceHeader.md +5 -0
  80. package/build/src/third_party/issue-descriptions/arNoRegisterOsTriggerHeader.md +5 -0
  81. package/build/src/third_party/issue-descriptions/arNoRegisterSourceHeader.md +5 -0
  82. package/build/src/third_party/issue-descriptions/arNoRegisterTriggerHeader.md +5 -0
  83. package/build/src/third_party/issue-descriptions/arNoWebOrOsSupport.md +4 -0
  84. package/build/src/third_party/issue-descriptions/arOsSourceIgnored.md +18 -0
  85. package/build/src/third_party/issue-descriptions/arOsTriggerIgnored.md +19 -0
  86. package/build/src/third_party/issue-descriptions/arPermissionPolicyDisabled.md +8 -0
  87. package/build/src/third_party/issue-descriptions/arSourceAndTriggerHeaders.md +9 -0
  88. package/build/src/third_party/issue-descriptions/arSourceIgnored.md +13 -0
  89. package/build/src/third_party/issue-descriptions/arTriggerIgnored.md +12 -0
  90. package/build/src/third_party/issue-descriptions/arUntrustworthyReportingOrigin.md +10 -0
  91. package/build/src/third_party/issue-descriptions/arWebAndOsHeaders.md +11 -0
  92. package/build/src/third_party/issue-descriptions/bounceTrackingMitigations.md +3 -0
  93. package/build/src/third_party/issue-descriptions/clientHintMetaTagAllowListInvalidOrigin.md +4 -0
  94. package/build/src/third_party/issue-descriptions/clientHintMetaTagModifiedHTML.md +4 -0
  95. package/build/src/third_party/issue-descriptions/connectionAllowlistInvalidAllowlistItemType.md +12 -0
  96. package/build/src/third_party/issue-descriptions/connectionAllowlistInvalidHeader.md +12 -0
  97. package/build/src/third_party/issue-descriptions/connectionAllowlistInvalidUrlPattern.md +8 -0
  98. package/build/src/third_party/issue-descriptions/connectionAllowlistItemNotInnerList.md +12 -0
  99. package/build/src/third_party/issue-descriptions/connectionAllowlistMoreThanOneList.md +7 -0
  100. package/build/src/third_party/issue-descriptions/connectionAllowlistReportingEndpointNotToken.md +10 -0
  101. package/build/src/third_party/issue-descriptions/cookieCrossSiteRedirectDowngrade.md +12 -0
  102. package/build/src/third_party/issue-descriptions/cookieExcludeBlockedWithinRelatedWebsiteSet.md +4 -0
  103. package/build/src/third_party/issue-descriptions/cookieExcludeDomainNonAscii.md +11 -0
  104. package/build/src/third_party/issue-descriptions/cookieExcludePortMismatch.md +8 -0
  105. package/build/src/third_party/issue-descriptions/cookieExcludeSchemeMismatch.md +7 -0
  106. package/build/src/third_party/issue-descriptions/cookieExcludeThirdPartyPhaseoutRead.md +6 -0
  107. package/build/src/third_party/issue-descriptions/cookieExcludeThirdPartyPhaseoutSet.md +6 -0
  108. package/build/src/third_party/issue-descriptions/cookieWarnDomainNonAscii.md +11 -0
  109. package/build/src/third_party/issue-descriptions/cookieWarnMetadataGrantRead.md +4 -0
  110. package/build/src/third_party/issue-descriptions/cookieWarnMetadataGrantSet.md +4 -0
  111. package/build/src/third_party/issue-descriptions/cookieWarnThirdPartyPhaseoutRead.md +6 -0
  112. package/build/src/third_party/issue-descriptions/cookieWarnThirdPartyPhaseoutSet.md +6 -0
  113. package/build/src/third_party/issue-descriptions/corsAllowCredentialsRequired.md +6 -0
  114. package/build/src/third_party/issue-descriptions/corsDisabledScheme.md +7 -0
  115. package/build/src/third_party/issue-descriptions/corsDisallowedByMode.md +7 -0
  116. package/build/src/third_party/issue-descriptions/corsHeaderDisallowedByPreflightResponse.md +5 -0
  117. package/build/src/third_party/issue-descriptions/corsInvalidHeaderValues.md +7 -0
  118. package/build/src/third_party/issue-descriptions/corsLocalNetworkAccessPermissionDenied.md +19 -0
  119. package/build/src/third_party/issue-descriptions/corsMethodDisallowedByPreflightResponse.md +5 -0
  120. package/build/src/third_party/issue-descriptions/corsNoCorsRedirectModeNotFollow.md +5 -0
  121. package/build/src/third_party/issue-descriptions/corsOriginMismatch.md +6 -0
  122. package/build/src/third_party/issue-descriptions/corsPreflightResponseInvalid.md +5 -0
  123. package/build/src/third_party/issue-descriptions/corsRedirectContainsCredentials.md +5 -0
  124. package/build/src/third_party/issue-descriptions/corsWildcardOriginNotAllowed.md +8 -0
  125. package/build/src/third_party/issue-descriptions/cspEvalViolation.md +9 -0
  126. package/build/src/third_party/issue-descriptions/cspInlineViolation.md +10 -0
  127. package/build/src/third_party/issue-descriptions/cspTrustedTypesPolicyViolation.md +5 -0
  128. package/build/src/third_party/issue-descriptions/cspTrustedTypesSinkViolation.md +8 -0
  129. package/build/src/third_party/issue-descriptions/cspURLViolation.md +10 -0
  130. package/build/src/third_party/issue-descriptions/deprecation.md +3 -0
  131. package/build/src/third_party/issue-descriptions/federatedAuthRequestAccountsHttpNotFound.md +1 -0
  132. package/build/src/third_party/issue-descriptions/federatedAuthRequestAccountsInvalidResponse.md +1 -0
  133. package/build/src/third_party/issue-descriptions/federatedAuthRequestAccountsNoResponse.md +1 -0
  134. package/build/src/third_party/issue-descriptions/federatedAuthRequestApprovalDeclined.md +1 -0
  135. package/build/src/third_party/issue-descriptions/federatedAuthRequestCanceled.md +1 -0
  136. package/build/src/third_party/issue-descriptions/federatedAuthRequestErrorFetchingSignin.md +1 -0
  137. package/build/src/third_party/issue-descriptions/federatedAuthRequestErrorIdToken.md +1 -0
  138. package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenHttpNotFound.md +1 -0
  139. package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenInvalidRequest.md +1 -0
  140. package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenInvalidResponse.md +1 -0
  141. package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenNoResponse.md +1 -0
  142. package/build/src/third_party/issue-descriptions/federatedAuthRequestInvalidSigninResponse.md +1 -0
  143. package/build/src/third_party/issue-descriptions/federatedAuthRequestManifestHttpNotFound.md +1 -0
  144. package/build/src/third_party/issue-descriptions/federatedAuthRequestManifestInvalidResponse.md +1 -0
  145. package/build/src/third_party/issue-descriptions/federatedAuthRequestManifestNoResponse.md +1 -0
  146. package/build/src/third_party/issue-descriptions/federatedAuthRequestTooManyRequests.md +1 -0
  147. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestInvalidAccountsResponse.md +1 -0
  148. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestInvalidConfigOrWellKnown.md +1 -0
  149. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNoAccountSharingPermission.md +1 -0
  150. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNoApiPermission.md +1 -0
  151. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNoReturningUserFromFetchedAccounts.md +1 -0
  152. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotIframe.md +1 -0
  153. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotPotentiallyTrustworthy.md +1 -0
  154. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotSameOrigin.md +1 -0
  155. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotSignedInWithIdp.md +1 -0
  156. package/build/src/third_party/issue-descriptions/fetchingPartitionedBlobURL.md +7 -0
  157. package/build/src/third_party/issue-descriptions/genericFormAriaLabelledByToNonExistingIdError.md +8 -0
  158. package/build/src/third_party/issue-descriptions/genericFormAutocompleteAttributeEmptyError.md +5 -0
  159. package/build/src/third_party/issue-descriptions/genericFormDuplicateIdForInputError.md +5 -0
  160. package/build/src/third_party/issue-descriptions/genericFormEmptyIdAndNameAttributesForInputError.md +5 -0
  161. package/build/src/third_party/issue-descriptions/genericFormInputAssignedAutocompleteValueToIdOrNameAttributeError.md +5 -0
  162. package/build/src/third_party/issue-descriptions/genericFormInputHasWrongButWellIntendedAutocompleteValueError.md +5 -0
  163. package/build/src/third_party/issue-descriptions/genericFormInputWithNoLabelError.md +5 -0
  164. package/build/src/third_party/issue-descriptions/genericFormLabelForMatchesNonExistingIdError.md +5 -0
  165. package/build/src/third_party/issue-descriptions/genericFormLabelForNameError.md +5 -0
  166. package/build/src/third_party/issue-descriptions/genericFormLabelHasNeitherForNorNestedInputError.md +5 -0
  167. package/build/src/third_party/issue-descriptions/genericFormModelContextMissingToolDescription.md +5 -0
  168. package/build/src/third_party/issue-descriptions/genericFormModelContextMissingToolName.md +5 -0
  169. package/build/src/third_party/issue-descriptions/genericFormModelContextParameterMissingName.md +5 -0
  170. package/build/src/third_party/issue-descriptions/genericFormModelContextParameterMissingTitleAndDescription.md +5 -0
  171. package/build/src/third_party/issue-descriptions/genericFormModelContextRequiredParameterMissingName.md +5 -0
  172. package/build/src/third_party/issue-descriptions/genericNavigationEntryMarkedSkippable.md +7 -0
  173. package/build/src/third_party/issue-descriptions/genericResponseWasBlockedByORB.md +4 -0
  174. package/build/src/third_party/issue-descriptions/heavyAd.md +10 -0
  175. package/build/src/third_party/issue-descriptions/mixedContent.md +5 -0
  176. package/build/src/third_party/issue-descriptions/navigatingPartitionedBlobURL.md +5 -0
  177. package/build/src/third_party/issue-descriptions/permissionElementActivationDisabled.md +7 -0
  178. package/build/src/third_party/issue-descriptions/permissionElementActivationDisabledWithOccluder.md +9 -0
  179. package/build/src/third_party/issue-descriptions/permissionElementActivationDisabledWithOccluderParent.md +9 -0
  180. package/build/src/third_party/issue-descriptions/permissionElementCspFrameAncestorsMissing.md +5 -0
  181. package/build/src/third_party/issue-descriptions/permissionElementFencedFrameDisallowed.md +5 -0
  182. package/build/src/third_party/issue-descriptions/permissionElementFontSizeTooLarge.md +5 -0
  183. package/build/src/third_party/issue-descriptions/permissionElementFontSizeTooSmall.md +5 -0
  184. package/build/src/third_party/issue-descriptions/permissionElementGeolocationDeprecated.md +5 -0
  185. package/build/src/third_party/issue-descriptions/permissionElementInsetBoxShadowUnsupported.md +5 -0
  186. package/build/src/third_party/issue-descriptions/permissionElementInvalidDisplayStyle.md +5 -0
  187. package/build/src/third_party/issue-descriptions/permissionElementInvalidSizeValue.md +5 -0
  188. package/build/src/third_party/issue-descriptions/permissionElementInvalidType.md +5 -0
  189. package/build/src/third_party/issue-descriptions/permissionElementInvalidTypeActivation.md +5 -0
  190. package/build/src/third_party/issue-descriptions/permissionElementLowContrast.md +5 -0
  191. package/build/src/third_party/issue-descriptions/permissionElementNonOpaqueColor.md +5 -0
  192. package/build/src/third_party/issue-descriptions/permissionElementPaddingBottomUnsupported.md +6 -0
  193. package/build/src/third_party/issue-descriptions/permissionElementPaddingRightUnsupported.md +6 -0
  194. package/build/src/third_party/issue-descriptions/permissionElementPermissionsPolicyBlocked.md +5 -0
  195. package/build/src/third_party/issue-descriptions/permissionElementRegistrationFailed.md +5 -0
  196. package/build/src/third_party/issue-descriptions/permissionElementRequestInProgress.md +5 -0
  197. package/build/src/third_party/issue-descriptions/permissionElementSecurityChecksFailed.md +5 -0
  198. package/build/src/third_party/issue-descriptions/permissionElementTypeNotSupported.md +5 -0
  199. package/build/src/third_party/issue-descriptions/permissionElementUntrustedEvent.md +7 -0
  200. package/build/src/third_party/issue-descriptions/placeholderDescriptionForInvisibleIssues.md +3 -0
  201. package/build/src/third_party/issue-descriptions/propertyRuleInvalidNameIssue.md +3 -0
  202. package/build/src/third_party/issue-descriptions/propertyRuleIssue.md +7 -0
  203. package/build/src/third_party/issue-descriptions/selectElementAccessibilityDisallowedOptGroupChild.md +7 -0
  204. package/build/src/third_party/issue-descriptions/selectElementAccessibilityDisallowedSelectChild.md +7 -0
  205. package/build/src/third_party/issue-descriptions/selectElementAccessibilityInteractiveContentAttributesSelectDescendant.md +3 -0
  206. package/build/src/third_party/issue-descriptions/selectElementAccessibilityInteractiveContentLegendChild.md +3 -0
  207. package/build/src/third_party/issue-descriptions/selectElementAccessibilityInteractiveContentOptionChild.md +3 -0
  208. package/build/src/third_party/issue-descriptions/selectElementAccessibilityNonPhrasingContentOptionChild.md +3 -0
  209. package/build/src/third_party/issue-descriptions/selectivePermissionsIntervention.md +7 -0
  210. package/build/src/third_party/issue-descriptions/sharedArrayBuffer.md +7 -0
  211. package/build/src/third_party/issue-descriptions/sharedDictionaryUseErrorCrossOriginNoCorsRequest.md +1 -0
  212. package/build/src/third_party/issue-descriptions/sharedDictionaryUseErrorDictionaryLoadFailure.md +3 -0
  213. package/build/src/third_party/issue-descriptions/sharedDictionaryUseErrorMatchingDictionaryNotUsed.md +3 -0
  214. package/build/src/third_party/issue-descriptions/sharedDictionaryUseErrorUnexpectedContentDictionaryHeader.md +1 -0
  215. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorCossOriginNoCorsRequest.md +1 -0
  216. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorDisallowedBySettings.md +1 -0
  217. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorExpiredResponse.md +3 -0
  218. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorFeatureDisabled.md +3 -0
  219. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorInsufficientResources.md +1 -0
  220. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorInvalidMatchField.md +1 -0
  221. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorInvalidStructuredHeader.md +1 -0
  222. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorInvalidTTLField.md +1 -0
  223. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNavigationRequest.md +3 -0
  224. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNoMatchField.md +1 -0
  225. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonIntegerTTLField.md +1 -0
  226. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonListMatchDestField.md +1 -0
  227. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonSecureContext.md +3 -0
  228. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonStringIdField.md +1 -0
  229. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonStringInMatchDestList.md +1 -0
  230. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonStringMatchField.md +1 -0
  231. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonTokenTypeField.md +1 -0
  232. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorRequestAborted.md +1 -0
  233. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorShuttingDown.md +1 -0
  234. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorTooLongIdField.md +3 -0
  235. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorUnsupportedType.md +3 -0
  236. package/build/src/third_party/issue-descriptions/sriInvalidSignatureHeader.md +14 -0
  237. package/build/src/third_party/issue-descriptions/sriInvalidSignatureInputHeader.md +15 -0
  238. package/build/src/third_party/issue-descriptions/sriMissingSignatureHeader.md +8 -0
  239. package/build/src/third_party/issue-descriptions/sriMissingSignatureInputHeader.md +7 -0
  240. package/build/src/third_party/issue-descriptions/sriSignatureHeaderValueIsIncorrectLength.md +11 -0
  241. package/build/src/third_party/issue-descriptions/sriSignatureHeaderValueIsNotByteSequence.md +14 -0
  242. package/build/src/third_party/issue-descriptions/sriSignatureHeaderValueIsParameterized.md +15 -0
  243. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidComponentName.md +8 -0
  244. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidComponentType.md +13 -0
  245. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidDerivedComponentParameter.md +4 -0
  246. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidHeaderComponentParameter.md +5 -0
  247. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidParameter.md +11 -0
  248. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderKeyIdLength.md +12 -0
  249. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderMissingLabel.md +6 -0
  250. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderMissingRequiredParameters.md +8 -0
  251. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderValueMissingComponents.md +11 -0
  252. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderValueNotInnerList.md +11 -0
  253. package/build/src/third_party/issue-descriptions/sriValidationFailedIntegrityMismatch.md +10 -0
  254. package/build/src/third_party/issue-descriptions/sriValidationFailedInvalidLength.md +5 -0
  255. package/build/src/third_party/issue-descriptions/sriValidationFailedSignatureExpired.md +6 -0
  256. package/build/src/third_party/issue-descriptions/sriValidationFailedSignatureMismatch.md +11 -0
  257. package/build/src/third_party/issue-descriptions/stylesheetLateImport.md +4 -0
  258. package/build/src/third_party/issue-descriptions/stylesheetRequestFailed.md +3 -0
  259. package/build/src/third_party/issue-descriptions/summaryElementAccessibilityInteractiveContentSummaryDescendant.md +3 -0
  260. package/build/src/third_party/issue-descriptions/unencodedDigestIncorrectDigestLength.md +12 -0
  261. package/build/src/third_party/issue-descriptions/unencodedDigestIncorrectDigestType.md +17 -0
  262. package/build/src/third_party/issue-descriptions/unencodedDigestMalformedDictionary.md +14 -0
  263. package/build/src/third_party/issue-descriptions/unencodedDigestUnknownAlgorithm.md +15 -0
  264. package/build/src/third_party/lighthouse-devtools-mcp-bundle.js +61598 -0
  265. package/build/src/tools/ToolDefinition.js +73 -0
  266. package/build/src/tools/categories.js +36 -0
  267. package/build/src/tools/console.js +91 -0
  268. package/build/src/tools/emulation.js +57 -0
  269. package/build/src/tools/extensions.js +96 -0
  270. package/build/src/tools/input.js +461 -0
  271. package/build/src/tools/lighthouse.js +131 -0
  272. package/build/src/tools/memory.js +106 -0
  273. package/build/src/tools/network.js +125 -0
  274. package/build/src/tools/pages.js +411 -0
  275. package/build/src/tools/performance.js +196 -0
  276. package/build/src/tools/screencast.js +95 -0
  277. package/build/src/tools/screenshot.js +87 -0
  278. package/build/src/tools/script.js +151 -0
  279. package/build/src/tools/slim/tools.js +85 -0
  280. package/build/src/tools/snapshot.js +60 -0
  281. package/build/src/tools/thirdPartyDeveloper.js +75 -0
  282. package/build/src/tools/tools.js +56 -0
  283. package/build/src/tools/webmcp.js +64 -0
  284. package/build/src/trace-processing/parse.js +85 -0
  285. package/build/src/types.js +7 -0
  286. package/build/src/utils/check-for-updates.js +74 -0
  287. package/build/src/utils/files.js +18 -0
  288. package/build/src/utils/id.js +16 -0
  289. package/build/src/utils/keyboard.js +297 -0
  290. package/build/src/utils/pagination.js +50 -0
  291. package/build/src/utils/string.js +37 -0
  292. package/build/src/utils/types.js +7 -0
  293. package/build/src/version.js +10 -0
  294. package/package.json +93 -0
@@ -0,0 +1,38 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google Inc.
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ export class Mutex {
7
+ static Guard = class Guard {
8
+ #mutex;
9
+ constructor(mutex) {
10
+ this.#mutex = mutex;
11
+ }
12
+ dispose() {
13
+ return this.#mutex.release();
14
+ }
15
+ };
16
+ #locked = false;
17
+ #acquirers = [];
18
+ // This is FIFO.
19
+ async acquire() {
20
+ if (!this.#locked) {
21
+ this.#locked = true;
22
+ return new Mutex.Guard(this);
23
+ }
24
+ const { resolve, promise } = Promise.withResolvers();
25
+ this.#acquirers.push(resolve);
26
+ await promise;
27
+ return new Mutex.Guard(this);
28
+ }
29
+ release() {
30
+ const resolve = this.#acquirers.shift();
31
+ if (!resolve) {
32
+ this.#locked = false;
33
+ return;
34
+ }
35
+ resolve();
36
+ }
37
+ }
38
+ //# sourceMappingURL=Mutex.js.map
@@ -0,0 +1,297 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import { FakeIssuesManager } from './DevtoolsUtils.js';
7
+ import { logger } from './logger.js';
8
+ import { DevTools } from './third_party/index.js';
9
+ import { createIdGenerator, stableIdSymbol, } from './utils/id.js';
10
+ export class UncaughtError {
11
+ details;
12
+ targetId;
13
+ constructor(details, targetId) {
14
+ this.details = details;
15
+ this.targetId = targetId;
16
+ }
17
+ }
18
+ export class PageCollector {
19
+ #browser;
20
+ #listenersInitializer;
21
+ #listeners = new WeakMap();
22
+ maxNavigationSaved = 3;
23
+ /**
24
+ * This maps a Page to a list of navigations with a sub-list
25
+ * of all collected resources.
26
+ * The newer navigations come first.
27
+ */
28
+ storage = new WeakMap();
29
+ constructor(browser, listeners) {
30
+ this.#browser = browser;
31
+ this.#listenersInitializer = listeners;
32
+ }
33
+ async init(pages) {
34
+ for (const page of pages) {
35
+ this.addPage(page);
36
+ }
37
+ this.#browser.on('targetcreated', this.#onTargetCreated);
38
+ this.#browser.on('targetdestroyed', this.#onTargetDestroyed);
39
+ }
40
+ dispose() {
41
+ this.#browser.off('targetcreated', this.#onTargetCreated);
42
+ this.#browser.off('targetdestroyed', this.#onTargetDestroyed);
43
+ }
44
+ #onTargetCreated = async (target) => {
45
+ try {
46
+ const page = await target.page();
47
+ if (!page) {
48
+ return;
49
+ }
50
+ this.addPage(page);
51
+ }
52
+ catch (err) {
53
+ logger('Error getting a page for a target onTargetCreated', err);
54
+ }
55
+ };
56
+ #onTargetDestroyed = async (target) => {
57
+ try {
58
+ const page = await target.page();
59
+ if (!page) {
60
+ return;
61
+ }
62
+ this.cleanupPageDestroyed(page);
63
+ }
64
+ catch (err) {
65
+ logger('Error getting a page for a target onTargetDestroyed', err);
66
+ }
67
+ };
68
+ addPage(page) {
69
+ this.#initializePage(page);
70
+ }
71
+ #initializePage(page) {
72
+ if (this.storage.has(page)) {
73
+ return;
74
+ }
75
+ const idGenerator = createIdGenerator();
76
+ const storedLists = [[]];
77
+ this.storage.set(page, storedLists);
78
+ const listeners = this.#listenersInitializer(value => {
79
+ const withId = value;
80
+ withId[stableIdSymbol] = idGenerator();
81
+ const navigations = this.storage.get(page) ?? [[]];
82
+ navigations[0].push(withId);
83
+ });
84
+ listeners['framenavigated'] = (frame) => {
85
+ // Only split the storage on main frame navigation
86
+ if (frame !== page.mainFrame()) {
87
+ return;
88
+ }
89
+ this.splitAfterNavigation(page);
90
+ };
91
+ for (const [name, listener] of Object.entries(listeners)) {
92
+ page.on(name, listener);
93
+ }
94
+ this.#listeners.set(page, listeners);
95
+ }
96
+ splitAfterNavigation(page) {
97
+ const navigations = this.storage.get(page);
98
+ if (!navigations) {
99
+ return;
100
+ }
101
+ // Add the latest navigation first
102
+ navigations.unshift([]);
103
+ navigations.splice(this.maxNavigationSaved);
104
+ }
105
+ cleanupPageDestroyed(page) {
106
+ const listeners = this.#listeners.get(page);
107
+ if (listeners) {
108
+ for (const [name, listener] of Object.entries(listeners)) {
109
+ page.off(name, listener);
110
+ }
111
+ }
112
+ this.storage.delete(page);
113
+ }
114
+ getData(page, includePreservedData) {
115
+ const navigations = this.storage.get(page);
116
+ if (!navigations) {
117
+ return [];
118
+ }
119
+ if (!includePreservedData) {
120
+ return navigations[0];
121
+ }
122
+ const data = [];
123
+ for (let index = this.maxNavigationSaved; index >= 0; index--) {
124
+ if (navigations[index]) {
125
+ data.push(...navigations[index]);
126
+ }
127
+ }
128
+ return data;
129
+ }
130
+ getIdForResource(resource) {
131
+ return resource[stableIdSymbol] ?? -1;
132
+ }
133
+ getById(page, stableId) {
134
+ const navigations = this.storage.get(page);
135
+ if (!navigations) {
136
+ throw new Error('No requests found for selected page');
137
+ }
138
+ const item = this.find(page, item => item[stableIdSymbol] === stableId);
139
+ if (item) {
140
+ return item;
141
+ }
142
+ throw new Error('Request not found for selected page');
143
+ }
144
+ find(page, filter) {
145
+ const navigations = this.storage.get(page);
146
+ if (!navigations) {
147
+ return;
148
+ }
149
+ for (const navigation of navigations) {
150
+ const item = navigation.find(filter);
151
+ if (item) {
152
+ return item;
153
+ }
154
+ }
155
+ return;
156
+ }
157
+ }
158
+ export class ConsoleCollector extends PageCollector {
159
+ #subscribedPages = new WeakMap();
160
+ addPage(page) {
161
+ super.addPage(page);
162
+ if (!this.#subscribedPages.has(page)) {
163
+ const subscriber = new PageEventSubscriber(page);
164
+ this.#subscribedPages.set(page, subscriber);
165
+ void subscriber.subscribe();
166
+ }
167
+ }
168
+ cleanupPageDestroyed(page) {
169
+ super.cleanupPageDestroyed(page);
170
+ this.#subscribedPages.get(page)?.unsubscribe();
171
+ this.#subscribedPages.delete(page);
172
+ }
173
+ }
174
+ class PageEventSubscriber {
175
+ #issueManager = new FakeIssuesManager();
176
+ #issueAggregator = new DevTools.IssueAggregator(this.#issueManager);
177
+ #seenKeys = new Set();
178
+ #seenIssues = new Set();
179
+ #page;
180
+ #session;
181
+ #targetId;
182
+ constructor(page) {
183
+ this.#page = page;
184
+ // @ts-expect-error use existing CDP client (internal Puppeteer API).
185
+ this.#session = this.#page._client();
186
+ // @ts-expect-error use internal Puppeteer API to get target ID
187
+ this.#targetId = this.#session.target()._targetId;
188
+ }
189
+ #resetIssueAggregator() {
190
+ this.#issueManager = new FakeIssuesManager();
191
+ if (this.#issueAggregator) {
192
+ this.#issueAggregator.removeEventListener("AggregatedIssueUpdated" /* DevTools.IssueAggregatorEvents.AGGREGATED_ISSUE_UPDATED */, this.#onAggregatedIssue);
193
+ }
194
+ this.#issueAggregator = new DevTools.IssueAggregator(this.#issueManager);
195
+ this.#issueAggregator.addEventListener("AggregatedIssueUpdated" /* DevTools.IssueAggregatorEvents.AGGREGATED_ISSUE_UPDATED */, this.#onAggregatedIssue);
196
+ }
197
+ async subscribe() {
198
+ this.#resetIssueAggregator();
199
+ this.#page.on('framenavigated', this.#onFrameNavigated);
200
+ this.#page.on('issue', this.#onIssueAdded);
201
+ this.#session.on('Runtime.exceptionThrown', this.#onExceptionThrown);
202
+ }
203
+ unsubscribe() {
204
+ this.#seenKeys.clear();
205
+ this.#seenIssues.clear();
206
+ this.#page.off('framenavigated', this.#onFrameNavigated);
207
+ this.#page.off('issue', this.#onIssueAdded);
208
+ this.#session.off('Runtime.exceptionThrown', this.#onExceptionThrown);
209
+ if (this.#issueAggregator) {
210
+ this.#issueAggregator.removeEventListener("AggregatedIssueUpdated" /* DevTools.IssueAggregatorEvents.AGGREGATED_ISSUE_UPDATED */, this.#onAggregatedIssue);
211
+ }
212
+ }
213
+ #onAggregatedIssue = (event) => {
214
+ if (this.#seenIssues.has(event.data)) {
215
+ return;
216
+ }
217
+ this.#seenIssues.add(event.data);
218
+ this.#page.emit('devtoolsAggregatedIssue', event.data);
219
+ };
220
+ #onExceptionThrown = (event) => {
221
+ this.#page.emit('uncaughtError', new UncaughtError(event.exceptionDetails, this.#targetId));
222
+ };
223
+ // On navigation, we reset issue aggregation.
224
+ #onFrameNavigated = (frame) => {
225
+ // Only split the storage on main frame navigation
226
+ if (frame !== frame.page().mainFrame()) {
227
+ return;
228
+ }
229
+ this.#seenKeys.clear();
230
+ this.#seenIssues.clear();
231
+ this.#resetIssueAggregator();
232
+ };
233
+ #onIssueAdded = (inspectorIssue) => {
234
+ try {
235
+ // DevTools currently defines this protocol issue code but has no
236
+ // IssuesManager handler for it, so calling into the mapper only warns.
237
+ if (String(inspectorIssue.code) === 'PerformanceIssue') {
238
+ return;
239
+ }
240
+ const issue = DevTools.createIssuesFromProtocolIssue(null,
241
+ // @ts-expect-error Protocol types diverge.
242
+ inspectorIssue)[0];
243
+ if (!issue) {
244
+ logger('No issue mapping for for the issue: ', inspectorIssue.code);
245
+ return;
246
+ }
247
+ const primaryKey = issue.primaryKey();
248
+ if (this.#seenKeys.has(primaryKey)) {
249
+ return;
250
+ }
251
+ this.#seenKeys.add(primaryKey);
252
+ this.#issueManager.dispatchEventToListeners("IssueAdded" /* DevTools.IssuesManagerEvents.ISSUE_ADDED */, {
253
+ issue,
254
+ // @ts-expect-error We don't care that issues model is null
255
+ issuesModel: null,
256
+ });
257
+ }
258
+ catch (error) {
259
+ logger('Error creating a new issue', error);
260
+ }
261
+ };
262
+ }
263
+ export class NetworkCollector extends PageCollector {
264
+ constructor(browser, listeners = collect => {
265
+ return {
266
+ request: req => {
267
+ collect(req);
268
+ },
269
+ };
270
+ }) {
271
+ super(browser, listeners);
272
+ }
273
+ splitAfterNavigation(page) {
274
+ const navigations = this.storage.get(page) ?? [];
275
+ if (!navigations) {
276
+ return;
277
+ }
278
+ const requests = navigations[0];
279
+ const lastRequestIdx = requests.findLastIndex(request => {
280
+ return request.frame() === page.mainFrame()
281
+ ? request.isNavigationRequest()
282
+ : false;
283
+ });
284
+ // Keep all requests since the last navigation request including that
285
+ // navigation request itself.
286
+ // Keep the reference
287
+ if (lastRequestIdx !== -1) {
288
+ const fromCurrentNavigation = requests.splice(lastRequestIdx);
289
+ navigations.unshift(fromCurrentNavigation);
290
+ }
291
+ else {
292
+ navigations.unshift([]);
293
+ }
294
+ navigations.splice(this.maxNavigationSaved);
295
+ }
296
+ }
297
+ //# sourceMappingURL=PageCollector.js.map
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import { McpResponse } from './McpResponse.js';
7
+ export class SlimMcpResponse extends McpResponse {
8
+ async handle(_toolName, _context) {
9
+ const text = {
10
+ type: 'text',
11
+ text: this.responseLines.join('\n'),
12
+ };
13
+ return {
14
+ content: [text],
15
+ structuredContent: text,
16
+ };
17
+ }
18
+ }
19
+ //# sourceMappingURL=SlimMcpResponse.js.map
@@ -0,0 +1,236 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import { logger } from './logger.js';
7
+ export class TextSnapshot {
8
+ static nextSnapshotId = 1;
9
+ static resetCounter() {
10
+ TextSnapshot.nextSnapshotId = 1;
11
+ }
12
+ root;
13
+ idToNode;
14
+ snapshotId;
15
+ selectedElementUid;
16
+ hasSelectedElement;
17
+ verbose;
18
+ constructor(data) {
19
+ this.root = data.root;
20
+ this.idToNode = data.idToNode;
21
+ this.snapshotId = data.snapshotId;
22
+ this.selectedElementUid = data.selectedElementUid;
23
+ this.hasSelectedElement = data.hasSelectedElement;
24
+ this.verbose = data.verbose;
25
+ }
26
+ static async create(page, options = {}) {
27
+ const verbose = options.verbose ?? false;
28
+ const rootNode = await page.pptrPage.accessibility.snapshot({
29
+ includeIframes: true,
30
+ interestingOnly: !verbose,
31
+ });
32
+ if (!rootNode) {
33
+ throw new Error('Failed to create accessibility snapshot');
34
+ }
35
+ const { uniqueBackendNodeIdToMcpId } = page;
36
+ const snapshotId = TextSnapshot.nextSnapshotId++;
37
+ // Iterate through the whole accessibility node tree and assign node ids that
38
+ // will be used for the tree serialization and mapping ids back to nodes.
39
+ let idCounter = 0;
40
+ const idToNode = new Map();
41
+ const seenUniqueIds = new Set();
42
+ const seenBackendNodeIds = new Set();
43
+ const assignIds = (node) => {
44
+ let id = '';
45
+ // @ts-expect-error untyped backendNodeId.
46
+ const backendNodeId = node.backendNodeId;
47
+ // @ts-expect-error untyped loaderId.
48
+ const uniqueBackendId = `${node.loaderId}_${backendNodeId}`;
49
+ const existingMcpId = uniqueBackendNodeIdToMcpId.get(uniqueBackendId);
50
+ if (existingMcpId !== undefined) {
51
+ // Re-use MCP exposed ID if the uniqueId is the same.
52
+ id = existingMcpId;
53
+ }
54
+ else {
55
+ // Only generate a new ID if we have not seen the node before.
56
+ id = `${snapshotId}_${idCounter++}`;
57
+ uniqueBackendNodeIdToMcpId.set(uniqueBackendId, id);
58
+ }
59
+ seenUniqueIds.add(uniqueBackendId);
60
+ seenBackendNodeIds.add(backendNodeId);
61
+ const nodeWithId = {
62
+ ...node,
63
+ id,
64
+ children: node.children
65
+ ? node.children.map(child => assignIds(child))
66
+ : [],
67
+ };
68
+ // The AXNode for an option doesn't contain its `value`.
69
+ // Therefore, set text content of the option as value.
70
+ if (node.role === 'option') {
71
+ const optionText = node.name;
72
+ if (optionText) {
73
+ nodeWithId.value = optionText.toString();
74
+ }
75
+ }
76
+ idToNode.set(nodeWithId.id, nodeWithId);
77
+ return nodeWithId;
78
+ };
79
+ const rootNodeWithId = assignIds(rootNode);
80
+ await TextSnapshot.insertExtraNodes(page, idToNode, seenUniqueIds, snapshotId, idCounter, rootNodeWithId, seenBackendNodeIds, options.extraHandles ?? []);
81
+ const snapshot = new TextSnapshot({
82
+ root: rootNodeWithId,
83
+ snapshotId: String(snapshotId),
84
+ idToNode,
85
+ hasSelectedElement: false,
86
+ verbose,
87
+ });
88
+ const data = options.devtoolsData ?? (await page.getDevToolsData());
89
+ if (data?.cdpBackendNodeId) {
90
+ snapshot.hasSelectedElement = true;
91
+ snapshot.selectedElementUid = page.resolveCdpElementId(data.cdpBackendNodeId);
92
+ }
93
+ // Clean up unique IDs that we did not see anymore.
94
+ for (const key of uniqueBackendNodeIdToMcpId.keys()) {
95
+ if (!seenUniqueIds.has(key)) {
96
+ uniqueBackendNodeIdToMcpId.delete(key);
97
+ }
98
+ }
99
+ return snapshot;
100
+ }
101
+ // ExtraHandles represent DOM nodes which might not be part of the accessibility tree, e.g. DOM nodes
102
+ // returned by third-party developer tools. We insert them into the tree by finding the closest ancestor
103
+ // in the tree and inserting the node as a child. The ancestor's child nodes are re-parented if necessary.
104
+ static async insertExtraNodes(page, idToNode, seenUniqueIds, snapshotId, idCounter, rootNodeWithId, seenBackendNodeIds, extraHandles) {
105
+ const { uniqueBackendNodeIdToMcpId } = page;
106
+ const createExtraNode = async (handle) => {
107
+ const backendNodeId = await handle.backendNodeId();
108
+ if (!backendNodeId || seenBackendNodeIds.has(backendNodeId)) {
109
+ return null;
110
+ }
111
+ const uniqueBackendId = `custom_${backendNodeId}`;
112
+ if (seenUniqueIds.has(uniqueBackendId)) {
113
+ return null;
114
+ }
115
+ seenBackendNodeIds.add(backendNodeId);
116
+ let id = '';
117
+ const mcpId = uniqueBackendNodeIdToMcpId.get(uniqueBackendId);
118
+ if (mcpId !== undefined) {
119
+ id = mcpId;
120
+ }
121
+ else {
122
+ id = `${snapshotId}_${idCounter++}`;
123
+ uniqueBackendNodeIdToMcpId.set(uniqueBackendId, id);
124
+ }
125
+ seenUniqueIds.add(uniqueBackendId);
126
+ const tagHandle = await handle.getProperty('localName');
127
+ const tagValue = await tagHandle.jsonValue();
128
+ const extraNode = {
129
+ role: tagValue,
130
+ id,
131
+ backendNodeId,
132
+ children: [],
133
+ elementHandle: async () => handle,
134
+ };
135
+ return extraNode;
136
+ };
137
+ const findAncestorNode = async (handle) => {
138
+ let ancestorHandle = await handle.evaluateHandle(el => el.parentElement);
139
+ while (ancestorHandle) {
140
+ const ancestorElement = ancestorHandle.asElement();
141
+ if (!ancestorElement) {
142
+ await ancestorHandle.dispose();
143
+ return null;
144
+ }
145
+ const ancestorBackendId = await ancestorElement.backendNodeId();
146
+ if (ancestorBackendId) {
147
+ const ancestorNode = idToNode
148
+ .values()
149
+ .find(node => node.backendNodeId === ancestorBackendId);
150
+ if (ancestorNode) {
151
+ await ancestorHandle.dispose();
152
+ return ancestorNode;
153
+ }
154
+ }
155
+ const nextHandle = await ancestorElement.evaluateHandle(el => el.parentElement);
156
+ await ancestorHandle.dispose();
157
+ ancestorHandle = nextHandle;
158
+ }
159
+ return null;
160
+ };
161
+ const findDescendantNodes = async (backendNodeId) => {
162
+ const descendantIds = new Set();
163
+ if (!backendNodeId) {
164
+ return descendantIds;
165
+ }
166
+ try {
167
+ // @ts-expect-error internal API
168
+ const client = page.pptrPage._client();
169
+ if (client) {
170
+ const { node } = await client.send('DOM.describeNode', {
171
+ backendNodeId,
172
+ depth: -1,
173
+ pierce: true,
174
+ });
175
+ const collect = (node) => {
176
+ if (node.backendNodeId && node.backendNodeId !== backendNodeId) {
177
+ descendantIds.add(node.backendNodeId);
178
+ }
179
+ if (node.children) {
180
+ for (const child of node.children) {
181
+ collect(child);
182
+ }
183
+ }
184
+ };
185
+ collect(node);
186
+ }
187
+ }
188
+ catch (e) {
189
+ logger(`Failed to collect descendants for backend node ${backendNodeId}`, e);
190
+ }
191
+ return descendantIds;
192
+ };
193
+ const moveChildNodes = (attachTarget, extraNode, descendantIds) => {
194
+ let firstMovedIndex = -1;
195
+ if (descendantIds.size > 0 && attachTarget.children) {
196
+ const remainingChildren = [];
197
+ for (const child of attachTarget.children) {
198
+ if (child.backendNodeId && descendantIds.has(child.backendNodeId)) {
199
+ if (firstMovedIndex === -1) {
200
+ firstMovedIndex = remainingChildren.length;
201
+ }
202
+ extraNode.children.push(child);
203
+ }
204
+ else {
205
+ remainingChildren.push(child);
206
+ }
207
+ }
208
+ attachTarget.children = remainingChildren;
209
+ }
210
+ return firstMovedIndex !== -1
211
+ ? firstMovedIndex
212
+ : attachTarget.children
213
+ ? attachTarget.children.length
214
+ : 0;
215
+ };
216
+ if (extraHandles.length) {
217
+ page.extraHandles = extraHandles;
218
+ }
219
+ const reorgInfo = [];
220
+ for (const handle of page.extraHandles) {
221
+ const extraNode = await createExtraNode(handle);
222
+ if (!extraNode) {
223
+ continue;
224
+ }
225
+ idToNode.set(extraNode.id, extraNode);
226
+ const attachTarget = (await findAncestorNode(handle)) || rootNodeWithId;
227
+ const descendantIds = await findDescendantNodes(extraNode.backendNodeId);
228
+ reorgInfo.push({ extraNode, attachTarget, descendantIds });
229
+ }
230
+ for (const { extraNode, attachTarget, descendantIds } of reorgInfo) {
231
+ const index = moveChildNodes(attachTarget, extraNode, descendantIds);
232
+ attachTarget.children.splice(index, 0, extraNode);
233
+ }
234
+ }
235
+ }
236
+ //# sourceMappingURL=TextSnapshot.js.map