From 93ddb34e9d699eb56320e82d6ec571ccafaf7d35 Mon Sep 17 00:00:00 2001 From: Arshad Narmawala <12823156+anarmawala@users.noreply.github.com> Date: Fri, 6 Feb 2026 17:49:22 -0800 Subject: [PATCH 1/6] Update Dependency chart while making sure the api remains the same --- apps/playwright-browser-tunnel/package.json | 2 +- .../ITunneledBrowser.ts | 20 ++++ .../ITunneledBrowserConnection.ts | 37 ++++++ .../TunneledBrowser.ts | 52 +++++++++ .../TunneledBrowserConnection.ts} | 109 +++--------------- .../tunneledBrowserConnection/constants.ts | 5 + .../src/tunneledBrowserConnection/index.ts | 8 ++ 7 files changed, 136 insertions(+), 97 deletions(-) create mode 100644 apps/playwright-browser-tunnel/src/tunneledBrowserConnection/ITunneledBrowser.ts create mode 100644 apps/playwright-browser-tunnel/src/tunneledBrowserConnection/ITunneledBrowserConnection.ts create mode 100644 apps/playwright-browser-tunnel/src/tunneledBrowserConnection/TunneledBrowser.ts rename apps/playwright-browser-tunnel/src/{tunneledBrowserConnection.ts => tunneledBrowserConnection/TunneledBrowserConnection.ts} (73%) create mode 100644 apps/playwright-browser-tunnel/src/tunneledBrowserConnection/constants.ts create mode 100644 apps/playwright-browser-tunnel/src/tunneledBrowserConnection/index.ts diff --git a/apps/playwright-browser-tunnel/package.json b/apps/playwright-browser-tunnel/package.json index 888762adb1..62ddc1ddd3 100644 --- a/apps/playwright-browser-tunnel/package.json +++ b/apps/playwright-browser-tunnel/package.json @@ -1,6 +1,6 @@ { "name": "@rushstack/playwright-browser-tunnel", - "version": "0.2.3", + "version": "0.2.4", "description": "Run a remote Playwright Browser Tunnel. Useful in remote development environments.", "license": "MIT", "repository": { diff --git a/apps/playwright-browser-tunnel/src/tunneledBrowserConnection/ITunneledBrowser.ts b/apps/playwright-browser-tunnel/src/tunneledBrowserConnection/ITunneledBrowser.ts new file mode 100644 index 0000000000..810dbd8c3e --- /dev/null +++ b/apps/playwright-browser-tunnel/src/tunneledBrowserConnection/ITunneledBrowser.ts @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { Browser } from 'playwright-core'; + +/** + * Disposable handle returned by {@link createTunneledBrowserAsync}. + * @beta + */ +export interface IDisposableTunneledBrowser { + /** + * The connected Playwright Browser instance. + */ + browser: Browser; + /** + * Async dispose method that closes the browser connection. + * Called automatically when using `await using` syntax. + */ + [Symbol.asyncDispose]: () => Promise; +} diff --git a/apps/playwright-browser-tunnel/src/tunneledBrowserConnection/ITunneledBrowserConnection.ts b/apps/playwright-browser-tunnel/src/tunneledBrowserConnection/ITunneledBrowserConnection.ts new file mode 100644 index 0000000000..986641372f --- /dev/null +++ b/apps/playwright-browser-tunnel/src/tunneledBrowserConnection/ITunneledBrowserConnection.ts @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { LaunchOptions } from 'playwright-core'; + +import type { BrowserName } from '../PlaywrightBrowserTunnel'; + +export interface IHandshake { + action: 'handshake'; + browserName: BrowserName; + launchOptions: LaunchOptions; + playwrightVersion: string; +} + +export interface IHandshakeAck { + action: 'handshakeAck'; +} + +/** + * Disposable handle returned by {@link tunneledBrowserConnection}. + * @beta + */ +export interface IDisposableTunneledBrowserConnection { + /** + * The WebSocket endpoint URL that the local Playwright client should connect to. + */ + remoteEndpoint: string; + /** + * Dispose method that closes the WebSocket servers. + * Called automatically when using `using` syntax. + */ + [Symbol.dispose]: () => void; + /** + * Promise that resolves when the remote WebSocket server closes. + */ + closePromise: Promise; +} diff --git a/apps/playwright-browser-tunnel/src/tunneledBrowserConnection/TunneledBrowser.ts b/apps/playwright-browser-tunnel/src/tunneledBrowserConnection/TunneledBrowser.ts new file mode 100644 index 0000000000..3875e2e513 --- /dev/null +++ b/apps/playwright-browser-tunnel/src/tunneledBrowserConnection/TunneledBrowser.ts @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { Browser, LaunchOptions } from 'playwright-core'; +import playwright from 'playwright-core'; + +import type { ITerminal } from '@rushstack/terminal'; +import { ConsoleTerminalProvider, Terminal } from '@rushstack/terminal'; + +import type { BrowserName } from '../PlaywrightBrowserTunnel'; +import { DEFAULT_LISTEN_PORT } from './constants'; +import type { IDisposableTunneledBrowser } from './ITunneledBrowser'; +import type { IDisposableTunneledBrowserConnection } from './ITunneledBrowserConnection'; +import { tunneledBrowserConnection } from './TunneledBrowserConnection'; + +/** + * Creates a Playwright Browser instance connected via a tunneled WebSocket connection. + * @beta + */ +export async function createTunneledBrowserAsync( + browserName: BrowserName, + launchOptions: LaunchOptions, + logger?: ITerminal, + port: number = DEFAULT_LISTEN_PORT +): Promise { + // Establish the tunnel first (remoteEndpoint here refers to local proxy endpoint for connect()) + + if (!logger) { + const terminalProvider: ConsoleTerminalProvider = new ConsoleTerminalProvider(); + logger = new Terminal(terminalProvider); + } + + const connection: IDisposableTunneledBrowserConnection = await tunneledBrowserConnection(logger, port); + const { remoteEndpoint } = connection; + // Append query params for browser and launchOptions + const urlObj: URL = new URL(remoteEndpoint); + urlObj.searchParams.set('browser', browserName); + urlObj.searchParams.set('launchOptions', JSON.stringify(launchOptions || {})); + const connectEndpoint: string = urlObj.toString(); + const browser: Browser = await playwright[browserName].connect(connectEndpoint); + logger.writeLine(`Connected to remote browser at ${connectEndpoint}`); + + return { + browser, + async [Symbol.asyncDispose]() { + logger.writeLine('Disposing browser'); + await browser.close(); + // Dispose the tunnel connection after browser is closed + connection[Symbol.dispose](); + } + }; +} diff --git a/apps/playwright-browser-tunnel/src/tunneledBrowserConnection.ts b/apps/playwright-browser-tunnel/src/tunneledBrowserConnection/TunneledBrowserConnection.ts similarity index 73% rename from apps/playwright-browser-tunnel/src/tunneledBrowserConnection.ts rename to apps/playwright-browser-tunnel/src/tunneledBrowserConnection/TunneledBrowserConnection.ts index 9a32024768..0d466c6067 100644 --- a/apps/playwright-browser-tunnel/src/tunneledBrowserConnection.ts +++ b/apps/playwright-browser-tunnel/src/tunneledBrowserConnection/TunneledBrowserConnection.ts @@ -1,59 +1,30 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import playwright from 'playwright-core'; -import type { Browser, LaunchOptions } from 'playwright-core'; -import { WebSocketServer, WebSocket, type RawData } from 'ws'; +import type { LaunchOptions } from 'playwright-core'; import playwrightPackageJson from 'playwright-core/package.json'; +import type { RawData } from 'ws'; +import { WebSocket, WebSocketServer } from 'ws'; -import { type ITerminal, Terminal, ConsoleTerminalProvider } from '@rushstack/terminal'; +import type { ITerminal } from '@rushstack/terminal'; -import type { BrowserName } from './PlaywrightBrowserTunnel'; -import { HttpServer } from './HttpServer'; +import { HttpServer } from '../HttpServer'; +import type { BrowserName } from '../PlaywrightBrowserTunnel'; import { getNormalizedErrorString, getWebSocketCloseReason, getWebSocketReadyStateString, WebSocketCloseCode -} from './utilities'; +} from '../utilities'; +import type { + IDisposableTunneledBrowserConnection, + IHandshake, + IHandshakeAck +} from './ITunneledBrowserConnection'; +import { DEFAULT_LISTEN_PORT, SUPPORTED_BROWSER_NAMES } from './constants'; const { version: playwrightVersion } = playwrightPackageJson; -const SUPPORTED_BROWSER_NAMES: Set = new Set(['chromium', 'firefox', 'webkit']); - -interface IHandshake { - action: 'handshake'; - browserName: BrowserName; - launchOptions: LaunchOptions; - playwrightVersion: string; -} - -interface IHandshakeAck { - action: 'handshakeAck'; -} - -const DEFAULT_LISTEN_PORT: number = 56767; - -/** - * Disposable handle returned by {@link tunneledBrowserConnection}. - * @beta - */ -export interface IDisposableTunneledBrowserConnection { - /** - * The WebSocket endpoint URL that the local Playwright client should connect to. - */ - remoteEndpoint: string; - /** - * Dispose method that closes the WebSocket servers. - * Called automatically when using `using` syntax. - */ - [Symbol.dispose]: () => void; - /** - * Promise that resolves when the remote WebSocket server closes. - */ - closePromise: Promise; -} - /** * Creates a tunneled WebSocket endpoint that a local Playwright client can connect to. * @beta @@ -259,57 +230,3 @@ export async function tunneledBrowserConnection( }); }); } - -/** - * Disposable handle returned by {@link createTunneledBrowserAsync}. - * @beta - */ -export interface IDisposableTunneledBrowser { - /** - * The connected Playwright Browser instance. - */ - browser: Browser; - /** - * Async dispose method that closes the browser connection. - * Called automatically when using `await using` syntax. - */ - [Symbol.asyncDispose]: () => Promise; -} - -/** - * Creates a Playwright Browser instance connected via a tunneled WebSocket connection. - * @beta - */ -export async function createTunneledBrowserAsync( - browserName: BrowserName, - launchOptions: LaunchOptions, - logger?: ITerminal, - port: number = DEFAULT_LISTEN_PORT -): Promise { - // Establish the tunnel first (remoteEndpoint here refers to local proxy endpoint for connect()) - - if (!logger) { - const terminalProvider: ConsoleTerminalProvider = new ConsoleTerminalProvider(); - logger = new Terminal(terminalProvider); - } - - const connection: IDisposableTunneledBrowserConnection = await tunneledBrowserConnection(logger, port); - const { remoteEndpoint } = connection; - // Append query params for browser and launchOptions - const urlObj: URL = new URL(remoteEndpoint); - urlObj.searchParams.set('browser', browserName); - urlObj.searchParams.set('launchOptions', JSON.stringify(launchOptions || {})); - const connectEndpoint: string = urlObj.toString(); - const browser: Browser = await playwright[browserName].connect(connectEndpoint); - logger.writeLine(`Connected to remote browser at ${connectEndpoint}`); - - return { - browser, - async [Symbol.asyncDispose]() { - logger.writeLine('Disposing browser'); - await browser.close(); - // Dispose the tunnel connection after browser is closed - connection[Symbol.dispose](); - } - }; -} diff --git a/apps/playwright-browser-tunnel/src/tunneledBrowserConnection/constants.ts b/apps/playwright-browser-tunnel/src/tunneledBrowserConnection/constants.ts new file mode 100644 index 0000000000..d0eb39d807 --- /dev/null +++ b/apps/playwright-browser-tunnel/src/tunneledBrowserConnection/constants.ts @@ -0,0 +1,5 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +export const SUPPORTED_BROWSER_NAMES: Set = new Set(['chromium', 'firefox', 'webkit']); +export const DEFAULT_LISTEN_PORT: number = 56767; diff --git a/apps/playwright-browser-tunnel/src/tunneledBrowserConnection/index.ts b/apps/playwright-browser-tunnel/src/tunneledBrowserConnection/index.ts new file mode 100644 index 0000000000..87c6e342f6 --- /dev/null +++ b/apps/playwright-browser-tunnel/src/tunneledBrowserConnection/index.ts @@ -0,0 +1,8 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +export { createTunneledBrowserAsync } from './TunneledBrowser'; +export { tunneledBrowserConnection } from './TunneledBrowserConnection'; + +export type { IDisposableTunneledBrowser } from './ITunneledBrowser'; +export type { IDisposableTunneledBrowserConnection } from './ITunneledBrowserConnection'; From b8623daa04f38e1c198f4f0adb2b50477f92e9e2 Mon Sep 17 00:00:00 2001 From: Arshad Narmawala <12823156+anarmawala@users.noreply.github.com> Date: Fri, 6 Feb 2026 17:52:48 -0800 Subject: [PATCH 2/6] changelogs --- .../fix-update-dependency-chat_2026-02-07-01-52.json | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 common/changes/@rushstack/playwright-browser-tunnel/fix-update-dependency-chat_2026-02-07-01-52.json diff --git a/common/changes/@rushstack/playwright-browser-tunnel/fix-update-dependency-chat_2026-02-07-01-52.json b/common/changes/@rushstack/playwright-browser-tunnel/fix-update-dependency-chat_2026-02-07-01-52.json new file mode 100644 index 0000000000..ff17a7aa75 --- /dev/null +++ b/common/changes/@rushstack/playwright-browser-tunnel/fix-update-dependency-chat_2026-02-07-01-52.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@rushstack/playwright-browser-tunnel", + "comment": "Update dependency chart to not always require playwright", + "type": "patch" + } + ], + "packageName": "@rushstack/playwright-browser-tunnel" +} \ No newline at end of file From fe73bef41a0b8a4fe913a885171ef62cfd1f4294 Mon Sep 17 00:00:00 2001 From: Arshad Narmawala <12823156+anarmawala@users.noreply.github.com> Date: Fri, 6 Feb 2026 19:34:15 -0800 Subject: [PATCH 3/6] 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-update-dependency-chat_2026-02-07-01-52.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/changes/@rushstack/playwright-browser-tunnel/fix-update-dependency-chat_2026-02-07-01-52.json b/common/changes/@rushstack/playwright-browser-tunnel/fix-update-dependency-chat_2026-02-07-01-52.json index ff17a7aa75..d3d7035dff 100644 --- a/common/changes/@rushstack/playwright-browser-tunnel/fix-update-dependency-chat_2026-02-07-01-52.json +++ b/common/changes/@rushstack/playwright-browser-tunnel/fix-update-dependency-chat_2026-02-07-01-52.json @@ -2,7 +2,7 @@ "changes": [ { "packageName": "@rushstack/playwright-browser-tunnel", - "comment": "Update dependency chart to not always require playwright", + "comment": "Update module layout to isolate code that does not depend on Playwright.", "type": "patch" } ], From d61236824fdabb155d952688af8be4fdc045b8cf Mon Sep 17 00:00:00 2001 From: Arshad Narmawala <12823156+anarmawala@users.noreply.github.com> Date: Fri, 6 Feb 2026 20:28:24 -0800 Subject: [PATCH 4/6] fix package.json --- apps/playwright-browser-tunnel/package.json | 2 +- .../package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/playwright-browser-tunnel/package.json b/apps/playwright-browser-tunnel/package.json index 62ddc1ddd3..888762adb1 100644 --- a/apps/playwright-browser-tunnel/package.json +++ b/apps/playwright-browser-tunnel/package.json @@ -1,6 +1,6 @@ { "name": "@rushstack/playwright-browser-tunnel", - "version": "0.2.4", + "version": "0.2.3", "description": "Run a remote Playwright Browser Tunnel. Useful in remote development environments.", "license": "MIT", "repository": { diff --git a/vscode-extensions/playwright-local-browser-server-vscode-extension/package.json b/vscode-extensions/playwright-local-browser-server-vscode-extension/package.json index 9c6e601ab6..1cff56f68e 100644 --- a/vscode-extensions/playwright-local-browser-server-vscode-extension/package.json +++ b/vscode-extensions/playwright-local-browser-server-vscode-extension/package.json @@ -1,6 +1,6 @@ { "name": "playwright-local-browser-server", - "version": "0.1.1", + "version": "0.1.2", "repository": { "type": "git", "url": "https://github.com/microsoft/rushstack.git", From 77d954dd6d8f179e252a85d0aa885e1bfc86bf49 Mon Sep 17 00:00:00 2001 From: Arshad Narmawala <12823156+anarmawala@users.noreply.github.com> Date: Fri, 6 Feb 2026 21:22:18 -0800 Subject: [PATCH 5/6] no need to update vscode extension --- .../package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vscode-extensions/playwright-local-browser-server-vscode-extension/package.json b/vscode-extensions/playwright-local-browser-server-vscode-extension/package.json index 1cff56f68e..9c6e601ab6 100644 --- a/vscode-extensions/playwright-local-browser-server-vscode-extension/package.json +++ b/vscode-extensions/playwright-local-browser-server-vscode-extension/package.json @@ -1,6 +1,6 @@ { "name": "playwright-local-browser-server", - "version": "0.1.2", + "version": "0.1.1", "repository": { "type": "git", "url": "https://github.com/microsoft/rushstack.git", From 6434b6c084465dbd232011313f5930644c3c2ec3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arshad=20=F0=9F=91=8B?= <12823156+anarmawala@users.noreply.github.com> Date: Wed, 11 Feb 2026 11:39:28 -0800 Subject: [PATCH 6/6] manually update vscode extension version --- .../package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vscode-extensions/playwright-local-browser-server-vscode-extension/package.json b/vscode-extensions/playwright-local-browser-server-vscode-extension/package.json index 9c6e601ab6..1cff56f68e 100644 --- a/vscode-extensions/playwright-local-browser-server-vscode-extension/package.json +++ b/vscode-extensions/playwright-local-browser-server-vscode-extension/package.json @@ -1,6 +1,6 @@ { "name": "playwright-local-browser-server", - "version": "0.1.1", + "version": "0.1.2", "repository": { "type": "git", "url": "https://github.com/microsoft/rushstack.git",