豆豆友情提示:这是一个非官方 GitHub 代理镜像,主要用于网络测试或访问加速。请勿在此进行登录、注册或处理任何敏感信息。进行这些操作请务必访问官方网站 github.com。 Raw 内容也通过此代理提供。
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"test:node20": "node --import ./build/tests/setup.js --test-reporter spec --test-force-exit --test build/tests",
"test:no-build": "node --import ./build/tests/setup.js --no-warnings=ExperimentalWarning --experimental-print-required-tla --test-reporter spec --test-force-exit --test \"build/tests/**/*.test.js\"",
"test": "npm run build && npm run test:no-build",
"test:only": "npm run build && node --import ./build/tests/setup.js --no-warnings=ExperimentalWarning --test-reporter spec --test-force-exit --test --test-only \"build/tests/**/*.test.js\"",
"test:only": "npm run build && npm run test:only:no-build",
"test:only:no-build": "node --import ./build/tests/setup.js --no-warnings=ExperimentalWarning --test-reporter spec --test-force-exit --test --test-only \"build/tests/**/*.test.js\"",
"test:update-snapshots": "npm run build && node --import ./build/tests/setup.js --no-warnings=ExperimentalWarning --test-force-exit --test --test-update-snapshots \"build/tests/**/*.test.js\"",
"prepare": "node --experimental-strip-types scripts/prepare.ts",
Expand Down
23 changes: 14 additions & 9 deletions src/DevToolsConnectionAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
* SPDX-License-Identifier: Apache-2.0
*/

import type {CDPConnection as devtools} from './third_party/index.js';
import type * as puppeteer from './third_party/index.js';
import type {DevTools} from './third_party/index.js';
import {CDPSessionEvent} from './third_party/index.js';

/**
Expand All @@ -16,9 +16,11 @@ import {CDPSessionEvent} from './third_party/index.js';
* We don't have to recursively listen for 'sessionattached' as the "root" CDP session sees all child session attached
* events, regardless how deeply nested they are.
*/
export class PuppeteerDevToolsConnection implements devtools.CDPConnection {
export class PuppeteerDevToolsConnection
implements DevTools.CDPConnection.CDPConnection
{
readonly #connection: puppeteer.Connection;
readonly #observers = new Set<devtools.CDPConnectionObserver>();
readonly #observers = new Set<DevTools.CDPConnection.CDPConnectionObserver>();
readonly #sessionEventHandlers = new Map<
string,
puppeteer.Handler<unknown>
Expand All @@ -39,11 +41,14 @@ export class PuppeteerDevToolsConnection implements devtools.CDPConnection {
this.#startForwardingCdpEvents(session);
}

send<T extends devtools.Command>(
send<T extends DevTools.CDPConnection.Command>(
method: T,
params: devtools.CommandParams<T>,
params: DevTools.CDPConnection.CommandParams<T>,
sessionId: string | undefined,
): Promise<{result: devtools.CommandResult<T>} | {error: devtools.CDPError}> {
): Promise<
| {result: DevTools.CDPConnection.CommandResult<T>}
| {error: DevTools.CDPConnection.CDPError}
> {
if (sessionId === undefined) {
throw new Error(
'Attempting to send on the root session. This must not happen',
Expand All @@ -62,11 +67,11 @@ export class PuppeteerDevToolsConnection implements devtools.CDPConnection {
/* eslint-enable @typescript-eslint/no-explicit-any */
}

observe(observer: devtools.CDPConnectionObserver): void {
observe(observer: DevTools.CDPConnection.CDPConnectionObserver): void {
this.#observers.add(observer);
}

unobserve(observer: devtools.CDPConnectionObserver): void {
unobserve(observer: DevTools.CDPConnection.CDPConnectionObserver): void {
this.#observers.delete(observer);
}

Expand Down Expand Up @@ -98,7 +103,7 @@ export class PuppeteerDevToolsConnection implements devtools.CDPConnection {
) {
this.#observers.forEach(observer =>
observer.onEvent({
method: type as devtools.Event,
method: type as DevTools.CDPConnection.Event,
sessionId,
params: event,
}),
Expand Down
62 changes: 26 additions & 36 deletions src/DevtoolsUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,7 @@ import {PuppeteerDevToolsConnection} from './DevToolsConnectionAdapter.js';
import {ISSUE_UTILS} from './issue-descriptions.js';
import {logger} from './logger.js';
import {Mutex} from './Mutex.js';
import {
type Issue,
type AggregatedIssue,
type IssuesManagerEventTypes,
type SDKTarget as Target,
DebuggerModel,
Foundation,
TargetManager,
MarkdownIssueDescription,
Marked,
ProtocolClient,
Common,
I18n,
} from './third_party/index.js';
import {DevTools} from './third_party/index.js';
import type {
Browser,
Page,
Expand Down Expand Up @@ -87,14 +74,14 @@ function normalizeUrl(url: string): string {
* A mock implementation of an issues manager that only implements the methods
* that are actually used by the IssuesAggregator
*/
export class FakeIssuesManager extends Common.ObjectWrapper
.ObjectWrapper<IssuesManagerEventTypes> {
issues(): Issue[] {
export class FakeIssuesManager extends DevTools.Common.ObjectWrapper
.ObjectWrapper<DevTools.IssuesManagerEventTypes> {
issues(): DevTools.Issue[] {
return [];
}
}

export function mapIssueToMessageObject(issue: AggregatedIssue) {
export function mapIssueToMessageObject(issue: DevTools.AggregatedIssue) {
const count = issue.getAggregatedIssuesCount();
const markdownDescription = issue.getDescription();
const filename = markdownDescription?.file;
Expand All @@ -113,12 +100,14 @@ export function mapIssueToMessageObject(issue: AggregatedIssue) {
let title: string | null;

try {
processedMarkdown = MarkdownIssueDescription.substitutePlaceholders(
rawMarkdown,
markdownDescription.substitutions,
);
const markdownAst = Marked.Marked.lexer(processedMarkdown);
title = MarkdownIssueDescription.findTitleFromMarkdownAst(markdownAst);
processedMarkdown =
DevTools.MarkdownIssueDescription.substitutePlaceholders(
rawMarkdown,
markdownDescription.substitutions,
);
const markdownAst = DevTools.Marked.Marked.lexer(processedMarkdown);
title =
DevTools.MarkdownIssueDescription.findTitleFromMarkdownAst(markdownAst);
} catch {
logger('error parsing markdown for issue ' + issue.code());
return null;
Expand All @@ -137,22 +126,22 @@ export function mapIssueToMessageObject(issue: AggregatedIssue) {
}

// DevTools CDP errors can get noisy.
ProtocolClient.InspectorBackend.test.suppressRequestErrors = true;
DevTools.ProtocolClient.InspectorBackend.test.suppressRequestErrors = true;

I18n.DevToolsLocale.DevToolsLocale.instance({
DevTools.I18n.DevToolsLocale.DevToolsLocale.instance({
create: true,
data: {
navigatorLanguage: 'en-US',
settingLanguage: 'en-US',
lookupClosestDevToolsLocale: l => l,
},
});
I18n.i18n.registerLocaleDataForTest('en-US', {});
DevTools.I18n.i18n.registerLocaleDataForTest('en-US', {});

export interface TargetUniverse {
/** The DevTools target corresponding to the puppeteer Page */
target: Target;
universe: Foundation.Universe.Universe;
target: DevTools.SDKTarget;
universe: DevTools.Foundation.Universe.Universe;
}
export type TargetUniverseFactoryFn = (page: Page) => Promise<TargetUniverse>;

Expand Down Expand Up @@ -231,22 +220,23 @@ export class UniverseManager {
}

const DEFAULT_FACTORY: TargetUniverseFactoryFn = async (page: Page) => {
const settingStorage = new Common.Settings.SettingsStorage({});
const universe = new Foundation.Universe.Universe({
const settingStorage = new DevTools.Common.Settings.SettingsStorage({});
const universe = new DevTools.Foundation.Universe.Universe({
settingsCreationOptions: {
syncedStorage: settingStorage,
globalStorage: settingStorage,
localStorage: settingStorage,
settingRegistrations: Common.SettingRegistration.getRegisteredSettings(),
settingRegistrations:
DevTools.Common.SettingRegistration.getRegisteredSettings(),
},
overrideAutoStartModels: new Set([DebuggerModel]),
overrideAutoStartModels: new Set([DevTools.DebuggerModel]),
});

const session = await page.createCDPSession();
const connection = new PuppeteerDevToolsConnection(session);

const targetManager = universe.context.get(TargetManager);
targetManager.observeModels(DebuggerModel, SKIP_ALL_PAUSES);
const targetManager = universe.context.get(DevTools.TargetManager);
targetManager.observeModels(DevTools.DebuggerModel, SKIP_ALL_PAUSES);

const target = targetManager.createTarget(
'main',
Expand All @@ -266,7 +256,7 @@ const DEFAULT_FACTORY: TargetUniverseFactoryFn = async (page: Page) => {
// sent. This means DevTools can still pause, step and do whatever. We just won't
// see the `Debugger.paused`/`Debugger.resumed` events on the MCP side.
const SKIP_ALL_PAUSES = {
modelAdded(model: DebuggerModel): void {
modelAdded(model: DevTools.DebuggerModel): void {
void model.agent.invoke_setSkipAllPauses({skip: true});
},

Expand Down
10 changes: 6 additions & 4 deletions src/McpContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import path from 'node:path';
import {extractUrlLikeFromDevToolsTitle, urlsEqual} from './DevtoolsUtils.js';
import type {ListenerMap} from './PageCollector.js';
import {NetworkCollector, ConsoleCollector} from './PageCollector.js';
import {type AggregatedIssue} from './third_party/index.js';
import {Locator} from './third_party/index.js';
import type {DevTools} from './third_party/index.js';
import type {
Browser,
ConsoleMessage,
Expand Down Expand Up @@ -221,18 +221,20 @@ export class McpContext implements Context {

getConsoleData(
includePreservedMessages?: boolean,
): Array<ConsoleMessage | Error | AggregatedIssue> {
): Array<ConsoleMessage | Error | DevTools.AggregatedIssue> {
const page = this.getSelectedPage();
return this.#consoleCollector.getData(page, includePreservedMessages);
}

getConsoleMessageStableId(
message: ConsoleMessage | Error | AggregatedIssue,
message: ConsoleMessage | Error | DevTools.AggregatedIssue,
): number {
return this.#consoleCollector.getIdForResource(message);
}

getConsoleMessageById(id: number): ConsoleMessage | Error | AggregatedIssue {
getConsoleMessageById(
id: number,
): ConsoleMessage | Error | DevTools.AggregatedIssue {
return this.#consoleCollector.getById(this.getSelectedPage(), id);
}

Expand Down
8 changes: 4 additions & 4 deletions src/McpResponse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
} from './formatters/networkFormatter.js';
import {formatSnapshotNode} from './formatters/snapshotFormatter.js';
import type {McpContext} from './McpContext.js';
import {AggregatedIssue} from './third_party/index.js';
import {DevTools} from './third_party/index.js';
import type {
ConsoleMessage,
ImageContent,
Expand Down Expand Up @@ -250,7 +250,7 @@ export class McpResponse implements Response {
}),
),
};
} else if (message instanceof AggregatedIssue) {
} else if (message instanceof DevTools.AggregatedIssue) {
const mappedIssueMessage = mapIssueToMessageObject(message);
if (!mappedIssueMessage)
throw new Error(
Expand Down Expand Up @@ -282,7 +282,7 @@ export class McpResponse implements Response {
if ('type' in message) {
return normalizedTypes.has(message.type());
}
if (message instanceof AggregatedIssue) {
if (message instanceof DevTools.AggregatedIssue) {
return normalizedTypes.has('issue');
}
return normalizedTypes.has('error');
Expand Down Expand Up @@ -312,7 +312,7 @@ export class McpResponse implements Response {
),
};
}
if (item instanceof AggregatedIssue) {
if (item instanceof DevTools.AggregatedIssue) {
const mappedIssueMessage = mapIssueToMessageObject(item);
if (!mappedIssueMessage) return null;
return {
Expand Down
40 changes: 17 additions & 23 deletions src/PageCollector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,12 @@
import {FakeIssuesManager} from './DevtoolsUtils.js';
import {logger} from './logger.js';
import type {
Target,
CDPSession,
ConsoleMessage,
Protocol,
Target,
Common,
} from './third_party/index.js';
import {
type AggregatedIssue,
IssueAggregatorEvents,
IssuesManagerEvents,
createIssuesFromProtocolIssue,
IssueAggregator,
} from './third_party/index.js';
import {DevTools} from './third_party/index.js';
import {
type Browser,
type Frame,
Expand All @@ -30,7 +23,7 @@ import {
} from './third_party/index.js';

interface PageEvents extends PuppeteerPageEvents {
issue: AggregatedIssue;
issue: DevTools.AggregatedIssue;
}

export type ListenerMap<EventMap extends PageEvents = PageEvents> = {
Expand Down Expand Up @@ -218,7 +211,7 @@ export class PageCollector<T> {
}

export class ConsoleCollector extends PageCollector<
ConsoleMessage | Error | AggregatedIssue
ConsoleMessage | Error | DevTools.AggregatedIssue
> {
#subscribedPages = new WeakMap<Page, PageIssueSubscriber>();

Expand All @@ -240,9 +233,9 @@ export class ConsoleCollector extends PageCollector<

class PageIssueSubscriber {
#issueManager = new FakeIssuesManager();
#issueAggregator = new IssueAggregator(this.#issueManager);
#issueAggregator = new DevTools.IssueAggregator(this.#issueManager);
#seenKeys = new Set<string>();
#seenIssues = new Set<AggregatedIssue>();
#seenIssues = new Set<DevTools.AggregatedIssue>();
#page: Page;
#session: CDPSession;

Expand All @@ -256,14 +249,13 @@ class PageIssueSubscriber {
this.#issueManager = new FakeIssuesManager();
if (this.#issueAggregator) {
this.#issueAggregator.removeEventListener(
IssueAggregatorEvents.AGGREGATED_ISSUE_UPDATED,
DevTools.IssueAggregatorEvents.AGGREGATED_ISSUE_UPDATED,
this.#onAggregatedissue,
);
}
this.#issueAggregator = new IssueAggregator(this.#issueManager);

this.#issueAggregator = new DevTools.IssueAggregator(this.#issueManager);
this.#issueAggregator.addEventListener(
IssueAggregatorEvents.AGGREGATED_ISSUE_UPDATED,
DevTools.IssueAggregatorEvents.AGGREGATED_ISSUE_UPDATED,
this.#onAggregatedissue,
);
}
Expand All @@ -286,7 +278,7 @@ class PageIssueSubscriber {
this.#session.off('Audits.issueAdded', this.#onIssueAdded);
if (this.#issueAggregator) {
this.#issueAggregator.removeEventListener(
IssueAggregatorEvents.AGGREGATED_ISSUE_UPDATED,
DevTools.IssueAggregatorEvents.AGGREGATED_ISSUE_UPDATED,
this.#onAggregatedissue,
);
}
Expand All @@ -296,7 +288,7 @@ class PageIssueSubscriber {
}

#onAggregatedissue = (
event: Common.EventTarget.EventTargetEvent<AggregatedIssue>,
event: DevTools.Common.EventTarget.EventTargetEvent<DevTools.AggregatedIssue>,
) => {
if (this.#seenIssues.has(event.data)) {
return;
Expand All @@ -319,9 +311,11 @@ class PageIssueSubscriber {
#onIssueAdded = (data: Protocol.Audits.IssueAddedEvent) => {
try {
const inspectorIssue = data.issue;
// @ts-expect-error Types of protocol from Puppeteer and CDP are
// incomparable for InspectorIssueCode, one is union, other is enum.
const issue = createIssuesFromProtocolIssue(null, inspectorIssue)[0];
const issue = DevTools.createIssuesFromProtocolIssue(
null,
// @ts-expect-error Protocol types diverge.
inspectorIssue,
)[0];
if (!issue) {
logger('No issue mapping for for the issue: ', inspectorIssue.code);
return;
Expand All @@ -333,7 +327,7 @@ class PageIssueSubscriber {
}
this.#seenKeys.add(primaryKey);
this.#issueManager.dispatchEventToListeners(
IssuesManagerEvents.ISSUE_ADDED,
DevTools.IssuesManagerEvents.ISSUE_ADDED,
{
issue,
// @ts-expect-error We don't care that issues model is null
Expand Down
Loading