Skip to content

Commit dc92aac

Browse files
authored
Merge branch 'main' into fix/screenshot-mime-type-config
2 parents c231bad + eea5b80 commit dc92aac

File tree

6 files changed

+100
-28
lines changed

6 files changed

+100
-28
lines changed

.github/workflows/pre-release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
runs-on: ubuntu-latest
1414
steps:
1515
- name: Check out repository
16-
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
16+
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
1717
with:
1818
fetch-depth: 2
1919

.github/workflows/presubmit.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515

1616
steps:
1717
- name: Check out repository
18-
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
18+
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
1919
with:
2020
fetch-depth: 2
2121

@@ -37,7 +37,7 @@ jobs:
3737

3838
steps:
3939
- name: Check out repository
40-
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
40+
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
4141
with:
4242
fetch-depth: 2
4343

.github/workflows/publish-to-npm-on-tag.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ jobs:
2525
if: ${{ (github.event_name != 'workflow_dispatch') || (inputs.npm-publish && always()) }}
2626
steps:
2727
- name: Check out repository
28-
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
28+
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
2929
with:
3030
fetch-depth: 2
3131

@@ -58,7 +58,7 @@ jobs:
5858
if: ${{ (github.event_name != 'workflow_dispatch' && needs.publish-to-npm.result == 'success') || (inputs.mcp-publish && always()) }}
5959
steps:
6060
- name: Check out repository
61-
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
61+
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
6262
with:
6363
fetch-depth: 2
6464

.github/workflows/run-tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ jobs:
2626
- 24
2727
steps:
2828
- name: Check out repository
29-
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
29+
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
3030
with:
3131
fetch-depth: 2
3232

src/DevToolsConnectionAdapter.ts

Lines changed: 89 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,38 +4,106 @@
44
* SPDX-License-Identifier: Apache-2.0
55
*/
66

7-
import {ConnectionTransport as DevToolsConnectionTransport} from '../node_modules/chrome-devtools-frontend/mcp/mcp.js';
7+
import type {CDPConnection as devtools} from '../node_modules/chrome-devtools-frontend/mcp/mcp.js';
88

9-
import {type ConnectionTransport} from './third_party/index.js';
9+
import type * as puppeteer from './third_party/index.js';
10+
import {CDPSessionEvent} from './third_party/index.js';
1011

1112
/**
12-
* Allows a puppeteer {@link ConnectionTransport} to act like a DevTools {@link Connection}.
13+
* This class makes a puppeteer connection look like DevTools CDPConnection.
14+
*
15+
* Since we connect "root" DevTools targets to specific pages, we scope everything to a puppeteer CDP session.
16+
*
17+
* We don't have to recursively listen for 'sessionattached' as the "root" CDP session sees all child session attached
18+
* events, regardless how deeply nested they are.
1319
*/
14-
export class DevToolsConnectionAdapter extends DevToolsConnectionTransport {
15-
#transport: ConnectionTransport | null;
16-
#onDisconnect: ((arg0: string) => void) | null = null;
17-
18-
constructor(transport: ConnectionTransport) {
19-
super();
20-
this.#transport = transport;
21-
this.#transport.onclose = () => this.#onDisconnect?.('');
22-
this.#transport.onmessage = msg => this.onMessage?.(msg);
20+
export class PuppeteerDevToolsConnection implements devtools.CDPConnection {
21+
readonly #connection: puppeteer.Connection;
22+
readonly #observers = new Set<devtools.CDPConnectionObserver>();
23+
readonly #sessionEventHandlers = new Map<
24+
string,
25+
puppeteer.Handler<unknown>
26+
>();
27+
28+
constructor(session: puppeteer.CDPSession) {
29+
this.#connection = session.connection()!;
30+
31+
session.on(
32+
CDPSessionEvent.SessionAttached,
33+
this.#startForwardingCdpEvents.bind(this),
34+
);
35+
session.on(
36+
CDPSessionEvent.SessionDetached,
37+
this.#stopForwardingCdpEvents.bind(this),
38+
);
39+
40+
this.#startForwardingCdpEvents(session);
41+
}
42+
43+
send<T extends devtools.Command>(
44+
method: T,
45+
params: devtools.CommandParams<T>,
46+
sessionId: string | undefined,
47+
): Promise<{result: devtools.CommandResult<T>} | {error: devtools.CDPError}> {
48+
if (sessionId === undefined) {
49+
throw new Error(
50+
'Attempting to send on the root session. This must not happen',
51+
);
52+
}
53+
const session = this.#connection.session(sessionId);
54+
if (!session) {
55+
throw new Error('Unknown session ' + sessionId);
56+
}
57+
// Rolled protocol version between puppeteer and DevTools doesn't necessarily match
58+
/* eslint-disable @typescript-eslint/no-explicit-any */
59+
return session
60+
.send(method as any, params)
61+
.then(result => ({result}))
62+
.catch(error => ({error})) as any;
63+
/* eslint-enable @typescript-eslint/no-explicit-any */
64+
}
65+
66+
observe(observer: devtools.CDPConnectionObserver): void {
67+
this.#observers.add(observer);
2368
}
2469

25-
override setOnMessage(onMessage: (arg0: object | string) => void): void {
26-
this.onMessage = onMessage;
70+
unobserve(observer: devtools.CDPConnectionObserver): void {
71+
this.#observers.delete(observer);
2772
}
2873

29-
override setOnDisconnect(onDisconnect: (arg0: string) => void): void {
30-
this.#onDisconnect = onDisconnect;
74+
#startForwardingCdpEvents(session: puppeteer.CDPSession): void {
75+
const handler = this.#handleEvent.bind(
76+
this,
77+
session.id(),
78+
) as puppeteer.Handler<unknown>;
79+
this.#sessionEventHandlers.set(session.id(), handler);
80+
session.on('*', handler);
3181
}
3282

33-
override sendRawMessage(message: string): void {
34-
this.#transport?.send(message);
83+
#stopForwardingCdpEvents(session: puppeteer.CDPSession): void {
84+
const handler = this.#sessionEventHandlers.get(session.id());
85+
if (handler) {
86+
session.off('*', handler);
87+
}
3588
}
3689

37-
override async disconnect(): Promise<void> {
38-
this.#transport?.close();
39-
this.#transport = null;
90+
#handleEvent(
91+
sessionId: string,
92+
type: string | symbol | number,
93+
event: any, // eslint-disable-line @typescript-eslint/no-explicit-any
94+
): void {
95+
if (
96+
typeof type === 'string' &&
97+
type !== CDPSessionEvent.SessionAttached &&
98+
type !== CDPSessionEvent.SessionDetached
99+
) {
100+
this.#observers.forEach(observer =>
101+
observer.onEvent({
102+
method: type as devtools.Event,
103+
sessionId,
104+
params: event,
105+
}),
106+
);
107+
}
40108
}
41109
}

src/third_party/index.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,11 @@ export {
2020
type TextContent,
2121
} from '@modelcontextprotocol/sdk/types.js';
2222
export {z as zod} from 'zod';
23-
export {Locator, PredefinedNetworkConditions} from 'puppeteer-core';
23+
export {
24+
Locator,
25+
PredefinedNetworkConditions,
26+
CDPSessionEvent,
27+
} from 'puppeteer-core';
2428
export {default as puppeteer} from 'puppeteer-core';
2529
export type * from 'puppeteer-core';
2630
export type {CdpPage} from 'puppeteer-core/internal/cdp/Page.js';

0 commit comments

Comments
 (0)