feat: support channel in puppeteer.connect (#14466)

This commit is contained in:
Alex Rudenko
2025-12-01 12:23:00 +01:00
committed by GitHub
parent 1366682399
commit 7244c19490
9 changed files with 198 additions and 25 deletions
+21
View File
@@ -115,6 +115,27 @@ Only works for `protocol="webDriverBiDi"` and [Puppeteer.connect()](./puppeteer.
</td></tr>
<tr><td>
<span id="channel">channel</span>
</td><td>
`optional`
</td><td>
[ChromeReleaseChannel](./puppeteer.chromereleasechannel.md)
</td><td>
**_(Experimental)_** If specified, puppeteer looks for an open WebSocket at the well-known default user data directory for the specified channel and attempts to connect to it using ws://localhost:$ActivePort/devtools/browser. Only works for Chrome and when run in Node.js.
This option is experimental when used with puppeteer.connect().
</td><td>
</td></tr>
<tr><td>
<span id="defaultviewport">defaultViewport</span>
</td><td>
@@ -0,0 +1,71 @@
---
sidebar_label: resolveDefaultUserDataDir
---
# resolveDefaultUserDataDir() function
Returns the expected default user data dir for the given channel. It does not check if the dir actually exists.
### Signature
```typescript
export declare function resolveDefaultUserDataDir(
browser: Browser,
platform: BrowserPlatform,
channel: ChromeReleaseChannel,
): string;
```
## Parameters
<table><thead><tr><th>
Parameter
</th><th>
Type
</th><th>
Description
</th></tr></thead>
<tbody><tr><td>
browser
</td><td>
[Browser](./browsers.browser.md)
</td><td>
</td></tr>
<tr><td>
platform
</td><td>
[BrowserPlatform](./browsers.browserplatform.md)
</td><td>
</td></tr>
<tr><td>
channel
</td><td>
[ChromeReleaseChannel](./browsers.chromereleasechannel.md)
</td><td>
</td></tr>
</tbody></table>
**Returns:**
string
+9
View File
@@ -309,6 +309,15 @@ Launches a browser process according to [LaunchOptions](./browsers.launchoptions
</td></tr>
<tr><td>
<span id="resolvedefaultuserdatadir">[resolveDefaultUserDataDir(browser, platform, channel)](./browsers.resolvedefaultuserdatadir.md)</span>
</td><td>
Returns the expected default user data dir for the given channel. It does not check if the dir actually exists.
</td></tr>
<tr><td>
<span id="uninstall">[uninstall(options)](./browsers.uninstall.md)</span>
</td><td>
+1
View File
@@ -40,6 +40,7 @@ export {
ChromeReleaseChannel,
createProfile,
getVersionComparator,
resolveDefaultUserDataDir,
} from './browser-data/browser-data.js';
export {CLI} from './CLI.js';
export {
@@ -27,6 +27,9 @@ export default {
if (id.includes('node_modules')) {
return true;
}
if (id.includes('@puppeteer/browsers')) {
return true;
}
if (id.startsWith(path.join(cwd, `lib`, `esm`, `puppeteer`, `bidi`))) {
return true;
}
@@ -7,7 +7,7 @@
import type {Browser} from '../api/Browser.js';
import {_connectToBiDiBrowser} from '../bidi/BrowserConnector.js';
import {_connectToCdpBrowser} from '../cdp/BrowserConnector.js';
import {isNode} from '../environment.js';
import {environment, isNode} from '../environment.js';
import {assert} from '../util/assert.js';
import {isErrorLike} from '../util/ErrorLike.js';
@@ -84,6 +84,50 @@ async function getConnectionTransport(
connectionTransport: connectionTransport,
endpointUrl: connectionURL,
};
} else if (options.channel && isNode) {
const {detectBrowserPlatform, resolveDefaultUserDataDir, Browser} =
await import('@puppeteer/browsers');
const platform = detectBrowserPlatform();
if (!platform) {
throw new Error('Could not detect required browser platform');
}
const {convertPuppeteerChannelToBrowsersChannel} = await import(
'../node/LaunchOptions.js'
);
const {join} = await import('node:path');
const userDataDir = resolveDefaultUserDataDir(
Browser.CHROME,
platform,
convertPuppeteerChannelToBrowsersChannel(options.channel),
);
const portPath = join(userDataDir, 'DevToolsActivePort');
try {
const portRawValue = await environment.value.fs.promises.readFile(
portPath,
'ascii',
);
const port = parseInt(portRawValue, 10);
if (isNaN(port) || port <= 0 || port > 65535) {
throw new Error(`Invalid port '${portRawValue}' found`);
}
const browserWSEndpoint = `ws://localhost:${port}`;
const WebSocketClass = await getWebSocketTransportClass();
const connectionTransport = await WebSocketClass.create(
browserWSEndpoint,
headers,
);
return {
connectionTransport: connectionTransport,
endpointUrl: browserWSEndpoint,
};
} catch (error) {
throw new Error(
`Could not find DevToolsActivePort for ${options.channel} at ${portPath}`,
{
cause: error,
},
);
}
}
throw new Error('Invalid connection options');
}
@@ -38,6 +38,15 @@ export interface SupportedWebDriverCapabilities {
alwaysMatch?: SupportedWebDriverCapability;
}
/**
* @public
*/
export type ChromeReleaseChannel =
| 'chrome'
| 'chrome-beta'
| 'chrome-canary'
| 'chrome-dev';
/**
* Generic browser options that can be passed when launching any browser or when
* connecting to an existing browser instance.
@@ -49,6 +58,17 @@ export interface ConnectOptions {
* @defaultValue `false`
*/
acceptInsecureCerts?: boolean;
/**
* If specified, puppeteer looks for an open WebSocket at the well-known
* default user data directory for the specified channel and attempts to
* connect to it using ws://localhost:$ActivePort/devtools/browser. Only works
* for Chrome and when run in Node.js.
*
* This option is experimental when used with puppeteer.connect().
*
* @experimental
*/
channel?: ChromeReleaseChannel;
/**
* Experimental setting to disable monitoring network events by default. When
* set to `false`, parts of Puppeteer that depend on network events would not
@@ -11,7 +11,6 @@ import path from 'node:path';
import {
computeSystemExecutablePath,
Browser as SupportedBrowsers,
ChromeReleaseChannel as BrowsersChromeReleaseChannel,
} from '@puppeteer/browsers';
import type {Browser} from '../api/Browser.js';
@@ -19,7 +18,11 @@ import {debugError} from '../common/util.js';
import {assert} from '../util/assert.js';
import {BrowserLauncher, type ResolvedLaunchArgs} from './BrowserLauncher.js';
import type {ChromeReleaseChannel, LaunchOptions} from './LaunchOptions.js';
import {
convertPuppeteerChannelToBrowsersChannel,
type ChromeReleaseChannel,
type LaunchOptions,
} from './LaunchOptions.js';
import type {PuppeteerNode} from './PuppeteerNode.js';
import {rm} from './util/fs.js';
@@ -285,21 +288,6 @@ export class ChromeLauncher extends BrowserLauncher {
}
}
function convertPuppeteerChannelToBrowsersChannel(
channel: ChromeReleaseChannel,
): BrowsersChromeReleaseChannel {
switch (channel) {
case 'chrome':
return BrowsersChromeReleaseChannel.STABLE;
case 'chrome-dev':
return BrowsersChromeReleaseChannel.DEV;
case 'chrome-beta':
return BrowsersChromeReleaseChannel.BETA;
case 'chrome-canary':
return BrowsersChromeReleaseChannel.CANARY;
}
}
/**
* Extracts all features from the given command-line flag
* (e.g. `--enable-features`, `--enable-features=`).
@@ -4,17 +4,33 @@
* SPDX-License-Identifier: Apache-2.0
*/
import type {ConnectOptions} from '../common/ConnectOptions.js';
import {ChromeReleaseChannel as BrowsersChromeReleaseChannel} from '@puppeteer/browsers';
import type {
ChromeReleaseChannel,
ConnectOptions,
} from '../common/ConnectOptions.js';
import type {SupportedBrowser} from '../common/SupportedBrowser.js';
export type {ChromeReleaseChannel};
/**
* @public
* @internal
*/
export type ChromeReleaseChannel =
| 'chrome'
| 'chrome-beta'
| 'chrome-canary'
| 'chrome-dev';
export function convertPuppeteerChannelToBrowsersChannel(
channel: ChromeReleaseChannel,
): BrowsersChromeReleaseChannel {
switch (channel) {
case 'chrome':
return BrowsersChromeReleaseChannel.STABLE;
case 'chrome-dev':
return BrowsersChromeReleaseChannel.DEV;
case 'chrome-beta':
return BrowsersChromeReleaseChannel.BETA;
case 'chrome-canary':
return BrowsersChromeReleaseChannel.CANARY;
}
}
/**
* Generic launch options that can be passed when launching any browser.