new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

Playwright · all subjects

out of scope

78 notes in this subject, read out of this brain and free to use. This is page 1 of 2.

Out of scope: Java/JUnit API

This source documents the Java language binding for Playwright, not the JavaScript/TypeScript API that is the subject of this knowledge base.

CLI command test file location and fixtures

CLI command tests are created in `tests/mcp/cli-<category>.spec.ts` using fixtures from `./cli-fixtures`. The `cli(...args)` fixture runs CLI commands and returns an object with `output` (stdout text), `snapshot` (extracted ARIA snapshot if present), `attachments` (file attachments array with name and data), `error` (stderr text), and `exitCode` (process exit code).

Run CLI command tests

Run CLI command tests using `npm run ctest-mcp cli-<category>`. Do not run `test --debug`.

SKILL file location and documentation

The skill file is located at `packages/playwright/src/skill/SKILL.md` and contains documentation for all available CLI commands and MCP tools. Reference docs live in `packages/playwright/src/skill/references/` including: request-mocking.md, running-code.md, session-management.md, storage-state.md, test-generation.md, tracing.md, video-recording.md.

Update SKILL file when adding commands or tools

When adding new CLI commands or MCP tools, update `packages/playwright/src/skill/SKILL.md` with documentation. Run `npm run playwright-cli -- --help` to verify help output includes the new command.

MCP tool backend directory structure

MCP tool implementations are organized in `packages/playwright-core/src/tools/backend/` with files: tool.ts (tool types and definitions), tools.ts (registry), browserBackend.ts, context.ts, tab.ts, response.ts, common.ts, navigate.ts, snapshot.ts, form.ts, keyboard.ts, mouse.ts, tabs.ts, cookies.ts, webstorage.ts, storage.ts, network.ts, route.ts, console.ts, evaluate.ts, screenshot.ts, pdf.ts, files.ts, dialogs.ts, verify.ts, wait.ts, tracing.ts, video.ts, runCode.ts, devtools.ts, config.ts, and utils.ts.

MCP server and client directory structure

MCP infrastructure is organized in: `packages/playwright-core/src/tools/mcp/` (MCP server with config, program, browser factory, CDP relay, watchdog, logging), `packages/playwright-core/src/tools/cli-client/` (CLI client with program, session management, registry), `packages/playwright-core/src/tools/cli-daemon/` (CLI daemon with command types, command declarations, help generation, daemon server), and `packages/playwright-core/src/tools/dashboard/` (dashboard UI).

MCP tool execution flow

MCP Server execution flow: LLM sends MCP protocol → Server.callTool(name, args) → zod validates input → Tool.handle(context|tab, params, response) → response.serialize() → returns to LLM via MCP protocol.

CLI command execution flow

CLI command execution flow: User enters `playwright-cli my-command arg1 --opt=val` → Client parses with minimist → sends to Daemon via socket → parseCommand() maps CLI args to MCP tool params via zod → backend.callTool(toolName, toolParams) → Response formatted → printed to stdout.

MCP tool custom test matchers

MCP test custom matchers include `toHaveResponse({ code?, snapshot?, page?, error?, isError?, result?, events?, modalState? })` for matching parsed response sections, and `toHaveTextResponse(text)` for matching raw text with normalization.

MCP tool test response sections

Parsed response sections include: `code` (generated Playwright code without ```js fences), `snapshot` (ARIA page snapshot with ```yaml fences), `page` (page info: URL and title), `error` (error message), `result` (text result), `events` (console messages, downloads), `modalState` (active dialog/file chooser info), `tabs` (tab listing), and `isError` (boolean flag).

Run MCP tool tests

Run MCP tool tests using `npm run ctest-mcp <category>`. Do not run `test --debug`.

MCP tool file location and import pattern

MCP tools are created as TypeScript files in `packages/playwright-core/src/tools/backend/<your-tool>.ts`. Import zod from the MCP bundle using `import { z } from '../../zodBundle'` and use `import { defineTool, defineTabTool } from './tool'`.

Choose defineTabTool vs defineTool for MCP tools

`defineTabTool` is used by most tools and receives a `Tab` object, automatically handling modal state for dialogs and file choosers. `defineTool` receives the full `Context` and should be used when you need `context.ensureBrowserContext()` without a specific tab, or need custom tab management.

MCP tool schema definition fields and types

MCP tool schema contains: `name` (string, MCP tool name with browser_ prefix), `title` (human-readable title), `description` (description shown to LLM), `inputSchema` (zod object defining parameters), and `type` (one of 'action', 'input', 'assertion', or 'readOnly'). The schema is part of the tool definition object.

MCP tool schema type values and their meanings

Schema type values are: 'action' for state-changing operations like navigate, click, fill; 'input' for user input like typing and keyboard events; 'readOnly' for queries that don't modify state like listing cookies or getting snapshots; 'assertion' for testing and verification tools.

MCP tool response API methods

Tool response object provides methods: `response.addTextResult(text)` adds text to result section, `response.addError(error)` adds error message, `response.addCode(code)` adds generated Playwright code snippet, `response.setIncludeSnapshot()` includes ARIA snapshot in response, `response.setIncludeFullSnapshot(filename?)` forces full snapshot, `response.addResult(title, data, fileTemplate)` adds file result, and `response.registerImageResult(data, 'png'|'jpeg')` adds image.

ToolCapability values for MCP tools

ToolCapability type includes: 'config', 'core', 'core-navigation', 'core-tabs', 'core-input', 'core-install', 'network', 'pdf', 'storage', 'testing', 'vision', and 'devtools'. Tools with core* capabilities are always enabled. Other capabilities must be enabled via --caps or config capabilities array.

MCP tool registration location

MCP tools are registered in `packages/playwright-core/src/tools/backend/tools.ts` by importing the tool file and adding it to the `browserTools` array using the spread operator.

MCP tool test file location and fixture usage

MCP tool tests are created in `tests/mcp/<category>.spec.ts`. Tests use fixtures from `./fixtures` including `client` to call tools via `client.callTool({ name, arguments })`, `startClient(options?)` as a client factory, `server` for HTTP test server with `server.PREFIX` and `server.setContent()`, and `httpsServer` for HTTPS testing.

CLI command declaration with declareCommand

CLI commands are declared using `declareCommand()` in `packages/playwright-core/src/tools/cli-daemon/commands.ts`. The declaration includes: `name` (CLI command name in kebab-case), `description` (shown in help), `category` (for help grouping), `args` (zod object for positional arguments), `options` (zod object for named options), `toolName` (MCP tool name, can be string or function for dynamic routing), and `toolParams` (function mapping CLI args/options to MCP tool params).

CLI command categories and where to add new ones

CLI command categories are: 'core', 'navigation', 'keyboard', 'mouse', 'export', 'storage', 'tabs', 'network', 'devtools', 'browsers', 'config', 'install'. To add a new category: add it to the `Category` type in `packages/playwright-core/src/tools/cli-daemon/command.ts` and add it to the `categories` array in `packages/playwright-core/src/tools/cli-daemon/helpGenerator.ts` with a name and title.

CLI command special patterns and options

Special CLI command patterns include: `toolName: ''` for commands handled specially by daemon (e.g., close, list, install); use `numberArg` for numeric CLI arguments with `.describe()`; param renaming in toolParams like `({ w: width, h: height }) => ({ width, height })`; dynamic toolName using a function `({ clear }) => clear ? 'browser_clear' : 'browser_list'`.

CLI command registration

CLI commands are registered by adding them to the `commandsArray` at the bottom of `packages/playwright-core/src/tools/cli-daemon/commands.ts` in the correct category section.

Object lifecycle: creation, adoption, disposal, garbage collection

Object creation: Server creates SdkObject, dispatcher constructor sends __create__, client Connection.dispatch() instantiates ChannelOwner subclass. Object adoption: dispatcher.adopt(child) sends __adopt__, client reparents the ChannelOwner. Object disposal: dispatcher._dispose() recursively disposes children, sends __dispose__, client removes ChannelOwner from maps. Garbage collection: Server-side maybeDisposeStaleDispatchers() evicts oldest dispatchers per bucket when limits are exceeded (JSHandle/ElementHandle 100k limit, others 10k limit, evicts oldest 10% when exceeded).

Playwright client-server architecture overview

Playwright uses a client-server architecture with three layers: the client provides the public API through ChannelOwner subclasses, the server performs actual browser automation through SdkObject subclasses, and dispatchers bridge the two over an RPC channel defined in protocol.yml.

Client code never imports server code and vice versa

The key architectural rule is that client code NEVER imports server code, and server code NEVER imports client code. They communicate only through the protocol layer.

ChannelOwner base class for client API objects

Every client-side API object (Page, Frame, Browser, etc.) extends ChannelOwner<T>. It has _connection (the RPC connection), _channel (a Proxy that intercepts method calls and sends RPC messages), _guid (unique identifier matching server-side object), _type (type name like 'Page'), _parent (parent in object tree), and _objects (Map of child objects). The _channel Proxy intercepts property access, finds the validator for parameters, validates params, wraps in _wrapApiCall, and calls _connection.sendMessageToServer().

Client event subscription optimization

The _eventToSubscriptionMapping maps JavaScript event names to protocol subscription events. When the first listener is added, it calls updateSubscription(event, true) on the channel. When the last listener is removed, it calls updateSubscription(event, false). This way the server only sends events that have active listeners.

Connection class manages client-server transport

The Connection class in packages/playwright-core/src/client/connection.ts manages the client-server transport. It maintains _objects (Map of all live remote objects by GUID), _callbacks (Map of pending RPC calls by message ID), and methods: sendMessageToServer(object, method, params, apiZone) sends RPC calls and returns a promise; dispatch(message) handles incoming messages - Response with id resolves/rejects the matching callback, __create__ instantiates ChannelOwner subclass via factory switch, __adopt__ reparents a child object, __dispose__ disposes object and all children, and Event with method emits on the object's _channel.

SdkObject base class for server domain objects

Every server-side domain object extends SdkObject. Key properties are guid (unique identifier shared with client-side ChannelOwner), attribution (ownership chain containing playwright, browserType, browser, context, page, frame), and instrumentation (hooks for tracing, debugging, test runner integration). Attribution is inherited from parent on construction. Instrumentation hooks include onBeforeCall, onAfterCall, onBeforeInputAction, onCallLog, onPageOpen/Close, onBrowserOpen/Close, onDialog, onDownload.

Protocol layer defines RPC interfaces in protocol.yml

The protocol.yml file defines all RPC interfaces, commands (methods), events, and types. Code generation from protocol.yml produces: packages/protocol/src/channels.d.ts (TypeScript types like PageChannel, PageGotoParams, PageGotoResult, PageInitializer, event types), packages/playwright-core/src/protocol/validator.ts (runtime validators like scheme.PageGotoParams = tObject({...})), and packages/playwright-core/src/utils/isomorphic/protocolMetainfo.ts (method flags such as slowMo, snapshot).

RPC wire format for client-server communication

Client to Server RPC call format: { id, guid, method, params, metadata? }. Server to Client response format: { id, result } or { id, error, log? }. Server to Client event format: { guid, method, params }. Server to Client lifecycle format: { guid, method: '__create__'|'__adopt__'|'__dispose__', params }. Object references are serialized as { guid: 'object-guid' } and resolved by validators.

tests/library test directory for API and feature tests

tests/library tests the Playwright public API surface, browser lifecycle, and feature-level behavior using browserTest fixtures which provide direct access to browser, browserType, context, and contextFactory. What belongs in tests/library: Browser and BrowserType API (launch, connect, version, newContext), BrowserContext API (cookies, storage state, permissions, proxy, CSP, geolocation, network interception at context level), browser-specific features (chromium/ for CDP, tracing, extensions, JS/CSS coverage, OOPIF; firefox/ for launcher specifics), protocol and channel tests, Inspector, codegen, and recorder features (inspector/), event system tests (events/), unit tests for internal utilities (unit/). Key fixtures from browserTest: browser, browserType, context, contextFactory, launchPersistent, createUserDataDir, startRemoteServer, pageWithHar.

Dispatcher base class bridges server objects to protocol

Dispatchers do not implement things, they translate protocol to server code calls. Each Dispatcher<Type extends SdkObject, ChannelType, ParentScopeType extends DispatcherScope> wraps an SdkObject and exposes methods matching the protocol channel. Key properties: connection (DispatcherConnection, the server-side connection), _object (the wrapped server object), _guid (same GUID as server object), _type (type name matching protocol), _parent (parent dispatcher), _dispatchers (Map of child dispatchers). Key methods: _dispatchEvent(method, params) sends event to client via connection.sendEvent(), _runCommand(callMetadata, method, params) wraps method call in ProgressController and calls this[method](params, progress), _dispose() recursively disposes self and children and sends __dispose__ to client, adopt(child) reparents child dispatcher and sends __adopt__ to client, addObjectListener(event, handler) listens on wrapped server object with auto-cleanup on dispose.

Dispatcher factory pattern ensures one-dispatcher-per-object

Dispatchers use a static factory pattern to ensure one dispatcher per object. The pattern is: static from(parentScope, object): XxxDispatcher { return parentScope.connection.existingDispatcher<XxxDispatcher>(object) || new XxxDispatcher(parentScope, object); }. The constructor sends __create__ to the client with the initializer data.

DispatcherConnection server-side counterpart to client Connection

DispatcherConnection is the server-side counterpart to client's Connection. It maintains _dispatcherByGuid (all dispatchers by GUID), _dispatcherByObject (maps server objects to their dispatchers ensuring 1:1 relationship), and methods: dispatch(message) validates params, creates CallMetadata, calls instrumentation hooks, runs dispatcher method, validates result, sends response; sendCreate/sendAdopt/sendDispose/sendEvent send lifecycle messages to client. Garbage collection uses buckets with limits: JSHandle/ElementHandle 100k, others 10k; when exceeded, oldest 10% are disposed.

Package layout and dependency rules for Playwright core

packages/protocol/src/ contains protocol.yml (RPC protocol definition source of truth). packages/playwright-core/src/ contains: client/ (public API objects as ChannelOwner subclasses) with channels.d.ts (generated client channel interfaces), server/ (browser automation implementation as SdkObject subclasses) with channels.d.ts (generated server channel interfaces), server/dispatchers/ (protocol bridge as Dispatcher subclasses), protocol/ (validators generated and primitives), utils/isomorphic/ (shared code used by both client and server) with protocolMetainfo.ts (generated method metadata with flags and titles). Each directory has a DEPS.list constraining imports. client/ can import from ../protocol/, ../utils/isomorphic. server/ can import from ../protocol/, ../utils/, ../utils/isomorphic/, ../utilsBundle.ts, ./, ./codegen/, ./isomorphic/, ./har/, ./recorder/, ./registry/, ./utils/, and only playwright.ts can import browser engines ./chromium/, ./firefox/, ./webkit/, ./bidi/, ./android/, ./electron/, and only devtoolsController.ts can additionally import ./chromium/. server/dispatchers/ can import from ../../protocol/, ../../utils/, ../../utils/isomorphic/, ../**.

PageDelegate pattern for browser-specific operations

Page delegates browser-specific operations to a PageDelegate interface. Implementations exist for each browser: packages/playwright-core/src/server/chromium/crPage.ts (uses Chrome DevTools Protocol CDP), packages/playwright-core/src/server/firefox/ffPage.ts, packages/playwright-core/src/server/webkit/wkPage.ts. The PageDelegate interface includes methods like navigateFrame(frame, url, referer), takeScreenshot(progress, format, ...), adoptElementHandle(handle, to).

Browser engine directories and protocols

chromium/ uses Chrome DevTools Protocol (CDP) with key files crBrowser.ts, crPage.ts, crConnection.ts. firefox/ uses Firefox internal protocol with ffBrowser.ts, ffPage.ts, ffConnection.ts. webkit/ uses WebKit internal protocol with wkBrowser.ts, wkPage.ts, wkConnection.ts. bidi/ uses WebDriver BiDi with bidiChromium.ts, bidiFirefox.ts. android/ uses ADB with android.ts. electron/ uses Electron/CDP with electron.ts.

End-to-end flow example for page.goto()

For await page.goto('https://example.com'): CLIENT: Page.goto() calls _wrapApiCall() which captures stack trace and creates ApiZone, then _channel.goto({ url, timeout }) which Proxy validates PageGotoParams, then connection.sendMessageToServer(page, 'goto', params) which sends { id: 1, guid: 'page@abc', method: 'goto', params: {...} } and waits on callback promise. SERVER: DispatcherConnection.dispatch(message) validates PageGotoParams (wire to objects), creates CallMetadata, calls instrumentation.onBeforeCall(), PageDispatcher._runCommand('goto', params) calls ProgressController.run(progress => this.goto(params, progress)) calling PageDispatcher.goto() calling this._object.mainFrame().goto(progress, url, params) calling Frame.goto() calling PageDelegate.navigateFrame() calling CDP/protocol call, validates PageGotoResult (objects to wire), calls instrumentation.onAfterCall(), sends { id: 1, result: { response: { guid: 'response@xyz' } } }. CLIENT: connection.dispatch(response) validates PageGotoResult (wire to objects), resolves callback promise, _wrapApiCall completes and returns Response object.

tests/page test directory for page interaction tests

tests/page tests user-facing page interactions including clicking, typing, navigation, locators, assertions, and DOM operations using pageTest fixtures which provide a ready-to-use page plus test servers. What belongs in tests/page: Locator API (click, fill, type, select, query, filtering, convenience methods), ElementHandle interactions (click, screenshot, selection, bounding box), Expect/assertion matchers (boolean, text, value, accessibility), Page navigation (goto, waitForNavigation, waitForURL), Frame evaluation and hierarchy, Request/response interception at page level, JSHandle operations, screenshot and visual comparison tests. Key fixtures from pageTest/serverFixtures: page, server, httpsServer, proxyServer, asset.

Decision rule for placing tests in tests/library vs tests/page

Test placement decision rule: Does it test browser/context lifecycle or launch options? → tests/library. Does it test a browser-specific protocol feature (CDP, etc.)? → tests/library. Does it test user interaction with page content (click, type, assert)? → tests/page. Does it test locators, selectors, or DOM queries? → tests/page. Does the test need direct browser or browserType access? → tests/library. Does the test just need a page and a test server? → tests/page.

Running Playwright tests with npm run ctest and npm run test

npm run ctest <file> runs tests on Chromium only (fast, use during development). npm run test <file> runs tests on all browsers (Chromium, Firefox, WebKit). Examples: npm run ctest tests/library/browser-context-cookies.spec.ts, npm run ctest tests/page/locator-click.spec.ts, npm run test tests/library/browser-context-cookies.spec.ts.

Test configuration shared between tests/library and tests/page

Both tests/library and tests/page directories share a single config at tests/library/playwright.config.ts. It creates separate projects ({browserName}-library and {browserName}-page) each pointing to their respective testDir.

Playwright bundle structure and outputs

Playwright ships pre-built bundle files under `lib/`. For playwright-core: `lib/utilsBundle.js` (entry: `src/utilsBundle.ts`) contains vendored npm packages like debug, mime, ws, yauzl, yazl, @modelcontextprotocol/sdk, graceful-fs. `lib/coreBundle.js` (entry: `src/coreBundle.ts`) re-exports playwright-core's own modules as namespaces and inlines almost all playwright-core source except utilsBundle. `lib/server/electron/loader.js` (entry: `src/server/electron/loader.ts`) is a tiny Electron preload shim. For playwright: `lib/transform/babelBundle.js` wraps @babel/core, @babel/traverse, @babel/code-frame and plugins. `lib/transform/esmLoader.js` is a Node ESM loader. `lib/common/index.js` is a barrel of common/* and transform/* modules with state-holding singletons. `lib/runner/index.js` is a barrel of runner/*, reporters/*, plugins/*. `lib/matchers/expect.js` contains Jest-style matchers with expect inlined. `lib/worker/workerProcessEntry.js` is the entry point spawned per test worker. `lib/loader/loaderProcessEntry.js` is the entry point for the test file loader sub-process. `lib/runner/uiModeReporter.js` is loaded by require.resolve from testServer.

Dynamic import rewriting at bundle time

The `dynamicImportToRequirePlugin` in `utils/build/build.js` rewrites vendored npm imports at bundle time. A playwright-core source file containing `import debug from 'debug'` gets rewritten to `const debug = require('./utilsBundle').debug` before the bundler sees it, so vendored packages never get inlined into `coreBundle.js`. The mapping from npm package name to utilsBundle export key lives in `utils/build/utilsBundleMapping.js`.

Adding a vendored npm dependency

To add a new npm package to be inlined into utilsBundle, four steps are required: (1) Install the package by adding it to root `package.json` devDependencies. (2) Export it from `src/utilsBundle.ts` using one of: `export const foo = fooLibrary` (default), `export const foo = fooLibrary` (namespace via import * as), or `export { namedSymbol } from 'foo'` (named). Type-only exports are valid and don't affect runtime. (3) Add a mapping entry to `utils/build/utilsBundleMapping.js` using one of: `'foo': { default: 'foo' }` for default imports, `'foo': { namespace: 'foo' }` for namespace imports, or `'foo': { named: { namedSymbol: 'fooNamedSymbol' } }` for named imports. Multiple forms can coexist in one entry. The map key is the exact npm specifier as written in source. (4) Update DEPS.list to authorize `node_modules/<pkg>`. (5) Run `npm run flint` to validate.

Bundle sidecar files

Every bundled output has two sidecar files next to it: `<bundle>.js.txt` is a human-readable report listing inlined files sorted by path with per-file KB sizes, externals, and total bytes, written by `utils/build/bundle_report.js`. `<bundle>.js.LICENSE` contains third-party license texts for every npm package whose source got inlined, populated from `license-checker` and memoized once per build invocation. Both sidecars are included in the published npm package.

In-tree third-party helpers location and usage

Some vendored code that is not a published npm package lives in-tree at `packages/playwright-core/src/server/utils/third_party/` (e.g. `extractZip.ts`, `lockfile.ts`). These TypeScript files are exposed to callers via two routes: (1) Through `coreBundle.utils`, re-exported from `src/server/utils/index.ts` via `export * from './third_party/extractZip'` etc. Callers import via the `@utils/*` path alias, e.g. `import { extractZip } from '@utils/third_party/extractZip'`. This alias is rewritten at bundle time to `require('playwright-core/lib/coreBundle').utils.extractZip`. (2) When a third_party TS file imports an npm package (e.g., `lockfile.ts` imports `graceful-fs`, `retry`, `signal-exit`), those are rewritten through utilsBundle, so the mapping in `utilsBundleMapping.js` must list them.

DEPS.list syntax and constraints

Every directory under `packages/*/src/` has a `DEPS.list` constraining its imports. Syntax: `./somefile.ts`, `@isomorphic/**` for relative or alias source imports allowed; `node_modules/<pkg>` for npm package imports allowed with exact specifier match; `"strict"` for no other DEPS inherited, only what's listed allowed. Section headers `[filename.ts]` scope rules to a single file. The top-level `[*]` or no header applies to everything in the folder plus subfolders without their own DEPS.list. An entry of `node_modules/<pkg>` shortcuts both layers of the check: disallowed external dependency error AND dependencies not declared in package.json report.

Bundle externalization plugins for relative imports

Two onResolve plugins in `utils/build/build.js` normalize relative imports to sibling bundles: (1) `externalizeUtilsBundlePlugin` matches any relative specifier ending in `/utilsBundle` or `/utilsBundle.js` at any depth (`./utilsBundle`, `../utilsBundle`, `../../utilsBundle`) and marks it external with the single spelling `./utilsBundle`. This applies only to coreBundle build. (2) babelBundle case is handled differently: esmLoader bundle output placed at `lib/transform/esmLoader.js` (same folder as `babelBundle.js`) so `./babelBundle` resolves correctly; common and runner bundles declare `'../transform/babelBundle'` as static external with outputs at `lib/common/index.js` and `lib/runner/index.js` both at depth 1; `transform.ts`'s `require('./babelBundle')` was replaced with `require(libPath('transform', 'babelBundle'))` — an absolute path computed at runtime via `package.ts`, which works from any bundle.

Per-file compilation outside bundles

Files outside bundled entries are compiled 1:1 by esbuild and land under `lib/` mirroring their source layout. The per-file step in `utils/build/build.js` lists specific directories for the playwright package: `cli/`, `agents/`, `mcp/`, root `*.ts`, and a few targeted files like `runner/uiModeReporter.ts`. Other packages (playwright-test, html-reporter, trace-viewer, etc.) are compiled by the generic per-package loop.

check_deps.js validation process

`utils/check_deps.js` walks the TypeScript program and visits every `import` in `src/**`. For each npm specifier: (1) Skip if the source file's DEPS.list authorizes `node_modules/<specifier>`. (2) Record the top-level package name and file path that imported it. (3) Subtract peerDependencies, VENDORED_PACKAGES (from `utilsBundleMapping.js`), and packages that resolve without `node_modules/` (core modules or local files). (4) Subtract packages listed in `packages/<pkg>/package.json` dependencies. (5) Report anything left with specific file paths. The missing-dep error includes file paths showing which source file imported the undeclared dependency.

Common and runner bundle externalization of transform/babelBundle

The `common` and `runner` bundles externalize `../transform/babelBundle` among other things so babel code is not duplicated across them. The `lib/transform/transform.ts` module uses `libPath('transform', 'babelBundle')` (absolute path via `package.ts` root) to load the babel bundle at runtime, so it works regardless of which bundle has inlined it.

WebView backend targets unmodified iOS Safari

The WebView backend in packages/playwright-core/src/server/webkit/webview/ targets unmodified Safari on real iOS/iPadOS or the iOS Simulator over the standard Web Inspector Protocol. It differs from the regular WebKit backend one level up (webkit/), which runs against a Playwright-patched WebKit build.

WebView backend has limited protocol access

Stock Mobile Safari only exposes the upstream Web Inspector Protocol. Methods and events available are those in Source/JavaScriptCore/inspector/protocol/*.json on browser_upstream/main. Anything added by Playwright WebKit patches is not available in the WebView backend.

WebView not available features from Playwright patches

Not available in WebView backend: Target.setPauseOnStart / PageInspectorController::pauseOnStart for popups, the Playwright.* domain (cookies, navigate, global controls), and Network.continueWithAuth / intercepted-response body access via Network.

WebView architecture mirrors regular WebKit backend

The WebView backend architecture deliberately mirrors the regular WebKit backend. The outerSession is the WebView analogue of WebKit's page-proxy session. WVConnection is intentionally minimal, pumping transport into outerSession. WVPage creates per-target WVSessions, routes Target.dispatchMessageFromTarget by targetId to either _session or _provisionalPage._session, and handles Target.targetCreated/targetDestroyed/didCommitProvisionalTarget.

WebView process swap protocol sequence

Cross-origin navigation in an existing tab can trigger a process swap. The protocol sequence is: Target.targetCreated with isProvisional:true and isPaused:true, then events on the provisional target during navigation, then Target.didCommitProvisionalTarget with oldTargetId and newTargetId, then Target.targetDestroyed oldTargetId.

Give your agent this brain