Skip to content

Commit 7e4e068

Browse files
anarmawalabmiddha
andauthored
[playwright-browser-tunnel] Seperate out tunnelBrowserConnection and createTunneledBrowser to remove playwright dependency from tunnelBrowserConnection (#5609)
* Update Dependency chart while making sure the api remains the same * changelogs * Update common/changes/@rushstack/playwright-browser-tunnel/fix-update-dependency-chat_2026-02-07-01-52.json Co-authored-by: Bharat Middha <5100938+bmiddha@users.noreply.github.com> * fix package.json * no need to update vscode extension * manually update vscode extension version --------- Co-authored-by: Bharat Middha <5100938+bmiddha@users.noreply.github.com>
1 parent 1e81c81 commit 7e4e068

8 files changed

Lines changed: 146 additions & 97 deletions

File tree

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
2+
// See LICENSE in the project root for license information.
3+
4+
import type { Browser } from 'playwright-core';
5+
6+
/**
7+
* Disposable handle returned by {@link createTunneledBrowserAsync}.
8+
* @beta
9+
*/
10+
export interface IDisposableTunneledBrowser {
11+
/**
12+
* The connected Playwright Browser instance.
13+
*/
14+
browser: Browser;
15+
/**
16+
* Async dispose method that closes the browser connection.
17+
* Called automatically when using `await using` syntax.
18+
*/
19+
[Symbol.asyncDispose]: () => Promise<void>;
20+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
2+
// See LICENSE in the project root for license information.
3+
4+
import type { LaunchOptions } from 'playwright-core';
5+
6+
import type { BrowserName } from '../PlaywrightBrowserTunnel';
7+
8+
export interface IHandshake {
9+
action: 'handshake';
10+
browserName: BrowserName;
11+
launchOptions: LaunchOptions;
12+
playwrightVersion: string;
13+
}
14+
15+
export interface IHandshakeAck {
16+
action: 'handshakeAck';
17+
}
18+
19+
/**
20+
* Disposable handle returned by {@link tunneledBrowserConnection}.
21+
* @beta
22+
*/
23+
export interface IDisposableTunneledBrowserConnection {
24+
/**
25+
* The WebSocket endpoint URL that the local Playwright client should connect to.
26+
*/
27+
remoteEndpoint: string;
28+
/**
29+
* Dispose method that closes the WebSocket servers.
30+
* Called automatically when using `using` syntax.
31+
*/
32+
[Symbol.dispose]: () => void;
33+
/**
34+
* Promise that resolves when the remote WebSocket server closes.
35+
*/
36+
closePromise: Promise<void>;
37+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
2+
// See LICENSE in the project root for license information.
3+
4+
import type { Browser, LaunchOptions } from 'playwright-core';
5+
import playwright from 'playwright-core';
6+
7+
import type { ITerminal } from '@rushstack/terminal';
8+
import { ConsoleTerminalProvider, Terminal } from '@rushstack/terminal';
9+
10+
import type { BrowserName } from '../PlaywrightBrowserTunnel';
11+
import { DEFAULT_LISTEN_PORT } from './constants';
12+
import type { IDisposableTunneledBrowser } from './ITunneledBrowser';
13+
import type { IDisposableTunneledBrowserConnection } from './ITunneledBrowserConnection';
14+
import { tunneledBrowserConnection } from './TunneledBrowserConnection';
15+
16+
/**
17+
* Creates a Playwright Browser instance connected via a tunneled WebSocket connection.
18+
* @beta
19+
*/
20+
export async function createTunneledBrowserAsync(
21+
browserName: BrowserName,
22+
launchOptions: LaunchOptions,
23+
logger?: ITerminal,
24+
port: number = DEFAULT_LISTEN_PORT
25+
): Promise<IDisposableTunneledBrowser> {
26+
// Establish the tunnel first (remoteEndpoint here refers to local proxy endpoint for connect())
27+
28+
if (!logger) {
29+
const terminalProvider: ConsoleTerminalProvider = new ConsoleTerminalProvider();
30+
logger = new Terminal(terminalProvider);
31+
}
32+
33+
const connection: IDisposableTunneledBrowserConnection = await tunneledBrowserConnection(logger, port);
34+
const { remoteEndpoint } = connection;
35+
// Append query params for browser and launchOptions
36+
const urlObj: URL = new URL(remoteEndpoint);
37+
urlObj.searchParams.set('browser', browserName);
38+
urlObj.searchParams.set('launchOptions', JSON.stringify(launchOptions || {}));
39+
const connectEndpoint: string = urlObj.toString();
40+
const browser: Browser = await playwright[browserName].connect(connectEndpoint);
41+
logger.writeLine(`Connected to remote browser at ${connectEndpoint}`);
42+
43+
return {
44+
browser,
45+
async [Symbol.asyncDispose]() {
46+
logger.writeLine('Disposing browser');
47+
await browser.close();
48+
// Dispose the tunnel connection after browser is closed
49+
connection[Symbol.dispose]();
50+
}
51+
};
52+
}

apps/playwright-browser-tunnel/src/tunneledBrowserConnection.ts renamed to apps/playwright-browser-tunnel/src/tunneledBrowserConnection/TunneledBrowserConnection.ts

Lines changed: 13 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,30 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
22
// See LICENSE in the project root for license information.
33

4-
import playwright from 'playwright-core';
5-
import type { Browser, LaunchOptions } from 'playwright-core';
6-
import { WebSocketServer, WebSocket, type RawData } from 'ws';
4+
import type { LaunchOptions } from 'playwright-core';
75
import playwrightPackageJson from 'playwright-core/package.json';
6+
import type { RawData } from 'ws';
7+
import { WebSocket, WebSocketServer } from 'ws';
88

9-
import { type ITerminal, Terminal, ConsoleTerminalProvider } from '@rushstack/terminal';
9+
import type { ITerminal } from '@rushstack/terminal';
1010

11-
import type { BrowserName } from './PlaywrightBrowserTunnel';
12-
import { HttpServer } from './HttpServer';
11+
import { HttpServer } from '../HttpServer';
12+
import type { BrowserName } from '../PlaywrightBrowserTunnel';
1313
import {
1414
getNormalizedErrorString,
1515
getWebSocketCloseReason,
1616
getWebSocketReadyStateString,
1717
WebSocketCloseCode
18-
} from './utilities';
18+
} from '../utilities';
19+
import type {
20+
IDisposableTunneledBrowserConnection,
21+
IHandshake,
22+
IHandshakeAck
23+
} from './ITunneledBrowserConnection';
24+
import { DEFAULT_LISTEN_PORT, SUPPORTED_BROWSER_NAMES } from './constants';
1925

2026
const { version: playwrightVersion } = playwrightPackageJson;
2127

22-
const SUPPORTED_BROWSER_NAMES: Set<string> = new Set(['chromium', 'firefox', 'webkit']);
23-
24-
interface IHandshake {
25-
action: 'handshake';
26-
browserName: BrowserName;
27-
launchOptions: LaunchOptions;
28-
playwrightVersion: string;
29-
}
30-
31-
interface IHandshakeAck {
32-
action: 'handshakeAck';
33-
}
34-
35-
const DEFAULT_LISTEN_PORT: number = 56767;
36-
37-
/**
38-
* Disposable handle returned by {@link tunneledBrowserConnection}.
39-
* @beta
40-
*/
41-
export interface IDisposableTunneledBrowserConnection {
42-
/**
43-
* The WebSocket endpoint URL that the local Playwright client should connect to.
44-
*/
45-
remoteEndpoint: string;
46-
/**
47-
* Dispose method that closes the WebSocket servers.
48-
* Called automatically when using `using` syntax.
49-
*/
50-
[Symbol.dispose]: () => void;
51-
/**
52-
* Promise that resolves when the remote WebSocket server closes.
53-
*/
54-
closePromise: Promise<void>;
55-
}
56-
5728
/**
5829
* Creates a tunneled WebSocket endpoint that a local Playwright client can connect to.
5930
* @beta
@@ -259,57 +230,3 @@ export async function tunneledBrowserConnection(
259230
});
260231
});
261232
}
262-
263-
/**
264-
* Disposable handle returned by {@link createTunneledBrowserAsync}.
265-
* @beta
266-
*/
267-
export interface IDisposableTunneledBrowser {
268-
/**
269-
* The connected Playwright Browser instance.
270-
*/
271-
browser: Browser;
272-
/**
273-
* Async dispose method that closes the browser connection.
274-
* Called automatically when using `await using` syntax.
275-
*/
276-
[Symbol.asyncDispose]: () => Promise<void>;
277-
}
278-
279-
/**
280-
* Creates a Playwright Browser instance connected via a tunneled WebSocket connection.
281-
* @beta
282-
*/
283-
export async function createTunneledBrowserAsync(
284-
browserName: BrowserName,
285-
launchOptions: LaunchOptions,
286-
logger?: ITerminal,
287-
port: number = DEFAULT_LISTEN_PORT
288-
): Promise<IDisposableTunneledBrowser> {
289-
// Establish the tunnel first (remoteEndpoint here refers to local proxy endpoint for connect())
290-
291-
if (!logger) {
292-
const terminalProvider: ConsoleTerminalProvider = new ConsoleTerminalProvider();
293-
logger = new Terminal(terminalProvider);
294-
}
295-
296-
const connection: IDisposableTunneledBrowserConnection = await tunneledBrowserConnection(logger, port);
297-
const { remoteEndpoint } = connection;
298-
// Append query params for browser and launchOptions
299-
const urlObj: URL = new URL(remoteEndpoint);
300-
urlObj.searchParams.set('browser', browserName);
301-
urlObj.searchParams.set('launchOptions', JSON.stringify(launchOptions || {}));
302-
const connectEndpoint: string = urlObj.toString();
303-
const browser: Browser = await playwright[browserName].connect(connectEndpoint);
304-
logger.writeLine(`Connected to remote browser at ${connectEndpoint}`);
305-
306-
return {
307-
browser,
308-
async [Symbol.asyncDispose]() {
309-
logger.writeLine('Disposing browser');
310-
await browser.close();
311-
// Dispose the tunnel connection after browser is closed
312-
connection[Symbol.dispose]();
313-
}
314-
};
315-
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
2+
// See LICENSE in the project root for license information.
3+
4+
export const SUPPORTED_BROWSER_NAMES: Set<string> = new Set(['chromium', 'firefox', 'webkit']);
5+
export const DEFAULT_LISTEN_PORT: number = 56767;
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
2+
// See LICENSE in the project root for license information.
3+
4+
export { createTunneledBrowserAsync } from './TunneledBrowser';
5+
export { tunneledBrowserConnection } from './TunneledBrowserConnection';
6+
7+
export type { IDisposableTunneledBrowser } from './ITunneledBrowser';
8+
export type { IDisposableTunneledBrowserConnection } from './ITunneledBrowserConnection';
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"changes": [
3+
{
4+
"packageName": "@rushstack/playwright-browser-tunnel",
5+
"comment": "Update module layout to isolate code that does not depend on Playwright.",
6+
"type": "patch"
7+
}
8+
],
9+
"packageName": "@rushstack/playwright-browser-tunnel"
10+
}

vscode-extensions/playwright-local-browser-server-vscode-extension/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "playwright-local-browser-server",
3-
"version": "0.1.1",
3+
"version": "0.1.2",
44
"repository": {
55
"type": "git",
66
"url": "https://github.com/microsoft/rushstack.git",

0 commit comments

Comments
 (0)