@vibebrowser/chrome-devtools-mcp 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +873 -0
- package/build/src/DevToolsConnectionAdapter.js +70 -0
- package/build/src/DevtoolsUtils.js +295 -0
- package/build/src/HeapSnapshotManager.js +110 -0
- package/build/src/McpContext.js +605 -0
- package/build/src/McpPage.js +315 -0
- package/build/src/McpResponse.js +858 -0
- package/build/src/Mutex.js +38 -0
- package/build/src/PageCollector.js +297 -0
- package/build/src/SlimMcpResponse.js +19 -0
- package/build/src/TextSnapshot.js +236 -0
- package/build/src/ToolHandler.js +217 -0
- package/build/src/WaitForHelper.js +190 -0
- package/build/src/bin/check-latest-version.js +50 -0
- package/build/src/bin/chrome-devtools-cli-options.js +840 -0
- package/build/src/bin/chrome-devtools-mcp-cli-options.js +350 -0
- package/build/src/bin/chrome-devtools-mcp-main.js +94 -0
- package/build/src/bin/chrome-devtools-mcp.js +31 -0
- package/build/src/bin/chrome-devtools.js +189 -0
- package/build/src/bin/install-service.js +246 -0
- package/build/src/bin/service/chrome-devtools-mcp.service.template +17 -0
- package/build/src/bin/service/com.vibebrowser.chrome-devtools-mcp.plist.template +37 -0
- package/build/src/browser.js +204 -0
- package/build/src/daemon/client.js +154 -0
- package/build/src/daemon/daemon.js +204 -0
- package/build/src/daemon/types.js +7 -0
- package/build/src/daemon/utils.js +115 -0
- package/build/src/formatters/ConsoleFormatter.js +288 -0
- package/build/src/formatters/HeapSnapshotFormatter.js +54 -0
- package/build/src/formatters/IssueFormatter.js +193 -0
- package/build/src/formatters/NetworkFormatter.js +236 -0
- package/build/src/formatters/SnapshotFormatter.js +135 -0
- package/build/src/index.js +140 -0
- package/build/src/issue-descriptions.js +40 -0
- package/build/src/logger.js +37 -0
- package/build/src/polyfill.js +8 -0
- package/build/src/telemetry/ClearcutLogger.js +169 -0
- package/build/src/telemetry/WatchdogClient.js +61 -0
- package/build/src/telemetry/errors.js +18 -0
- package/build/src/telemetry/flagUtils.js +89 -0
- package/build/src/telemetry/metricsRegistry.js +89 -0
- package/build/src/telemetry/persistence.js +72 -0
- package/build/src/telemetry/transformation.js +134 -0
- package/build/src/telemetry/types.js +31 -0
- package/build/src/telemetry/watchdog/ClearcutSender.js +205 -0
- package/build/src/telemetry/watchdog/main.js +128 -0
- package/build/src/third_party/devtools-formatter-worker.js +8 -0
- package/build/src/third_party/devtools-heap-snapshot-worker.js +8 -0
- package/build/src/third_party/index.js +32 -0
- package/build/src/third_party/issue-descriptions/CoepCoopSandboxedIframeCannotNavigateToCoopPage.md +4 -0
- package/build/src/third_party/issue-descriptions/CoepCorpNotSameOrigin.md +8 -0
- package/build/src/third_party/issue-descriptions/CoepCorpNotSameOriginAfterDefaultedToSameOriginByCoep.md +18 -0
- package/build/src/third_party/issue-descriptions/CoepCorpNotSameSite.md +7 -0
- package/build/src/third_party/issue-descriptions/CoepFrameResourceNeedsCoepHeader.md +10 -0
- package/build/src/third_party/issue-descriptions/CompatibilityModeQuirks.md +5 -0
- package/build/src/third_party/issue-descriptions/CookieAttributeValueExceedsMaxSize.md +5 -0
- package/build/src/third_party/issue-descriptions/LowTextContrast.md +5 -0
- package/build/src/third_party/issue-descriptions/SameSiteExcludeContextDowngradeRead.md +8 -0
- package/build/src/third_party/issue-descriptions/SameSiteExcludeContextDowngradeSet.md +8 -0
- package/build/src/third_party/issue-descriptions/SameSiteExcludeNavigationContextDowngrade.md +8 -0
- package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureErrorRead.md +8 -0
- package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureErrorSet.md +8 -0
- package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureWarnRead.md +8 -0
- package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureWarnSet.md +8 -0
- package/build/src/third_party/issue-descriptions/SameSiteUnspecifiedLaxAllowUnsafeRead.md +9 -0
- package/build/src/third_party/issue-descriptions/SameSiteUnspecifiedLaxAllowUnsafeSet.md +9 -0
- package/build/src/third_party/issue-descriptions/SameSiteWarnCrossDowngradeRead.md +8 -0
- package/build/src/third_party/issue-descriptions/SameSiteWarnCrossDowngradeSet.md +8 -0
- package/build/src/third_party/issue-descriptions/SameSiteWarnStrictLaxDowngradeStrict.md +8 -0
- package/build/src/third_party/issue-descriptions/arInsecureContext.md +7 -0
- package/build/src/third_party/issue-descriptions/arInvalidInfoHeader.md +5 -0
- package/build/src/third_party/issue-descriptions/arInvalidRegisterOsSourceHeader.md +5 -0
- package/build/src/third_party/issue-descriptions/arInvalidRegisterOsTriggerHeader.md +5 -0
- package/build/src/third_party/issue-descriptions/arInvalidRegisterSourceHeader.md +5 -0
- package/build/src/third_party/issue-descriptions/arInvalidRegisterTriggerHeader.md +5 -0
- package/build/src/third_party/issue-descriptions/arNavigationRegistrationUniqueScopeAlreadySet.md +5 -0
- package/build/src/third_party/issue-descriptions/arNavigationRegistrationWithoutTransientUserActivation.md +6 -0
- package/build/src/third_party/issue-descriptions/arNoRegisterOsSourceHeader.md +5 -0
- package/build/src/third_party/issue-descriptions/arNoRegisterOsTriggerHeader.md +5 -0
- package/build/src/third_party/issue-descriptions/arNoRegisterSourceHeader.md +5 -0
- package/build/src/third_party/issue-descriptions/arNoRegisterTriggerHeader.md +5 -0
- package/build/src/third_party/issue-descriptions/arNoWebOrOsSupport.md +4 -0
- package/build/src/third_party/issue-descriptions/arOsSourceIgnored.md +18 -0
- package/build/src/third_party/issue-descriptions/arOsTriggerIgnored.md +19 -0
- package/build/src/third_party/issue-descriptions/arPermissionPolicyDisabled.md +8 -0
- package/build/src/third_party/issue-descriptions/arSourceAndTriggerHeaders.md +9 -0
- package/build/src/third_party/issue-descriptions/arSourceIgnored.md +13 -0
- package/build/src/third_party/issue-descriptions/arTriggerIgnored.md +12 -0
- package/build/src/third_party/issue-descriptions/arUntrustworthyReportingOrigin.md +10 -0
- package/build/src/third_party/issue-descriptions/arWebAndOsHeaders.md +11 -0
- package/build/src/third_party/issue-descriptions/bounceTrackingMitigations.md +3 -0
- package/build/src/third_party/issue-descriptions/clientHintMetaTagAllowListInvalidOrigin.md +4 -0
- package/build/src/third_party/issue-descriptions/clientHintMetaTagModifiedHTML.md +4 -0
- package/build/src/third_party/issue-descriptions/connectionAllowlistInvalidAllowlistItemType.md +12 -0
- package/build/src/third_party/issue-descriptions/connectionAllowlistInvalidHeader.md +12 -0
- package/build/src/third_party/issue-descriptions/connectionAllowlistInvalidUrlPattern.md +8 -0
- package/build/src/third_party/issue-descriptions/connectionAllowlistItemNotInnerList.md +12 -0
- package/build/src/third_party/issue-descriptions/connectionAllowlistMoreThanOneList.md +7 -0
- package/build/src/third_party/issue-descriptions/connectionAllowlistReportingEndpointNotToken.md +10 -0
- package/build/src/third_party/issue-descriptions/cookieCrossSiteRedirectDowngrade.md +12 -0
- package/build/src/third_party/issue-descriptions/cookieExcludeBlockedWithinRelatedWebsiteSet.md +4 -0
- package/build/src/third_party/issue-descriptions/cookieExcludeDomainNonAscii.md +11 -0
- package/build/src/third_party/issue-descriptions/cookieExcludePortMismatch.md +8 -0
- package/build/src/third_party/issue-descriptions/cookieExcludeSchemeMismatch.md +7 -0
- package/build/src/third_party/issue-descriptions/cookieExcludeThirdPartyPhaseoutRead.md +6 -0
- package/build/src/third_party/issue-descriptions/cookieExcludeThirdPartyPhaseoutSet.md +6 -0
- package/build/src/third_party/issue-descriptions/cookieWarnDomainNonAscii.md +11 -0
- package/build/src/third_party/issue-descriptions/cookieWarnMetadataGrantRead.md +4 -0
- package/build/src/third_party/issue-descriptions/cookieWarnMetadataGrantSet.md +4 -0
- package/build/src/third_party/issue-descriptions/cookieWarnThirdPartyPhaseoutRead.md +6 -0
- package/build/src/third_party/issue-descriptions/cookieWarnThirdPartyPhaseoutSet.md +6 -0
- package/build/src/third_party/issue-descriptions/corsAllowCredentialsRequired.md +6 -0
- package/build/src/third_party/issue-descriptions/corsDisabledScheme.md +7 -0
- package/build/src/third_party/issue-descriptions/corsDisallowedByMode.md +7 -0
- package/build/src/third_party/issue-descriptions/corsHeaderDisallowedByPreflightResponse.md +5 -0
- package/build/src/third_party/issue-descriptions/corsInvalidHeaderValues.md +7 -0
- package/build/src/third_party/issue-descriptions/corsLocalNetworkAccessPermissionDenied.md +19 -0
- package/build/src/third_party/issue-descriptions/corsMethodDisallowedByPreflightResponse.md +5 -0
- package/build/src/third_party/issue-descriptions/corsNoCorsRedirectModeNotFollow.md +5 -0
- package/build/src/third_party/issue-descriptions/corsOriginMismatch.md +6 -0
- package/build/src/third_party/issue-descriptions/corsPreflightResponseInvalid.md +5 -0
- package/build/src/third_party/issue-descriptions/corsRedirectContainsCredentials.md +5 -0
- package/build/src/third_party/issue-descriptions/corsWildcardOriginNotAllowed.md +8 -0
- package/build/src/third_party/issue-descriptions/cspEvalViolation.md +9 -0
- package/build/src/third_party/issue-descriptions/cspInlineViolation.md +10 -0
- package/build/src/third_party/issue-descriptions/cspTrustedTypesPolicyViolation.md +5 -0
- package/build/src/third_party/issue-descriptions/cspTrustedTypesSinkViolation.md +8 -0
- package/build/src/third_party/issue-descriptions/cspURLViolation.md +10 -0
- package/build/src/third_party/issue-descriptions/deprecation.md +3 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestAccountsHttpNotFound.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestAccountsInvalidResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestAccountsNoResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestApprovalDeclined.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestCanceled.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestErrorFetchingSignin.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestErrorIdToken.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenHttpNotFound.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenInvalidRequest.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenInvalidResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenNoResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestInvalidSigninResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestManifestHttpNotFound.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestManifestInvalidResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestManifestNoResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestTooManyRequests.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestInvalidAccountsResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestInvalidConfigOrWellKnown.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNoAccountSharingPermission.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNoApiPermission.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNoReturningUserFromFetchedAccounts.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotIframe.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotPotentiallyTrustworthy.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotSameOrigin.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotSignedInWithIdp.md +1 -0
- package/build/src/third_party/issue-descriptions/fetchingPartitionedBlobURL.md +7 -0
- package/build/src/third_party/issue-descriptions/genericFormAriaLabelledByToNonExistingIdError.md +8 -0
- package/build/src/third_party/issue-descriptions/genericFormAutocompleteAttributeEmptyError.md +5 -0
- package/build/src/third_party/issue-descriptions/genericFormDuplicateIdForInputError.md +5 -0
- package/build/src/third_party/issue-descriptions/genericFormEmptyIdAndNameAttributesForInputError.md +5 -0
- package/build/src/third_party/issue-descriptions/genericFormInputAssignedAutocompleteValueToIdOrNameAttributeError.md +5 -0
- package/build/src/third_party/issue-descriptions/genericFormInputHasWrongButWellIntendedAutocompleteValueError.md +5 -0
- package/build/src/third_party/issue-descriptions/genericFormInputWithNoLabelError.md +5 -0
- package/build/src/third_party/issue-descriptions/genericFormLabelForMatchesNonExistingIdError.md +5 -0
- package/build/src/third_party/issue-descriptions/genericFormLabelForNameError.md +5 -0
- package/build/src/third_party/issue-descriptions/genericFormLabelHasNeitherForNorNestedInputError.md +5 -0
- package/build/src/third_party/issue-descriptions/genericFormModelContextMissingToolDescription.md +5 -0
- package/build/src/third_party/issue-descriptions/genericFormModelContextMissingToolName.md +5 -0
- package/build/src/third_party/issue-descriptions/genericFormModelContextParameterMissingName.md +5 -0
- package/build/src/third_party/issue-descriptions/genericFormModelContextParameterMissingTitleAndDescription.md +5 -0
- package/build/src/third_party/issue-descriptions/genericFormModelContextRequiredParameterMissingName.md +5 -0
- package/build/src/third_party/issue-descriptions/genericNavigationEntryMarkedSkippable.md +7 -0
- package/build/src/third_party/issue-descriptions/genericResponseWasBlockedByORB.md +4 -0
- package/build/src/third_party/issue-descriptions/heavyAd.md +10 -0
- package/build/src/third_party/issue-descriptions/mixedContent.md +5 -0
- package/build/src/third_party/issue-descriptions/navigatingPartitionedBlobURL.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementActivationDisabled.md +7 -0
- package/build/src/third_party/issue-descriptions/permissionElementActivationDisabledWithOccluder.md +9 -0
- package/build/src/third_party/issue-descriptions/permissionElementActivationDisabledWithOccluderParent.md +9 -0
- package/build/src/third_party/issue-descriptions/permissionElementCspFrameAncestorsMissing.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementFencedFrameDisallowed.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementFontSizeTooLarge.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementFontSizeTooSmall.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementGeolocationDeprecated.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementInsetBoxShadowUnsupported.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementInvalidDisplayStyle.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementInvalidSizeValue.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementInvalidType.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementInvalidTypeActivation.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementLowContrast.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementNonOpaqueColor.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementPaddingBottomUnsupported.md +6 -0
- package/build/src/third_party/issue-descriptions/permissionElementPaddingRightUnsupported.md +6 -0
- package/build/src/third_party/issue-descriptions/permissionElementPermissionsPolicyBlocked.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementRegistrationFailed.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementRequestInProgress.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementSecurityChecksFailed.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementTypeNotSupported.md +5 -0
- package/build/src/third_party/issue-descriptions/permissionElementUntrustedEvent.md +7 -0
- package/build/src/third_party/issue-descriptions/placeholderDescriptionForInvisibleIssues.md +3 -0
- package/build/src/third_party/issue-descriptions/propertyRuleInvalidNameIssue.md +3 -0
- package/build/src/third_party/issue-descriptions/propertyRuleIssue.md +7 -0
- package/build/src/third_party/issue-descriptions/selectElementAccessibilityDisallowedOptGroupChild.md +7 -0
- package/build/src/third_party/issue-descriptions/selectElementAccessibilityDisallowedSelectChild.md +7 -0
- package/build/src/third_party/issue-descriptions/selectElementAccessibilityInteractiveContentAttributesSelectDescendant.md +3 -0
- package/build/src/third_party/issue-descriptions/selectElementAccessibilityInteractiveContentLegendChild.md +3 -0
- package/build/src/third_party/issue-descriptions/selectElementAccessibilityInteractiveContentOptionChild.md +3 -0
- package/build/src/third_party/issue-descriptions/selectElementAccessibilityNonPhrasingContentOptionChild.md +3 -0
- package/build/src/third_party/issue-descriptions/selectivePermissionsIntervention.md +7 -0
- package/build/src/third_party/issue-descriptions/sharedArrayBuffer.md +7 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryUseErrorCrossOriginNoCorsRequest.md +1 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryUseErrorDictionaryLoadFailure.md +3 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryUseErrorMatchingDictionaryNotUsed.md +3 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryUseErrorUnexpectedContentDictionaryHeader.md +1 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorCossOriginNoCorsRequest.md +1 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorDisallowedBySettings.md +1 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorExpiredResponse.md +3 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorFeatureDisabled.md +3 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorInsufficientResources.md +1 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorInvalidMatchField.md +1 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorInvalidStructuredHeader.md +1 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorInvalidTTLField.md +1 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNavigationRequest.md +3 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNoMatchField.md +1 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonIntegerTTLField.md +1 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonListMatchDestField.md +1 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonSecureContext.md +3 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonStringIdField.md +1 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonStringInMatchDestList.md +1 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonStringMatchField.md +1 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonTokenTypeField.md +1 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorRequestAborted.md +1 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorShuttingDown.md +1 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorTooLongIdField.md +3 -0
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorUnsupportedType.md +3 -0
- package/build/src/third_party/issue-descriptions/sriInvalidSignatureHeader.md +14 -0
- package/build/src/third_party/issue-descriptions/sriInvalidSignatureInputHeader.md +15 -0
- package/build/src/third_party/issue-descriptions/sriMissingSignatureHeader.md +8 -0
- package/build/src/third_party/issue-descriptions/sriMissingSignatureInputHeader.md +7 -0
- package/build/src/third_party/issue-descriptions/sriSignatureHeaderValueIsIncorrectLength.md +11 -0
- package/build/src/third_party/issue-descriptions/sriSignatureHeaderValueIsNotByteSequence.md +14 -0
- package/build/src/third_party/issue-descriptions/sriSignatureHeaderValueIsParameterized.md +15 -0
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidComponentName.md +8 -0
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidComponentType.md +13 -0
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidDerivedComponentParameter.md +4 -0
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidHeaderComponentParameter.md +5 -0
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidParameter.md +11 -0
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderKeyIdLength.md +12 -0
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderMissingLabel.md +6 -0
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderMissingRequiredParameters.md +8 -0
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderValueMissingComponents.md +11 -0
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderValueNotInnerList.md +11 -0
- package/build/src/third_party/issue-descriptions/sriValidationFailedIntegrityMismatch.md +10 -0
- package/build/src/third_party/issue-descriptions/sriValidationFailedInvalidLength.md +5 -0
- package/build/src/third_party/issue-descriptions/sriValidationFailedSignatureExpired.md +6 -0
- package/build/src/third_party/issue-descriptions/sriValidationFailedSignatureMismatch.md +11 -0
- package/build/src/third_party/issue-descriptions/stylesheetLateImport.md +4 -0
- package/build/src/third_party/issue-descriptions/stylesheetRequestFailed.md +3 -0
- package/build/src/third_party/issue-descriptions/summaryElementAccessibilityInteractiveContentSummaryDescendant.md +3 -0
- package/build/src/third_party/issue-descriptions/unencodedDigestIncorrectDigestLength.md +12 -0
- package/build/src/third_party/issue-descriptions/unencodedDigestIncorrectDigestType.md +17 -0
- package/build/src/third_party/issue-descriptions/unencodedDigestMalformedDictionary.md +14 -0
- package/build/src/third_party/issue-descriptions/unencodedDigestUnknownAlgorithm.md +15 -0
- package/build/src/third_party/lighthouse-devtools-mcp-bundle.js +61598 -0
- package/build/src/tools/ToolDefinition.js +73 -0
- package/build/src/tools/categories.js +36 -0
- package/build/src/tools/console.js +91 -0
- package/build/src/tools/emulation.js +57 -0
- package/build/src/tools/extensions.js +96 -0
- package/build/src/tools/input.js +461 -0
- package/build/src/tools/lighthouse.js +131 -0
- package/build/src/tools/memory.js +106 -0
- package/build/src/tools/network.js +125 -0
- package/build/src/tools/pages.js +411 -0
- package/build/src/tools/performance.js +196 -0
- package/build/src/tools/screencast.js +95 -0
- package/build/src/tools/screenshot.js +87 -0
- package/build/src/tools/script.js +151 -0
- package/build/src/tools/slim/tools.js +85 -0
- package/build/src/tools/snapshot.js +60 -0
- package/build/src/tools/thirdPartyDeveloper.js +75 -0
- package/build/src/tools/tools.js +56 -0
- package/build/src/tools/webmcp.js +64 -0
- package/build/src/trace-processing/parse.js +85 -0
- package/build/src/types.js +7 -0
- package/build/src/utils/check-for-updates.js +74 -0
- package/build/src/utils/files.js +18 -0
- package/build/src/utils/id.js +16 -0
- package/build/src/utils/keyboard.js +297 -0
- package/build/src/utils/pagination.js +50 -0
- package/build/src/utils/string.js +37 -0
- package/build/src/utils/types.js +7 -0
- package/build/src/version.js +10 -0
- package/package.json +93 -0
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @license
|
|
4
|
+
* Copyright 2026 Google LLC
|
|
5
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
6
|
+
*/
|
|
7
|
+
import fs from 'node:fs';
|
|
8
|
+
import { createServer } from 'node:net';
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
import process from 'node:process';
|
|
11
|
+
import { logger } from '../logger.js';
|
|
12
|
+
import { Client, PipeTransport, StdioClientTransport, } from '../third_party/index.js';
|
|
13
|
+
import { VERSION } from '../version.js';
|
|
14
|
+
import { DAEMON_CLIENT_NAME, getPidFilePath, getSocketPath, INDEX_SCRIPT_PATH, IS_WINDOWS, isDaemonRunning, } from './utils.js';
|
|
15
|
+
const sessionId = process.env.CHROME_DEVTOOLS_MCP_SESSION_ID || '';
|
|
16
|
+
logger(`Daemon sessionId: ${sessionId}`);
|
|
17
|
+
if (isDaemonRunning(sessionId)) {
|
|
18
|
+
logger('Another daemon process is running.');
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
const pidFilePath = getPidFilePath(sessionId);
|
|
22
|
+
fs.mkdirSync(path.dirname(pidFilePath), {
|
|
23
|
+
recursive: true,
|
|
24
|
+
});
|
|
25
|
+
fs.writeFileSync(pidFilePath, process.pid.toString());
|
|
26
|
+
logger(`Writing ${process.pid.toString()} to ${pidFilePath}`);
|
|
27
|
+
const socketPath = getSocketPath(sessionId);
|
|
28
|
+
const startDate = new Date();
|
|
29
|
+
const mcpServerArgs = process.argv.slice(2);
|
|
30
|
+
let mcpClient = null;
|
|
31
|
+
let mcpTransport = null;
|
|
32
|
+
let server = null;
|
|
33
|
+
async function setupMCPClient() {
|
|
34
|
+
console.log('Setting up MCP client connection...');
|
|
35
|
+
// Create stdio transport for chrome-devtools-mcp
|
|
36
|
+
mcpTransport = new StdioClientTransport({
|
|
37
|
+
command: process.execPath,
|
|
38
|
+
args: [INDEX_SCRIPT_PATH, ...mcpServerArgs],
|
|
39
|
+
env: process.env,
|
|
40
|
+
});
|
|
41
|
+
mcpClient = new Client({
|
|
42
|
+
name: DAEMON_CLIENT_NAME,
|
|
43
|
+
version: VERSION,
|
|
44
|
+
}, {
|
|
45
|
+
capabilities: {},
|
|
46
|
+
});
|
|
47
|
+
await mcpClient.connect(mcpTransport);
|
|
48
|
+
console.log('MCP client connected');
|
|
49
|
+
}
|
|
50
|
+
async function handleRequest(msg) {
|
|
51
|
+
try {
|
|
52
|
+
if (msg.method === 'invoke_tool') {
|
|
53
|
+
if (!mcpClient) {
|
|
54
|
+
throw new Error('MCP client not initialized');
|
|
55
|
+
}
|
|
56
|
+
const { tool, args } = msg;
|
|
57
|
+
const result = (await mcpClient.callTool({
|
|
58
|
+
name: tool,
|
|
59
|
+
arguments: args || {},
|
|
60
|
+
}));
|
|
61
|
+
return {
|
|
62
|
+
success: true,
|
|
63
|
+
result: JSON.stringify(result),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
else if (msg.method === 'stop') {
|
|
67
|
+
// Ensure we are not interrupting in-progress starting.
|
|
68
|
+
await started;
|
|
69
|
+
// Trigger cleanup asynchronously.
|
|
70
|
+
setImmediate(() => {
|
|
71
|
+
void cleanup();
|
|
72
|
+
});
|
|
73
|
+
return {
|
|
74
|
+
success: true,
|
|
75
|
+
message: 'stopping',
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
else if (msg.method === 'status') {
|
|
79
|
+
return {
|
|
80
|
+
success: true,
|
|
81
|
+
result: JSON.stringify({
|
|
82
|
+
pid: process.pid,
|
|
83
|
+
socketPath,
|
|
84
|
+
startDate: startDate.toISOString(),
|
|
85
|
+
version: VERSION,
|
|
86
|
+
args: mcpServerArgs,
|
|
87
|
+
}),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
{
|
|
91
|
+
return {
|
|
92
|
+
success: false,
|
|
93
|
+
error: `Unknown method: ${JSON.stringify(msg, null, 2)}`,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
99
|
+
return {
|
|
100
|
+
success: false,
|
|
101
|
+
error: errorMessage,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
async function startSocketServer() {
|
|
106
|
+
// Remove existing socket file if it exists (only on non-Windows)
|
|
107
|
+
if (!IS_WINDOWS) {
|
|
108
|
+
try {
|
|
109
|
+
fs.unlinkSync(socketPath);
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
// ignore errors.
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return await new Promise((resolve, reject) => {
|
|
116
|
+
server = createServer(socket => {
|
|
117
|
+
const transport = new PipeTransport(socket, socket);
|
|
118
|
+
transport.onmessage = async (message) => {
|
|
119
|
+
logger('onmessage', message);
|
|
120
|
+
const response = await handleRequest(JSON.parse(message));
|
|
121
|
+
transport.send(JSON.stringify(response));
|
|
122
|
+
socket.end();
|
|
123
|
+
};
|
|
124
|
+
socket.on('error', error => {
|
|
125
|
+
logger('Socket error:', error);
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
server.listen({
|
|
129
|
+
path: socketPath,
|
|
130
|
+
readableAll: false,
|
|
131
|
+
writableAll: false,
|
|
132
|
+
}, async () => {
|
|
133
|
+
console.log(`Daemon server listening on ${socketPath}`);
|
|
134
|
+
try {
|
|
135
|
+
// Setup MCP client
|
|
136
|
+
await setupMCPClient();
|
|
137
|
+
resolve();
|
|
138
|
+
}
|
|
139
|
+
catch (err) {
|
|
140
|
+
reject(err);
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
server.on('error', error => {
|
|
144
|
+
logger('Server error:', error);
|
|
145
|
+
reject(error);
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
async function cleanup() {
|
|
150
|
+
console.log('Cleaning up daemon...');
|
|
151
|
+
try {
|
|
152
|
+
await mcpClient?.close();
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
logger('Error closing MCP client:', error);
|
|
156
|
+
}
|
|
157
|
+
try {
|
|
158
|
+
await mcpTransport?.close();
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
logger('Error closing MCP transport:', error);
|
|
162
|
+
}
|
|
163
|
+
if (server) {
|
|
164
|
+
await new Promise(resolve => {
|
|
165
|
+
server.close(() => resolve());
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
if (!IS_WINDOWS) {
|
|
169
|
+
try {
|
|
170
|
+
fs.unlinkSync(socketPath);
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
// ignore errors
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
logger(`unlinking ${pidFilePath}`);
|
|
177
|
+
if (fs.existsSync(pidFilePath)) {
|
|
178
|
+
fs.unlinkSync(pidFilePath);
|
|
179
|
+
}
|
|
180
|
+
process.exit(0);
|
|
181
|
+
}
|
|
182
|
+
// Handle shutdown signals
|
|
183
|
+
process.on('SIGTERM', () => {
|
|
184
|
+
void cleanup();
|
|
185
|
+
});
|
|
186
|
+
process.on('SIGINT', () => {
|
|
187
|
+
void cleanup();
|
|
188
|
+
});
|
|
189
|
+
process.on('SIGHUP', () => {
|
|
190
|
+
void cleanup();
|
|
191
|
+
});
|
|
192
|
+
// Handle uncaught errors
|
|
193
|
+
process.on('uncaughtException', error => {
|
|
194
|
+
logger('Uncaught exception:', error);
|
|
195
|
+
});
|
|
196
|
+
process.on('unhandledRejection', error => {
|
|
197
|
+
logger('Unhandled rejection:', error);
|
|
198
|
+
});
|
|
199
|
+
// Start the server
|
|
200
|
+
const started = startSocketServer().catch(error => {
|
|
201
|
+
logger('Failed to start daemon server:', error);
|
|
202
|
+
process.exit(1);
|
|
203
|
+
});
|
|
204
|
+
//# sourceMappingURL=daemon.js.map
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import fs from 'node:fs';
|
|
7
|
+
import os from 'node:os';
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
import process from 'node:process';
|
|
10
|
+
import { logger } from '../logger.js';
|
|
11
|
+
export const DAEMON_SCRIPT_PATH = path.join(import.meta.dirname, 'daemon.js');
|
|
12
|
+
export const INDEX_SCRIPT_PATH = path.join(import.meta.dirname, '..', 'bin', 'chrome-devtools-mcp.js');
|
|
13
|
+
const APP_NAME = 'chrome-devtools-mcp';
|
|
14
|
+
export const DAEMON_CLIENT_NAME = 'chrome-devtools-cli-daemon';
|
|
15
|
+
// Using these paths due to strict limits on the POSIX socket path length.
|
|
16
|
+
export function getSocketPath(sessionId) {
|
|
17
|
+
const uid = os.userInfo().uid;
|
|
18
|
+
const suffix = sessionId ? `-${sessionId}` : '';
|
|
19
|
+
const appName = APP_NAME + suffix;
|
|
20
|
+
if (IS_WINDOWS) {
|
|
21
|
+
// Windows uses Named Pipes, not file paths.
|
|
22
|
+
// This format is required for server.listen()
|
|
23
|
+
return path.join('\\\\.\\pipe', appName, 'server.sock');
|
|
24
|
+
}
|
|
25
|
+
// 1. Try XDG_RUNTIME_DIR (Linux standard, sometimes macOS)
|
|
26
|
+
if (process.env.XDG_RUNTIME_DIR) {
|
|
27
|
+
return path.join(process.env.XDG_RUNTIME_DIR, appName, 'server.sock');
|
|
28
|
+
}
|
|
29
|
+
// 2. macOS/Unix Fallback: Use /tmp/
|
|
30
|
+
// We use /tmp/ because it is much shorter than ~/Library/Application Support/
|
|
31
|
+
// and keeps us well under the 104-character limit.
|
|
32
|
+
return path.join('/tmp', `${appName}-${uid}.sock`);
|
|
33
|
+
}
|
|
34
|
+
export function getRuntimeHome(sessionId) {
|
|
35
|
+
const platform = os.platform();
|
|
36
|
+
const uid = os.userInfo().uid;
|
|
37
|
+
const suffix = sessionId ? `-${sessionId}` : '';
|
|
38
|
+
const appName = APP_NAME + suffix;
|
|
39
|
+
// 1. Check for the modern Unix standard
|
|
40
|
+
if (process.env.XDG_RUNTIME_DIR) {
|
|
41
|
+
return path.join(process.env.XDG_RUNTIME_DIR, appName);
|
|
42
|
+
}
|
|
43
|
+
// 2. Fallback for macOS and older Linux
|
|
44
|
+
if (platform === 'darwin' || platform === 'linux') {
|
|
45
|
+
// /tmp is cleared on boot, making it perfect for PIDs
|
|
46
|
+
return path.join('/tmp', `${appName}-${uid}`);
|
|
47
|
+
}
|
|
48
|
+
// 3. Windows Fallback
|
|
49
|
+
return path.join(os.tmpdir(), appName);
|
|
50
|
+
}
|
|
51
|
+
export const IS_WINDOWS = os.platform() === 'win32';
|
|
52
|
+
export function getPidFilePath(sessionId) {
|
|
53
|
+
const runtimeDir = getRuntimeHome(sessionId);
|
|
54
|
+
return path.join(runtimeDir, 'daemon.pid');
|
|
55
|
+
}
|
|
56
|
+
export function getDaemonPid(sessionId) {
|
|
57
|
+
try {
|
|
58
|
+
const pidFile = getPidFilePath(sessionId);
|
|
59
|
+
logger(`Daemon pid file ${pidFile} sessionId=${sessionId}`);
|
|
60
|
+
if (!fs.existsSync(pidFile)) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
const pidContent = fs.readFileSync(pidFile, 'utf-8');
|
|
64
|
+
const pid = parseInt(pidContent.trim(), 10);
|
|
65
|
+
logger(`Daemon pid: ${pid}`);
|
|
66
|
+
if (isNaN(pid)) {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
return pid;
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
export function isDaemonRunning(sessionId) {
|
|
76
|
+
const pid = getDaemonPid(sessionId);
|
|
77
|
+
if (pid) {
|
|
78
|
+
try {
|
|
79
|
+
process.kill(pid, 0); // Throws if process doesn't exist
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
// Process is dead, stale PID file. Proceed with startup.
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
export function serializeArgs(options, argv) {
|
|
89
|
+
const args = [];
|
|
90
|
+
for (const key of Object.keys(options)) {
|
|
91
|
+
if (argv[key] === undefined || argv[key] === null) {
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
const value = argv[key];
|
|
95
|
+
const kebabKey = key.replace(/[A-Z]/g, m => `-${m.toLowerCase()}`);
|
|
96
|
+
if (typeof value === 'boolean') {
|
|
97
|
+
if (value) {
|
|
98
|
+
args.push(`--${kebabKey}`);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
args.push(`--no-${kebabKey}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
else if (Array.isArray(value)) {
|
|
105
|
+
for (const item of value) {
|
|
106
|
+
args.push(`--${kebabKey}`, String(item));
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
args.push(`--${kebabKey}`, String(value));
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return args;
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { createStackTraceForConsoleMessage, SymbolizedError, } from '../DevtoolsUtils.js';
|
|
7
|
+
import { UncaughtError } from '../PageCollector.js';
|
|
8
|
+
import * as DevTools from '../third_party/index.js';
|
|
9
|
+
export class ConsoleFormatter {
|
|
10
|
+
#id;
|
|
11
|
+
#type;
|
|
12
|
+
#text;
|
|
13
|
+
#argCount;
|
|
14
|
+
#resolvedArgs;
|
|
15
|
+
#stack;
|
|
16
|
+
#cause;
|
|
17
|
+
isIgnored;
|
|
18
|
+
constructor(params) {
|
|
19
|
+
this.#id = params.id;
|
|
20
|
+
this.#type = params.type;
|
|
21
|
+
this.#text = params.text;
|
|
22
|
+
this.#argCount = params.argCount ?? 0;
|
|
23
|
+
this.#resolvedArgs = params.resolvedArgs ?? [];
|
|
24
|
+
this.#stack = params.stack;
|
|
25
|
+
this.#cause = params.cause;
|
|
26
|
+
this.isIgnored = params.isIgnored;
|
|
27
|
+
}
|
|
28
|
+
static async from(msg, options) {
|
|
29
|
+
const ignoreListManager = options?.devTools?.universe.context.get(DevTools.DevTools.IgnoreListManager);
|
|
30
|
+
const isIgnored = options.isIgnoredForTesting ||
|
|
31
|
+
(frame => {
|
|
32
|
+
if (!ignoreListManager) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
if (frame.uiSourceCode) {
|
|
36
|
+
return ignoreListManager.isUserOrSourceMapIgnoreListedUISourceCode(frame.uiSourceCode);
|
|
37
|
+
}
|
|
38
|
+
if (frame.url) {
|
|
39
|
+
return ignoreListManager.isUserIgnoreListedURL(frame.url);
|
|
40
|
+
}
|
|
41
|
+
return false;
|
|
42
|
+
});
|
|
43
|
+
if (msg instanceof UncaughtError) {
|
|
44
|
+
const error = await SymbolizedError.fromDetails({
|
|
45
|
+
devTools: options?.devTools,
|
|
46
|
+
details: msg.details,
|
|
47
|
+
targetId: msg.targetId,
|
|
48
|
+
includeStackAndCause: options?.fetchDetailedData,
|
|
49
|
+
resolvedStackTraceForTesting: options?.resolvedStackTraceForTesting,
|
|
50
|
+
resolvedCauseForTesting: options?.resolvedCauseForTesting,
|
|
51
|
+
});
|
|
52
|
+
return new ConsoleFormatter({
|
|
53
|
+
id: options.id,
|
|
54
|
+
type: 'error',
|
|
55
|
+
text: error.message,
|
|
56
|
+
stack: error.stackTrace,
|
|
57
|
+
cause: error.cause,
|
|
58
|
+
isIgnored,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
let resolvedArgs = [];
|
|
62
|
+
if (options.resolvedArgsForTesting) {
|
|
63
|
+
resolvedArgs = options.resolvedArgsForTesting;
|
|
64
|
+
}
|
|
65
|
+
else if (options.fetchDetailedData) {
|
|
66
|
+
resolvedArgs = await Promise.all(msg.args().map(async (arg, i) => {
|
|
67
|
+
try {
|
|
68
|
+
const remoteObject = arg.remoteObject();
|
|
69
|
+
if (remoteObject.type === 'object' &&
|
|
70
|
+
remoteObject.subtype === 'error') {
|
|
71
|
+
return await SymbolizedError.fromError({
|
|
72
|
+
devTools: options.devTools,
|
|
73
|
+
error: remoteObject,
|
|
74
|
+
// @ts-expect-error Internal ConsoleMessage API
|
|
75
|
+
targetId: msg._targetId(),
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
return await arg.jsonValue();
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return `<error: Argument ${i} is no longer available>`;
|
|
82
|
+
}
|
|
83
|
+
}));
|
|
84
|
+
}
|
|
85
|
+
let stack;
|
|
86
|
+
if (options.resolvedStackTraceForTesting) {
|
|
87
|
+
stack = options.resolvedStackTraceForTesting;
|
|
88
|
+
}
|
|
89
|
+
else if (options.fetchDetailedData && options.devTools) {
|
|
90
|
+
try {
|
|
91
|
+
stack = await createStackTraceForConsoleMessage(options.devTools, msg);
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
// ignore
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return new ConsoleFormatter({
|
|
98
|
+
id: options.id,
|
|
99
|
+
type: msg.type(),
|
|
100
|
+
text: msg.text(),
|
|
101
|
+
argCount: resolvedArgs.length || msg.args().length,
|
|
102
|
+
resolvedArgs,
|
|
103
|
+
stack,
|
|
104
|
+
isIgnored,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
// The short format for a console message.
|
|
108
|
+
toString() {
|
|
109
|
+
return convertConsoleMessageConciseToString(this.toJSON());
|
|
110
|
+
}
|
|
111
|
+
// The verbose format for a console message, including all details.
|
|
112
|
+
toStringDetailed() {
|
|
113
|
+
return convertConsoleMessageConciseDetailedToString(this.toJSONDetailed());
|
|
114
|
+
}
|
|
115
|
+
#getArgs() {
|
|
116
|
+
if (this.#resolvedArgs.length > 0) {
|
|
117
|
+
const args = [...this.#resolvedArgs];
|
|
118
|
+
// If there is no text, the first argument serves as text (see formatMessage).
|
|
119
|
+
if (!this.#text) {
|
|
120
|
+
args.shift();
|
|
121
|
+
}
|
|
122
|
+
return args;
|
|
123
|
+
}
|
|
124
|
+
return [];
|
|
125
|
+
}
|
|
126
|
+
toJSON() {
|
|
127
|
+
return {
|
|
128
|
+
type: this.#type,
|
|
129
|
+
text: this.#text,
|
|
130
|
+
argsCount: this.#argCount,
|
|
131
|
+
id: this.#id,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Groups consecutive messages with the same type, text, and argument count.
|
|
136
|
+
* Similar to Chrome DevTools' console grouping behavior.
|
|
137
|
+
*/
|
|
138
|
+
static groupConsecutive(messages) {
|
|
139
|
+
const grouped = [];
|
|
140
|
+
for (const msg of messages) {
|
|
141
|
+
const prev = grouped[grouped.length - 1];
|
|
142
|
+
if (prev &&
|
|
143
|
+
prev.message instanceof ConsoleFormatter &&
|
|
144
|
+
msg instanceof ConsoleFormatter &&
|
|
145
|
+
prev.message.#type === msg.#type &&
|
|
146
|
+
prev.message.#text === msg.#text &&
|
|
147
|
+
prev.message.#argCount === msg.#argCount) {
|
|
148
|
+
prev.count++;
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
grouped.push({ message: msg, count: 1 });
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return grouped.map(({ message, count }) => count > 1 && message instanceof ConsoleFormatter
|
|
155
|
+
? new GroupedConsoleFormatter({
|
|
156
|
+
id: message.#id,
|
|
157
|
+
type: message.#type,
|
|
158
|
+
text: message.#text,
|
|
159
|
+
argCount: message.#argCount,
|
|
160
|
+
isIgnored: message.isIgnored,
|
|
161
|
+
}, count)
|
|
162
|
+
: message);
|
|
163
|
+
}
|
|
164
|
+
toJSONDetailed() {
|
|
165
|
+
return {
|
|
166
|
+
id: this.#id,
|
|
167
|
+
type: this.#type,
|
|
168
|
+
text: this.#text,
|
|
169
|
+
argsCount: this.#argCount,
|
|
170
|
+
args: this.#getArgs().map(arg => formatArg(arg, this)),
|
|
171
|
+
stackTrace: this.#stack
|
|
172
|
+
? formatStackTrace(this.#stack, this.#cause, this)
|
|
173
|
+
: undefined,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
export class GroupedConsoleFormatter extends ConsoleFormatter {
|
|
178
|
+
#count;
|
|
179
|
+
constructor(params, count) {
|
|
180
|
+
super(params);
|
|
181
|
+
this.#count = count;
|
|
182
|
+
}
|
|
183
|
+
toString() {
|
|
184
|
+
return convertConsoleMessageConciseToString(this.toJSON());
|
|
185
|
+
}
|
|
186
|
+
toJSON() {
|
|
187
|
+
const json = super.toJSON();
|
|
188
|
+
json.count = this.#count;
|
|
189
|
+
return json;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
function convertConsoleMessageConciseToString(msg) {
|
|
193
|
+
const countSuffix = msg.count && msg.count > 1 ? ` [${msg.count} times]` : '';
|
|
194
|
+
return `msgid=${msg.id} [${msg.type}] ${msg.text} (${msg.argsCount} args)${countSuffix}`;
|
|
195
|
+
}
|
|
196
|
+
function convertConsoleMessageConciseDetailedToString(msg) {
|
|
197
|
+
const result = [
|
|
198
|
+
`ID: ${msg.id}`,
|
|
199
|
+
`Message: ${msg.type}> ${msg.text}`,
|
|
200
|
+
formatArgs(msg),
|
|
201
|
+
...(msg.stackTrace ? ['### Stack trace', msg.stackTrace] : []),
|
|
202
|
+
].filter(line => !!line);
|
|
203
|
+
return result.join('\n');
|
|
204
|
+
}
|
|
205
|
+
function formatArgs(msg) {
|
|
206
|
+
const args = msg.args;
|
|
207
|
+
if (!args.length) {
|
|
208
|
+
return '';
|
|
209
|
+
}
|
|
210
|
+
const result = ['### Arguments'];
|
|
211
|
+
for (const [key, arg] of args.entries()) {
|
|
212
|
+
result.push(`Arg #${key}: ${arg}`);
|
|
213
|
+
}
|
|
214
|
+
return result.join('\n');
|
|
215
|
+
}
|
|
216
|
+
function formatArg(arg, formatter) {
|
|
217
|
+
if (arg instanceof SymbolizedError) {
|
|
218
|
+
return [
|
|
219
|
+
arg.message,
|
|
220
|
+
arg.stackTrace
|
|
221
|
+
? formatStackTrace(arg.stackTrace, arg.cause, formatter)
|
|
222
|
+
: undefined,
|
|
223
|
+
]
|
|
224
|
+
.filter(line => !!line)
|
|
225
|
+
.join('\n');
|
|
226
|
+
}
|
|
227
|
+
return typeof arg === 'object' ? JSON.stringify(arg) : String(arg);
|
|
228
|
+
}
|
|
229
|
+
const STACK_TRACE_MAX_LINES = 50;
|
|
230
|
+
function formatStackTrace(stackTrace, cause, formatter) {
|
|
231
|
+
const lines = formatStackTraceInner(stackTrace, cause, formatter);
|
|
232
|
+
const includedLines = lines.slice(0, STACK_TRACE_MAX_LINES);
|
|
233
|
+
const reminderCount = lines.length - includedLines.length;
|
|
234
|
+
return [
|
|
235
|
+
...includedLines,
|
|
236
|
+
reminderCount > 0 ? `... and ${reminderCount} more frames` : '',
|
|
237
|
+
'Note: line and column numbers use 1-based indexing',
|
|
238
|
+
]
|
|
239
|
+
.filter(line => !!line)
|
|
240
|
+
.join('\n');
|
|
241
|
+
}
|
|
242
|
+
function formatStackTraceInner(stackTrace, cause, formatter) {
|
|
243
|
+
if (!stackTrace) {
|
|
244
|
+
return [];
|
|
245
|
+
}
|
|
246
|
+
return [
|
|
247
|
+
...formatFragment(stackTrace.syncFragment, formatter),
|
|
248
|
+
...stackTrace.asyncFragments
|
|
249
|
+
.map(item => formatAsyncFragment(item, formatter))
|
|
250
|
+
.flat(),
|
|
251
|
+
...formatCause(cause, formatter),
|
|
252
|
+
];
|
|
253
|
+
}
|
|
254
|
+
function formatFragment(fragment, formatter) {
|
|
255
|
+
const frames = fragment.frames.filter(frame => !formatter.isIgnored(frame));
|
|
256
|
+
return frames.map(formatFrame);
|
|
257
|
+
}
|
|
258
|
+
function formatAsyncFragment(fragment, formatter) {
|
|
259
|
+
const formattedFrames = formatFragment(fragment, formatter);
|
|
260
|
+
if (formattedFrames.length === 0) {
|
|
261
|
+
return [];
|
|
262
|
+
}
|
|
263
|
+
const separatorLineLength = 40;
|
|
264
|
+
const prefix = `--- ${fragment.description || 'async'} `;
|
|
265
|
+
const separator = prefix + '-'.repeat(separatorLineLength - prefix.length);
|
|
266
|
+
return [separator, ...formattedFrames];
|
|
267
|
+
}
|
|
268
|
+
function formatFrame(frame) {
|
|
269
|
+
let result = `at ${frame.name ?? '<anonymous>'}`;
|
|
270
|
+
if (frame.uiSourceCode) {
|
|
271
|
+
const location = frame.uiSourceCode.uiLocation(frame.line, frame.column);
|
|
272
|
+
result += ` (${location.linkText(/* skipTrim */ false, /* showColumnNumber */ true)})`;
|
|
273
|
+
}
|
|
274
|
+
else if (frame.url) {
|
|
275
|
+
result += ` (${frame.url}:${frame.line}:${frame.column})`;
|
|
276
|
+
}
|
|
277
|
+
return result;
|
|
278
|
+
}
|
|
279
|
+
function formatCause(cause, formatter) {
|
|
280
|
+
if (!cause) {
|
|
281
|
+
return [];
|
|
282
|
+
}
|
|
283
|
+
return [
|
|
284
|
+
`Caused by: ${cause.message}`,
|
|
285
|
+
...formatStackTraceInner(cause.stackTrace, cause.cause, formatter),
|
|
286
|
+
];
|
|
287
|
+
}
|
|
288
|
+
//# sourceMappingURL=ConsoleFormatter.js.map
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { stableIdSymbol } from '../utils/id.js';
|
|
7
|
+
export function isNodeLike(item) {
|
|
8
|
+
return (typeof item === 'object' && item !== null && 'id' in item && 'name' in item);
|
|
9
|
+
}
|
|
10
|
+
export class HeapSnapshotFormatter {
|
|
11
|
+
#aggregates;
|
|
12
|
+
constructor(aggregates) {
|
|
13
|
+
this.#aggregates = aggregates;
|
|
14
|
+
}
|
|
15
|
+
static formatNodes(items) {
|
|
16
|
+
const lines = [];
|
|
17
|
+
if (items.length > 0 && isNodeLike(items[0])) {
|
|
18
|
+
lines.push('id,name,type,distance,selfSize,retainedSize');
|
|
19
|
+
}
|
|
20
|
+
for (const item of items) {
|
|
21
|
+
if (isNodeLike(item)) {
|
|
22
|
+
lines.push(`${item.id},"${item.name}",${item.type},${item.distance},${item.selfSize},${item.retainedSize}`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return lines.join('\n');
|
|
26
|
+
}
|
|
27
|
+
#getSortedAggregates() {
|
|
28
|
+
return Object.values(this.#aggregates).sort((a, b) => b.maxRet - a.maxRet);
|
|
29
|
+
}
|
|
30
|
+
toString() {
|
|
31
|
+
const sorted = this.#getSortedAggregates();
|
|
32
|
+
const lines = [];
|
|
33
|
+
lines.push('uid,className,count,selfSize,maxRetainedSize');
|
|
34
|
+
for (const info of sorted) {
|
|
35
|
+
const uid = info[stableIdSymbol] ?? '';
|
|
36
|
+
lines.push(`${uid},"${info.name}",${info.count},${info.self},${info.maxRet}`);
|
|
37
|
+
}
|
|
38
|
+
return lines.join('\n');
|
|
39
|
+
}
|
|
40
|
+
toJSON() {
|
|
41
|
+
const sorted = this.#getSortedAggregates();
|
|
42
|
+
return sorted.map(info => ({
|
|
43
|
+
uid: info[stableIdSymbol],
|
|
44
|
+
className: info.name,
|
|
45
|
+
count: info.count,
|
|
46
|
+
selfSize: info.self,
|
|
47
|
+
retainedSize: info.maxRet,
|
|
48
|
+
}));
|
|
49
|
+
}
|
|
50
|
+
static sort(aggregates) {
|
|
51
|
+
return Object.entries(aggregates).sort((a, b) => b[1].maxRet - a[1].maxRet);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=HeapSnapshotFormatter.js.map
|