@wordbricks/playwright-mcp 0.1.19 → 0.1.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/README.md +54 -44
  2. package/cli-wrapper.js +15 -14
  3. package/cli.js +1 -1
  4. package/config.d.ts +11 -6
  5. package/index.d.ts +7 -5
  6. package/index.js +1 -1
  7. package/lib/browserContextFactory.js +131 -58
  8. package/lib/browserServerBackend.js +14 -12
  9. package/lib/config.js +60 -46
  10. package/lib/context.js +41 -39
  11. package/lib/extension/cdpRelay.js +67 -61
  12. package/lib/extension/extensionContextFactory.js +10 -10
  13. package/lib/frameworkPatterns.js +21 -21
  14. package/lib/hooks/antiBotDetectionHook.js +178 -0
  15. package/lib/hooks/core.js +11 -10
  16. package/lib/hooks/eventConsumer.js +29 -16
  17. package/lib/hooks/events.js +3 -3
  18. package/lib/hooks/formatToolCallEvent.js +3 -7
  19. package/lib/hooks/frameworkStateHook.js +40 -40
  20. package/lib/hooks/grouping.js +3 -3
  21. package/lib/hooks/jsonLdDetectionHook.js +44 -37
  22. package/lib/hooks/networkFilters.js +24 -15
  23. package/lib/hooks/networkSetup.js +11 -6
  24. package/lib/hooks/networkTrackingHook.js +31 -19
  25. package/lib/hooks/pageHeightHook.js +9 -9
  26. package/lib/hooks/registry.js +18 -16
  27. package/lib/hooks/requireTabHook.js +3 -3
  28. package/lib/hooks/schema.js +44 -32
  29. package/lib/hooks/waitHook.js +7 -7
  30. package/lib/index.js +12 -10
  31. package/lib/mcp/inProcessTransport.js +3 -4
  32. package/lib/mcp/proxyBackend.js +43 -28
  33. package/lib/mcp/server.js +24 -19
  34. package/lib/mcp/tool.js +14 -8
  35. package/lib/mcp/transport.js +60 -53
  36. package/lib/playwrightTransformer.js +129 -106
  37. package/lib/program.js +54 -52
  38. package/lib/response.js +36 -30
  39. package/lib/sessionLog.js +19 -17
  40. package/lib/tab.js +41 -39
  41. package/lib/tools/common.js +19 -19
  42. package/lib/tools/console.js +11 -11
  43. package/lib/tools/dialogs.js +18 -15
  44. package/lib/tools/evaluate.js +26 -17
  45. package/lib/tools/extractFrameworkState.js +48 -37
  46. package/lib/tools/files.js +17 -14
  47. package/lib/tools/form.js +32 -23
  48. package/lib/tools/getSnapshot.js +14 -15
  49. package/lib/tools/getVisibleHtml.js +33 -17
  50. package/lib/tools/install.js +20 -20
  51. package/lib/tools/keyboard.js +29 -24
  52. package/lib/tools/mouse.js +29 -31
  53. package/lib/tools/navigate.js +19 -23
  54. package/lib/tools/network.js +12 -14
  55. package/lib/tools/networkDetail.js +68 -61
  56. package/lib/tools/networkSearch/bodySearch.js +46 -32
  57. package/lib/tools/networkSearch/grouping.js +15 -6
  58. package/lib/tools/networkSearch/helpers.js +4 -4
  59. package/lib/tools/networkSearch/searchHtml.js +25 -16
  60. package/lib/tools/networkSearch/urlSearch.js +56 -14
  61. package/lib/tools/networkSearch.js +65 -35
  62. package/lib/tools/pdf.js +13 -12
  63. package/lib/tools/repl.js +66 -54
  64. package/lib/tools/screenshot.js +57 -33
  65. package/lib/tools/scroll.js +29 -24
  66. package/lib/tools/snapshot.js +66 -49
  67. package/lib/tools/tabs.js +22 -19
  68. package/lib/tools/tool.js +5 -3
  69. package/lib/tools/utils.js +17 -13
  70. package/lib/tools/wait.js +24 -19
  71. package/lib/tools.js +21 -20
  72. package/lib/utils/adBlockFilter.js +29 -26
  73. package/lib/utils/codegen.js +20 -16
  74. package/lib/utils/extensionPath.js +4 -4
  75. package/lib/utils/fileUtils.js +17 -13
  76. package/lib/utils/graphql.js +69 -58
  77. package/lib/utils/guid.js +3 -3
  78. package/lib/utils/httpServer.js +9 -9
  79. package/lib/utils/log.js +3 -3
  80. package/lib/utils/manualPromise.js +7 -7
  81. package/lib/utils/networkFormat.js +7 -5
  82. package/lib/utils/package.js +4 -4
  83. package/lib/utils/sanitizeHtml.js +66 -34
  84. package/lib/utils/truncate.js +25 -25
  85. package/lib/utils/withTimeout.js +1 -1
  86. package/package.json +34 -57
  87. package/src/index.ts +27 -17
  88. package/LICENSE +0 -202
@@ -13,16 +13,16 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
- import { fileURLToPath } from 'url';
17
- import { Context } from './context.js';
18
- import { logUnhandledError } from './utils/log.js';
19
- import { Response } from './response.js';
20
- import { SessionLog } from './sessionLog.js';
21
- import { filteredTools } from './tools.js';
22
- import { packageJSON } from './utils/package.js';
23
- import { toMcpTool } from './mcp/tool.js';
16
+ import { fileURLToPath } from "url";
17
+ import { Context } from "./context.js";
18
+ import { toMcpTool } from "./mcp/tool.js";
19
+ import { Response } from "./response.js";
20
+ import { SessionLog } from "./sessionLog.js";
21
+ import { filteredTools } from "./tools.js";
22
+ import { logUnhandledError } from "./utils/log.js";
23
+ import { packageJSON } from "./utils/package.js";
24
24
  export class BrowserServerBackend {
25
- name = 'Playwright';
25
+ name = "Playwright";
26
26
  version = packageJSON.version;
27
27
  _tools;
28
28
  _context;
@@ -43,7 +43,9 @@ export class BrowserServerBackend {
43
43
  const url = firstRootUri ? new URL(firstRootUri) : undefined;
44
44
  rootPath = url ? fileURLToPath(url) : undefined;
45
45
  }
46
- this._sessionLog = this._config.saveSession ? await SessionLog.create(this._config, rootPath) : undefined;
46
+ this._sessionLog = this._config.saveSession
47
+ ? await SessionLog.create(this._config, rootPath)
48
+ : undefined;
47
49
  this._context = new Context({
48
50
  tools: this._tools,
49
51
  config: this._config,
@@ -55,11 +57,11 @@ export class BrowserServerBackend {
55
57
  await this._context.ensureTab();
56
58
  }
57
59
  async listTools() {
58
- return this._tools.map(tool => toMcpTool(tool.schema));
60
+ return this._tools.map((tool) => toMcpTool(tool.schema));
59
61
  }
60
62
  async callTool(name, rawArguments) {
61
63
  const context = this._context;
62
- const tool = context.tools.find(tool => tool.schema.name === name);
64
+ const tool = context.tools.find((tool) => tool.schema.name === name);
63
65
  if (!tool)
64
66
  throw new Error(`Tool "${name}" not found`);
65
67
  const parsedArguments = tool.schema.inputSchema.parse(rawArguments || {});
package/lib/config.js CHANGED
@@ -13,17 +13,17 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
- import fs from 'fs';
17
- import os from 'os';
18
- import path from 'path';
19
- import { devices } from 'playwright-core';
20
- import { sanitizeForFilePath } from './utils/fileUtils.js';
16
+ import fs from "fs";
17
+ import os from "os";
18
+ import path from "path";
19
+ import { devices } from "playwright-core";
20
+ import { sanitizeForFilePath } from "./utils/fileUtils.js";
21
21
  const defaultConfig = {
22
22
  browser: {
23
- browserName: 'chromium',
23
+ browserName: "chromium",
24
24
  launchOptions: {
25
- channel: 'chrome',
26
- headless: os.platform() === 'linux' && !process.env.DISPLAY,
25
+ channel: "chrome",
26
+ headless: os.platform() === "linux" && !process.env.DISPLAY,
27
27
  chromiumSandbox: true,
28
28
  },
29
29
  contextOptions: {
@@ -54,23 +54,23 @@ export function configFromCLIOptions(cliOptions) {
54
54
  let browserName;
55
55
  let channel;
56
56
  switch (cliOptions.browser) {
57
- case 'chrome':
58
- case 'chrome-beta':
59
- case 'chrome-canary':
60
- case 'chrome-dev':
61
- case 'chromium':
62
- case 'msedge':
63
- case 'msedge-beta':
64
- case 'msedge-canary':
65
- case 'msedge-dev':
66
- browserName = 'chromium';
57
+ case "chrome":
58
+ case "chrome-beta":
59
+ case "chrome-canary":
60
+ case "chrome-dev":
61
+ case "chromium":
62
+ case "msedge":
63
+ case "msedge-beta":
64
+ case "msedge-canary":
65
+ case "msedge-dev":
66
+ browserName = "chromium";
67
67
  channel = cliOptions.browser;
68
68
  break;
69
- case 'firefox':
70
- browserName = 'firefox';
69
+ case "firefox":
70
+ browserName = "firefox";
71
71
  break;
72
- case 'webkit':
73
- browserName = 'webkit';
72
+ case "webkit":
73
+ browserName = "webkit";
74
74
  break;
75
75
  }
76
76
  // Launch options
@@ -78,13 +78,25 @@ export function configFromCLIOptions(cliOptions) {
78
78
  channel,
79
79
  executablePath: cliOptions.executablePath,
80
80
  headless: cliOptions.headless,
81
+ // Ignore default automation flag to reduce bot detection
82
+ ignoreDefaultArgs: ["--enable-automation"],
81
83
  };
82
84
  // --no-sandbox was passed, disable the sandbox
83
85
  if (cliOptions.sandbox === false)
84
86
  launchOptions.chromiumSandbox = false;
85
- // Add default Chrome args to avoid automation detection
87
+ // Add default Chrome args to avoid automation detection and suppress popups
86
88
  launchOptions.args = launchOptions.args || [];
87
- launchOptions.args.push('--disable-infobars');
89
+ launchOptions.args.push("--disable-infobars",
90
+ // Password & credential popups
91
+ "--disable-save-password-bubble", "--password-store=basic", "--use-mock-keychain",
92
+ // Crash restore popup
93
+ "--hide-crash-restore-bubble",
94
+ // First run & default browser
95
+ "--no-first-run", "--no-default-browser-check",
96
+ // Disable various Chrome features (Translate, PasswordManager, Autofill, Sync)
97
+ "--disable-features=Translate,PasswordManager,PasswordManagerEnabled,PasswordManagerOnboarding,AutofillServerCommunication,CredentialManagerOnboarding", "--disable-sync",
98
+ // Disable password manager via experimental options
99
+ "--enable-features=DisablePasswordManager");
88
100
  // --app was passed, add app mode argument
89
101
  if (cliOptions.app) {
90
102
  launchOptions.args = launchOptions.args || [];
@@ -93,9 +105,9 @@ export function configFromCLIOptions(cliOptions) {
93
105
  // --window-position was passed, add window position argument
94
106
  if (cliOptions.windowPosition) {
95
107
  try {
96
- const [x, y] = cliOptions.windowPosition.split(',').map(n => +n);
108
+ const [x, y] = cliOptions.windowPosition.split(",").map((n) => +n);
97
109
  if (isNaN(x) || isNaN(y))
98
- throw new Error('bad values');
110
+ throw new Error("bad values");
99
111
  launchOptions.args = launchOptions.args || [];
100
112
  launchOptions.args.push(`--window-position=${x},${y}`);
101
113
  }
@@ -106,9 +118,9 @@ export function configFromCLIOptions(cliOptions) {
106
118
  // --window-size was passed, add window size argument
107
119
  if (cliOptions.windowSize) {
108
120
  try {
109
- const [width, height] = cliOptions.windowSize.split(',').map(n => +n);
121
+ const [width, height] = cliOptions.windowSize.split(",").map((n) => +n);
110
122
  if (isNaN(width) || isNaN(height))
111
- throw new Error('bad values');
123
+ throw new Error("bad values");
112
124
  launchOptions.args = launchOptions.args || [];
113
125
  launchOptions.args.push(`--window-size=${width},${height}`);
114
126
  }
@@ -118,24 +130,26 @@ export function configFromCLIOptions(cliOptions) {
118
130
  }
119
131
  if (cliOptions.proxyServer) {
120
132
  launchOptions.proxy = {
121
- server: cliOptions.proxyServer
133
+ server: cliOptions.proxyServer,
122
134
  };
123
135
  if (cliOptions.proxyBypass)
124
136
  launchOptions.proxy.bypass = cliOptions.proxyBypass;
125
137
  }
126
138
  if (cliOptions.device && cliOptions.cdpEndpoint)
127
- throw new Error('Device emulation is not supported with cdpEndpoint.');
139
+ throw new Error("Device emulation is not supported with cdpEndpoint.");
128
140
  // Context options
129
- const contextOptions = cliOptions.device ? devices[cliOptions.device] : {};
141
+ const contextOptions = cliOptions.device
142
+ ? devices[cliOptions.device]
143
+ : {};
130
144
  if (cliOptions.storageState)
131
145
  contextOptions.storageState = cliOptions.storageState;
132
146
  if (cliOptions.userAgent)
133
147
  contextOptions.userAgent = cliOptions.userAgent;
134
148
  if (cliOptions.viewportSize) {
135
149
  try {
136
- const [width, height] = cliOptions.viewportSize.split(',').map(n => +n);
150
+ const [width, height] = cliOptions.viewportSize.split(",").map((n) => +n);
137
151
  if (isNaN(width) || isNaN(height))
138
- throw new Error('bad values');
152
+ throw new Error("bad values");
139
153
  contextOptions.viewport = { width, height };
140
154
  }
141
155
  catch (e) {
@@ -145,7 +159,7 @@ export function configFromCLIOptions(cliOptions) {
145
159
  if (cliOptions.ignoreHttpsErrors)
146
160
  contextOptions.ignoreHTTPSErrors = true;
147
161
  if (cliOptions.blockServiceWorkers)
148
- contextOptions.serviceWorkers = 'block';
162
+ contextOptions.serviceWorkers = "block";
149
163
  const result = {
150
164
  browser: {
151
165
  browserName,
@@ -190,8 +204,8 @@ function configFromEnv() {
190
204
  options.ignoreHttpsErrors = envToBoolean(process.env.PLAYWRIGHT_MCP_IGNORE_HTTPS_ERRORS);
191
205
  options.initScript = envToString(process.env.PLAYWRIGHT_MCP_INIT_SCRIPT);
192
206
  options.isolated = envToBoolean(process.env.PLAYWRIGHT_MCP_ISOLATED);
193
- if (process.env.PLAYWRIGHT_MCP_IMAGE_RESPONSES === 'omit')
194
- options.imageResponses = 'omit';
207
+ if (process.env.PLAYWRIGHT_MCP_IMAGE_RESPONSES === "omit")
208
+ options.imageResponses = "omit";
195
209
  options.sandbox = envToBoolean(process.env.PLAYWRIGHT_MCP_SANDBOX);
196
210
  options.outputDir = envToString(process.env.PLAYWRIGHT_MCP_OUTPUT_DIR);
197
211
  options.port = envToNumber(process.env.PLAYWRIGHT_MCP_PORT);
@@ -210,16 +224,16 @@ async function loadConfig(configFile) {
210
224
  if (!configFile)
211
225
  return {};
212
226
  try {
213
- return JSON.parse(await fs.promises.readFile(configFile, 'utf8'));
227
+ return JSON.parse(await fs.promises.readFile(configFile, "utf8"));
214
228
  }
215
229
  catch (error) {
216
230
  throw new Error(`Failed to load config file: ${configFile}, ${error}`);
217
231
  }
218
232
  }
219
233
  export async function outputFile(config, rootPath, name) {
220
- const outputDir = config.outputDir
221
- ?? (rootPath ? path.join(rootPath, '.playwright-mcp') : undefined)
222
- ?? path.join(os.tmpdir(), 'playwright-mcp-output', sanitizeForFilePath(new Date().toISOString()));
234
+ const outputDir = config.outputDir ??
235
+ (rootPath ? path.join(rootPath, ".playwright-mcp") : undefined) ??
236
+ path.join(os.tmpdir(), "playwright-mcp-output", sanitizeForFilePath(new Date().toISOString()));
223
237
  await fs.promises.mkdir(outputDir, { recursive: true });
224
238
  const fileName = sanitizeForFilePath(name);
225
239
  return path.join(outputDir, fileName);
@@ -231,7 +245,7 @@ function mergeConfig(base, overrides) {
231
245
  const browser = {
232
246
  ...pickDefined(base.browser),
233
247
  ...pickDefined(overrides.browser),
234
- browserName: overrides.browser?.browserName ?? base.browser?.browserName ?? 'chromium',
248
+ browserName: overrides.browser?.browserName ?? base.browser?.browserName ?? "chromium",
235
249
  isolated: overrides.browser?.isolated ?? base.browser?.isolated ?? false,
236
250
  launchOptions: {
237
251
  ...pickDefined(base.browser?.launchOptions),
@@ -243,7 +257,7 @@ function mergeConfig(base, overrides) {
243
257
  ...pickDefined(overrides.browser?.contextOptions),
244
258
  },
245
259
  };
246
- if (browser.browserName !== 'chromium' && browser.launchOptions)
260
+ if (browser.browserName !== "chromium" && browser.launchOptions)
247
261
  delete browser.launchOptions.channel;
248
262
  return {
249
263
  ...pickDefined(base),
@@ -262,12 +276,12 @@ function mergeConfig(base, overrides) {
262
276
  export function semicolonSeparatedList(value) {
263
277
  if (!value)
264
278
  return undefined;
265
- return value.split(';').map(v => v.trim());
279
+ return value.split(";").map((v) => v.trim());
266
280
  }
267
281
  export function commaSeparatedList(value) {
268
282
  if (!value)
269
283
  return undefined;
270
- return value.split(',').map(v => v.trim());
284
+ return value.split(",").map((v) => v.trim());
271
285
  }
272
286
  function envToNumber(value) {
273
287
  if (!value)
@@ -275,9 +289,9 @@ function envToNumber(value) {
275
289
  return +value;
276
290
  }
277
291
  function envToBoolean(value) {
278
- if (value === 'true' || value === '1')
292
+ if (value === "true" || value === "1")
279
293
  return true;
280
- if (value === 'false' || value === '0')
294
+ if (value === "false" || value === "0")
281
295
  return false;
282
296
  return undefined;
283
297
  }
package/lib/context.js CHANGED
@@ -13,24 +13,24 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
- import debug from 'debug';
17
- import { logUnhandledError } from './utils/log.js';
18
- import { Tab } from './tab.js';
19
- import { applyHooksToTools, hookRegistryMap } from './hooks/core.js';
20
- import { buildHookRegistry } from './hooks/registry.js';
21
- import { setEventStore, createEventStore } from './hooks/events.js';
22
- import { setupNetworkTracking } from './hooks/networkSetup.js';
23
- import { outputFile } from './config.js';
24
- import * as codegen from './utils/codegen.js';
25
- import { shouldBlockRequest } from './utils/adBlockFilter.js';
26
- const testDebug = debug('pw:mcp:test');
16
+ import debug from "debug";
17
+ import { outputFile } from "./config.js";
18
+ import { applyHooksToTools, hookRegistryMap } from "./hooks/core.js";
19
+ import { createEventStore, setEventStore } from "./hooks/events.js";
20
+ import { setupNetworkTracking } from "./hooks/networkSetup.js";
21
+ import { buildHookRegistry } from "./hooks/registry.js";
22
+ import { Tab } from "./tab.js";
23
+ import { shouldBlockRequest } from "./utils/adBlockFilter.js";
24
+ import * as codegen from "./utils/codegen.js";
25
+ import { logUnhandledError } from "./utils/log.js";
26
+ const testDebug = debug("pw:mcp:test");
27
27
  const protocolPattern = /^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//;
28
28
  const defaultPortForProtocol = (protocol) => {
29
- if (protocol === 'http:')
30
- return '80';
31
- if (protocol === 'https:')
32
- return '443';
33
- return '';
29
+ if (protocol === "http:")
30
+ return "80";
31
+ if (protocol === "https:")
32
+ return "443";
33
+ return "";
34
34
  };
35
35
  const matchesOriginHost = (requestUrl, candidate) => {
36
36
  const normalized = candidate.trim();
@@ -47,15 +47,16 @@ const matchesOriginHost = (requestUrl, candidate) => {
47
47
  })();
48
48
  if (!parsed)
49
49
  return false;
50
- const candidateHost = parsed.hostname.toLowerCase().replace(/\.+$/, '');
50
+ const candidateHost = parsed.hostname.toLowerCase().replace(/\.+$/, "");
51
51
  if (!candidateHost)
52
52
  return false;
53
53
  const requestHost = requestUrl.hostname.toLowerCase();
54
- if (requestHost !== candidateHost && !requestHost.endsWith(`.${candidateHost}`))
54
+ if (requestHost !== candidateHost &&
55
+ !requestHost.endsWith(`.${candidateHost}`))
55
56
  return false;
56
57
  if (hasProtocol && parsed.protocol !== requestUrl.protocol)
57
58
  return false;
58
- if (parsed.port === '')
59
+ if (parsed.port === "")
59
60
  return true;
60
61
  const candidatePort = parsed.port || defaultPortForProtocol(parsed.protocol);
61
62
  if (!candidatePort)
@@ -88,11 +89,11 @@ export class Context {
88
89
  this.options = options;
89
90
  this._browserContextFactory = options.browserContextFactory;
90
91
  this._clientInfo = options.clientInfo;
91
- testDebug('create context');
92
+ testDebug("create context");
92
93
  Context._allContexts.add(this);
93
94
  }
94
95
  static async disposeAll() {
95
- await Promise.all([...Context._allContexts].map(context => context.dispose()));
96
+ await Promise.all([...Context._allContexts].map((context) => context.dispose()));
96
97
  }
97
98
  tabs() {
98
99
  return this._tabs;
@@ -108,7 +109,7 @@ export class Context {
108
109
  async newTab() {
109
110
  const { browserContext } = await this._ensureBrowserContext();
110
111
  const page = await browserContext.newPage();
111
- this._currentTab = this._tabs.find(t => t.page === page);
112
+ this._currentTab = this._tabs.find((t) => t.page === page);
112
113
  return this._currentTab;
113
114
  }
114
115
  async selectTab(index) {
@@ -137,7 +138,7 @@ export class Context {
137
138
  return outputFile(this.config, this._clientInfo.rootPath, name);
138
139
  }
139
140
  _onPageCreated(page) {
140
- const tab = new Tab(this, page, tab => this._onPageClosed(tab));
141
+ const tab = new Tab(this, page, (tab) => this._onPageClosed(tab));
141
142
  this._tabs.push(tab);
142
143
  if (!this._currentTab)
143
144
  this._currentTab = tab;
@@ -156,7 +157,8 @@ export class Context {
156
157
  }
157
158
  async closeBrowserContext() {
158
159
  if (!this._closeBrowserContextPromise)
159
- this._closeBrowserContextPromise = this._closeBrowserContextImpl().catch(logUnhandledError);
160
+ this._closeBrowserContextPromise =
161
+ this._closeBrowserContextImpl().catch(logUnhandledError);
160
162
  await this._closeBrowserContextPromise;
161
163
  this._closeBrowserContextPromise = undefined;
162
164
  }
@@ -169,7 +171,7 @@ export class Context {
169
171
  async _closeBrowserContextImpl() {
170
172
  if (!this._browserContextPromise)
171
173
  return;
172
- testDebug('close context');
174
+ testDebug("close context");
173
175
  const promise = this._browserContextPromise;
174
176
  this._browserContextPromise = undefined;
175
177
  await promise.then(async ({ browserContext, close }) => {
@@ -179,12 +181,12 @@ export class Context {
179
181
  });
180
182
  }
181
183
  async dispose() {
182
- this._abortController.abort('MCP context disposed');
184
+ this._abortController.abort("MCP context disposed");
183
185
  await this.closeBrowserContext();
184
186
  Context._allContexts.delete(this);
185
187
  }
186
188
  async _setupRequestInterception(context) {
187
- await context.route('**', route => {
189
+ await context.route("**", (route) => {
188
190
  const request = route.request();
189
191
  const url = request.url();
190
192
  const resourceType = request.resourceType();
@@ -198,20 +200,20 @@ export class Context {
198
200
  }
199
201
  const domain = urlObj.hostname;
200
202
  if (shouldBlockRequest(url, domain, resourceType)) {
201
- void route.abort('blockedbyclient');
203
+ void route.abort("blockedbyclient");
202
204
  return;
203
205
  }
204
206
  if (this.config.network?.allowedOrigins?.length) {
205
- const isAllowed = this.config.network.allowedOrigins.some(allowed => matchesOriginHost(urlObj, allowed));
207
+ const isAllowed = this.config.network.allowedOrigins.some((allowed) => matchesOriginHost(urlObj, allowed));
206
208
  if (!isAllowed) {
207
- void route.abort('blockedbyclient');
209
+ void route.abort("blockedbyclient");
208
210
  return;
209
211
  }
210
212
  }
211
213
  if (this.config.network?.blockedOrigins?.length) {
212
- const isBlocked = this.config.network.blockedOrigins.some(blocked => matchesOriginHost(urlObj, blocked));
214
+ const isBlocked = this.config.network.blockedOrigins.some((blocked) => matchesOriginHost(urlObj, blocked));
213
215
  if (isBlocked) {
214
- void route.abort('blockedbyclient');
216
+ void route.abort("blockedbyclient");
215
217
  return;
216
218
  }
217
219
  }
@@ -229,7 +231,7 @@ export class Context {
229
231
  }
230
232
  async _setupBrowserContext() {
231
233
  if (this._closeBrowserContextPromise)
232
- throw new Error('Another browser context is being closed.');
234
+ throw new Error("Another browser context is being closed.");
233
235
  // TODO: move to the browser context factory to make it based on isolation mode.
234
236
  const result = await this._browserContextFactory.createContext(this._clientInfo, this._abortController.signal);
235
237
  const { browserContext } = result;
@@ -238,10 +240,10 @@ export class Context {
238
240
  await InputRecorder.create(this, browserContext);
239
241
  for (const page of browserContext.pages())
240
242
  this._onPageCreated(page);
241
- browserContext.on('page', page => this._onPageCreated(page));
243
+ browserContext.on("page", (page) => this._onPageCreated(page));
242
244
  if (this.config.saveTrace) {
243
245
  await browserContext.tracing.start({
244
- name: 'trace',
246
+ name: "trace",
245
247
  screenshots: false,
246
248
  snapshots: true,
247
249
  sources: false,
@@ -273,8 +275,8 @@ export class InputRecorder {
273
275
  async _initialize() {
274
276
  const sessionLog = this._context.sessionLog;
275
277
  await this._browserContext._enableRecorder({
276
- mode: 'recording',
277
- recorderMode: 'api',
278
+ mode: "recording",
279
+ recorderMode: "api",
278
280
  }, {
279
281
  actionAdded: (page, data, code) => {
280
282
  if (this._context.isRunningTool())
@@ -293,11 +295,11 @@ export class InputRecorder {
293
295
  signalAdded: (page, data) => {
294
296
  if (this._context.isRunningTool())
295
297
  return;
296
- if (data.signal.name !== 'navigation')
298
+ if (data.signal.name !== "navigation")
297
299
  return;
298
300
  const tab = Tab.forPage(page);
299
301
  const navigateAction = {
300
- name: 'navigate',
302
+ name: "navigate",
301
303
  url: data.signal.url,
302
304
  signals: [],
303
305
  };