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.
- package/README.md +108 -8
- package/build/src/McpContext.js +365 -365
- package/build/src/McpPage.js +475 -48
- package/build/src/McpResponse.js +462 -288
- package/build/src/SlimMcpResponse.js +1 -1
- package/build/src/TextSnapshot.js +101 -22
- package/build/src/ToolHandler.js +142 -20
- package/build/src/bin/check-latest-version.js +27 -1
- package/build/src/bin/chrome-devtools-cli-options.js +614 -10
- package/build/src/bin/chrome-devtools-mcp-cli-options.js +119 -32
- package/build/src/bin/chrome-devtools-mcp-main.js +50 -7
- package/build/src/bin/chrome-devtools-mcp.js +7 -4
- package/build/src/bin/chrome-devtools.js +57 -14
- package/build/src/bin/opera-devtools-cli-options.js +3 -829
- package/build/src/bin/opera-devtools-mcp-cli-options.js +3 -348
- package/build/src/bin/opera-devtools-mcp-main.js +3 -37
- package/build/src/bin/opera-devtools-mcp.js +4 -19
- package/build/src/bin/opera-devtools.js +4 -185
- package/build/src/browser.js +45 -5
- package/build/src/{PageCollector.js → collectors/PageCollector.js} +64 -133
- package/build/src/collectors/ServiceWorkerCollector.js +171 -0
- package/build/src/daemon/client.js +75 -17
- package/build/src/daemon/daemon.js +92 -30
- package/build/src/daemon/utils.js +24 -8
- package/build/src/{DevtoolsUtils.js → devtools/DevtoolsUtils.js} +94 -88
- package/build/src/devtools/McpHostBindingAdapter.js +165 -0
- package/build/src/{issue-descriptions.js → devtools/issueDescriptions.js} +8 -6
- package/build/src/formatters/ConsoleFormatter.js +44 -11
- package/build/src/formatters/HeapSnapshotFormatter.js +140 -9
- package/build/src/formatters/IssueFormatter.js +7 -7
- package/build/src/formatters/NetworkFormatter.js +10 -8
- package/build/src/index.js +101 -232
- package/build/src/opera/branding.js +34 -0
- package/build/src/opera/browserFlags.js +85 -0
- package/build/src/opera/browserLaunch.js +52 -0
- package/build/src/opera/policy.js +50 -0
- package/build/src/opera/serviceWorkerRetry.js +37 -0
- package/build/src/opera/toolHandlerHooks.js +39 -0
- package/build/src/{tools → opera/tools}/opera.js +116 -21
- package/build/src/processors/HeapSnapshotManager.js +258 -0
- package/build/src/{trace-processing/parse.js → processors/PerformanceTrace.js} +9 -9
- package/build/src/telemetry/ClearcutLogger.js +12 -119
- package/build/src/telemetry/WatchdogClient.js +5 -5
- package/build/src/telemetry/errors.js +1 -1
- package/build/src/telemetry/flagUtils.js +6 -6
- package/build/src/telemetry/{toolMetricsUtils.js → metricsRegistry.js} +4 -11
- package/build/src/telemetry/persistence.js +3 -3
- package/build/src/telemetry/transformation.js +193 -0
- package/build/src/telemetry/watchdog/ClearcutSender.js +11 -11
- package/build/src/telemetry/watchdog/main.js +6 -6
- package/build/src/third_party/THIRD_PARTY_NOTICES +232 -1061
- package/build/src/third_party/bundled-packages.json +7 -8
- package/build/src/third_party/devtools-formatter-worker.js +63 -22
- package/build/src/third_party/devtools-heap-snapshot-worker.js +809 -182
- package/build/src/third_party/index.js +69558 -74691
- package/build/src/third_party/issue-descriptions/CoepCorpNotSameOrigin.md +2 -2
- package/build/src/third_party/issue-descriptions/CoepCorpNotSameOriginAfterDefaultedToSameOriginByCoep.md +3 -3
- package/build/src/third_party/issue-descriptions/CoepCorpNotSameSite.md +2 -2
- package/build/src/third_party/issue-descriptions/CoepFrameResourceNeedsCoepHeader.md +1 -1
- package/build/src/third_party/issue-descriptions/CompatibilityModeQuirks.md +1 -1
- package/build/src/third_party/issue-descriptions/CookieAttributeValueExceedsMaxSize.md +2 -2
- package/build/src/third_party/issue-descriptions/LowTextContrast.md +1 -1
- package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureErrorRead.md +1 -1
- package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureErrorSet.md +1 -1
- package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureWarnRead.md +1 -1
- package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureWarnSet.md +1 -1
- package/build/src/third_party/issue-descriptions/SameSiteUnspecifiedLaxAllowUnsafeRead.md +3 -3
- package/build/src/third_party/issue-descriptions/SameSiteUnspecifiedLaxAllowUnsafeSet.md +3 -3
- package/build/src/third_party/issue-descriptions/bounceTrackingMitigations.md +1 -1
- package/build/src/third_party/issue-descriptions/connectionAllowlistEmbeddingRequirementNotSatisfied.md +26 -0
- package/build/src/third_party/issue-descriptions/connectionAllowlistIFrameAttributeLoosensEmbeddingRequirement.md +19 -0
- package/build/src/third_party/issue-descriptions/connectionAllowlistInvalidAllowConnectionAllowlistFrom.md +21 -0
- package/build/src/third_party/issue-descriptions/connectionAllowlistInvalidAllowlistItemType.md +2 -2
- package/build/src/third_party/issue-descriptions/connectionAllowlistInvalidHeader.md +2 -2
- package/build/src/third_party/issue-descriptions/connectionAllowlistInvalidUrlPattern.md +3 -3
- package/build/src/third_party/issue-descriptions/connectionAllowlistItemNotInnerList.md +2 -2
- package/build/src/third_party/issue-descriptions/connectionAllowlistMoreThanOneList.md +2 -2
- package/build/src/third_party/issue-descriptions/connectionAllowlistReportingEndpointNotToken.md +1 -1
- package/build/src/third_party/issue-descriptions/cookieCrossSiteRedirectDowngrade.md +3 -3
- package/build/src/third_party/issue-descriptions/cookieExcludeBlockedWithinRelatedWebsiteSet.md +1 -1
- package/build/src/third_party/issue-descriptions/cookieExcludePortMismatch.md +1 -1
- package/build/src/third_party/issue-descriptions/cookieExcludeSchemeMismatch.md +1 -1
- package/build/src/third_party/issue-descriptions/cookieExcludeThirdPartyPhaseoutSet.md +1 -1
- package/build/src/third_party/issue-descriptions/cookieWarnThirdPartyPhaseoutRead.md +1 -1
- package/build/src/third_party/issue-descriptions/corsAllowCredentialsRequired.md +1 -1
- package/build/src/third_party/issue-descriptions/corsDisabledScheme.md +2 -2
- package/build/src/third_party/issue-descriptions/corsDisallowedByMode.md +2 -2
- package/build/src/third_party/issue-descriptions/corsHeaderDisallowedByPreflightResponse.md +1 -1
- package/build/src/third_party/issue-descriptions/corsInvalidHeaderValues.md +3 -3
- package/build/src/third_party/issue-descriptions/corsLocalNetworkAccessPermissionDenied.md +1 -1
- package/build/src/third_party/issue-descriptions/corsMethodDisallowedByPreflightResponse.md +1 -1
- package/build/src/third_party/issue-descriptions/corsNoCorsRedirectModeNotFollow.md +1 -1
- package/build/src/third_party/issue-descriptions/corsOriginMismatch.md +3 -3
- package/build/src/third_party/issue-descriptions/corsPreflightResponseInvalid.md +2 -2
- package/build/src/third_party/issue-descriptions/corsRedirectContainsCredentials.md +3 -3
- package/build/src/third_party/issue-descriptions/corsWildcardOriginNotAllowed.md +4 -3
- package/build/src/third_party/issue-descriptions/cspEvalViolation.md +4 -4
- package/build/src/third_party/issue-descriptions/cspInlineViolation.md +2 -2
- package/build/src/third_party/issue-descriptions/cspTrustedTypesPolicyViolation.md +2 -2
- package/build/src/third_party/issue-descriptions/cspTrustedTypesSinkViolation.md +4 -4
- package/build/src/third_party/issue-descriptions/cspURLViolation.md +4 -4
- package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsEmptyList.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsHttpNotFound.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsInvalidContentType.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsInvalidResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsNoResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestDnsFetchFailed.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestDnsInvalidRecord.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestEmailVerificationWellKnownHttpNotFound.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestEmailVerificationWellKnownInvalidContentType.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestEmailVerificationWellKnownInvalidResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestEmailVerificationWellKnownNoResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestInvalidEmail.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestJwksHttpNotFound.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestJwksInvalidResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestKeyBindingSigningFailed.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestRpOriginIsOpaque.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenHttpNotFound.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenInvalidContentType.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenInvalidResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenInvalidSdJwt.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenMalformedSdJwt.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenNoResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidAudience.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidIssuedAt.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidNonce.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidSdHash.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidTyp.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingAud.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingCnf.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingIat.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingNonce.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingSdHash.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbSignatureFailed.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidEmail.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidEmailVerified.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidHolderKey.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidIssuedAt.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidIssuer.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtJwksMissingKeys.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtMissingCnf.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtMissingEmail.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtMissingIat.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtMissingIss.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtSignatureFailed.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtUnsupportedHeaderAlg.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestUserLoggedOut.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownAccountsEndpointCrossOrigin.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownHttpNotFound.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownInvalidContentType.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownInvalidResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownIssuanceEndpointCrossOrigin.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownListEmpty.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownMissingAccountsEndpoint.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownMissingIssuanceEndpoint.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownNoResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownUnsupportedSigningAlgorithm.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestAccountsHttpNotFound.md +1 -1
- package/build/src/third_party/issue-descriptions/federatedAuthRequestAccountsInvalidResponse.md +1 -1
- package/build/src/third_party/issue-descriptions/federatedAuthRequestAccountsNoResponse.md +1 -1
- package/build/src/third_party/issue-descriptions/federatedAuthRequestApprovalDeclined.md +1 -1
- package/build/src/third_party/issue-descriptions/federatedAuthRequestCanceled.md +1 -1
- package/build/src/third_party/issue-descriptions/federatedAuthRequestErrorFetchingSignin.md +1 -1
- package/build/src/third_party/issue-descriptions/federatedAuthRequestErrorIdToken.md +1 -1
- package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenHttpNotFound.md +1 -1
- package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenInvalidRequest.md +1 -1
- package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenInvalidResponse.md +1 -1
- package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenNoResponse.md +1 -1
- package/build/src/third_party/issue-descriptions/federatedAuthRequestInvalidSigninResponse.md +1 -1
- package/build/src/third_party/issue-descriptions/federatedAuthRequestManifestHttpNotFound.md +1 -1
- package/build/src/third_party/issue-descriptions/federatedAuthRequestManifestInvalidResponse.md +1 -1
- package/build/src/third_party/issue-descriptions/federatedAuthRequestManifestNoResponse.md +1 -1
- package/build/src/third_party/issue-descriptions/federatedAuthRequestTooManyRequests.md +1 -1
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestInvalidAccountsResponse.md +1 -1
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestInvalidConfigOrWellKnown.md +1 -1
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNoAccountSharingPermission.md +1 -1
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNoApiPermission.md +1 -1
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNoReturningUserFromFetchedAccounts.md +1 -1
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotIframe.md +1 -1
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotPotentiallyTrustworthy.md +1 -1
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotSameOrigin.md +1 -1
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotSignedInWithIdp.md +1 -1
- package/build/src/third_party/issue-descriptions/fetchingPartitionedBlobURL.md +2 -2
- package/build/src/third_party/issue-descriptions/genericBackUINavigationWouldSkipAd.md +4 -0
- package/build/src/third_party/issue-descriptions/genericFormAriaLabelledByToNonExistingIdError.md +2 -2
- package/build/src/third_party/issue-descriptions/genericFormAutocompleteAttributeEmptyError.md +1 -1
- package/build/src/third_party/issue-descriptions/genericFormDuplicateIdForInputError.md +1 -1
- package/build/src/third_party/issue-descriptions/genericFormEmptyIdAndNameAttributesForInputError.md +2 -2
- package/build/src/third_party/issue-descriptions/genericFormInputAssignedAutocompleteValueToIdOrNameAttributeError.md +2 -2
- package/build/src/third_party/issue-descriptions/genericFormInputHasWrongButWellIntendedAutocompleteValueError.md +1 -1
- package/build/src/third_party/issue-descriptions/genericFormInputWithNoLabelError.md +1 -1
- package/build/src/third_party/issue-descriptions/genericFormLabelForMatchesNonExistingIdError.md +2 -2
- package/build/src/third_party/issue-descriptions/genericFormLabelForNameError.md +1 -1
- package/build/src/third_party/issue-descriptions/genericFormLabelHasNeitherForNorNestedInputError.md +1 -1
- package/build/src/third_party/issue-descriptions/genericFormModelContextMissingToolName.md +1 -1
- package/build/src/third_party/issue-descriptions/genericFormModelContextParameterMissingName.md +1 -1
- package/build/src/third_party/issue-descriptions/genericFormModelContextRequiredParameterMissingName.md +1 -1
- package/build/src/third_party/issue-descriptions/genericNavigationEntryMarkedSkippable.md +2 -2
- package/build/src/third_party/issue-descriptions/heavyAd.md +1 -1
- package/build/src/third_party/issue-descriptions/lazyLoadImageZeroSize.md +7 -0
- package/build/src/third_party/issue-descriptions/navigatingPartitionedBlobURL.md +3 -3
- package/build/src/third_party/issue-descriptions/permissionElementActivationDisabled.md +1 -1
- package/build/src/third_party/issue-descriptions/permissionElementActivationDisabledWithOccluder.md +1 -1
- package/build/src/third_party/issue-descriptions/permissionElementActivationDisabledWithOccluderParent.md +1 -1
- package/build/src/third_party/issue-descriptions/permissionElementFencedFrameDisallowed.md +2 -2
- package/build/src/third_party/issue-descriptions/permissionElementFontSizeTooLarge.md +2 -2
- package/build/src/third_party/issue-descriptions/permissionElementFontSizeTooSmall.md +2 -2
- package/build/src/third_party/issue-descriptions/permissionElementInsetBoxShadowUnsupported.md +1 -1
- package/build/src/third_party/issue-descriptions/permissionElementInvalidDisplayStyle.md +2 -2
- package/build/src/third_party/issue-descriptions/permissionElementInvalidSizeValue.md +2 -2
- package/build/src/third_party/issue-descriptions/permissionElementInvalidType.md +2 -2
- package/build/src/third_party/issue-descriptions/permissionElementInvalidTypeActivation.md +2 -2
- package/build/src/third_party/issue-descriptions/permissionElementNonOpaqueColor.md +1 -1
- package/build/src/third_party/issue-descriptions/permissionElementPaddingBottomUnsupported.md +2 -2
- package/build/src/third_party/issue-descriptions/permissionElementPaddingRightUnsupported.md +2 -2
- package/build/src/third_party/issue-descriptions/permissionElementRequestInProgress.md +2 -2
- package/build/src/third_party/issue-descriptions/permissionElementSecurityChecksFailed.md +1 -1
- package/build/src/third_party/issue-descriptions/permissionElementTypeNotSupported.md +2 -2
- package/build/src/third_party/issue-descriptions/permissionElementUntrustedEvent.md +1 -1
- package/build/src/third_party/issue-descriptions/placeholderDescriptionForInvisibleIssues.md +1 -1
- package/build/src/third_party/issue-descriptions/selectElementAccessibilityDisallowedOptGroupChild.md +1 -1
- package/build/src/third_party/issue-descriptions/selectElementAccessibilityDisallowedSelectChild.md +1 -1
- package/build/src/third_party/issue-descriptions/selectElementAccessibilityInteractiveContentAttributesSelectDescendant.md +1 -1
- package/build/src/third_party/issue-descriptions/selectElementAccessibilityInteractiveContentLegendChild.md +1 -1
- package/build/src/third_party/issue-descriptions/selectElementAccessibilityInteractiveContentOptionChild.md +1 -1
- package/build/src/third_party/issue-descriptions/selectElementAccessibilityNonPhrasingContentOptionChild.md +1 -1
- package/build/src/third_party/issue-descriptions/selectivePermissionsIntervention.md +3 -3
- package/build/src/third_party/issue-descriptions/sharedArrayBuffer.md +1 -1
- package/build/src/third_party/issue-descriptions/sharedDictionaryUseErrorCrossOriginNoCorsRequest.md +1 -1
- package/build/src/third_party/issue-descriptions/sharedDictionaryUseErrorMatchingDictionaryNotUsed.md +1 -1
- package/build/src/third_party/issue-descriptions/sharedDictionaryUseErrorUnexpectedContentDictionaryHeader.md +1 -1
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorCossOriginNoCorsRequest.md +1 -1
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorDisallowedBySettings.md +1 -1
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorExpiredResponse.md +1 -1
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorFeatureDisabled.md +2 -2
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorInvalidStructuredHeader.md +1 -1
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNavigationRequest.md +2 -2
- package/build/src/third_party/issue-descriptions/sriInvalidSignatureHeader.md +4 -4
- package/build/src/third_party/issue-descriptions/sriInvalidSignatureInputHeader.md +4 -4
- package/build/src/third_party/issue-descriptions/sriMissingSignatureHeader.md +1 -1
- package/build/src/third_party/issue-descriptions/sriMissingSignatureInputHeader.md +2 -2
- package/build/src/third_party/issue-descriptions/sriSignatureHeaderValueIsIncorrectLength.md +2 -2
- package/build/src/third_party/issue-descriptions/sriSignatureHeaderValueIsNotByteSequence.md +4 -4
- package/build/src/third_party/issue-descriptions/sriSignatureHeaderValueIsParameterized.md +4 -4
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidComponentName.md +1 -1
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidComponentType.md +2 -2
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidDerivedComponentParameter.md +1 -1
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidHeaderComponentParameter.md +1 -1
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidParameter.md +2 -2
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderKeyIdLength.md +5 -5
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderMissingLabel.md +3 -3
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderMissingRequiredParameters.md +2 -2
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderValueMissingComponents.md +2 -2
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderValueNotInnerList.md +2 -2
- package/build/src/third_party/issue-descriptions/sriValidationFailedIntegrityMismatch.md +4 -4
- package/build/src/third_party/issue-descriptions/sriValidationFailedInvalidLength.md +3 -3
- package/build/src/third_party/issue-descriptions/sriValidationFailedSignatureExpired.md +2 -2
- package/build/src/third_party/issue-descriptions/sriValidationFailedSignatureMismatch.md +2 -2
- package/build/src/third_party/issue-descriptions/stylesheetLateImport.md +1 -1
- package/build/src/third_party/issue-descriptions/summaryElementAccessibilityInteractiveContentSummaryDescendant.md +1 -1
- package/build/src/third_party/issue-descriptions/unencodedDigestIncorrectDigestLength.md +2 -2
- package/build/src/third_party/issue-descriptions/unencodedDigestIncorrectDigestType.md +3 -3
- package/build/src/third_party/issue-descriptions/unencodedDigestMalformedDictionary.md +2 -2
- package/build/src/third_party/issue-descriptions/unencodedDigestUnknownAlgorithm.md +1 -1
- package/build/src/third_party/lighthouse-devtools-mcp-bundle.js +3177 -6340
- package/build/src/tools/ToolDefinition.js +2 -2
- package/build/src/tools/categories.js +3 -0
- package/build/src/tools/console.js +13 -0
- package/build/src/tools/emulation.js +30 -3
- package/build/src/tools/extensions.js +8 -1
- package/build/src/tools/input.js +246 -128
- package/build/src/tools/lighthouse.js +14 -7
- package/build/src/tools/memory.js +289 -25
- package/build/src/tools/network.js +10 -7
- package/build/src/tools/pages.js +33 -92
- package/build/src/tools/performance.js +67 -52
- package/build/src/tools/pwa.js +128 -0
- package/build/src/tools/screencast.js +35 -12
- package/build/src/tools/screenshot.js +234 -75
- package/build/src/tools/script.js +130 -45
- package/build/src/tools/slim/tools.js +3 -0
- package/build/src/tools/snapshot.js +7 -4
- package/build/src/tools/thirdPartyDeveloper.js +19 -9
- package/build/src/tools/tools.js +3 -1
- package/build/src/tools/webmcp.js +3 -1
- package/build/src/utils/WaitForHelper.js +299 -0
- package/build/src/utils/check-for-updates.js +8 -2
- package/build/src/utils/files.js +42 -3
- package/build/src/utils/logger.js +51 -0
- package/build/src/utils/polyfill.js +11 -0
- package/build/src/utils/url.js +43 -0
- package/build/src/version.js +1 -4
- package/package.json +43 -33
- package/build/src/DevToolsConnectionAdapter.js +0 -70
- package/build/src/HeapSnapshotManager.js +0 -110
- package/build/src/Mutex.js +0 -38
- package/build/src/WaitForHelper.js +0 -195
- package/build/src/bin/cliDefinitions.js +0 -616
- package/build/src/logger.js +0 -37
- package/build/src/polyfill.js +0 -8
- package/build/src/telemetry/metricUtils.js +0 -15
- package/build/src/third_party/issue-descriptions/SameSiteExcludeContextDowngradeRead.md +0 -8
- package/build/src/third_party/issue-descriptions/SameSiteExcludeContextDowngradeSet.md +0 -8
- package/build/src/third_party/issue-descriptions/SameSiteExcludeNavigationContextDowngrade.md +0 -8
- package/build/src/third_party/issue-descriptions/SameSiteWarnCrossDowngradeRead.md +0 -8
- package/build/src/third_party/issue-descriptions/SameSiteWarnCrossDowngradeSet.md +0 -8
- package/build/src/third_party/issue-descriptions/SameSiteWarnStrictLaxDowngradeStrict.md +0 -8
- package/build/src/third_party/issue-descriptions/arInsecureContext.md +0 -7
- package/build/src/third_party/issue-descriptions/arInvalidInfoHeader.md +0 -5
- package/build/src/third_party/issue-descriptions/arInvalidRegisterOsSourceHeader.md +0 -5
- package/build/src/third_party/issue-descriptions/arInvalidRegisterOsTriggerHeader.md +0 -5
- package/build/src/third_party/issue-descriptions/arInvalidRegisterSourceHeader.md +0 -5
- package/build/src/third_party/issue-descriptions/arInvalidRegisterTriggerHeader.md +0 -5
- package/build/src/third_party/issue-descriptions/arNavigationRegistrationUniqueScopeAlreadySet.md +0 -5
- package/build/src/third_party/issue-descriptions/arNavigationRegistrationWithoutTransientUserActivation.md +0 -6
- package/build/src/third_party/issue-descriptions/arNoRegisterOsSourceHeader.md +0 -5
- package/build/src/third_party/issue-descriptions/arNoRegisterOsTriggerHeader.md +0 -5
- package/build/src/third_party/issue-descriptions/arNoRegisterSourceHeader.md +0 -5
- package/build/src/third_party/issue-descriptions/arNoRegisterTriggerHeader.md +0 -5
- package/build/src/third_party/issue-descriptions/arNoWebOrOsSupport.md +0 -4
- package/build/src/third_party/issue-descriptions/arOsSourceIgnored.md +0 -18
- package/build/src/third_party/issue-descriptions/arOsTriggerIgnored.md +0 -19
- package/build/src/third_party/issue-descriptions/arPermissionPolicyDisabled.md +0 -8
- package/build/src/third_party/issue-descriptions/arSourceAndTriggerHeaders.md +0 -9
- package/build/src/third_party/issue-descriptions/arSourceIgnored.md +0 -13
- package/build/src/third_party/issue-descriptions/arTriggerIgnored.md +0 -12
- package/build/src/third_party/issue-descriptions/arUntrustworthyReportingOrigin.md +0 -10
- package/build/src/third_party/issue-descriptions/arWebAndOsHeaders.md +0 -11
- package/build/src/utils/string.js +0 -37
- 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 '
|
|
8
|
-
import { ToolCategory } from '
|
|
9
|
-
import { definePageTool } from '
|
|
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=
|
|
85
|
+
//# sourceMappingURL=PerformanceTrace.js.map
|