Add SPA session validation and buglist, update migration docs

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
root
2025-12-03 21:28:08 +00:00
parent 9be3dfc14e
commit cff287e870
24169 changed files with 10223 additions and 7120 deletions

0
node_modules/playwright/lib/mcp/browser/actions.d.js generated vendored Normal file → Executable file
View File

0
node_modules/playwright/lib/mcp/browser/browserContextFactory.js generated vendored Normal file → Executable file
View File

2
node_modules/playwright/lib/mcp/browser/browserServerBackend.js generated vendored Normal file → Executable file
View File

@@ -33,7 +33,7 @@ class BrowserServerBackend {
this._browserContextFactory = factory;
this._tools = (0, import_tools.filteredTools)(config);
}
async initialize(server, clientInfo) {
async initialize(clientInfo) {
this._sessionLog = this._config.saveSession ? await import_sessionLog.SessionLog.create(this._config, clientInfo) : void 0;
this._context = new import_context.Context({
config: this._config,

0
node_modules/playwright/lib/mcp/browser/codegen.js generated vendored Normal file → Executable file
View File

33
node_modules/playwright/lib/mcp/browser/config.js generated vendored Normal file → Executable file
View File

@@ -38,8 +38,7 @@ __export(config_exports, {
outputFile: () => outputFile,
resolutionParser: () => resolutionParser,
resolveCLIConfig: () => resolveCLIConfig,
resolveConfig: () => resolveConfig,
semicolonSeparatedList: () => semicolonSeparatedList
resolveConfig: () => resolveConfig
});
module.exports = __toCommonJS(config_exports);
var import_fs = __toESM(require("fs"));
@@ -61,10 +60,6 @@ const defaultConfig = {
viewport: null
}
},
network: {
allowedOrigins: void 0,
blockedOrigins: void 0
},
server: {},
saveTrace: false,
timeouts: {
@@ -164,6 +159,7 @@ function configFromCLIOptions(cliOptions) {
contextOptions,
cdpEndpoint: cliOptions.cdpEndpoint,
cdpHeaders: cliOptions.cdpHeader,
initPage: cliOptions.initPage,
initScript: cliOptions.initScript
},
server: {
@@ -172,10 +168,6 @@ function configFromCLIOptions(cliOptions) {
allowedHosts: cliOptions.allowedHosts
},
capabilities: cliOptions.caps,
network: {
allowedOrigins: cliOptions.allowedOrigins,
blockedOrigins: cliOptions.blockedOrigins
},
saveSession: cliOptions.saveSession,
saveTrace: cliOptions.saveTrace,
saveVideo: cliOptions.saveVideo,
@@ -183,6 +175,7 @@ function configFromCLIOptions(cliOptions) {
sharedBrowserContext: cliOptions.sharedBrowserContext,
outputDir: cliOptions.outputDir,
imageResponses: cliOptions.imageResponses,
testIdAttribute: cliOptions.testIdAttribute,
timeouts: {
action: cliOptions.timeoutAction,
navigation: cliOptions.timeoutNavigation
@@ -193,8 +186,6 @@ function configFromCLIOptions(cliOptions) {
function configFromEnv() {
const options = {};
options.allowedHosts = commaSeparatedList(process.env.PLAYWRIGHT_MCP_ALLOWED_HOSTNAMES);
options.allowedOrigins = semicolonSeparatedList(process.env.PLAYWRIGHT_MCP_ALLOWED_ORIGINS);
options.blockedOrigins = semicolonSeparatedList(process.env.PLAYWRIGHT_MCP_BLOCKED_ORIGINS);
options.blockServiceWorkers = envToBoolean(process.env.PLAYWRIGHT_MCP_BLOCK_SERVICE_WORKERS);
options.browser = envToString(process.env.PLAYWRIGHT_MCP_BROWSER);
options.caps = commaSeparatedList(process.env.PLAYWRIGHT_MCP_CAPS);
@@ -207,6 +198,9 @@ function configFromEnv() {
options.headless = envToBoolean(process.env.PLAYWRIGHT_MCP_HEADLESS);
options.host = envToString(process.env.PLAYWRIGHT_MCP_HOST);
options.ignoreHttpsErrors = envToBoolean(process.env.PLAYWRIGHT_MCP_IGNORE_HTTPS_ERRORS);
const initPage = envToString(process.env.PLAYWRIGHT_MCP_INIT_PAGE);
if (initPage)
options.initPage = [initPage];
const initScript = envToString(process.env.PLAYWRIGHT_MCP_INIT_SCRIPT);
if (initScript)
options.initScript = [initScript];
@@ -222,6 +216,7 @@ function configFromEnv() {
options.saveVideo = resolutionParser("--save-video", process.env.PLAYWRIGHT_MCP_SAVE_VIDEO);
options.secrets = dotenvFileLoader(process.env.PLAYWRIGHT_MCP_SECRETS_FILE);
options.storageState = envToString(process.env.PLAYWRIGHT_MCP_STORAGE_STATE);
options.testIdAttribute = envToString(process.env.PLAYWRIGHT_MCP_TEST_ID_ATTRIBUTE);
options.timeoutAction = numberParser(process.env.PLAYWRIGHT_MCP_TIMEOUT_ACTION);
options.timeoutNavigation = numberParser(process.env.PLAYWRIGHT_MCP_TIMEOUT_NAVIGATION);
options.userAgent = envToString(process.env.PLAYWRIGHT_MCP_USER_AGENT);
@@ -258,7 +253,7 @@ async function resolveFile(config, clientInfo, fileName, options) {
fileName = fileName.split("\\").join("/");
const resolvedFile = import_path.default.resolve(dir, fileName);
if (!resolvedFile.startsWith(import_path.default.resolve(dir) + import_path.default.sep))
throw new Error(`Resolved file path for ${fileName} is outside of the output directory`);
throw new Error(`Resolved file path ${resolvedFile} is outside of the output directory ${dir}. Use relative file names to stay within the output directory.`);
return resolvedFile;
}
return import_path.default.join(dir, sanitizeForFilePath(fileName));
@@ -290,10 +285,6 @@ function mergeConfig(base, overrides) {
...pickDefined(base),
...pickDefined(overrides),
browser,
network: {
...pickDefined(base.network),
...pickDefined(overrides.network)
},
server: {
...pickDefined(base.server),
...pickDefined(overrides.server)
@@ -304,11 +295,6 @@ function mergeConfig(base, overrides) {
}
};
}
function semicolonSeparatedList(value) {
if (!value)
return void 0;
return value.split(";").map((v) => v.trim());
}
function commaSeparatedList(value) {
if (!value)
return void 0;
@@ -378,6 +364,5 @@ function sanitizeForFilePath(s) {
outputFile,
resolutionParser,
resolveCLIConfig,
resolveConfig,
semicolonSeparatedList
resolveConfig
});

25
node_modules/playwright/lib/mcp/browser/context.js generated vendored Normal file → Executable file
View File

@@ -35,6 +35,7 @@ module.exports = __toCommonJS(context_exports);
var import_fs = __toESM(require("fs"));
var import_path = __toESM(require("path"));
var import_utilsBundle = require("playwright-core/lib/utilsBundle");
var import_playwright_core = require("playwright-core");
var import_log = require("../log");
var import_tab = require("./tab");
var import_config = require("./config");
@@ -74,6 +75,7 @@ class Context {
const { browserContext } = await this._ensureBrowserContext();
const page = await browserContext.newPage();
this._currentTab = this._tabs.find((t) => t.page === page);
await this._currentTab.initializedPromise;
return this._currentTab;
}
async selectTab(index) {
@@ -167,17 +169,6 @@ class Context {
await this.closeBrowserContext();
Context._allContexts.delete(this);
}
async _setupRequestInterception(context) {
if (this.config.network?.allowedOrigins?.length) {
await context.route("**", (route) => route.abort("blockedbyclient"));
for (const origin of this.config.network.allowedOrigins)
await context.route(originOrHostGlob(origin), (route) => route.continue());
}
if (this.config.network?.blockedOrigins?.length) {
for (const origin of this.config.network.blockedOrigins)
await context.route(originOrHostGlob(origin), (route) => route.abort("blockedbyclient"));
}
}
async ensureBrowserContext() {
const { browserContext } = await this._ensureBrowserContext();
return browserContext;
@@ -194,9 +185,10 @@ class Context {
async _setupBrowserContext() {
if (this._closeBrowserContextPromise)
throw new Error("Another browser context is being closed.");
if (this.config.testIdAttribute)
import_playwright_core.selectors.setTestIdAttribute(this.config.testIdAttribute);
const result = await this._browserContextFactory.createContext(this._clientInfo, this._abortController.signal, this._runningToolName);
const { browserContext } = result;
await this._setupRequestInterception(browserContext);
if (this.sessionLog)
await InputRecorder.create(this, browserContext);
for (const page of browserContext.pages())
@@ -221,15 +213,6 @@ class Context {
};
}
}
function originOrHostGlob(originOrHost) {
try {
const url = new URL(originOrHost);
if (url.origin !== "null")
return `${url.origin}/**`;
} catch {
}
return `*://${originOrHost}/**`;
}
class InputRecorder {
constructor(context, browserContext) {
this._context = context;

31
node_modules/playwright/lib/mcp/browser/response.js generated vendored Normal file → Executable file
View File

@@ -31,7 +31,7 @@ class Response {
this._result = [];
this._code = [];
this._images = [];
this._includeSnapshot = false;
this._includeSnapshot = "none";
this._includeTabs = false;
this._context = context;
this.toolName = toolName;
@@ -62,14 +62,14 @@ class Response {
images() {
return this._images;
}
setIncludeSnapshot() {
this._includeSnapshot = true;
setIncludeSnapshot(full) {
this._includeSnapshot = full ?? "incremental";
}
setIncludeTabs() {
this._includeTabs = true;
}
async finish() {
if (this._includeSnapshot && this._context.currentTab())
if (this._includeSnapshot !== "none" && this._context.currentTab())
this._tabSnapshot = await this._context.currentTabOrDie().captureSnapshot();
for (const tab of this._context.tabs())
await tab.updateTitle();
@@ -99,13 +99,14 @@ ${this._code.join("\n")}
\`\`\``);
response.push("");
}
if (this._includeSnapshot || this._includeTabs)
if (this._includeSnapshot !== "none" || this._includeTabs)
response.push(...renderTabsMarkdown(this._context.tabs(), this._includeTabs));
if (this._tabSnapshot?.modalStates.length) {
response.push(...(0, import_tab.renderModalStates)(this._context, this._tabSnapshot.modalStates));
response.push("");
} else if (this._tabSnapshot) {
response.push(renderTabSnapshot(this._tabSnapshot, options));
const includeSnapshot = options.omitSnapshot ? "none" : this._includeSnapshot;
response.push(renderTabSnapshot(this._tabSnapshot, includeSnapshot));
response.push("");
}
const content = [
@@ -129,7 +130,7 @@ ${this._code.join("\n")}
}
}
}
function renderTabSnapshot(tabSnapshot, options = {}) {
function renderTabSnapshot(tabSnapshot, includeSnapshot) {
const lines = [];
if (tabSnapshot.consoleMessages.length) {
lines.push(`### New console messages`);
@@ -147,13 +148,21 @@ function renderTabSnapshot(tabSnapshot, options = {}) {
}
lines.push("");
}
if (includeSnapshot === "incremental" && tabSnapshot.ariaSnapshotDiff === "") {
return lines.join("\n");
}
lines.push(`### Page state`);
lines.push(`- Page URL: ${tabSnapshot.url}`);
lines.push(`- Page Title: ${tabSnapshot.title}`);
lines.push(`- Page Snapshot:`);
lines.push("```yaml");
lines.push(options.omitSnapshot ? "<snapshot>" : tabSnapshot.ariaSnapshot);
lines.push("```");
if (includeSnapshot !== "none") {
lines.push(`- Page Snapshot:`);
lines.push("```yaml");
if (includeSnapshot === "incremental" && tabSnapshot.ariaSnapshotDiff !== void 0)
lines.push(tabSnapshot.ariaSnapshotDiff);
else
lines.push(tabSnapshot.ariaSnapshot);
lines.push("```");
}
return lines.join("\n");
}
function renderTabsMarkdown(tabs, force = false) {

0
node_modules/playwright/lib/mcp/browser/sessionLog.js generated vendored Normal file → Executable file
View File

35
node_modules/playwright/lib/mcp/browser/tab.js generated vendored Normal file → Executable file
View File

@@ -29,6 +29,7 @@ var import_utils2 = require("./tools/utils");
var import_log = require("../log");
var import_dialogs = require("./tools/dialogs");
var import_files = require("./tools/files");
var import_transform = require("../../transform/transform");
const TabEvents = {
modalState: "modalState"
};
@@ -41,6 +42,7 @@ class Tab extends import_events.EventEmitter {
this._requests = /* @__PURE__ */ new Set();
this._modalStates = [];
this._downloads = [];
this._needsFullSnapshot = false;
this.context = context;
this.page = page;
this._onPageClose = onPageClose;
@@ -63,7 +65,7 @@ class Tab extends import_events.EventEmitter {
page.setDefaultNavigationTimeout(this.context.config.timeouts.navigation);
page.setDefaultTimeout(this.context.config.timeouts.action);
page[tabSymbol] = this;
this._initializedPromise = this._initialize();
this.initializedPromise = this._initialize();
}
static forPage(page) {
return page[tabSymbol];
@@ -84,6 +86,14 @@ class Tab extends import_events.EventEmitter {
const requests = await this.page.requests().catch(() => []);
for (const request of requests)
this._requests.add(request);
for (const initPage of this.context.config.browser.initPage || []) {
try {
const { default: func } = await (0, import_transform.requireOrImport)(initPage);
await func({ page: this.page });
} catch (e) {
(0, import_log.logUnhandledError)(e);
}
}
}
modalStates() {
return this._modalStates;
@@ -165,21 +175,22 @@ class Tab extends import_events.EventEmitter {
await this.waitForLoadState("load", { timeout: 5e3 });
}
async consoleMessages(type) {
await this._initializedPromise;
await this.initializedPromise;
return this._consoleMessages.filter((message) => type ? message.type === type : true);
}
async requests() {
await this._initializedPromise;
await this.initializedPromise;
return this._requests;
}
async captureSnapshot() {
let tabSnapshot;
const modalStates = await this._raceAgainstModalStates(async () => {
const snapshot = await this.page._snapshotForAI();
const snapshot = await this.page._snapshotForAI({ track: "response" });
tabSnapshot = {
url: this.page.url(),
title: await this.page.title(),
ariaSnapshot: snapshot,
ariaSnapshot: snapshot.full,
ariaSnapshotDiff: this._needsFullSnapshot ? void 0 : snapshot.incremental,
modalStates: [],
consoleMessages: [],
downloads: this._downloads
@@ -189,6 +200,7 @@ class Tab extends import_events.EventEmitter {
tabSnapshot.consoleMessages = this._recentConsoleMessages;
this._recentConsoleMessages = [];
}
this._needsFullSnapshot = !tabSnapshot;
return tabSnapshot ?? {
url: this.page.url(),
title: "",
@@ -222,12 +234,15 @@ class Tab extends import_events.EventEmitter {
return (await this.refLocators([params]))[0];
}
async refLocators(params) {
const snapshot = await this.page._snapshotForAI();
return params.map((param) => {
if (!snapshot.includes(`[ref=${param.ref}]`))
return Promise.all(params.map(async (param) => {
try {
const locator = this.page.locator(`aria-ref=${param.ref}`).describe(param.element);
const { resolvedSelector } = await locator._resolveSelector();
return { locator, resolved: (0, import_utils.asLocator)("javascript", resolvedSelector) };
} catch (e) {
throw new Error(`Ref ${param.ref} not found in the current page snapshot. Try capturing new snapshot.`);
return this.page.locator(`aria-ref=${param.ref}`).describe(param.element);
});
}
}));
}
async waitForTimeout(time) {
if (this._javaScriptBlocked()) {

2
node_modules/playwright/lib/mcp/browser/tools.js generated vendored Normal file → Executable file
View File

@@ -44,6 +44,7 @@ var import_mouse = __toESM(require("./tools/mouse"));
var import_navigate = __toESM(require("./tools/navigate"));
var import_network = __toESM(require("./tools/network"));
var import_pdf = __toESM(require("./tools/pdf"));
var import_runCode = __toESM(require("./tools/runCode"));
var import_snapshot = __toESM(require("./tools/snapshot"));
var import_screenshot = __toESM(require("./tools/screenshot"));
var import_tabs = __toESM(require("./tools/tabs"));
@@ -63,6 +64,7 @@ const browserTools = [
...import_network.default,
...import_mouse.default,
...import_pdf.default,
...import_runCode.default,
...import_screenshot.default,
...import_snapshot.default,
...import_tabs.default,

0
node_modules/playwright/lib/mcp/browser/tools/common.js generated vendored Normal file → Executable file
View File

0
node_modules/playwright/lib/mcp/browser/tools/console.js generated vendored Normal file → Executable file
View File

0
node_modules/playwright/lib/mcp/browser/tools/dialogs.js generated vendored Normal file → Executable file
View File

5
node_modules/playwright/lib/mcp/browser/tools/evaluate.js generated vendored Normal file → Executable file
View File

@@ -34,7 +34,6 @@ module.exports = __toCommonJS(evaluate_exports);
var import_bundle = require("../../sdk/bundle");
var import_tool = require("./tool");
var javascript = __toESM(require("../codegen"));
var import_utils = require("./utils");
const evaluateSchema = import_bundle.z.object({
function: import_bundle.z.string().describe("() => { /* code */ } or (element) => { /* code */ } when element is provided"),
element: import_bundle.z.string().optional().describe("Human-readable element description used to obtain permission to interact with the element"),
@@ -54,12 +53,12 @@ const evaluate = (0, import_tool.defineTabTool)({
let locator;
if (params.ref && params.element) {
locator = await tab.refLocator({ ref: params.ref, element: params.element });
response.addCode(`await page.${await (0, import_utils.generateLocator)(locator)}.evaluate(${javascript.quote(params.function)});`);
response.addCode(`await page.${locator.resolved}.evaluate(${javascript.quote(params.function)});`);
} else {
response.addCode(`await page.evaluate(${javascript.quote(params.function)});`);
}
await tab.waitForCompletion(async () => {
const receiver = locator ?? tab.page;
const receiver = locator?.locator ?? tab.page;
const result = await receiver._evaluateFunction(params.function);
response.addResult(JSON.stringify(result, null, 2) || "undefined");
});

0
node_modules/playwright/lib/mcp/browser/tools/files.js generated vendored Normal file → Executable file
View File

5
node_modules/playwright/lib/mcp/browser/tools/form.js generated vendored Normal file → Executable file
View File

@@ -33,7 +33,6 @@ __export(form_exports, {
module.exports = __toCommonJS(form_exports);
var import_bundle = require("../../sdk/bundle");
var import_tool = require("./tool");
var import_utils = require("./utils");
var codegen = __toESM(require("../codegen"));
const fillForm = (0, import_tool.defineTabTool)({
capability: "core",
@@ -53,8 +52,8 @@ const fillForm = (0, import_tool.defineTabTool)({
},
handle: async (tab, params, response) => {
for (const field of params.fields) {
const locator = await tab.refLocator({ element: field.name, ref: field.ref });
const locatorSource = `await page.${await (0, import_utils.generateLocator)(locator)}`;
const { locator, resolved } = await tab.refLocator({ element: field.name, ref: field.ref });
const locatorSource = `await page.${resolved}`;
if (field.type === "textbox" || field.type === "slider") {
const secret = tab.context.lookupSecret(field.value);
await locator.fill(secret.value);

0
node_modules/playwright/lib/mcp/browser/tools/install.js generated vendored Normal file → Executable file
View File

9
node_modules/playwright/lib/mcp/browser/tools/keyboard.js generated vendored Normal file → Executable file
View File

@@ -24,7 +24,6 @@ module.exports = __toCommonJS(keyboard_exports);
var import_bundle = require("../../sdk/bundle");
var import_tool = require("./tool");
var import_snapshot = require("./snapshot");
var import_utils = require("./utils");
const pressKey = (0, import_tool.defineTabTool)({
capability: "core",
schema: {
@@ -60,20 +59,20 @@ const type = (0, import_tool.defineTabTool)({
type: "input"
},
handle: async (tab, params, response) => {
const locator = await tab.refLocator(params);
const { locator, resolved } = await tab.refLocator(params);
const secret = tab.context.lookupSecret(params.text);
await tab.waitForCompletion(async () => {
if (params.slowly) {
response.setIncludeSnapshot();
response.addCode(`await page.${await (0, import_utils.generateLocator)(locator)}.pressSequentially(${secret.code});`);
response.addCode(`await page.${resolved}.pressSequentially(${secret.code});`);
await locator.pressSequentially(secret.value);
} else {
response.addCode(`await page.${await (0, import_utils.generateLocator)(locator)}.fill(${secret.code});`);
response.addCode(`await page.${resolved}.fill(${secret.code});`);
await locator.fill(secret.value);
}
if (params.submit) {
response.setIncludeSnapshot();
response.addCode(`await page.${await (0, import_utils.generateLocator)(locator)}.press('Enter');`);
response.addCode(`await page.${resolved}.press('Enter');`);
await locator.press("Enter");
}
});

0
node_modules/playwright/lib/mcp/browser/tools/mouse.js generated vendored Normal file → Executable file
View File

0
node_modules/playwright/lib/mcp/browser/tools/navigate.js generated vendored Normal file → Executable file
View File

0
node_modules/playwright/lib/mcp/browser/tools/network.js generated vendored Normal file → Executable file
View File

2
node_modules/playwright/lib/mcp/browser/tools/pdf.js generated vendored Normal file → Executable file
View File

@@ -36,7 +36,7 @@ var import_tool = require("./tool");
var javascript = __toESM(require("../codegen"));
var import_utils = require("./utils");
const pdfSchema = import_bundle.z.object({
filename: import_bundle.z.string().optional().describe("File name to save the pdf to. Defaults to `page-{timestamp}.pdf` if not specified.")
filename: import_bundle.z.string().optional().describe("File name to save the pdf to. Defaults to `page-{timestamp}.pdf` if not specified. Prefer relative file names to stay within the output directory.")
});
const pdf = (0, import_tool.defineTabTool)({
capability: "pdf",

75
node_modules/playwright/lib/mcp/browser/tools/runCode.js generated vendored Executable file
View File

@@ -0,0 +1,75 @@
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var runCode_exports = {};
__export(runCode_exports, {
default: () => runCode_default
});
module.exports = __toCommonJS(runCode_exports);
var import_vm = __toESM(require("vm"));
var import_utils = require("playwright-core/lib/utils");
var import_bundle = require("../../sdk/bundle");
var import_tool = require("./tool");
const codeSchema = import_bundle.z.object({
code: import_bundle.z.string().describe(`Playwright code snippet to run. The snippet should access the \`page\` object to interact with the page. Can make multiple statements. For example: \`await page.getByRole('button', { name: 'Submit' }).click();\``)
});
const runCode = (0, import_tool.defineTabTool)({
capability: "core",
schema: {
name: "browser_run_code",
title: "Run Playwright code",
description: "Run Playwright code snippet",
inputSchema: codeSchema,
type: "action"
},
handle: async (tab, params, response) => {
response.setIncludeSnapshot();
response.addCode(params.code);
const __end__ = new import_utils.ManualPromise();
const context = {
page: tab.page,
__end__
};
import_vm.default.createContext(context);
await tab.waitForCompletion(async () => {
const snippet = `(async () => {
try {
${params.code};
__end__.resolve();
} catch (e) {
__end__.reject(e);
}
})()`;
import_vm.default.runInContext(snippet, context);
await __end__;
});
}
});
var runCode_default = [
runCode
];

48
node_modules/playwright/lib/mcp/browser/tools/screenshot.js generated vendored Normal file → Executable file
View File

@@ -28,16 +28,20 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var screenshot_exports = {};
__export(screenshot_exports, {
default: () => screenshot_default
default: () => screenshot_default,
scaleImageToFitMessage: () => scaleImageToFitMessage
});
module.exports = __toCommonJS(screenshot_exports);
var import_fs = __toESM(require("fs"));
var import_utils = require("playwright-core/lib/utils");
var import_utilsBundle = require("playwright-core/lib/utilsBundle");
var import_bundle = require("../../sdk/bundle");
var import_tool = require("./tool");
var javascript = __toESM(require("../codegen"));
var import_utils = require("./utils");
var import_utils2 = require("./utils");
const screenshotSchema = import_bundle.z.object({
type: import_bundle.z.enum(["png", "jpeg"]).default("png").describe("Image format for the screenshot. Default is png."),
filename: import_bundle.z.string().optional().describe("File name to save the screenshot to. Defaults to `page-{timestamp}.{png|jpeg}` if not specified."),
filename: import_bundle.z.string().optional().describe("File name to save the screenshot to. Defaults to `page-{timestamp}.{png|jpeg}` if not specified. Prefer relative file names to stay within the output directory."),
element: import_bundle.z.string().optional().describe("Human-readable element description used to obtain permission to screenshot the element. If not provided, the screenshot will be taken of viewport. If element is provided, ref must be provided too."),
ref: import_bundle.z.string().optional().describe("Exact target element reference from the page snapshot. If not provided, the screenshot will be taken of viewport. If ref is provided, element must be provided too."),
fullPage: import_bundle.z.boolean().optional().describe("When true, takes a screenshot of the full scrollable page, instead of the currently visible viewport. Cannot be used with element screenshots.")
@@ -57,32 +61,46 @@ const screenshot = (0, import_tool.defineTabTool)({
if (params.fullPage && params.ref)
throw new Error("fullPage cannot be used with element screenshots.");
const fileType = params.type || "png";
const fileName = await tab.context.outputFile(params.filename ?? (0, import_utils.dateAsFileName)(fileType), { origin: "llm", reason: "Saving screenshot" });
const fileName = await tab.context.outputFile(params.filename || (0, import_utils2.dateAsFileName)(fileType), { origin: "llm", reason: "Saving screenshot" });
const options = {
type: fileType,
quality: fileType === "png" ? void 0 : 90,
scale: "css",
path: fileName,
...params.fullPage !== void 0 && { fullPage: params.fullPage }
};
const isElementScreenshot = params.element && params.ref;
const screenshotTarget = isElementScreenshot ? params.element : params.fullPage ? "full page" : "viewport";
response.addCode(`// Screenshot ${screenshotTarget} and save it as ${fileName}`);
const locator = params.ref ? await tab.refLocator({ element: params.element || "", ref: params.ref }) : null;
if (locator)
response.addCode(`await page.${await (0, import_utils.generateLocator)(locator)}.screenshot(${javascript.formatObject(options)});`);
const ref = params.ref ? await tab.refLocator({ element: params.element || "", ref: params.ref }) : null;
if (ref)
response.addCode(`await page.${ref.resolved}.screenshot(${javascript.formatObject(options)});`);
else
response.addCode(`await page.screenshot(${javascript.formatObject(options)});`);
const buffer = locator ? await locator.screenshot(options) : await tab.page.screenshot(options);
const buffer = ref ? await ref.locator.screenshot(options) : await tab.page.screenshot(options);
await (0, import_utils.mkdirIfNeeded)(fileName);
await import_fs.default.promises.writeFile(fileName, buffer);
response.addResult(`Took the ${screenshotTarget} screenshot and saved it as ${fileName}`);
if (!params.fullPage) {
response.addImage({
contentType: fileType === "png" ? "image/png" : "image/jpeg",
data: buffer
});
}
response.addImage({
contentType: fileType === "png" ? "image/png" : "image/jpeg",
data: scaleImageToFitMessage(buffer, fileType)
});
}
});
function scaleImageToFitMessage(buffer, imageType) {
const image = imageType === "png" ? import_utilsBundle.PNG.sync.read(buffer) : import_utilsBundle.jpegjs.decode(buffer, { maxMemoryUsageInMB: 512 });
const pixels = image.width * image.height;
const shrink = Math.min(1568 / image.width, 1568 / image.height, Math.sqrt(1.15 * 1024 * 1024 / pixels));
if (shrink > 1)
return buffer;
const width = image.width * shrink | 0;
const height = image.height * shrink | 0;
const scaledImage = (0, import_utils.scaleImageToSize)(image, { width, height });
return imageType === "png" ? import_utilsBundle.PNG.sync.write(scaledImage) : import_utilsBundle.jpegjs.encode(scaledImage, 80).data;
}
var screenshot_default = [
screenshot
];
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
scaleImageToFitMessage
});

27
node_modules/playwright/lib/mcp/browser/tools/snapshot.js generated vendored Normal file → Executable file
View File

@@ -35,7 +35,6 @@ module.exports = __toCommonJS(snapshot_exports);
var import_bundle = require("../../sdk/bundle");
var import_tool = require("./tool");
var javascript = __toESM(require("../codegen"));
var import_utils = require("./utils");
const snapshot = (0, import_tool.defineTool)({
capability: "core",
schema: {
@@ -47,7 +46,7 @@ const snapshot = (0, import_tool.defineTool)({
},
handle: async (context, params, response) => {
await context.ensureTab();
response.setIncludeSnapshot();
response.setIncludeSnapshot("full");
}
});
const elementSchema = import_bundle.z.object({
@@ -70,7 +69,7 @@ const click = (0, import_tool.defineTabTool)({
},
handle: async (tab, params, response) => {
response.setIncludeSnapshot();
const locator = await tab.refLocator(params);
const { locator, resolved } = await tab.refLocator(params);
const options = {
button: params.button,
modifiers: params.modifiers
@@ -78,9 +77,9 @@ const click = (0, import_tool.defineTabTool)({
const formatted = javascript.formatObject(options, " ", "oneline");
const optionsAttr = formatted !== "{}" ? formatted : "";
if (params.doubleClick)
response.addCode(`await page.${await (0, import_utils.generateLocator)(locator)}.dblclick(${optionsAttr});`);
response.addCode(`await page.${resolved}.dblclick(${optionsAttr});`);
else
response.addCode(`await page.${await (0, import_utils.generateLocator)(locator)}.click(${optionsAttr});`);
response.addCode(`await page.${resolved}.click(${optionsAttr});`);
await tab.waitForCompletion(async () => {
if (params.doubleClick)
await locator.dblclick(options);
@@ -105,14 +104,14 @@ const drag = (0, import_tool.defineTabTool)({
},
handle: async (tab, params, response) => {
response.setIncludeSnapshot();
const [startLocator, endLocator] = await tab.refLocators([
const [start, end] = await tab.refLocators([
{ ref: params.startRef, element: params.startElement },
{ ref: params.endRef, element: params.endElement }
]);
await tab.waitForCompletion(async () => {
await startLocator.dragTo(endLocator);
await start.locator.dragTo(end.locator);
});
response.addCode(`await page.${await (0, import_utils.generateLocator)(startLocator)}.dragTo(page.${await (0, import_utils.generateLocator)(endLocator)});`);
response.addCode(`await page.${start.resolved}.dragTo(page.${end.resolved});`);
}
});
const hover = (0, import_tool.defineTabTool)({
@@ -126,8 +125,8 @@ const hover = (0, import_tool.defineTabTool)({
},
handle: async (tab, params, response) => {
response.setIncludeSnapshot();
const locator = await tab.refLocator(params);
response.addCode(`await page.${await (0, import_utils.generateLocator)(locator)}.hover();`);
const { locator, resolved } = await tab.refLocator(params);
response.addCode(`await page.${resolved}.hover();`);
await tab.waitForCompletion(async () => {
await locator.hover();
});
@@ -147,8 +146,8 @@ const selectOption = (0, import_tool.defineTabTool)({
},
handle: async (tab, params, response) => {
response.setIncludeSnapshot();
const locator = await tab.refLocator(params);
response.addCode(`await page.${await (0, import_utils.generateLocator)(locator)}.selectOption(${javascript.formatObject(params.values)});`);
const { locator, resolved } = await tab.refLocator(params);
response.addCode(`await page.${resolved}.selectOption(${javascript.formatObject(params.values)});`);
await tab.waitForCompletion(async () => {
await locator.selectOption(params.values);
});
@@ -164,8 +163,8 @@ const pickLocator = (0, import_tool.defineTabTool)({
type: "readOnly"
},
handle: async (tab, params, response) => {
const locator = await tab.refLocator(params);
response.addResult(await (0, import_utils.generateLocator)(locator));
const { resolved } = await tab.refLocator(params);
response.addResult(resolved);
}
});
var snapshot_default = [

4
node_modules/playwright/lib/mcp/browser/tools/tabs.js generated vendored Normal file → Executable file
View File

@@ -49,14 +49,14 @@ const browserTabs = (0, import_tool.defineTool)({
}
case "close": {
await context.closeTab(params.index);
response.setIncludeSnapshot();
response.setIncludeSnapshot("full");
return;
}
case "select": {
if (params.index === void 0)
throw new Error("Tab index is required");
await context.selectTab(params.index);
response.setIncludeSnapshot();
response.setIncludeSnapshot("full");
return;
}
}

0
node_modules/playwright/lib/mcp/browser/tools/tool.js generated vendored Normal file → Executable file
View File

0
node_modules/playwright/lib/mcp/browser/tools/tracing.js generated vendored Normal file → Executable file
View File

11
node_modules/playwright/lib/mcp/browser/tools/utils.js generated vendored Normal file → Executable file
View File

@@ -20,11 +20,9 @@ var utils_exports = {};
__export(utils_exports, {
callOnPageNoTrace: () => callOnPageNoTrace,
dateAsFileName: () => dateAsFileName,
generateLocator: () => generateLocator,
waitForCompletion: () => waitForCompletion
});
module.exports = __toCommonJS(utils_exports);
var import_utils = require("playwright-core/lib/utils");
async function waitForCompletion(tab, callback) {
const requests = /* @__PURE__ */ new Set();
let frameNavigated = false;
@@ -76,14 +74,6 @@ async function waitForCompletion(tab, callback) {
dispose();
}
}
async function generateLocator(locator) {
try {
const { resolvedSelector } = await locator._resolveSelector();
return (0, import_utils.asLocator)("javascript", resolvedSelector);
} catch (e) {
throw new Error("Ref not found, likely because element was removed. Use browser_snapshot to see what elements are currently on the page.");
}
}
async function callOnPageNoTrace(page, callback) {
return await page._wrapApiCall(() => callback(page), { internal: true });
}
@@ -95,6 +85,5 @@ function dateAsFileName(extension) {
0 && (module.exports = {
callOnPageNoTrace,
dateAsFileName,
generateLocator,
waitForCompletion
});

7
node_modules/playwright/lib/mcp/browser/tools/verify.js generated vendored Normal file → Executable file
View File

@@ -34,7 +34,6 @@ module.exports = __toCommonJS(verify_exports);
var import_bundle = require("../../sdk/bundle");
var import_tool = require("./tool");
var javascript = __toESM(require("../codegen"));
var import_utils = require("./utils");
const verifyElement = (0, import_tool.defineTabTool)({
capability: "testing",
schema: {
@@ -92,7 +91,7 @@ const verifyList = (0, import_tool.defineTabTool)({
type: "assertion"
},
handle: async (tab, params, response) => {
const locator = await tab.refLocator({ ref: params.ref, element: params.element });
const { locator } = await tab.refLocator({ ref: params.ref, element: params.element });
const itemTexts = [];
for (const item of params.items) {
const itemLocator = locator.getByText(item);
@@ -125,8 +124,8 @@ const verifyValue = (0, import_tool.defineTabTool)({
type: "assertion"
},
handle: async (tab, params, response) => {
const locator = await tab.refLocator({ ref: params.ref, element: params.element });
const locatorSource = `page.${await (0, import_utils.generateLocator)(locator)}`;
const { locator, resolved } = await tab.refLocator({ ref: params.ref, element: params.element });
const locatorSource = `page.${resolved}`;
if (params.type === "textbox" || params.type === "slider" || params.type === "combobox") {
const value = await locator.inputValue();
if (value !== params.value) {

0
node_modules/playwright/lib/mcp/browser/tools/wait.js generated vendored Normal file → Executable file
View File

0
node_modules/playwright/lib/mcp/browser/watchdog.js generated vendored Normal file → Executable file
View File