@yezack/chrome-devtools-mcp-108 0.0.0-stage → 1.10.2
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/FORK-NOTICE.md +45 -0
- package/LICENSE +202 -0
- package/README.md +45 -2
- package/build/src/BrowserManager.js +401 -0
- package/build/src/McpContext.js +642 -0
- package/build/src/McpPage.js +850 -0
- package/build/src/McpResponse.js +1086 -0
- package/build/src/SlimMcpResponse.js +19 -0
- package/build/src/TextSnapshot.js +315 -0
- package/build/src/ToolHandler.js +322 -0
- package/build/src/bin/check-latest-version.js +51 -0
- package/build/src/bin/chrome-devtools-mcp-main.js +77 -0
- package/build/src/bin/chrome-devtools-mcp.js +23 -0
- package/build/src/bin/chrome-devtools.js +222 -0
- package/build/src/collectors/PageCollector.js +291 -0
- package/build/src/collectors/ServiceWorkerCollector.js +171 -0
- package/build/src/config/browser-options.js +125 -0
- package/build/src/config/category-options.js +89 -0
- package/build/src/config/cli-commands.js +26 -0
- package/build/src/config/cli-options.js +1473 -0
- package/build/src/config/mcp-options.js +417 -0
- package/build/src/daemon/client.js +221 -0
- package/build/src/daemon/daemon.js +266 -0
- package/build/src/daemon/types.js +7 -0
- package/build/src/daemon/utils.js +136 -0
- package/build/src/devtools/DevToolsCommentBridge.js +91 -0
- package/build/src/devtools/DevtoolsUtils.js +307 -0
- package/build/src/devtools/McpHostBindingAdapter.js +165 -0
- package/build/src/devtools/issueDescriptions.js +42 -0
- package/build/src/formatters/CommentFormatter.js +82 -0
- package/build/src/formatters/ConsoleFormatter.js +321 -0
- package/build/src/formatters/CssFormatter.js +812 -0
- package/build/src/formatters/HeapSnapshotFormatter.js +185 -0
- package/build/src/formatters/IssueFormatter.js +193 -0
- package/build/src/formatters/NetworkFormatter.js +241 -0
- package/build/src/formatters/SnapshotFormatter.js +135 -0
- package/build/src/index.js +237 -0
- package/build/src/processors/ChunkedTraceParser.js +361 -0
- package/build/src/processors/HeapSnapshotManager.js +270 -0
- package/build/src/processors/PerformanceTrace.js +132 -0
- package/build/src/telemetry/ClearcutLogger.js +242 -0
- package/build/src/telemetry/WatchdogClient.js +61 -0
- package/build/src/telemetry/errors.js +18 -0
- package/build/src/telemetry/flagUtils.js +90 -0
- package/build/src/telemetry/metricsRegistry.js +85 -0
- package/build/src/telemetry/persistence.js +82 -0
- package/build/src/telemetry/transformation.js +233 -0
- package/build/src/telemetry/types.js +36 -0
- package/build/src/telemetry/watchdog/ClearcutSender.js +205 -0
- package/build/src/telemetry/watchdog/main.js +128 -0
- package/build/src/third_party/THIRD_PARTY_NOTICES +4077 -0
- package/build/src/third_party/bundled-packages.json +12 -0
- package/build/src/third_party/devtools-formatter-worker.js +15494 -0
- package/build/src/third_party/devtools-heap-snapshot-worker.js +10979 -0
- package/build/src/third_party/index.js +200978 -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/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/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/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 +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/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 +9 -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/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/federatedAuthRequestAccountsBlockedByConnectionAllowlist.md +1 -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/federatedAuthRequestConfigBlockedByConnectionAllowlist.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/federatedAuthRequestIdTokenBlockedByConnectionAllowlist.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/federatedAuthRequestWellKnownBlockedByConnectionAllowlist.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/genericBackUINavigationWouldSkipAd.md +4 -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/lazyLoadImageZeroSize.md +7 -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/issue-descriptions/webInstallManifestMissingId.md +3 -0
- package/build/src/third_party/issue-descriptions/webInstallManifestMissingNameOrShortName.md +3 -0
- package/build/src/third_party/issue-descriptions/webInstallManifestParsingOrNetworkError.md +3 -0
- package/build/src/third_party/issue-descriptions/webInstallNoManifest.md +3 -0
- package/build/src/third_party/issue-descriptions/webInstallStartUrlInvalid.md +3 -0
- package/build/src/third_party/lighthouse-devtools-mcp-bundle.js +58435 -0
- package/build/src/tools/ToolDefinition.js +80 -0
- package/build/src/tools/categories.js +33 -0
- package/build/src/tools/comments.js +169 -0
- package/build/src/tools/console.js +104 -0
- package/build/src/tools/css.js +45 -0
- package/build/src/tools/emulation.js +84 -0
- package/build/src/tools/extensions.js +103 -0
- package/build/src/tools/input.js +652 -0
- package/build/src/tools/lighthouse.js +149 -0
- package/build/src/tools/memory.js +424 -0
- package/build/src/tools/network.js +128 -0
- package/build/src/tools/pages.js +363 -0
- package/build/src/tools/performance.js +212 -0
- package/build/src/tools/pwa.js +128 -0
- package/build/src/tools/screencast.js +119 -0
- package/build/src/tools/screenshot.js +277 -0
- package/build/src/tools/script.js +232 -0
- package/build/src/tools/slim/tools.js +96 -0
- package/build/src/tools/snapshot.js +63 -0
- package/build/src/tools/thirdPartyDeveloper.js +85 -0
- package/build/src/tools/tools.js +55 -0
- package/build/src/tools/webmcp.js +66 -0
- package/build/src/types.js +10 -0
- package/build/src/utils/WaitForHelper.js +316 -0
- package/build/src/utils/bytes.js +105 -0
- package/build/src/utils/check-for-updates.js +75 -0
- package/build/src/utils/files.js +57 -0
- package/build/src/utils/id.js +16 -0
- package/build/src/utils/keyboard.js +297 -0
- package/build/src/utils/logger.js +52 -0
- package/build/src/utils/pagination.js +50 -0
- package/build/src/utils/polyfill.js +11 -0
- package/build/src/utils/url.js +116 -0
- package/build/src/version.js +10 -0
- package/package.json +41 -4
- package/skills/a11y-debugging/SKILL.md +91 -0
- package/skills/a11y-debugging/references/a11y-snippets.md +92 -0
- package/skills/chrome-devtools/SKILL.md +74 -0
- package/skills/chrome-devtools-cli/SKILL.md +194 -0
- package/skills/chrome-devtools-cli/references/installation.md +14 -0
- package/skills/cookie-debugging/SKILL.md +149 -0
- package/skills/debug-optimize-lcp/SKILL.md +121 -0
- package/skills/debug-optimize-lcp/references/elements-and-size.md +27 -0
- package/skills/debug-optimize-lcp/references/lcp-breakdown.md +23 -0
- package/skills/debug-optimize-lcp/references/lcp-snippets.md +79 -0
- package/skills/debug-optimize-lcp/references/optimization-strategies.md +38 -0
- package/skills/memory-leak-debugging/SKILL.md +58 -0
- package/skills/memory-leak-debugging/references/common-leaks.md +33 -0
- package/skills/troubleshooting/SKILL.md +99 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { execSync } from 'node:child_process';
|
|
7
|
+
import fs from 'node:fs/promises';
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
import process from 'node:process';
|
|
10
|
+
const DEFAULT_REGISTRY = 'https://registry.npmjs.org';
|
|
11
|
+
function getRegistry() {
|
|
12
|
+
// Use the user's configured npm registry so update checks work behind
|
|
13
|
+
// corporate proxies and private registries. `npm config get registry`
|
|
14
|
+
// honors .npmrc files at every scope and respects npm_config_registry,
|
|
15
|
+
// so it covers direct CLI invocations as well as `npx` / `npm run`.
|
|
16
|
+
try {
|
|
17
|
+
const registry = execSync('npm config get registry', {
|
|
18
|
+
encoding: 'utf8',
|
|
19
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
20
|
+
timeout: 5000,
|
|
21
|
+
windowsHide: true,
|
|
22
|
+
})
|
|
23
|
+
.trim()
|
|
24
|
+
.replace(/\/$/, '');
|
|
25
|
+
if (registry && registry !== 'undefined' && /^https?:\/\//.test(registry)) {
|
|
26
|
+
return registry;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
// npm not on PATH or other errors, fall back to default.
|
|
31
|
+
}
|
|
32
|
+
return DEFAULT_REGISTRY;
|
|
33
|
+
}
|
|
34
|
+
const cachePath = process.argv[2];
|
|
35
|
+
if (cachePath) {
|
|
36
|
+
try {
|
|
37
|
+
const response = await fetch(`${getRegistry()}/chrome-devtools-mcp/latest`);
|
|
38
|
+
const data = response.ok ? await response.json() : null;
|
|
39
|
+
if (data &&
|
|
40
|
+
typeof data === 'object' &&
|
|
41
|
+
'version' in data &&
|
|
42
|
+
typeof data.version === 'string') {
|
|
43
|
+
await fs.mkdir(path.dirname(cachePath), { recursive: true });
|
|
44
|
+
await fs.writeFile(cachePath, JSON.stringify({ version: data.version }));
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
// Ignore errors.
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=check-latest-version.js.map
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import '../utils/polyfill.js';
|
|
7
|
+
import process from 'node:process';
|
|
8
|
+
import { BrowserManager } from '../BrowserManager.js';
|
|
9
|
+
import { McpServer, logDisclaimers } from '../index.js';
|
|
10
|
+
import { ClearcutLogger } from '../telemetry/ClearcutLogger.js';
|
|
11
|
+
import { computeFlagUsage } from '../telemetry/flagUtils.js';
|
|
12
|
+
import { StdioServerTransport } from '../third_party/index.js';
|
|
13
|
+
import { checkForUpdates } from '../utils/check-for-updates.js';
|
|
14
|
+
import { logger, saveLogsToFile } from '../utils/logger.js';
|
|
15
|
+
import { VERSION } from '../version.js';
|
|
16
|
+
import { mcpOptions, parseArguments } from '../config/mcp-options.js';
|
|
17
|
+
await checkForUpdates('Run `npm install chrome-devtools-mcp@latest` to update.');
|
|
18
|
+
export const args = parseArguments(VERSION);
|
|
19
|
+
const logFile = args.logFile ? saveLogsToFile(args.logFile) : undefined;
|
|
20
|
+
if (process.env['CHROME_DEVTOOLS_MCP_CRASH_ON_UNCAUGHT'] !== 'true') {
|
|
21
|
+
process.on('unhandledRejection', (reason, promise) => {
|
|
22
|
+
logger?.('Unhandled promise rejection', promise, reason);
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
logger?.(`Starting Chrome DevTools MCP Server v${VERSION}`);
|
|
26
|
+
const browserManager = new BrowserManager(args, {
|
|
27
|
+
logFile,
|
|
28
|
+
});
|
|
29
|
+
const serverPromise = McpServer.from(args, {
|
|
30
|
+
browserManager,
|
|
31
|
+
});
|
|
32
|
+
// Shutdown on stdin EOF (stdio MCP convention — the client closes the
|
|
33
|
+
// transport to signal exit) and on standard termination signals. Without
|
|
34
|
+
// this, an active Chrome subprocess keeps the Node event loop ref'd after
|
|
35
|
+
// stdin closes and the server hangs until something else kills it.
|
|
36
|
+
let shuttingDown = false;
|
|
37
|
+
async function shutdown(reason) {
|
|
38
|
+
if (shuttingDown) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
shuttingDown = true;
|
|
42
|
+
logger?.(`Shutting down (${reason})`);
|
|
43
|
+
// Backstop in case browser teardown hangs (e.g. unresponsive Chrome,
|
|
44
|
+
// slow beforeunload handlers, many tabs). Exits 0 because we still
|
|
45
|
+
// honored the shutdown request; the log line preserves observability.
|
|
46
|
+
// Unref'd so it doesn't keep the loop alive on the clean path.
|
|
47
|
+
setTimeout(() => {
|
|
48
|
+
logger?.('Shutdown timeout exceeded, forcing exit');
|
|
49
|
+
process.exit(0);
|
|
50
|
+
}, 5000).unref();
|
|
51
|
+
const server = await serverPromise;
|
|
52
|
+
await server.close();
|
|
53
|
+
process.exit(0);
|
|
54
|
+
}
|
|
55
|
+
process.stdin.on('end', () => {
|
|
56
|
+
void shutdown('stdin end');
|
|
57
|
+
});
|
|
58
|
+
process.stdin.on('close', () => {
|
|
59
|
+
void shutdown('stdin close');
|
|
60
|
+
});
|
|
61
|
+
process.on('SIGTERM', () => {
|
|
62
|
+
void shutdown('SIGTERM');
|
|
63
|
+
});
|
|
64
|
+
process.on('SIGINT', () => {
|
|
65
|
+
void shutdown('SIGINT');
|
|
66
|
+
});
|
|
67
|
+
process.on('SIGHUP', () => {
|
|
68
|
+
void shutdown('SIGHUP');
|
|
69
|
+
});
|
|
70
|
+
const server = await serverPromise;
|
|
71
|
+
const transport = new StdioServerTransport();
|
|
72
|
+
await server.connect(transport);
|
|
73
|
+
logger?.('Chrome DevTools MCP Server connected');
|
|
74
|
+
logDisclaimers(args);
|
|
75
|
+
void ClearcutLogger.get()?.logDailyActiveIfNeeded();
|
|
76
|
+
void ClearcutLogger.get()?.logServerStart(computeFlagUsage(args, mcpOptions));
|
|
77
|
+
//# sourceMappingURL=chrome-devtools-mcp-main.js.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @license
|
|
4
|
+
* Copyright 2025 Google LLC
|
|
5
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
6
|
+
*/
|
|
7
|
+
process.title = 'chrome-devtools-mcp';
|
|
8
|
+
import { version } from 'node:process';
|
|
9
|
+
const [major, minor] = version.substring(1).split('.').map(Number);
|
|
10
|
+
if (major === 20 && minor < 19) {
|
|
11
|
+
console.error(`ERROR: \`chrome-devtools-mcp\` does not support Node ${process.version}. Please upgrade to Node 20.19.0 LTS or a newer LTS.`);
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
if (major === 22 && minor < 12) {
|
|
15
|
+
console.error(`ERROR: \`chrome-devtools-mcp\` does not support Node ${process.version}. Please upgrade to Node 22.12.0 LTS or a newer LTS.`);
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
if (major < 20) {
|
|
19
|
+
console.error(`ERROR: \`chrome-devtools-mcp\` does not support Node ${process.version}. Please upgrade to Node 20.19.0 LTS or a newer LTS.`);
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
await import('./chrome-devtools-mcp-main.js');
|
|
23
|
+
//# sourceMappingURL=chrome-devtools-mcp.js.map
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @license
|
|
4
|
+
* Copyright 2026 Google LLC
|
|
5
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
6
|
+
*/
|
|
7
|
+
process.title = 'chrome-devtools';
|
|
8
|
+
import process from 'node:process';
|
|
9
|
+
import { startDaemon, stopDaemon, sendCommand, handleResponse, verifyDaemonVersion, } from '../daemon/client.js';
|
|
10
|
+
import { isDaemonRunning, serializeArgs, assertValidSessionId, } from '../daemon/utils.js';
|
|
11
|
+
import { logDisclaimers } from '../index.js';
|
|
12
|
+
import { hideBin, yargs } from '../third_party/index.js';
|
|
13
|
+
import { checkForUpdates } from '../utils/check-for-updates.js';
|
|
14
|
+
import { VERSION } from '../version.js';
|
|
15
|
+
import { buildCommand } from '../config/cli-commands.js';
|
|
16
|
+
import { commands } from '../config/cli-options.js';
|
|
17
|
+
import { mcpOptions, parseArguments, getMcpOptionsForViaCli, } from '../config/mcp-options.js';
|
|
18
|
+
await checkForUpdates('Run `npm install -g chrome-devtools-mcp@latest` and `chrome-devtools start` to update and restart the daemon.');
|
|
19
|
+
const DEFAULT_CLI_ARGS = ['--viaCli'];
|
|
20
|
+
async function start(args, sessionId) {
|
|
21
|
+
const combinedArgs = [...DEFAULT_CLI_ARGS, ...args];
|
|
22
|
+
await startDaemon(combinedArgs, sessionId);
|
|
23
|
+
logDisclaimers(parseArguments(VERSION, combinedArgs));
|
|
24
|
+
}
|
|
25
|
+
function getCliOptions() {
|
|
26
|
+
const options = {
|
|
27
|
+
...getMcpOptionsForViaCli(),
|
|
28
|
+
};
|
|
29
|
+
// Missing CLI serialization.
|
|
30
|
+
delete options.viewport;
|
|
31
|
+
// Change the defaults for the CLI.
|
|
32
|
+
delete options.experimentalStructuredContent;
|
|
33
|
+
delete options.experimentalInteropTools;
|
|
34
|
+
return options;
|
|
35
|
+
}
|
|
36
|
+
const y = yargs(hideBin(process.argv))
|
|
37
|
+
.locale('en') // Force English to ensure error string matching works in .fail, all custom messages we output are in English anyways
|
|
38
|
+
.scriptName('chrome-devtools')
|
|
39
|
+
.showHelpOnFail(true)
|
|
40
|
+
.usage('chrome-devtools <command> [...args] --flags')
|
|
41
|
+
.usage(`Run 'chrome-devtools <command> --help' for help on the specific command.`)
|
|
42
|
+
.option('sessionId', {
|
|
43
|
+
type: 'string',
|
|
44
|
+
description: 'Session ID for daemon scoping',
|
|
45
|
+
default: process.env.CD4A_INTERNAL_DAEMON_SESSION_ID || '',
|
|
46
|
+
hidden: true,
|
|
47
|
+
coerce: (sessionId) => {
|
|
48
|
+
assertValidSessionId(sessionId);
|
|
49
|
+
return sessionId;
|
|
50
|
+
},
|
|
51
|
+
})
|
|
52
|
+
.demandCommand()
|
|
53
|
+
.version(VERSION)
|
|
54
|
+
.strict()
|
|
55
|
+
.help(true)
|
|
56
|
+
.wrap(120)
|
|
57
|
+
.fail((msg, err) => {
|
|
58
|
+
if (msg) {
|
|
59
|
+
console.error('Error:', msg);
|
|
60
|
+
if (msg.includes('Not enough non-option arguments') ||
|
|
61
|
+
msg.includes('Unknown argument') ||
|
|
62
|
+
msg.includes('Unknown arguments')) {
|
|
63
|
+
console.error('\n=========================================');
|
|
64
|
+
console.error('💡 TIP FOR AI AGENT / DEVELOPER:');
|
|
65
|
+
console.error('In the `chrome-devtools` CLI:');
|
|
66
|
+
console.error('1. Required parameters MUST be passed as positional arguments (without flags).');
|
|
67
|
+
console.error(' - INCORRECT: chrome-devtools click --pageId 1 --uid "1_2"');
|
|
68
|
+
console.error(' - CORRECT: chrome-devtools click 1 "1_2"');
|
|
69
|
+
console.error('2. Optional parameters are passed as double-dash options/flags (e.g. --dblClick true).');
|
|
70
|
+
console.error('3. Make sure to escape quotes properly for your shell environment.');
|
|
71
|
+
console.error('Run `chrome-devtools <command> --help` to see exact positional and optional parameters.');
|
|
72
|
+
console.error('=========================================');
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
else if (err) {
|
|
76
|
+
console.error(err);
|
|
77
|
+
}
|
|
78
|
+
process.exit(1);
|
|
79
|
+
});
|
|
80
|
+
y.command('start', 'Start or restart chrome-devtools-mcp', y => y
|
|
81
|
+
.options(getCliOptions())
|
|
82
|
+
.example('$0 start --browserUrl http://localhost:9222', 'Start the server connecting to an existing browser')
|
|
83
|
+
.strict(), async (argv) => {
|
|
84
|
+
if (isDaemonRunning(argv.sessionId)) {
|
|
85
|
+
await stopDaemon(argv.sessionId);
|
|
86
|
+
}
|
|
87
|
+
// Defaults but we do not want to affect the yargs conflict resolution.
|
|
88
|
+
if (argv.isolated === undefined &&
|
|
89
|
+
argv.userDataDir === undefined &&
|
|
90
|
+
!argv.autoConnect &&
|
|
91
|
+
!argv.browserUrl &&
|
|
92
|
+
!argv.wsEndpoint) {
|
|
93
|
+
argv.isolated = true;
|
|
94
|
+
}
|
|
95
|
+
if (argv.headless === undefined &&
|
|
96
|
+
!argv.autoConnect &&
|
|
97
|
+
!argv.browserUrl &&
|
|
98
|
+
!argv.wsEndpoint) {
|
|
99
|
+
argv.headless = true;
|
|
100
|
+
}
|
|
101
|
+
const args = serializeArgs(getCliOptions(), argv);
|
|
102
|
+
await start(args, argv.sessionId);
|
|
103
|
+
process.exit(0);
|
|
104
|
+
}).strict(); // Re-enable strict validation for other commands; this is applied to the yargs instance itself
|
|
105
|
+
y.command('status', 'Checks if chrome-devtools-mcp is running', y => y, async (argv) => {
|
|
106
|
+
if (isDaemonRunning(argv.sessionId)) {
|
|
107
|
+
console.log('chrome-devtools-mcp daemon is running.');
|
|
108
|
+
const response = await sendCommand({
|
|
109
|
+
method: 'status',
|
|
110
|
+
}, argv.sessionId);
|
|
111
|
+
if (response.success) {
|
|
112
|
+
const data = JSON.parse(response.result);
|
|
113
|
+
console.log(`pid=${data.pid} socket=${data.socketPath} start-date=${data.startDate} version=${data.version}`);
|
|
114
|
+
console.log(`args=${JSON.stringify(data.args)}`);
|
|
115
|
+
if (data.version !== VERSION) {
|
|
116
|
+
console.warn(`Warning: Daemon server version (${data.version}) does not match CLI version (${VERSION}). Run 'chrome-devtools start' to update and restart the daemon.`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
console.error('Error:', response.error);
|
|
121
|
+
process.exit(1);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
console.log('chrome-devtools-mcp daemon is not running.');
|
|
126
|
+
}
|
|
127
|
+
process.exit(0);
|
|
128
|
+
});
|
|
129
|
+
y.command('stop', 'Stop chrome-devtools-mcp if any', y => y, async (argv) => {
|
|
130
|
+
const sessionId = argv.sessionId;
|
|
131
|
+
if (!isDaemonRunning(sessionId)) {
|
|
132
|
+
process.exit(0);
|
|
133
|
+
}
|
|
134
|
+
await stopDaemon(sessionId);
|
|
135
|
+
process.exit(0);
|
|
136
|
+
});
|
|
137
|
+
for (const [commandName, commandDef] of Object.entries(commands)) {
|
|
138
|
+
const args = commandDef.args;
|
|
139
|
+
const { command, usage } = buildCommand(commandName, args);
|
|
140
|
+
y.command(command, commandDef.description, y => {
|
|
141
|
+
y.usage(usage);
|
|
142
|
+
y.option('output-format', {
|
|
143
|
+
choices: ['md', 'json'],
|
|
144
|
+
default: 'md',
|
|
145
|
+
});
|
|
146
|
+
for (const [argName, opt] of Object.entries(args)) {
|
|
147
|
+
const type = opt.type === 'integer' || opt.type === 'number'
|
|
148
|
+
? 'number'
|
|
149
|
+
: opt.type === 'boolean'
|
|
150
|
+
? 'boolean'
|
|
151
|
+
: opt.type === 'array'
|
|
152
|
+
? 'array'
|
|
153
|
+
: 'string';
|
|
154
|
+
if (opt.required) {
|
|
155
|
+
const options = {
|
|
156
|
+
describe: opt.description,
|
|
157
|
+
type: type,
|
|
158
|
+
};
|
|
159
|
+
if (opt.default !== undefined) {
|
|
160
|
+
options.default = opt.default;
|
|
161
|
+
}
|
|
162
|
+
if (opt.enum) {
|
|
163
|
+
options.choices = opt.enum;
|
|
164
|
+
}
|
|
165
|
+
y.positional(argName, options);
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
const options = {
|
|
169
|
+
describe: opt.description,
|
|
170
|
+
type: type,
|
|
171
|
+
};
|
|
172
|
+
if (opt.default !== undefined) {
|
|
173
|
+
options.default = opt.default;
|
|
174
|
+
}
|
|
175
|
+
if (opt.enum) {
|
|
176
|
+
options.choices = opt.enum;
|
|
177
|
+
}
|
|
178
|
+
y.option(argName, options);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}, async (argv) => {
|
|
182
|
+
const sessionId = argv.sessionId;
|
|
183
|
+
try {
|
|
184
|
+
const versionWarningPromise = isDaemonRunning(sessionId)
|
|
185
|
+
? verifyDaemonVersion(sessionId, VERSION)
|
|
186
|
+
: Promise.resolve(undefined);
|
|
187
|
+
if (!isDaemonRunning(sessionId)) {
|
|
188
|
+
await start(serializeArgs(mcpOptions, argv), sessionId);
|
|
189
|
+
}
|
|
190
|
+
const commandArgs = {};
|
|
191
|
+
for (const argName of Object.keys(args)) {
|
|
192
|
+
if (argName in argv) {
|
|
193
|
+
commandArgs[argName] = argv[argName];
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
const response = await sendCommand({
|
|
197
|
+
method: 'invoke_tool',
|
|
198
|
+
tool: commandName,
|
|
199
|
+
args: commandArgs,
|
|
200
|
+
}, sessionId);
|
|
201
|
+
if (response.success) {
|
|
202
|
+
console.log(await handleResponse(JSON.parse(response.result), argv['output-format']));
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
console.error('Error:', response.error);
|
|
206
|
+
}
|
|
207
|
+
const versionWarning = await versionWarningPromise;
|
|
208
|
+
if (versionWarning) {
|
|
209
|
+
console.warn(versionWarning);
|
|
210
|
+
}
|
|
211
|
+
if (!response.success) {
|
|
212
|
+
process.exit(1);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
catch (error) {
|
|
216
|
+
console.error('Failed to execute command:', error);
|
|
217
|
+
process.exit(1);
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
await y.parse();
|
|
222
|
+
//# sourceMappingURL=chrome-devtools.js.map
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { FakeIssuesManager } from '../devtools/DevtoolsUtils.js';
|
|
7
|
+
import { DevTools, FrameEvent } from '../third_party/index.js';
|
|
8
|
+
import { createIdGenerator, stableIdSymbol, } from '../utils/id.js';
|
|
9
|
+
import { logger } from '../utils/logger.js';
|
|
10
|
+
export class UncaughtError {
|
|
11
|
+
details;
|
|
12
|
+
targetId;
|
|
13
|
+
constructor(details, targetId) {
|
|
14
|
+
this.details = details;
|
|
15
|
+
this.targetId = targetId;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export class PageCollector {
|
|
19
|
+
pptrPage;
|
|
20
|
+
#listeners;
|
|
21
|
+
#pendingSameDocumentNavigation = false;
|
|
22
|
+
#frameNavigatedWithinDocumentTarget;
|
|
23
|
+
maxNavigationSaved = 3;
|
|
24
|
+
/**
|
|
25
|
+
* This maps a Page to a list of navigations with a sub-list
|
|
26
|
+
* of all collected resources.
|
|
27
|
+
* The newer navigations come first.
|
|
28
|
+
*/
|
|
29
|
+
storage = [[]];
|
|
30
|
+
constructor(page, listeners, maxResourcesPerNavigation) {
|
|
31
|
+
this.pptrPage = page;
|
|
32
|
+
this.#attachFrameNavigatedWithinDocumentListener();
|
|
33
|
+
const idGenerator = createIdGenerator();
|
|
34
|
+
const listenerMap = listeners(value => {
|
|
35
|
+
const withId = value;
|
|
36
|
+
withId[stableIdSymbol] = idGenerator();
|
|
37
|
+
this.storage[0].push(withId);
|
|
38
|
+
if (maxResourcesPerNavigation !== undefined &&
|
|
39
|
+
this.storage[0].length > maxResourcesPerNavigation) {
|
|
40
|
+
this.storage[0].splice(0, this.storage[0].length - maxResourcesPerNavigation);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
listenerMap['framenavigated'] = (frame) => {
|
|
44
|
+
// Only split the storage on main frame navigation
|
|
45
|
+
if (frame !== this.pptrPage.mainFrame()) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
// Re-attach the listener to the current main frame: a cross-document
|
|
49
|
+
// navigation may have replaced the frame object, so the reference
|
|
50
|
+
// captured at construction time would be stale.
|
|
51
|
+
this.#attachFrameNavigatedWithinDocumentListener();
|
|
52
|
+
// Same-document (SPA) navigations also emit `framenavigated`, but they
|
|
53
|
+
// must not rotate the retained history. Puppeteer emits
|
|
54
|
+
// `FrameNavigatedWithinDocument` right before `FrameNavigated` for such
|
|
55
|
+
// navigations, so consume the flag here to skip the split.
|
|
56
|
+
if (this.#pendingSameDocumentNavigation) {
|
|
57
|
+
this.#pendingSameDocumentNavigation = false;
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
this.splitAfterNavigation();
|
|
61
|
+
};
|
|
62
|
+
for (const [name, listener] of Object.entries(listenerMap)) {
|
|
63
|
+
this.pptrPage.on(name, listener);
|
|
64
|
+
}
|
|
65
|
+
this.#listeners = listenerMap;
|
|
66
|
+
}
|
|
67
|
+
dispose() {
|
|
68
|
+
this.#detachFrameNavigatedWithinDocumentListener();
|
|
69
|
+
if (this.#listeners) {
|
|
70
|
+
for (const [name, listener] of Object.entries(this.#listeners)) {
|
|
71
|
+
this.pptrPage.off(name, listener);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// Puppeteer emits this right before the page-level navigation event for
|
|
76
|
+
// same-document (SPA) navigations, which lets `framenavigated` skip the
|
|
77
|
+
// history rotation for those navigations.
|
|
78
|
+
#onFrameNavigatedWithinDocument = () => {
|
|
79
|
+
this.#pendingSameDocumentNavigation = true;
|
|
80
|
+
};
|
|
81
|
+
#attachFrameNavigatedWithinDocumentListener() {
|
|
82
|
+
this.#detachFrameNavigatedWithinDocumentListener();
|
|
83
|
+
this.#frameNavigatedWithinDocumentTarget = this.pptrPage.mainFrame();
|
|
84
|
+
this.#frameNavigatedWithinDocumentTarget.on(FrameEvent.FrameNavigatedWithinDocument, this.#onFrameNavigatedWithinDocument);
|
|
85
|
+
}
|
|
86
|
+
#detachFrameNavigatedWithinDocumentListener() {
|
|
87
|
+
if (this.#frameNavigatedWithinDocumentTarget) {
|
|
88
|
+
this.#frameNavigatedWithinDocumentTarget.off(FrameEvent.FrameNavigatedWithinDocument, this.#onFrameNavigatedWithinDocument);
|
|
89
|
+
this.#frameNavigatedWithinDocumentTarget = undefined;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
splitAfterNavigation() {
|
|
93
|
+
// Add the latest navigation first
|
|
94
|
+
this.storage.unshift([]);
|
|
95
|
+
this.storage.splice(this.maxNavigationSaved);
|
|
96
|
+
}
|
|
97
|
+
getData(includePreservedData) {
|
|
98
|
+
if (!includePreservedData) {
|
|
99
|
+
return this.storage[0];
|
|
100
|
+
}
|
|
101
|
+
const data = [];
|
|
102
|
+
for (let index = this.maxNavigationSaved; index >= 0; index--) {
|
|
103
|
+
if (this.storage[index]) {
|
|
104
|
+
data.push(...this.storage[index]);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return data;
|
|
108
|
+
}
|
|
109
|
+
getIdForResource(resource) {
|
|
110
|
+
return resource[stableIdSymbol] ?? -1;
|
|
111
|
+
}
|
|
112
|
+
getById(stableId) {
|
|
113
|
+
const item = this.find(item => item[stableIdSymbol] === stableId);
|
|
114
|
+
if (!item) {
|
|
115
|
+
throw new Error('Request not found for selected page');
|
|
116
|
+
}
|
|
117
|
+
return item;
|
|
118
|
+
}
|
|
119
|
+
find(filter) {
|
|
120
|
+
for (const navigation of this.storage) {
|
|
121
|
+
const item = navigation.find(filter);
|
|
122
|
+
if (item) {
|
|
123
|
+
return item;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
export class ConsoleCollector extends PageCollector {
|
|
130
|
+
static MAX_MESSAGES_PER_NAVIGATION = 10_000;
|
|
131
|
+
#subscriber;
|
|
132
|
+
constructor(page, listeners, maxMessagesPerNavigation = ConsoleCollector.MAX_MESSAGES_PER_NAVIGATION) {
|
|
133
|
+
super(page, listeners, maxMessagesPerNavigation);
|
|
134
|
+
this.#subscriber = new PageEventSubscriber(this.pptrPage);
|
|
135
|
+
this.#subscriber.subscribe();
|
|
136
|
+
}
|
|
137
|
+
dispose() {
|
|
138
|
+
super.dispose();
|
|
139
|
+
this.#subscriber?.unsubscribe();
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
class PageEventSubscriber {
|
|
143
|
+
#issueManager = new FakeIssuesManager();
|
|
144
|
+
#issueAggregator = new DevTools.IssueAggregator(this.#issueManager);
|
|
145
|
+
#seenKeys = new Set();
|
|
146
|
+
#seenIssues = new Set();
|
|
147
|
+
#page;
|
|
148
|
+
#session;
|
|
149
|
+
#targetId;
|
|
150
|
+
#pendingSameDocumentNavigation = false;
|
|
151
|
+
#frameNavigatedWithinDocumentTarget;
|
|
152
|
+
constructor(page) {
|
|
153
|
+
this.#page = page;
|
|
154
|
+
// @ts-expect-error use existing CDP client (internal Puppeteer API).
|
|
155
|
+
this.#session = this.#page._client();
|
|
156
|
+
// @ts-expect-error use internal Puppeteer API to get target ID
|
|
157
|
+
this.#targetId = this.#session.target()._targetId;
|
|
158
|
+
}
|
|
159
|
+
#resetIssueAggregator() {
|
|
160
|
+
this.#issueManager = new FakeIssuesManager();
|
|
161
|
+
if (this.#issueAggregator) {
|
|
162
|
+
this.#issueAggregator.removeEventListener("AggregatedIssueUpdated" /* DevTools.IssueAggregatorEvents.AGGREGATED_ISSUE_UPDATED */, this.#onAggregatedIssue);
|
|
163
|
+
}
|
|
164
|
+
this.#issueAggregator = new DevTools.IssueAggregator(this.#issueManager);
|
|
165
|
+
this.#issueAggregator.addEventListener("AggregatedIssueUpdated" /* DevTools.IssueAggregatorEvents.AGGREGATED_ISSUE_UPDATED */, this.#onAggregatedIssue);
|
|
166
|
+
}
|
|
167
|
+
subscribe() {
|
|
168
|
+
this.#resetIssueAggregator();
|
|
169
|
+
this.#page.on('framenavigated', this.#onFrameNavigated);
|
|
170
|
+
this.#page.on('issue', this.#onIssueAdded);
|
|
171
|
+
this.#session.on('Runtime.exceptionThrown', this.#onExceptionThrown);
|
|
172
|
+
this.#attachFrameNavigatedWithinDocumentListener();
|
|
173
|
+
}
|
|
174
|
+
unsubscribe() {
|
|
175
|
+
this.#seenKeys.clear();
|
|
176
|
+
this.#seenIssues.clear();
|
|
177
|
+
this.#detachFrameNavigatedWithinDocumentListener();
|
|
178
|
+
this.#page.off('framenavigated', this.#onFrameNavigated);
|
|
179
|
+
this.#page.off('issue', this.#onIssueAdded);
|
|
180
|
+
this.#session.off('Runtime.exceptionThrown', this.#onExceptionThrown);
|
|
181
|
+
if (this.#issueAggregator) {
|
|
182
|
+
this.#issueAggregator.removeEventListener("AggregatedIssueUpdated" /* DevTools.IssueAggregatorEvents.AGGREGATED_ISSUE_UPDATED */, this.#onAggregatedIssue);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
#onAggregatedIssue = (event) => {
|
|
186
|
+
if (this.#seenIssues.has(event.data)) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
this.#seenIssues.add(event.data);
|
|
190
|
+
this.#page.emit('devtoolsAggregatedIssue', event.data);
|
|
191
|
+
};
|
|
192
|
+
#onExceptionThrown = (event) => {
|
|
193
|
+
this.#page.emit('uncaughtError', new UncaughtError(event.exceptionDetails, this.#targetId));
|
|
194
|
+
};
|
|
195
|
+
// On navigation, we reset issue aggregation.
|
|
196
|
+
#onFrameNavigated = (frame) => {
|
|
197
|
+
// Only split the storage on main frame navigation
|
|
198
|
+
if (frame !== frame.page().mainFrame()) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
// Re-attach the listener to the current main frame: a cross-document
|
|
202
|
+
// navigation may have replaced the frame object, so the reference
|
|
203
|
+
// captured at construction time would be stale.
|
|
204
|
+
this.#attachFrameNavigatedWithinDocumentListener();
|
|
205
|
+
if (this.#pendingSameDocumentNavigation) {
|
|
206
|
+
this.#pendingSameDocumentNavigation = false;
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
this.#seenKeys.clear();
|
|
210
|
+
this.#seenIssues.clear();
|
|
211
|
+
this.#resetIssueAggregator();
|
|
212
|
+
};
|
|
213
|
+
// Puppeteer emits this right before the page-level navigation event for
|
|
214
|
+
// same-document (SPA) navigations, which lets `framenavigated` skip the
|
|
215
|
+
// issue aggregation reset for those navigations.
|
|
216
|
+
#onFrameNavigatedWithinDocument = () => {
|
|
217
|
+
this.#pendingSameDocumentNavigation = true;
|
|
218
|
+
};
|
|
219
|
+
#attachFrameNavigatedWithinDocumentListener() {
|
|
220
|
+
this.#detachFrameNavigatedWithinDocumentListener();
|
|
221
|
+
this.#frameNavigatedWithinDocumentTarget = this.#page.mainFrame();
|
|
222
|
+
this.#frameNavigatedWithinDocumentTarget.on(FrameEvent.FrameNavigatedWithinDocument, this.#onFrameNavigatedWithinDocument);
|
|
223
|
+
}
|
|
224
|
+
#detachFrameNavigatedWithinDocumentListener() {
|
|
225
|
+
if (this.#frameNavigatedWithinDocumentTarget) {
|
|
226
|
+
this.#frameNavigatedWithinDocumentTarget.off(FrameEvent.FrameNavigatedWithinDocument, this.#onFrameNavigatedWithinDocument);
|
|
227
|
+
this.#frameNavigatedWithinDocumentTarget = undefined;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
#onIssueAdded = (inspectorIssue) => {
|
|
231
|
+
try {
|
|
232
|
+
// @ts-expect-error The types are missmatched but they
|
|
233
|
+
// are coming from CDP
|
|
234
|
+
if (!DevTools.isIssueCodeSupported(inspectorIssue.code)) {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
const issue = DevTools.createIssuesFromProtocolIssue(null,
|
|
238
|
+
// @ts-expect-error Protocol types diverge.
|
|
239
|
+
inspectorIssue)[0];
|
|
240
|
+
if (!issue) {
|
|
241
|
+
logger?.('No issue mapping for for the issue: ', inspectorIssue.code);
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
const primaryKey = issue.primaryKey();
|
|
245
|
+
if (this.#seenKeys.has(primaryKey)) {
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
this.#seenKeys.add(primaryKey);
|
|
249
|
+
this.#issueManager.dispatchEventToListeners("IssueAdded" /* DevTools.IssuesManagerEvents.ISSUE_ADDED */, {
|
|
250
|
+
issue,
|
|
251
|
+
// @ts-expect-error We don't care that issues model is null
|
|
252
|
+
issuesModel: null,
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
catch (error) {
|
|
256
|
+
logger?.('Error creating a new issue', error);
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
export class NetworkCollector extends PageCollector {
|
|
261
|
+
static MAX_REQUESTS_PER_NAVIGATION = 1_000;
|
|
262
|
+
constructor(page, maxRequestsPerNavigation = NetworkCollector.MAX_REQUESTS_PER_NAVIGATION, listeners = collect => {
|
|
263
|
+
return {
|
|
264
|
+
request: req => {
|
|
265
|
+
collect(req);
|
|
266
|
+
},
|
|
267
|
+
};
|
|
268
|
+
}) {
|
|
269
|
+
super(page, listeners, maxRequestsPerNavigation);
|
|
270
|
+
}
|
|
271
|
+
splitAfterNavigation() {
|
|
272
|
+
const requests = this.storage[0];
|
|
273
|
+
const lastRequestIdx = requests.findLastIndex(request => {
|
|
274
|
+
return request.frame() === this.pptrPage.mainFrame()
|
|
275
|
+
? request.isNavigationRequest()
|
|
276
|
+
: false;
|
|
277
|
+
});
|
|
278
|
+
// Keep all requests since the last navigation request including that
|
|
279
|
+
// navigation request itself.
|
|
280
|
+
// Keep the reference
|
|
281
|
+
if (lastRequestIdx !== -1) {
|
|
282
|
+
const fromCurrentNavigation = requests.splice(lastRequestIdx);
|
|
283
|
+
this.storage.unshift(fromCurrentNavigation);
|
|
284
|
+
}
|
|
285
|
+
else {
|
|
286
|
+
this.storage.unshift([]);
|
|
287
|
+
}
|
|
288
|
+
this.storage.splice(this.maxNavigationSaved);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
//# sourceMappingURL=PageCollector.js.map
|