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
@@ -0,0 +1,39 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Opera Norway AS. All rights reserved.
4
+ *
5
+ * This file is an original work developed by Opera.
6
+ */
7
+ import { ToolCategory } from '../tools/categories.js';
8
+ import { ensureBrowserFlagsForTool } from './browserFlags.js';
9
+ export function createOperaToolHooks(deps) {
10
+ return {
11
+ bypassMutex(tool) {
12
+ return tool.annotations.category === ToolCategory.OPERA;
13
+ },
14
+ async beforeInvoke(tool) {
15
+ await ensureBrowserFlagsForTool(tool.name, deps.serverArgs, deps.logFile, { resetContext: deps.resetContext });
16
+ },
17
+ makeLogCallback(extra) {
18
+ const sendNotification = extra?.sendNotification;
19
+ if (!sendNotification) {
20
+ return undefined;
21
+ }
22
+ return (message) => {
23
+ // `logger` carries the MCP request ID so the opera-cli bridge can route
24
+ // this chunk to the correct HTTP response (see bridge.ts requestLoggers).
25
+ // `data` stays a plain string so non-bridge MCP hosts (Claude Desktop,
26
+ // VS Code, etc.) continue to render it as readable text.
27
+ void sendNotification.call(extra, {
28
+ method: 'notifications/message',
29
+ params: {
30
+ level: 'info',
31
+ data: message,
32
+ logger: String(extra?.requestId),
33
+ },
34
+ });
35
+ };
36
+ },
37
+ };
38
+ }
39
+ //# sourceMappingURL=toolHandlerHooks.js.map
@@ -4,28 +4,11 @@
4
4
  *
5
5
  * This file is an original work developed by Opera.
6
6
  */
7
- import { zod } from '../third_party/index.js';
8
- import { ToolCategory } from './categories.js';
9
- import { definePageTool } from './ToolDefinition.js';
7
+ import { zod } from '../../third_party/index.js';
8
+ import { ToolCategory } from '../../tools/categories.js';
9
+ import { definePageTool } from '../../tools/ToolDefinition.js';
10
+ import { withServiceWorkerRetry } from '../serviceWorkerRetry.js';
10
11
  const getCDPSession = (page) => page._client();
11
- const MAX_SW_RETRIES = 5;
12
- const SW_RETRY_DELAY_MS = 2500;
13
- const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
14
- async function withServiceWorkerRetry(fn) {
15
- let lastError;
16
- for (let attempt = 0; attempt < MAX_SW_RETRIES; attempt++) {
17
- try {
18
- return await fn();
19
- }
20
- catch (e) {
21
- lastError = e;
22
- if (attempt < MAX_SW_RETRIES - 1) {
23
- await sleep(SW_RETRY_DELAY_MS);
24
- }
25
- }
26
- }
27
- throw lastError;
28
- }
29
12
  const dispatchAction = async (session, payload) => {
30
13
  const response = (await withServiceWorkerRetry(() => session.send('Opera.dispatchAction', { payload })));
31
14
  return response.result;
@@ -77,6 +60,7 @@ export const operaChat = definePageTool({
77
60
  name: 'opera_chat',
78
61
  description: "Send a chat prompt to Opera's built-in AI and return the response. Only available when connected to Opera Neon.",
79
62
  blockedByDialog: false,
63
+ verifyFilesSchema: {},
80
64
  annotations: {
81
65
  category: ToolCategory.OPERA,
82
66
  readOnlyHint: false,
@@ -111,6 +95,7 @@ export const operaDo = definePageTool({
111
95
  name: 'opera_do',
112
96
  description: "Instruct Opera's built-in AI to perform an action on the current page and return the result. Only available when connected to Opera Neon.",
113
97
  blockedByDialog: false,
98
+ verifyFilesSchema: {},
114
99
  annotations: {
115
100
  category: ToolCategory.OPERA,
116
101
  readOnlyHint: false,
@@ -142,6 +127,7 @@ export const operaMake = definePageTool({
142
127
  name: 'opera_make',
143
128
  description: "Ask Opera's built-in AI to create or generate content and return the result. Only available when connected to Opera Neon.",
144
129
  blockedByDialog: false,
130
+ verifyFilesSchema: {},
145
131
  annotations: {
146
132
  category: ToolCategory.OPERA,
147
133
  readOnlyHint: false,
@@ -168,6 +154,7 @@ export const operaResearch = definePageTool({
168
154
  name: 'opera_research',
169
155
  description: "Ask Opera's built-in AI to research a topic and return a summary. Only available when connected to Opera Neon.",
170
156
  blockedByDialog: false,
157
+ verifyFilesSchema: {},
171
158
  annotations: {
172
159
  category: ToolCategory.OPERA,
173
160
  readOnlyHint: false,
@@ -205,6 +192,7 @@ export const operaListModels = definePageTool({
205
192
  name: 'opera_list_models',
206
193
  description: 'List available AI models for Opera chat. Returns model IDs, display names, and which is the default. Only available when connected to Opera Neon.',
207
194
  blockedByDialog: false,
195
+ verifyFilesSchema: {},
208
196
  annotations: {
209
197
  category: ToolCategory.OPERA,
210
198
  readOnlyHint: true,
@@ -222,4 +210,111 @@ export const operaListModels = definePageTool({
222
210
  }
223
211
  },
224
212
  });
213
+ export const operaListMcpServers = definePageTool({
214
+ name: 'opera_list_mcp_servers',
215
+ description: 'List MCP servers registered in the browser, ' +
216
+ 'including their connection status. ' +
217
+ 'Only available when connected to Opera Neon.',
218
+ blockedByDialog: false,
219
+ verifyFilesSchema: {},
220
+ annotations: {
221
+ category: ToolCategory.OPERA,
222
+ readOnlyHint: true,
223
+ },
224
+ schema: {},
225
+ handler: async (request, response) => {
226
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
227
+ const session = getCDPSession(request.page.pptrPage);
228
+ try {
229
+ const result = await dispatchAction(session, {
230
+ action: 'listMcpServers',
231
+ });
232
+ response.appendResponseLine(result);
233
+ }
234
+ catch (e) {
235
+ response.appendResponseLine(`Opera.dispatchAction(listMcpServers) failed with error: ${e.message}`);
236
+ }
237
+ },
238
+ });
239
+ export const operaListMcpTools = definePageTool({
240
+ name: 'opera_list_mcp_tools',
241
+ description: 'List the tools exposed by a single MCP server. Prefer opera_list_mcp_servers ' +
242
+ 'when discovering everything at once; use this for a targeted refresh of one server. ' +
243
+ 'Only available when connected to Opera Neon.',
244
+ blockedByDialog: false,
245
+ verifyFilesSchema: {},
246
+ annotations: {
247
+ category: ToolCategory.OPERA,
248
+ readOnlyHint: true,
249
+ },
250
+ schema: {
251
+ server: zod
252
+ .string()
253
+ .min(1)
254
+ .describe('The MCP server name (from opera_list_mcp_servers).'),
255
+ },
256
+ handler: async (request, response) => {
257
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
258
+ const session = getCDPSession(request.page.pptrPage);
259
+ try {
260
+ const result = await dispatchAction(session, {
261
+ action: 'listMcpTools',
262
+ server: request.params.server,
263
+ });
264
+ response.appendResponseLine(result);
265
+ }
266
+ catch (e) {
267
+ response.appendResponseLine(`Opera.dispatchAction(listMcpTools) failed with error: ${e.message}`);
268
+ }
269
+ },
270
+ });
271
+ export const operaCallMcpTool = definePageTool({
272
+ name: 'opera_call_mcp_tool',
273
+ description: 'Execute a tool on a specific MCP server registered in the browser. ' +
274
+ 'Use opera_list_mcp_servers to discover available ' +
275
+ 'servers and tools. ' +
276
+ 'Only available when connected to Opera Neon.',
277
+ blockedByDialog: false,
278
+ verifyFilesSchema: {},
279
+ annotations: {
280
+ category: ToolCategory.OPERA,
281
+ readOnlyHint: false,
282
+ },
283
+ schema: {
284
+ server: zod
285
+ .string()
286
+ .min(1)
287
+ .describe('The MCP server name (from opera_list_mcp_servers).'),
288
+ tool: zod
289
+ .string()
290
+ .min(1)
291
+ .describe('The tool name to execute on the server.'),
292
+ parameters: zod
293
+ .record(zod.unknown())
294
+ .optional()
295
+ .describe('Parameters to pass to the tool. Omit if the tool takes none.'),
296
+ },
297
+ handler: async (request, response) => {
298
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
299
+ const session = getCDPSession(request.page.pptrPage);
300
+ try {
301
+ const payload = {
302
+ action: 'callMcpTool',
303
+ server: request.params.server,
304
+ tool: request.params.tool,
305
+ };
306
+ if (request.params.parameters !== undefined) {
307
+ payload['parameters'] = request.params.parameters;
308
+ }
309
+ const result = await dispatchWithStreamedResponse(session, payload, chunk => response.sendLog(chunk), request.signal);
310
+ response.appendResponseLine(result);
311
+ }
312
+ catch (e) {
313
+ if (e.name === 'AbortError') {
314
+ throw e;
315
+ }
316
+ response.appendResponseLine(`Opera.dispatchWithStreamedResponse(callMcpTool) failed with error: ${e.message}`);
317
+ }
318
+ },
319
+ });
225
320
  //# sourceMappingURL=opera.js.map
@@ -0,0 +1,258 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import fsSync from 'node:fs';
7
+ import path from 'node:path';
8
+ import { DevTools } from '../third_party/index.js';
9
+ import { createIdGenerator, stableIdSymbol, } from '../utils/id.js';
10
+ export class HeapSnapshotManager {
11
+ #snapshotIdGenerator = createIdGenerator();
12
+ #snapshots = new Map();
13
+ async getSnapshot(filePath) {
14
+ const absolutePath = path.resolve(filePath);
15
+ const cached = this.#snapshots.get(absolutePath);
16
+ if (cached) {
17
+ return cached.snapshot;
18
+ }
19
+ const uid = this.#snapshotIdGenerator();
20
+ const { snapshot, worker } = await this.#loadSnapshot(absolutePath, uid);
21
+ this.#snapshots.set(absolutePath, {
22
+ snapshot,
23
+ worker,
24
+ idToClassKey: [''],
25
+ classKeyToId: new Map(),
26
+ });
27
+ return snapshot;
28
+ }
29
+ async #applyNodeFilter(snapshot, filter, filterName, objectId) {
30
+ if (filterName === 'attributedToSpecificNativeContext') {
31
+ if (objectId === undefined) {
32
+ throw new Error('objectId is required when filterName is attributedToSpecificNativeContext');
33
+ }
34
+ const nodeIndex = await snapshot.nodeIndexForId(objectId);
35
+ if (nodeIndex === undefined) {
36
+ throw new Error(`Node with ID ${objectId} not found`);
37
+ }
38
+ filter.filterName = `nativeContext_${nodeIndex}`;
39
+ }
40
+ else if (filterName) {
41
+ filter.filterName = filterName;
42
+ }
43
+ }
44
+ async getAggregates(filePath, filterName, objectId) {
45
+ const snapshot = await this.getSnapshot(filePath);
46
+ const filter = new DevTools.HeapSnapshotModel.HeapSnapshotModel.NodeFilter();
47
+ await this.#applyNodeFilter(snapshot, filter, filterName, objectId);
48
+ const aggregates = await snapshot.aggregatesWithFilter(filter);
49
+ let objectCount = 0;
50
+ let totalSelfSize = 0;
51
+ for (const [key, aggregate] of Object.entries(aggregates)) {
52
+ const id = await this.getOrCreateIdForClassKey(filePath, key);
53
+ aggregate[stableIdSymbol] = id;
54
+ objectCount += aggregate.count;
55
+ totalSelfSize += aggregate.self;
56
+ }
57
+ return {
58
+ aggregates,
59
+ objectCount,
60
+ totalSelfSize,
61
+ };
62
+ }
63
+ async getStats(filePath) {
64
+ const snapshot = await this.getSnapshot(filePath);
65
+ return await snapshot.getStatistics();
66
+ }
67
+ async getStaticData(filePath) {
68
+ const snapshot = await this.getSnapshot(filePath);
69
+ return snapshot.staticData;
70
+ }
71
+ async getNativeContextSizes(filePath) {
72
+ const snapshot = await this.getSnapshot(filePath);
73
+ return await snapshot.getNativeContextSizes();
74
+ }
75
+ async getRetainedByContextSummary(filePath) {
76
+ const snapshot = await this.getSnapshot(filePath);
77
+ return await snapshot.getRetainedByContextSummary();
78
+ }
79
+ async getOrCreateIdForClassKey(filePath, classKey) {
80
+ const cached = this.#getCachedSnapshot(filePath);
81
+ let id = cached.classKeyToId.get(classKey);
82
+ if (!id) {
83
+ id = cached.idToClassKey.length;
84
+ cached.classKeyToId.set(classKey, id);
85
+ cached.idToClassKey.push(classKey);
86
+ }
87
+ return id;
88
+ }
89
+ async getNodesById(filePath, id, filterName, objectId) {
90
+ const snapshot = await this.getSnapshot(filePath);
91
+ const filter = new DevTools.HeapSnapshotModel.HeapSnapshotModel.NodeFilter();
92
+ await this.#applyNodeFilter(snapshot, filter, filterName, objectId);
93
+ const className = await this.resolveClassKeyFromId(filePath, id);
94
+ if (!className) {
95
+ throw new Error(`Class with ID ${id} not found in heap snapshot`);
96
+ }
97
+ const provider = snapshot.createNodesProviderForClass(className, filter);
98
+ return await provider.serializeItemsRange(0, Infinity);
99
+ }
100
+ async getRetainers(filePath, nodeId) {
101
+ const snapshot = await this.getSnapshot(filePath);
102
+ const nodeIndex = await snapshot.nodeIndexForId(nodeId);
103
+ if (nodeIndex === undefined) {
104
+ throw new Error(`Node with ID ${nodeId} not found`);
105
+ }
106
+ const provider = snapshot.createRetainingEdgesProvider(nodeIndex);
107
+ return await provider.serializeItemsRange(0, Infinity);
108
+ }
109
+ async getObjectInfo(filePath, nodeId) {
110
+ const snapshot = await this.getSnapshot(filePath);
111
+ const nodeIndex = await snapshot.nodeIndexForId(nodeId);
112
+ if (nodeIndex === undefined) {
113
+ throw new Error(`Node with ID ${nodeId} not found`);
114
+ }
115
+ return await snapshot.getObjectInfo(nodeIndex);
116
+ }
117
+ async getRetainingPaths(filePath, nodeId, maxDepth, maxNodes, maxSiblings) {
118
+ const snapshot = await this.getSnapshot(filePath);
119
+ const nodeIndex = await snapshot.nodeIndexForId(nodeId);
120
+ if (nodeIndex === undefined) {
121
+ throw new Error(`Node with ID ${nodeId} not found`);
122
+ }
123
+ return await snapshot.getRetainingPaths(nodeIndex, maxDepth, maxNodes, maxSiblings);
124
+ }
125
+ async getDominatorsOf(filePath, nodeId) {
126
+ const snapshot = await this.getSnapshot(filePath);
127
+ const nodeIndex = await snapshot.nodeIndexForId(nodeId);
128
+ if (nodeIndex === undefined) {
129
+ throw new Error(`Node with ID ${nodeId} not found`);
130
+ }
131
+ return await snapshot.getDominatorsOf(nodeIndex);
132
+ }
133
+ async getEdges(filePath, nodeId, options) {
134
+ const snapshot = await this.getSnapshot(filePath);
135
+ const nodeIndex = await snapshot.nodeIndexForId(nodeId);
136
+ if (nodeIndex === undefined) {
137
+ throw new Error(`Node with ID ${nodeId} not found`);
138
+ }
139
+ const provider = snapshot.createEdgesProvider(nodeIndex, options);
140
+ return await provider.serializeItemsRange(0, Infinity);
141
+ }
142
+ async getClassDiffs(baseFilePath, currentFilePath) {
143
+ const rawDiffs = await this.#getSortedRawClassDiffs(baseFilePath, currentFilePath);
144
+ return rawDiffs.map(rawDiff => ({
145
+ className: rawDiff.name,
146
+ addedCount: rawDiff.addedCount,
147
+ removedCount: rawDiff.removedCount,
148
+ countDelta: rawDiff.countDelta,
149
+ addedSize: rawDiff.addedSize,
150
+ removedSize: rawDiff.removedSize,
151
+ sizeDelta: rawDiff.sizeDelta,
152
+ }));
153
+ }
154
+ async getDetailedClassDiff(baseFilePath, currentFilePath, classIndex) {
155
+ const classDiffs = await this.#getSortedRawClassDiffs(baseFilePath, currentFilePath);
156
+ const rawDiff = classDiffs[classIndex];
157
+ if (!rawDiff) {
158
+ throw new Error(`Invalid classIndex: ${classIndex}. Total classes with changes: ${classDiffs.length}`);
159
+ }
160
+ return {
161
+ className: rawDiff.name,
162
+ addedCount: rawDiff.addedCount,
163
+ removedCount: rawDiff.removedCount,
164
+ countDelta: rawDiff.countDelta,
165
+ addedSize: rawDiff.addedSize,
166
+ removedSize: rawDiff.removedSize,
167
+ sizeDelta: rawDiff.sizeDelta,
168
+ addedIds: rawDiff.addedIds ?? [],
169
+ addedSelfSizes: rawDiff.addedSelfSizes ?? [],
170
+ deletedIds: rawDiff.deletedIds ?? [],
171
+ deletedSelfSizes: rawDiff.deletedSelfSizes ?? [],
172
+ };
173
+ }
174
+ #getCachedSnapshot(filePath) {
175
+ const absolutePath = path.resolve(filePath);
176
+ const cached = this.#snapshots.get(absolutePath);
177
+ if (!cached) {
178
+ throw new Error(`Snapshot not loaded for ${filePath}`);
179
+ }
180
+ return cached;
181
+ }
182
+ async #getSortedRawClassDiffs(baseFilePath, currentFilePath) {
183
+ const baseSnapshot = await this.getSnapshot(baseFilePath);
184
+ const currentSnapshot = await this.getSnapshot(currentFilePath);
185
+ const interfaceDefinitions = await currentSnapshot.interfaceDefinitions();
186
+ const aggregatesForDiff = await baseSnapshot.aggregatesForDiff(interfaceDefinitions);
187
+ const baseSnapshotId = baseSnapshot.uid;
188
+ if (baseSnapshotId === undefined) {
189
+ throw new Error('Base snapshot UID is undefined');
190
+ }
191
+ // DevTools calculateSnapshotDiff uses the first parameter (baseSnapshotId)
192
+ // as a cache key. We pass the unique UID of the base snapshot.
193
+ const rawDiffs = await currentSnapshot.calculateSnapshotDiff(baseSnapshotId, aggregatesForDiff);
194
+ // Return a filtered and sorted array here to ensure that
195
+ // compare_heapsnapshot_summary and compare_heapsnapshot_details agree
196
+ // on indices.
197
+ return Object.values(rawDiffs)
198
+ .filter(diff => diff.addedCount > 0 || diff.removedCount > 0)
199
+ .sort((a, b) => b.sizeDelta - a.sizeDelta);
200
+ }
201
+ async resolveClassKeyFromId(filePath, id) {
202
+ const cached = this.#getCachedSnapshot(filePath);
203
+ return cached.idToClassKey[id];
204
+ }
205
+ async #loadSnapshot(absolutePath, uid) {
206
+ const workerProxy = new DevTools.HeapSnapshotModel.HeapSnapshotProxy.HeapSnapshotWorkerProxy(() => {
207
+ /* noop */
208
+ }, DevTools.Common.Console.Console.instance(), import.meta.resolve('../third_party/devtools-heap-snapshot-worker.js'));
209
+ try {
210
+ const { promise: snapshotPromise, resolve: resolveSnapshot } = Promise.withResolvers();
211
+ const loaderProxy = workerProxy.createLoader(uid, snapshotProxy => {
212
+ resolveSnapshot(snapshotProxy);
213
+ });
214
+ const fileStream = fsSync.createReadStream(absolutePath, {
215
+ encoding: 'utf-8',
216
+ highWaterMark: 1024 * 1024,
217
+ });
218
+ for await (const chunk of fileStream) {
219
+ await loaderProxy.write(chunk);
220
+ }
221
+ await loaderProxy.close();
222
+ const snapshot = await snapshotPromise;
223
+ return { snapshot, worker: workerProxy };
224
+ }
225
+ catch (error) {
226
+ // The worker is created before the read, and a failed load never reaches
227
+ // the #snapshots map, so dispose()/disposeAll() can never clean it up.
228
+ // Dispose it here to avoid leaking a worker on every failed load (e.g. a
229
+ // missing or invalid .heapsnapshot path).
230
+ workerProxy.dispose();
231
+ throw error;
232
+ }
233
+ }
234
+ async getDuplicateStrings(filePath) {
235
+ const snapshot = await this.getSnapshot(filePath);
236
+ return await snapshot.getDuplicateStrings();
237
+ }
238
+ hasSnapshots() {
239
+ return this.#snapshots.size > 0;
240
+ }
241
+ disposeSnapshot(filePath) {
242
+ const absolutePath = path.resolve(filePath);
243
+ const cached = this.#snapshots.get(absolutePath);
244
+ if (cached) {
245
+ cached.worker.dispose();
246
+ this.#snapshots.delete(absolutePath);
247
+ return true;
248
+ }
249
+ return false;
250
+ }
251
+ dispose() {
252
+ for (const cached of this.#snapshots.values()) {
253
+ cached.worker.dispose();
254
+ }
255
+ this.#snapshots.clear();
256
+ }
257
+ }
258
+ //# sourceMappingURL=HeapSnapshotManager.js.map
@@ -3,13 +3,13 @@
3
3
  * Copyright 2025 Google LLC
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
- import { logger } from '../logger.js';
7
6
  import { DevTools } from '../third_party/index.js';
7
+ import { logger } from '../utils/logger.js';
8
8
  const engine = DevTools.TraceEngine.TraceModel.Model.createWithAllHandlers();
9
9
  export function traceResultIsSuccess(x) {
10
10
  return 'parsedTrace' in x;
11
11
  }
12
- export async function parseRawTraceBuffer(buffer) {
12
+ export async function parseRawTraceBuffer(buffer, metadata) {
13
13
  engine.resetProcessor();
14
14
  if (!buffer) {
15
15
  return {
@@ -25,7 +25,7 @@ export async function parseRawTraceBuffer(buffer) {
25
25
  try {
26
26
  const data = JSON.parse(asString);
27
27
  const events = Array.isArray(data) ? data : data.traceEvents;
28
- await engine.parse(events);
28
+ await engine.parse(events, { metadata });
29
29
  const parsedTrace = engine.parsedTrace();
30
30
  if (!parsedTrace) {
31
31
  return {
@@ -40,7 +40,7 @@ export async function parseRawTraceBuffer(buffer) {
40
40
  }
41
41
  catch (e) {
42
42
  const errorText = e instanceof Error ? e.message : JSON.stringify(e);
43
- logger(`Unexpected error parsing trace: ${errorText}`);
43
+ logger?.(`Unexpected error parsing trace: ${errorText}`);
44
44
  return {
45
45
  error: errorText,
46
46
  };
@@ -51,9 +51,9 @@ const extraFormatDescriptions = `Information on performance traces may contain m
51
51
  ${DevTools.PerformanceTraceFormatter.callFrameDataFormatDescription}
52
52
 
53
53
  ${DevTools.PerformanceTraceFormatter.networkDataFormatDescription}`;
54
- export function getTraceSummary(result) {
54
+ export function getTraceSummary(result, deviceScope) {
55
55
  const focus = DevTools.AgentFocus.fromParsedTrace(result.parsedTrace);
56
- const formatter = new DevTools.PerformanceTraceFormatter(focus);
56
+ const formatter = new DevTools.PerformanceTraceFormatter(focus, deviceScope);
57
57
  const summaryText = formatter.formatTraceSummary();
58
58
  return `## Summary of Performance trace findings:
59
59
  ${summaryText}
@@ -61,7 +61,7 @@ ${summaryText}
61
61
  ## Details on call tree & network request formats:
62
62
  ${extraFormatDescriptions}`;
63
63
  }
64
- export function getInsightOutput(result, insightSetId, insightName) {
64
+ export function getInsightOutput(result, insightSetId, insightName, deviceScope) {
65
65
  if (!result.insights) {
66
66
  return {
67
67
  error: 'No Performance insights are available for this trace.',
@@ -79,7 +79,7 @@ export function getInsightOutput(result, insightSetId, insightName) {
79
79
  error: `No Insight with the name ${insightName} found. Double check the name you provided is accurate and try again.`,
80
80
  };
81
81
  }
82
- const formatter = new DevTools.PerformanceInsightFormatter(DevTools.AgentFocus.fromParsedTrace(result.parsedTrace), matchingInsight);
82
+ const formatter = new DevTools.PerformanceInsightFormatter(DevTools.AgentFocus.fromParsedTrace(result.parsedTrace), matchingInsight, deviceScope);
83
83
  return { output: formatter.formatInsight() };
84
84
  }
85
- //# sourceMappingURL=parse.js.map
85
+ //# sourceMappingURL=PerformanceTrace.js.map