opera-devtools-mcp 0.2.4 → 0.4.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 (330) hide show
  1. package/README.md +108 -8
  2. package/build/src/McpContext.js +365 -365
  3. package/build/src/McpPage.js +475 -48
  4. package/build/src/McpResponse.js +462 -288
  5. package/build/src/SlimMcpResponse.js +1 -1
  6. package/build/src/TextSnapshot.js +101 -22
  7. package/build/src/ToolHandler.js +142 -20
  8. package/build/src/bin/check-latest-version.js +27 -1
  9. package/build/src/bin/chrome-devtools-cli-options.js +614 -10
  10. package/build/src/bin/chrome-devtools-mcp-cli-options.js +119 -32
  11. package/build/src/bin/chrome-devtools-mcp-main.js +50 -7
  12. package/build/src/bin/chrome-devtools-mcp.js +7 -4
  13. package/build/src/bin/chrome-devtools.js +57 -14
  14. package/build/src/bin/opera-devtools-cli-options.js +3 -829
  15. package/build/src/bin/opera-devtools-mcp-cli-options.js +3 -348
  16. package/build/src/bin/opera-devtools-mcp-main.js +3 -37
  17. package/build/src/bin/opera-devtools-mcp.js +4 -19
  18. package/build/src/bin/opera-devtools.js +4 -185
  19. package/build/src/browser.js +45 -5
  20. package/build/src/{PageCollector.js → collectors/PageCollector.js} +64 -133
  21. package/build/src/collectors/ServiceWorkerCollector.js +171 -0
  22. package/build/src/daemon/client.js +75 -17
  23. package/build/src/daemon/daemon.js +92 -30
  24. package/build/src/daemon/utils.js +24 -8
  25. package/build/src/{DevtoolsUtils.js → devtools/DevtoolsUtils.js} +94 -88
  26. package/build/src/devtools/McpHostBindingAdapter.js +165 -0
  27. package/build/src/{issue-descriptions.js → devtools/issueDescriptions.js} +8 -6
  28. package/build/src/formatters/ConsoleFormatter.js +44 -11
  29. package/build/src/formatters/HeapSnapshotFormatter.js +140 -9
  30. package/build/src/formatters/IssueFormatter.js +7 -7
  31. package/build/src/formatters/NetworkFormatter.js +10 -8
  32. package/build/src/index.js +101 -232
  33. package/build/src/opera/branding.js +34 -0
  34. package/build/src/opera/browserFlags.js +85 -0
  35. package/build/src/opera/browserLaunch.js +52 -0
  36. package/build/src/opera/policy.js +50 -0
  37. package/build/src/opera/serviceWorkerRetry.js +37 -0
  38. package/build/src/opera/toolHandlerHooks.js +39 -0
  39. package/build/src/{tools → opera/tools}/opera.js +116 -21
  40. package/build/src/processors/HeapSnapshotManager.js +258 -0
  41. package/build/src/{trace-processing/parse.js → processors/PerformanceTrace.js} +9 -9
  42. package/build/src/telemetry/ClearcutLogger.js +12 -119
  43. package/build/src/telemetry/WatchdogClient.js +5 -5
  44. package/build/src/telemetry/errors.js +1 -1
  45. package/build/src/telemetry/flagUtils.js +6 -6
  46. package/build/src/telemetry/{toolMetricsUtils.js → metricsRegistry.js} +4 -11
  47. package/build/src/telemetry/persistence.js +3 -3
  48. package/build/src/telemetry/transformation.js +193 -0
  49. package/build/src/telemetry/watchdog/ClearcutSender.js +11 -11
  50. package/build/src/telemetry/watchdog/main.js +6 -6
  51. package/build/src/third_party/THIRD_PARTY_NOTICES +232 -1061
  52. package/build/src/third_party/bundled-packages.json +7 -8
  53. package/build/src/third_party/devtools-formatter-worker.js +63 -22
  54. package/build/src/third_party/devtools-heap-snapshot-worker.js +809 -182
  55. package/build/src/third_party/index.js +69558 -74691
  56. package/build/src/third_party/issue-descriptions/CoepCorpNotSameOrigin.md +2 -2
  57. package/build/src/third_party/issue-descriptions/CoepCorpNotSameOriginAfterDefaultedToSameOriginByCoep.md +3 -3
  58. package/build/src/third_party/issue-descriptions/CoepCorpNotSameSite.md +2 -2
  59. package/build/src/third_party/issue-descriptions/CoepFrameResourceNeedsCoepHeader.md +1 -1
  60. package/build/src/third_party/issue-descriptions/CompatibilityModeQuirks.md +1 -1
  61. package/build/src/third_party/issue-descriptions/CookieAttributeValueExceedsMaxSize.md +2 -2
  62. package/build/src/third_party/issue-descriptions/LowTextContrast.md +1 -1
  63. package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureErrorRead.md +1 -1
  64. package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureErrorSet.md +1 -1
  65. package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureWarnRead.md +1 -1
  66. package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureWarnSet.md +1 -1
  67. package/build/src/third_party/issue-descriptions/SameSiteUnspecifiedLaxAllowUnsafeRead.md +3 -3
  68. package/build/src/third_party/issue-descriptions/SameSiteUnspecifiedLaxAllowUnsafeSet.md +3 -3
  69. package/build/src/third_party/issue-descriptions/bounceTrackingMitigations.md +1 -1
  70. package/build/src/third_party/issue-descriptions/connectionAllowlistEmbeddingRequirementNotSatisfied.md +26 -0
  71. package/build/src/third_party/issue-descriptions/connectionAllowlistIFrameAttributeLoosensEmbeddingRequirement.md +19 -0
  72. package/build/src/third_party/issue-descriptions/connectionAllowlistInvalidAllowConnectionAllowlistFrom.md +21 -0
  73. package/build/src/third_party/issue-descriptions/connectionAllowlistInvalidAllowlistItemType.md +2 -2
  74. package/build/src/third_party/issue-descriptions/connectionAllowlistInvalidHeader.md +2 -2
  75. package/build/src/third_party/issue-descriptions/connectionAllowlistInvalidUrlPattern.md +3 -3
  76. package/build/src/third_party/issue-descriptions/connectionAllowlistItemNotInnerList.md +2 -2
  77. package/build/src/third_party/issue-descriptions/connectionAllowlistMoreThanOneList.md +2 -2
  78. package/build/src/third_party/issue-descriptions/connectionAllowlistReportingEndpointNotToken.md +1 -1
  79. package/build/src/third_party/issue-descriptions/cookieCrossSiteRedirectDowngrade.md +3 -3
  80. package/build/src/third_party/issue-descriptions/cookieExcludeBlockedWithinRelatedWebsiteSet.md +1 -1
  81. package/build/src/third_party/issue-descriptions/cookieExcludePortMismatch.md +1 -1
  82. package/build/src/third_party/issue-descriptions/cookieExcludeSchemeMismatch.md +1 -1
  83. package/build/src/third_party/issue-descriptions/cookieExcludeThirdPartyPhaseoutSet.md +1 -1
  84. package/build/src/third_party/issue-descriptions/cookieWarnThirdPartyPhaseoutRead.md +1 -1
  85. package/build/src/third_party/issue-descriptions/corsAllowCredentialsRequired.md +1 -1
  86. package/build/src/third_party/issue-descriptions/corsDisabledScheme.md +2 -2
  87. package/build/src/third_party/issue-descriptions/corsDisallowedByMode.md +2 -2
  88. package/build/src/third_party/issue-descriptions/corsHeaderDisallowedByPreflightResponse.md +1 -1
  89. package/build/src/third_party/issue-descriptions/corsInvalidHeaderValues.md +3 -3
  90. package/build/src/third_party/issue-descriptions/corsLocalNetworkAccessPermissionDenied.md +1 -1
  91. package/build/src/third_party/issue-descriptions/corsMethodDisallowedByPreflightResponse.md +1 -1
  92. package/build/src/third_party/issue-descriptions/corsNoCorsRedirectModeNotFollow.md +1 -1
  93. package/build/src/third_party/issue-descriptions/corsOriginMismatch.md +3 -3
  94. package/build/src/third_party/issue-descriptions/corsPreflightResponseInvalid.md +2 -2
  95. package/build/src/third_party/issue-descriptions/corsRedirectContainsCredentials.md +3 -3
  96. package/build/src/third_party/issue-descriptions/corsWildcardOriginNotAllowed.md +4 -3
  97. package/build/src/third_party/issue-descriptions/cspEvalViolation.md +4 -4
  98. package/build/src/third_party/issue-descriptions/cspInlineViolation.md +2 -2
  99. package/build/src/third_party/issue-descriptions/cspTrustedTypesPolicyViolation.md +2 -2
  100. package/build/src/third_party/issue-descriptions/cspTrustedTypesSinkViolation.md +4 -4
  101. package/build/src/third_party/issue-descriptions/cspURLViolation.md +4 -4
  102. package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsEmptyList.md +1 -0
  103. package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsHttpNotFound.md +1 -0
  104. package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsInvalidContentType.md +1 -0
  105. package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsInvalidResponse.md +1 -0
  106. package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsNoResponse.md +1 -0
  107. package/build/src/third_party/issue-descriptions/emailVerificationRequestDnsFetchFailed.md +1 -0
  108. package/build/src/third_party/issue-descriptions/emailVerificationRequestDnsInvalidRecord.md +1 -0
  109. package/build/src/third_party/issue-descriptions/emailVerificationRequestEmailVerificationWellKnownHttpNotFound.md +1 -0
  110. package/build/src/third_party/issue-descriptions/emailVerificationRequestEmailVerificationWellKnownInvalidContentType.md +1 -0
  111. package/build/src/third_party/issue-descriptions/emailVerificationRequestEmailVerificationWellKnownInvalidResponse.md +1 -0
  112. package/build/src/third_party/issue-descriptions/emailVerificationRequestEmailVerificationWellKnownNoResponse.md +1 -0
  113. package/build/src/third_party/issue-descriptions/emailVerificationRequestInvalidEmail.md +1 -0
  114. package/build/src/third_party/issue-descriptions/emailVerificationRequestJwksHttpNotFound.md +1 -0
  115. package/build/src/third_party/issue-descriptions/emailVerificationRequestJwksInvalidResponse.md +1 -0
  116. package/build/src/third_party/issue-descriptions/emailVerificationRequestKeyBindingSigningFailed.md +1 -0
  117. package/build/src/third_party/issue-descriptions/emailVerificationRequestRpOriginIsOpaque.md +1 -0
  118. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenHttpNotFound.md +1 -0
  119. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenInvalidContentType.md +1 -0
  120. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenInvalidResponse.md +1 -0
  121. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenInvalidSdJwt.md +1 -0
  122. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenMalformedSdJwt.md +1 -0
  123. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenNoResponse.md +1 -0
  124. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidAudience.md +1 -0
  125. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidIssuedAt.md +1 -0
  126. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidNonce.md +1 -0
  127. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidSdHash.md +1 -0
  128. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidTyp.md +1 -0
  129. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingAud.md +1 -0
  130. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingCnf.md +1 -0
  131. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingIat.md +1 -0
  132. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingNonce.md +1 -0
  133. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingSdHash.md +1 -0
  134. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbSignatureFailed.md +1 -0
  135. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidEmail.md +1 -0
  136. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidEmailVerified.md +1 -0
  137. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidHolderKey.md +1 -0
  138. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidIssuedAt.md +1 -0
  139. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidIssuer.md +1 -0
  140. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtJwksMissingKeys.md +1 -0
  141. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtMissingCnf.md +1 -0
  142. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtMissingEmail.md +1 -0
  143. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtMissingIat.md +1 -0
  144. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtMissingIss.md +1 -0
  145. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtSignatureFailed.md +1 -0
  146. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtUnsupportedHeaderAlg.md +1 -0
  147. package/build/src/third_party/issue-descriptions/emailVerificationRequestUserLoggedOut.md +1 -0
  148. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownAccountsEndpointCrossOrigin.md +1 -0
  149. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownHttpNotFound.md +1 -0
  150. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownInvalidContentType.md +1 -0
  151. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownInvalidResponse.md +1 -0
  152. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownIssuanceEndpointCrossOrigin.md +1 -0
  153. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownListEmpty.md +1 -0
  154. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownMissingAccountsEndpoint.md +1 -0
  155. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownMissingIssuanceEndpoint.md +1 -0
  156. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownNoResponse.md +1 -0
  157. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownUnsupportedSigningAlgorithm.md +1 -0
  158. package/build/src/third_party/issue-descriptions/federatedAuthRequestAccountsHttpNotFound.md +1 -1
  159. package/build/src/third_party/issue-descriptions/federatedAuthRequestAccountsInvalidResponse.md +1 -1
  160. package/build/src/third_party/issue-descriptions/federatedAuthRequestAccountsNoResponse.md +1 -1
  161. package/build/src/third_party/issue-descriptions/federatedAuthRequestApprovalDeclined.md +1 -1
  162. package/build/src/third_party/issue-descriptions/federatedAuthRequestCanceled.md +1 -1
  163. package/build/src/third_party/issue-descriptions/federatedAuthRequestErrorFetchingSignin.md +1 -1
  164. package/build/src/third_party/issue-descriptions/federatedAuthRequestErrorIdToken.md +1 -1
  165. package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenHttpNotFound.md +1 -1
  166. package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenInvalidRequest.md +1 -1
  167. package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenInvalidResponse.md +1 -1
  168. package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenNoResponse.md +1 -1
  169. package/build/src/third_party/issue-descriptions/federatedAuthRequestInvalidSigninResponse.md +1 -1
  170. package/build/src/third_party/issue-descriptions/federatedAuthRequestManifestHttpNotFound.md +1 -1
  171. package/build/src/third_party/issue-descriptions/federatedAuthRequestManifestInvalidResponse.md +1 -1
  172. package/build/src/third_party/issue-descriptions/federatedAuthRequestManifestNoResponse.md +1 -1
  173. package/build/src/third_party/issue-descriptions/federatedAuthRequestTooManyRequests.md +1 -1
  174. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestInvalidAccountsResponse.md +1 -1
  175. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestInvalidConfigOrWellKnown.md +1 -1
  176. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNoAccountSharingPermission.md +1 -1
  177. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNoApiPermission.md +1 -1
  178. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNoReturningUserFromFetchedAccounts.md +1 -1
  179. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotIframe.md +1 -1
  180. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotPotentiallyTrustworthy.md +1 -1
  181. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotSameOrigin.md +1 -1
  182. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotSignedInWithIdp.md +1 -1
  183. package/build/src/third_party/issue-descriptions/fetchingPartitionedBlobURL.md +2 -2
  184. package/build/src/third_party/issue-descriptions/genericBackUINavigationWouldSkipAd.md +4 -0
  185. package/build/src/third_party/issue-descriptions/genericFormAriaLabelledByToNonExistingIdError.md +2 -2
  186. package/build/src/third_party/issue-descriptions/genericFormAutocompleteAttributeEmptyError.md +1 -1
  187. package/build/src/third_party/issue-descriptions/genericFormDuplicateIdForInputError.md +1 -1
  188. package/build/src/third_party/issue-descriptions/genericFormEmptyIdAndNameAttributesForInputError.md +2 -2
  189. package/build/src/third_party/issue-descriptions/genericFormInputAssignedAutocompleteValueToIdOrNameAttributeError.md +2 -2
  190. package/build/src/third_party/issue-descriptions/genericFormInputHasWrongButWellIntendedAutocompleteValueError.md +1 -1
  191. package/build/src/third_party/issue-descriptions/genericFormInputWithNoLabelError.md +1 -1
  192. package/build/src/third_party/issue-descriptions/genericFormLabelForMatchesNonExistingIdError.md +2 -2
  193. package/build/src/third_party/issue-descriptions/genericFormLabelForNameError.md +1 -1
  194. package/build/src/third_party/issue-descriptions/genericFormLabelHasNeitherForNorNestedInputError.md +1 -1
  195. package/build/src/third_party/issue-descriptions/genericFormModelContextMissingToolName.md +1 -1
  196. package/build/src/third_party/issue-descriptions/genericFormModelContextParameterMissingName.md +1 -1
  197. package/build/src/third_party/issue-descriptions/genericFormModelContextRequiredParameterMissingName.md +1 -1
  198. package/build/src/third_party/issue-descriptions/genericNavigationEntryMarkedSkippable.md +2 -2
  199. package/build/src/third_party/issue-descriptions/heavyAd.md +1 -1
  200. package/build/src/third_party/issue-descriptions/lazyLoadImageZeroSize.md +7 -0
  201. package/build/src/third_party/issue-descriptions/navigatingPartitionedBlobURL.md +3 -3
  202. package/build/src/third_party/issue-descriptions/permissionElementActivationDisabled.md +1 -1
  203. package/build/src/third_party/issue-descriptions/permissionElementActivationDisabledWithOccluder.md +1 -1
  204. package/build/src/third_party/issue-descriptions/permissionElementActivationDisabledWithOccluderParent.md +1 -1
  205. package/build/src/third_party/issue-descriptions/permissionElementFencedFrameDisallowed.md +2 -2
  206. package/build/src/third_party/issue-descriptions/permissionElementFontSizeTooLarge.md +2 -2
  207. package/build/src/third_party/issue-descriptions/permissionElementFontSizeTooSmall.md +2 -2
  208. package/build/src/third_party/issue-descriptions/permissionElementInsetBoxShadowUnsupported.md +1 -1
  209. package/build/src/third_party/issue-descriptions/permissionElementInvalidDisplayStyle.md +2 -2
  210. package/build/src/third_party/issue-descriptions/permissionElementInvalidSizeValue.md +2 -2
  211. package/build/src/third_party/issue-descriptions/permissionElementInvalidType.md +2 -2
  212. package/build/src/third_party/issue-descriptions/permissionElementInvalidTypeActivation.md +2 -2
  213. package/build/src/third_party/issue-descriptions/permissionElementNonOpaqueColor.md +1 -1
  214. package/build/src/third_party/issue-descriptions/permissionElementPaddingBottomUnsupported.md +2 -2
  215. package/build/src/third_party/issue-descriptions/permissionElementPaddingRightUnsupported.md +2 -2
  216. package/build/src/third_party/issue-descriptions/permissionElementRequestInProgress.md +2 -2
  217. package/build/src/third_party/issue-descriptions/permissionElementSecurityChecksFailed.md +1 -1
  218. package/build/src/third_party/issue-descriptions/permissionElementTypeNotSupported.md +2 -2
  219. package/build/src/third_party/issue-descriptions/permissionElementUntrustedEvent.md +1 -1
  220. package/build/src/third_party/issue-descriptions/placeholderDescriptionForInvisibleIssues.md +1 -1
  221. package/build/src/third_party/issue-descriptions/selectElementAccessibilityDisallowedOptGroupChild.md +1 -1
  222. package/build/src/third_party/issue-descriptions/selectElementAccessibilityDisallowedSelectChild.md +1 -1
  223. package/build/src/third_party/issue-descriptions/selectElementAccessibilityInteractiveContentAttributesSelectDescendant.md +1 -1
  224. package/build/src/third_party/issue-descriptions/selectElementAccessibilityInteractiveContentLegendChild.md +1 -1
  225. package/build/src/third_party/issue-descriptions/selectElementAccessibilityInteractiveContentOptionChild.md +1 -1
  226. package/build/src/third_party/issue-descriptions/selectElementAccessibilityNonPhrasingContentOptionChild.md +1 -1
  227. package/build/src/third_party/issue-descriptions/selectivePermissionsIntervention.md +3 -3
  228. package/build/src/third_party/issue-descriptions/sharedArrayBuffer.md +1 -1
  229. package/build/src/third_party/issue-descriptions/sharedDictionaryUseErrorCrossOriginNoCorsRequest.md +1 -1
  230. package/build/src/third_party/issue-descriptions/sharedDictionaryUseErrorMatchingDictionaryNotUsed.md +1 -1
  231. package/build/src/third_party/issue-descriptions/sharedDictionaryUseErrorUnexpectedContentDictionaryHeader.md +1 -1
  232. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorCossOriginNoCorsRequest.md +1 -1
  233. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorDisallowedBySettings.md +1 -1
  234. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorExpiredResponse.md +1 -1
  235. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorFeatureDisabled.md +2 -2
  236. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorInvalidStructuredHeader.md +1 -1
  237. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNavigationRequest.md +2 -2
  238. package/build/src/third_party/issue-descriptions/sriInvalidSignatureHeader.md +4 -4
  239. package/build/src/third_party/issue-descriptions/sriInvalidSignatureInputHeader.md +4 -4
  240. package/build/src/third_party/issue-descriptions/sriMissingSignatureHeader.md +1 -1
  241. package/build/src/third_party/issue-descriptions/sriMissingSignatureInputHeader.md +2 -2
  242. package/build/src/third_party/issue-descriptions/sriSignatureHeaderValueIsIncorrectLength.md +2 -2
  243. package/build/src/third_party/issue-descriptions/sriSignatureHeaderValueIsNotByteSequence.md +4 -4
  244. package/build/src/third_party/issue-descriptions/sriSignatureHeaderValueIsParameterized.md +4 -4
  245. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidComponentName.md +1 -1
  246. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidComponentType.md +2 -2
  247. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidDerivedComponentParameter.md +1 -1
  248. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidHeaderComponentParameter.md +1 -1
  249. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidParameter.md +2 -2
  250. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderKeyIdLength.md +5 -5
  251. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderMissingLabel.md +3 -3
  252. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderMissingRequiredParameters.md +2 -2
  253. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderValueMissingComponents.md +2 -2
  254. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderValueNotInnerList.md +2 -2
  255. package/build/src/third_party/issue-descriptions/sriValidationFailedIntegrityMismatch.md +4 -4
  256. package/build/src/third_party/issue-descriptions/sriValidationFailedInvalidLength.md +3 -3
  257. package/build/src/third_party/issue-descriptions/sriValidationFailedSignatureExpired.md +2 -2
  258. package/build/src/third_party/issue-descriptions/sriValidationFailedSignatureMismatch.md +2 -2
  259. package/build/src/third_party/issue-descriptions/stylesheetLateImport.md +1 -1
  260. package/build/src/third_party/issue-descriptions/summaryElementAccessibilityInteractiveContentSummaryDescendant.md +1 -1
  261. package/build/src/third_party/issue-descriptions/unencodedDigestIncorrectDigestLength.md +2 -2
  262. package/build/src/third_party/issue-descriptions/unencodedDigestIncorrectDigestType.md +3 -3
  263. package/build/src/third_party/issue-descriptions/unencodedDigestMalformedDictionary.md +2 -2
  264. package/build/src/third_party/issue-descriptions/unencodedDigestUnknownAlgorithm.md +1 -1
  265. package/build/src/third_party/lighthouse-devtools-mcp-bundle.js +3177 -6340
  266. package/build/src/tools/ToolDefinition.js +2 -2
  267. package/build/src/tools/categories.js +3 -0
  268. package/build/src/tools/console.js +13 -0
  269. package/build/src/tools/emulation.js +30 -3
  270. package/build/src/tools/extensions.js +8 -1
  271. package/build/src/tools/input.js +246 -128
  272. package/build/src/tools/lighthouse.js +14 -7
  273. package/build/src/tools/memory.js +289 -25
  274. package/build/src/tools/network.js +10 -7
  275. package/build/src/tools/pages.js +33 -92
  276. package/build/src/tools/performance.js +67 -52
  277. package/build/src/tools/pwa.js +128 -0
  278. package/build/src/tools/screencast.js +35 -12
  279. package/build/src/tools/screenshot.js +234 -75
  280. package/build/src/tools/script.js +130 -45
  281. package/build/src/tools/slim/tools.js +3 -0
  282. package/build/src/tools/snapshot.js +7 -4
  283. package/build/src/tools/thirdPartyDeveloper.js +19 -9
  284. package/build/src/tools/tools.js +3 -1
  285. package/build/src/tools/webmcp.js +3 -1
  286. package/build/src/utils/WaitForHelper.js +299 -0
  287. package/build/src/utils/check-for-updates.js +8 -2
  288. package/build/src/utils/files.js +42 -3
  289. package/build/src/utils/logger.js +51 -0
  290. package/build/src/utils/polyfill.js +11 -0
  291. package/build/src/utils/url.js +43 -0
  292. package/build/src/version.js +1 -4
  293. package/package.json +43 -33
  294. package/build/src/DevToolsConnectionAdapter.js +0 -70
  295. package/build/src/HeapSnapshotManager.js +0 -110
  296. package/build/src/Mutex.js +0 -38
  297. package/build/src/WaitForHelper.js +0 -195
  298. package/build/src/bin/cliDefinitions.js +0 -616
  299. package/build/src/logger.js +0 -37
  300. package/build/src/polyfill.js +0 -8
  301. package/build/src/telemetry/metricUtils.js +0 -15
  302. package/build/src/third_party/issue-descriptions/SameSiteExcludeContextDowngradeRead.md +0 -8
  303. package/build/src/third_party/issue-descriptions/SameSiteExcludeContextDowngradeSet.md +0 -8
  304. package/build/src/third_party/issue-descriptions/SameSiteExcludeNavigationContextDowngrade.md +0 -8
  305. package/build/src/third_party/issue-descriptions/SameSiteWarnCrossDowngradeRead.md +0 -8
  306. package/build/src/third_party/issue-descriptions/SameSiteWarnCrossDowngradeSet.md +0 -8
  307. package/build/src/third_party/issue-descriptions/SameSiteWarnStrictLaxDowngradeStrict.md +0 -8
  308. package/build/src/third_party/issue-descriptions/arInsecureContext.md +0 -7
  309. package/build/src/third_party/issue-descriptions/arInvalidInfoHeader.md +0 -5
  310. package/build/src/third_party/issue-descriptions/arInvalidRegisterOsSourceHeader.md +0 -5
  311. package/build/src/third_party/issue-descriptions/arInvalidRegisterOsTriggerHeader.md +0 -5
  312. package/build/src/third_party/issue-descriptions/arInvalidRegisterSourceHeader.md +0 -5
  313. package/build/src/third_party/issue-descriptions/arInvalidRegisterTriggerHeader.md +0 -5
  314. package/build/src/third_party/issue-descriptions/arNavigationRegistrationUniqueScopeAlreadySet.md +0 -5
  315. package/build/src/third_party/issue-descriptions/arNavigationRegistrationWithoutTransientUserActivation.md +0 -6
  316. package/build/src/third_party/issue-descriptions/arNoRegisterOsSourceHeader.md +0 -5
  317. package/build/src/third_party/issue-descriptions/arNoRegisterOsTriggerHeader.md +0 -5
  318. package/build/src/third_party/issue-descriptions/arNoRegisterSourceHeader.md +0 -5
  319. package/build/src/third_party/issue-descriptions/arNoRegisterTriggerHeader.md +0 -5
  320. package/build/src/third_party/issue-descriptions/arNoWebOrOsSupport.md +0 -4
  321. package/build/src/third_party/issue-descriptions/arOsSourceIgnored.md +0 -18
  322. package/build/src/third_party/issue-descriptions/arOsTriggerIgnored.md +0 -19
  323. package/build/src/third_party/issue-descriptions/arPermissionPolicyDisabled.md +0 -8
  324. package/build/src/third_party/issue-descriptions/arSourceAndTriggerHeaders.md +0 -9
  325. package/build/src/third_party/issue-descriptions/arSourceIgnored.md +0 -13
  326. package/build/src/third_party/issue-descriptions/arTriggerIgnored.md +0 -12
  327. package/build/src/third_party/issue-descriptions/arUntrustworthyReportingOrigin.md +0 -10
  328. package/build/src/third_party/issue-descriptions/arWebAndOsHeaders.md +0 -11
  329. package/build/src/utils/string.js +0 -37
  330. package/build/src/utils/types.js +0 -7
@@ -9,9 +9,10 @@ import { execSync } from 'node:child_process';
9
9
  import fs from 'node:fs';
10
10
  import os from 'node:os';
11
11
  import path from 'node:path';
12
- import { logger } from './logger.js';
13
12
  import { puppeteer } from './third_party/index.js';
13
+ import { logger, puppeteerLogger } from './utils/logger.js';
14
14
  let browser;
15
+ let browserMode;
15
16
  function makeTargetFilter(enableExtensions = false) {
16
17
  const ignoredPrefixes = new Set(['chrome://', 'chrome-untrusted://']);
17
18
  if (!enableExtensions) {
@@ -42,6 +43,9 @@ export async function ensureBrowserConnected(options) {
42
43
  targetFilter: makeTargetFilter(enableExtensions),
43
44
  defaultViewport: null,
44
45
  handleDevToolsAsPage: true,
46
+ blocklist: options.blocklist,
47
+ allowlist: options.allowlist,
48
+ logger: puppeteerLogger,
45
49
  };
46
50
  let autoConnect = false;
47
51
  if (options.wsEndpoint) {
@@ -95,16 +99,21 @@ export async function ensureBrowserConnected(options) {
95
99
  else {
96
100
  throw new Error('Either browserURL, wsEndpoint, channel or userDataDir must be provided');
97
101
  }
98
- logger('Connecting Puppeteer to ', JSON.stringify(connectOptions));
102
+ logger?.('Connecting Puppeteer to ', JSON.stringify(connectOptions));
99
103
  try {
100
- browser = await puppeteer.connect(connectOptions);
104
+ // Assign mode before browser so a concurrent closeBrowser() never sees
105
+ // `browser` set with `browserMode` still undefined (would fall through
106
+ // to the disconnect() path and orphan a launched Chrome).
107
+ const connected = await puppeteer.connect(connectOptions);
108
+ browserMode = 'connected';
109
+ browser = connected;
101
110
  }
102
111
  catch (err) {
103
112
  throw new Error(`Could not connect to Chrome. ${autoConnect ? `Check if Chrome is running and remote debugging is enabled by going to chrome://inspect/#remote-debugging.` : `Check if Chrome is running.`}`, {
104
113
  cause: err,
105
114
  });
106
115
  }
107
- logger('Connected Puppeteer');
116
+ logger?.('Connected Puppeteer');
108
117
  return browser;
109
118
  }
110
119
  export function detectDisplay() {
@@ -170,6 +179,9 @@ export async function launch(options) {
170
179
  acceptInsecureCerts: options.acceptInsecureCerts,
171
180
  handleDevToolsAsPage: true,
172
181
  enableExtensions: options.enableExtensions,
182
+ blocklist: options.blocklist,
183
+ allowlist: options.allowlist,
184
+ logger: puppeteerLogger,
173
185
  });
174
186
  if (options.logFile) {
175
187
  // FIXME: we are probably subscribing too late to catch startup logs. We
@@ -200,12 +212,40 @@ export async function ensureBrowserLaunched(options) {
200
212
  if (browser?.connected) {
201
213
  return browser;
202
214
  }
203
- browser = await launch(options);
215
+ // Assign mode before browser; see the connect path above for rationale.
216
+ const launched = await launch(options);
217
+ browserMode = 'launched';
218
+ browser = launched;
204
219
  return browser;
205
220
  }
206
221
  export function getCurrentBrowser() {
207
222
  return browser;
208
223
  }
224
+ /**
225
+ * Shutdown hook for the active browser. Closes a launched browser (so the
226
+ * Chrome subprocess is reaped) or disconnects from an attached browser (so
227
+ * the user's Chrome instance stays alive). No-op if no browser is active or
228
+ * the connection has already been dropped. Called from the server entrypoint
229
+ * on stdin EOF / SIGTERM / SIGINT.
230
+ */
231
+ export async function closeBrowser() {
232
+ const b = browser;
233
+ const mode = browserMode;
234
+ browser = undefined;
235
+ browserMode = undefined;
236
+ if (!b || !b.connected) {
237
+ return;
238
+ }
239
+ if (mode === 'launched') {
240
+ await b.close().catch(err => {
241
+ logger?.('Failed to close browser', err);
242
+ });
243
+ return;
244
+ }
245
+ await b.disconnect().catch(err => {
246
+ logger?.('Failed to disconnect from browser', err);
247
+ });
248
+ }
209
249
  export async function closeBrowserIfOpen() {
210
250
  if (browser?.connected) {
211
251
  try {
@@ -3,10 +3,10 @@
3
3
  * Copyright 2025 Google LLC
4
4
  * SPDX-License-Identifier: Apache-2.0
5
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';
6
+ import { FakeIssuesManager } from '../devtools/DevtoolsUtils.js';
7
+ import { DevTools } from '../third_party/index.js';
8
+ import { createIdGenerator, stableIdSymbol, } from '../utils/id.js';
9
+ import { logger } from '../utils/logger.js';
10
10
  export class UncaughtError {
11
11
  details;
12
12
  targetId;
@@ -16,113 +16,59 @@ export class UncaughtError {
16
16
  }
17
17
  }
18
18
  export class PageCollector {
19
- #browser;
20
- #listenersInitializer;
21
- #listeners = new WeakMap();
19
+ pptrPage;
20
+ #listeners;
22
21
  maxNavigationSaved = 3;
23
22
  /**
24
23
  * This maps a Page to a list of navigations with a sub-list
25
24
  * of all collected resources.
26
25
  * The newer navigations come first.
27
26
  */
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
- }
27
+ storage = [[]];
28
+ constructor(page, listeners, maxResourcesPerNavigation) {
29
+ this.pptrPage = page;
75
30
  const idGenerator = createIdGenerator();
76
- const storedLists = [[]];
77
- this.storage.set(page, storedLists);
78
- const listeners = this.#listenersInitializer(value => {
31
+ const listenerMap = listeners(value => {
79
32
  const withId = value;
80
33
  withId[stableIdSymbol] = idGenerator();
81
- const navigations = this.storage.get(page) ?? [[]];
82
- navigations[0].push(withId);
34
+ this.storage[0].push(withId);
35
+ if (maxResourcesPerNavigation !== undefined &&
36
+ this.storage[0].length > maxResourcesPerNavigation) {
37
+ this.storage[0].splice(0, this.storage[0].length - maxResourcesPerNavigation);
38
+ }
83
39
  });
84
- listeners['framenavigated'] = (frame) => {
40
+ listenerMap['framenavigated'] = (frame) => {
85
41
  // Only split the storage on main frame navigation
86
- if (frame !== page.mainFrame()) {
42
+ if (frame !== this.pptrPage.mainFrame()) {
87
43
  return;
88
44
  }
89
- this.splitAfterNavigation(page);
45
+ this.splitAfterNavigation();
90
46
  };
91
- for (const [name, listener] of Object.entries(listeners)) {
92
- page.on(name, listener);
47
+ for (const [name, listener] of Object.entries(listenerMap)) {
48
+ this.pptrPage.on(name, listener);
93
49
  }
94
- this.#listeners.set(page, listeners);
50
+ this.#listeners = listenerMap;
95
51
  }
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);
52
+ dispose() {
53
+ if (this.#listeners) {
54
+ for (const [name, listener] of Object.entries(this.#listeners)) {
55
+ this.pptrPage.off(name, listener);
110
56
  }
111
57
  }
112
- this.storage.delete(page);
113
58
  }
114
- getData(page, includePreservedData) {
115
- const navigations = this.storage.get(page);
116
- if (!navigations) {
117
- return [];
118
- }
59
+ splitAfterNavigation() {
60
+ // Add the latest navigation first
61
+ this.storage.unshift([]);
62
+ this.storage.splice(this.maxNavigationSaved);
63
+ }
64
+ getData(includePreservedData) {
119
65
  if (!includePreservedData) {
120
- return navigations[0];
66
+ return this.storage[0];
121
67
  }
122
68
  const data = [];
123
69
  for (let index = this.maxNavigationSaved; index >= 0; index--) {
124
- if (navigations[index]) {
125
- data.push(...navigations[index]);
70
+ if (this.storage[index]) {
71
+ data.push(...this.storage[index]);
126
72
  }
127
73
  }
128
74
  return data;
@@ -130,23 +76,15 @@ export class PageCollector {
130
76
  getIdForResource(resource) {
131
77
  return resource[stableIdSymbol] ?? -1;
132
78
  }
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;
79
+ getById(stableId) {
80
+ const item = this.find(item => item[stableIdSymbol] === stableId);
81
+ if (!item) {
82
+ throw new Error('Request not found for selected page');
141
83
  }
142
- throw new Error('Request not found for selected page');
84
+ return item;
143
85
  }
144
- find(page, filter) {
145
- const navigations = this.storage.get(page);
146
- if (!navigations) {
147
- return;
148
- }
149
- for (const navigation of navigations) {
86
+ find(filter) {
87
+ for (const navigation of this.storage) {
150
88
  const item = navigation.find(filter);
151
89
  if (item) {
152
90
  return item;
@@ -156,19 +94,15 @@ export class PageCollector {
156
94
  }
157
95
  }
158
96
  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
- }
97
+ #subscriber;
98
+ constructor(page, listeners) {
99
+ super(page, listeners);
100
+ this.#subscriber = new PageEventSubscriber(this.pptrPage);
101
+ this.#subscriber.subscribe();
167
102
  }
168
- cleanupPageDestroyed(page) {
169
- super.cleanupPageDestroyed(page);
170
- this.#subscribedPages.get(page)?.unsubscribe();
171
- this.#subscribedPages.delete(page);
103
+ dispose() {
104
+ super.dispose();
105
+ this.#subscriber?.unsubscribe();
172
106
  }
173
107
  }
174
108
  class PageEventSubscriber {
@@ -194,7 +128,7 @@ class PageEventSubscriber {
194
128
  this.#issueAggregator = new DevTools.IssueAggregator(this.#issueManager);
195
129
  this.#issueAggregator.addEventListener("AggregatedIssueUpdated" /* DevTools.IssueAggregatorEvents.AGGREGATED_ISSUE_UPDATED */, this.#onAggregatedIssue);
196
130
  }
197
- async subscribe() {
131
+ subscribe() {
198
132
  this.#resetIssueAggregator();
199
133
  this.#page.on('framenavigated', this.#onFrameNavigated);
200
134
  this.#page.on('issue', this.#onIssueAdded);
@@ -232,16 +166,16 @@ class PageEventSubscriber {
232
166
  };
233
167
  #onIssueAdded = (inspectorIssue) => {
234
168
  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') {
169
+ // @ts-expect-error The types are missmatched but they
170
+ // are coming from CDP
171
+ if (!DevTools.isIssueCodeSupported(inspectorIssue.code)) {
238
172
  return;
239
173
  }
240
174
  const issue = DevTools.createIssuesFromProtocolIssue(null,
241
175
  // @ts-expect-error Protocol types diverge.
242
176
  inspectorIssue)[0];
243
177
  if (!issue) {
244
- logger('No issue mapping for for the issue: ', inspectorIssue.code);
178
+ logger?.('No issue mapping for for the issue: ', inspectorIssue.code);
245
179
  return;
246
180
  }
247
181
  const primaryKey = issue.primaryKey();
@@ -256,28 +190,25 @@ class PageEventSubscriber {
256
190
  });
257
191
  }
258
192
  catch (error) {
259
- logger('Error creating a new issue', error);
193
+ logger?.('Error creating a new issue', error);
260
194
  }
261
195
  };
262
196
  }
263
197
  export class NetworkCollector extends PageCollector {
264
- constructor(browser, listeners = collect => {
198
+ static MAX_REQUESTS_PER_NAVIGATION = 1_000;
199
+ constructor(page, maxRequestsPerNavigation = NetworkCollector.MAX_REQUESTS_PER_NAVIGATION, listeners = collect => {
265
200
  return {
266
201
  request: req => {
267
202
  collect(req);
268
203
  },
269
204
  };
270
205
  }) {
271
- super(browser, listeners);
206
+ super(page, listeners, maxRequestsPerNavigation);
272
207
  }
273
- splitAfterNavigation(page) {
274
- const navigations = this.storage.get(page) ?? [];
275
- if (!navigations) {
276
- return;
277
- }
278
- const requests = navigations[0];
208
+ splitAfterNavigation() {
209
+ const requests = this.storage[0];
279
210
  const lastRequestIdx = requests.findLastIndex(request => {
280
- return request.frame() === page.mainFrame()
211
+ return request.frame() === this.pptrPage.mainFrame()
281
212
  ? request.isNavigationRequest()
282
213
  : false;
283
214
  });
@@ -286,12 +217,12 @@ export class NetworkCollector extends PageCollector {
286
217
  // Keep the reference
287
218
  if (lastRequestIdx !== -1) {
288
219
  const fromCurrentNavigation = requests.splice(lastRequestIdx);
289
- navigations.unshift(fromCurrentNavigation);
220
+ this.storage.unshift(fromCurrentNavigation);
290
221
  }
291
222
  else {
292
- navigations.unshift([]);
223
+ this.storage.unshift([]);
293
224
  }
294
- navigations.splice(this.maxNavigationSaved);
225
+ this.storage.splice(this.maxNavigationSaved);
295
226
  }
296
227
  }
297
228
  //# sourceMappingURL=PageCollector.js.map
@@ -0,0 +1,171 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import { UncaughtError } from './PageCollector.js';
7
+ import { createIdGenerator, stableIdSymbol } from '../utils/id.js';
8
+ const CHROME_EXTENSION_PREFIX = 'chrome-extension://';
9
+ export class ServiceWorkerSubscriber {
10
+ #target;
11
+ #callback;
12
+ #session;
13
+ #worker;
14
+ constructor(target, callback) {
15
+ this.#target = target;
16
+ this.#callback = callback;
17
+ }
18
+ async subscribe() {
19
+ this.#session = await this.#target.createCDPSession();
20
+ await this.#session.send('Runtime.enable');
21
+ this.#session.on('Runtime.exceptionThrown', this.#onExceptionThrown);
22
+ this.#worker = (await this.#target.worker()) ?? undefined;
23
+ if (this.#worker) {
24
+ this.#worker.on('console', this.#onConsole);
25
+ }
26
+ }
27
+ async unsubscribe() {
28
+ if (this.#worker) {
29
+ this.#worker.off('console', this.#onConsole);
30
+ }
31
+ await this.#session?.detach();
32
+ }
33
+ #onConsole = (message) => {
34
+ this.#callback(message);
35
+ };
36
+ #onExceptionThrown = (event) => {
37
+ const url = this.#target.url();
38
+ const extensionId = extractExtensionId(url);
39
+ if (extensionId) {
40
+ this.#callback(new UncaughtError(event.exceptionDetails, extensionId));
41
+ }
42
+ };
43
+ }
44
+ export class ServiceWorkerConsoleCollector {
45
+ #storage = new Map();
46
+ #maxLogs;
47
+ #browser;
48
+ #serviceWorkerSubscribers = new Map();
49
+ #idGenerator = createIdGenerator();
50
+ constructor(browser, maxLogs = 1000) {
51
+ this.#browser = browser;
52
+ this.#maxLogs = maxLogs;
53
+ }
54
+ async init(workers) {
55
+ if (!this.#browser) {
56
+ return;
57
+ }
58
+ this.#browser.on('targetcreated', this.#onTargetCreated);
59
+ this.#browser.on('targetdestroyed', this.#onTargetDestroyed);
60
+ for (const worker of workers) {
61
+ void this.#onTargetCreated(worker.target);
62
+ }
63
+ }
64
+ dispose() {
65
+ if (!this.#browser) {
66
+ return;
67
+ }
68
+ this.#browser.off('targetcreated', this.#onTargetCreated);
69
+ this.#browser.off('targetdestroyed', this.#onTargetDestroyed);
70
+ for (const subscriber of this.#serviceWorkerSubscribers.values()) {
71
+ subscriber.unsubscribe().catch(err => {
72
+ if (err instanceof Error &&
73
+ !err.message.includes('Target closed') &&
74
+ !err.message.includes('Session closed')) {
75
+ // Swallow error as we are tearing down the system
76
+ }
77
+ });
78
+ }
79
+ this.#serviceWorkerSubscribers.clear();
80
+ }
81
+ #onTargetCreated = async (target) => {
82
+ if (this.#serviceWorkerSubscribers.has(target)) {
83
+ return;
84
+ }
85
+ const origin = target.url();
86
+ if (target.type() === 'service_worker' && isExtensionOrigin(origin)) {
87
+ const extensionId = extractExtensionId(origin);
88
+ if (!extensionId) {
89
+ return;
90
+ }
91
+ const subscriber = new ServiceWorkerSubscriber(target, item => {
92
+ this.addLog(extensionId, item);
93
+ });
94
+ try {
95
+ await subscriber.subscribe();
96
+ }
97
+ catch (err) {
98
+ if (err instanceof Error &&
99
+ !err.message.includes('Target closed') &&
100
+ !err.message.includes('Session closed')) {
101
+ throw err;
102
+ }
103
+ }
104
+ this.#serviceWorkerSubscribers.set(target, subscriber);
105
+ }
106
+ };
107
+ #onTargetDestroyed = async (target) => {
108
+ const subscriber = this.#serviceWorkerSubscribers.get(target);
109
+ if (subscriber) {
110
+ try {
111
+ await subscriber.unsubscribe();
112
+ }
113
+ catch (err) {
114
+ if (err instanceof Error &&
115
+ !err.message.includes('Target closed') &&
116
+ !err.message.includes('Session closed')) {
117
+ throw err;
118
+ }
119
+ }
120
+ this.#serviceWorkerSubscribers.delete(target);
121
+ }
122
+ };
123
+ addLog(extensionId, log) {
124
+ const logs = this.#storage.get(extensionId) ?? [];
125
+ const withId = log;
126
+ withId[stableIdSymbol] = this.#idGenerator();
127
+ logs.push(withId);
128
+ if (logs.length > this.#maxLogs) {
129
+ logs.shift();
130
+ }
131
+ this.#storage.set(extensionId, logs);
132
+ }
133
+ getData(extensionId) {
134
+ return this.#storage.get(extensionId) ?? [];
135
+ }
136
+ getById(extensionId, stableId) {
137
+ const logs = this.#storage.get(extensionId);
138
+ if (!logs) {
139
+ throw new Error('No logs found for selected extension');
140
+ }
141
+ const item = logs.find(item => item[stableIdSymbol] === stableId);
142
+ if (item) {
143
+ return item;
144
+ }
145
+ throw new Error('Log not found for selected extension');
146
+ }
147
+ find(extensionId, filter) {
148
+ const logs = this.#storage.get(extensionId);
149
+ if (!logs) {
150
+ return;
151
+ }
152
+ return logs.find(filter);
153
+ }
154
+ clearLogs(extensionId) {
155
+ this.#storage.delete(extensionId);
156
+ }
157
+ }
158
+ function extractExtensionId(origin) {
159
+ if (!origin || !isExtensionOrigin(origin)) {
160
+ return null;
161
+ }
162
+ const pathPart = origin.substring(CHROME_EXTENSION_PREFIX.length);
163
+ const slashIndex = pathPart.indexOf('/');
164
+ // if there's no / it means that pathPart is now the extensionId, otherwise
165
+ // we take everything until the first /
166
+ return slashIndex === -1 ? pathPart : pathPart.substring(0, slashIndex);
167
+ }
168
+ function isExtensionOrigin(origin) {
169
+ return origin.startsWith(CHROME_EXTENSION_PREFIX);
170
+ }
171
+ //# sourceMappingURL=ServiceWorkerCollector.js.map