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 00000000000..810dbd8c3eb --- /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 00000000000..986641372f4 --- /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 00000000000..3875e2e5136 --- /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 9a32024768c..0d466c6067f 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 00000000000..d0eb39d807a --- /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 00000000000..87c6e342f6d --- /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'; 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 00000000000..d3d7035dff7 --- /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 module layout to isolate code that does not depend on Playwright.", + "type": "patch" + } + ], + "packageName": "@rushstack/playwright-browser-tunnel" +} \ No newline at end of file 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 9c6e601ab6f..1cff56f68ea 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",