From 053f1f830d051ec415f4b00e645f5a1aff8554a1 Mon Sep 17 00:00:00 2001 From: Nikolay Vitkov <34244704+Lightning00Blade@users.noreply.github.com> Date: Tue, 21 Oct 2025 10:36:09 +0200 Subject: [PATCH 001/456] fix: allow evaluating in Frames (#443) Closes https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/439 --- src/tools/script.ts | 30 +++++++++++++++++++++--------- tests/tools/script.test.ts | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 9 deletions(-) diff --git a/src/tools/script.ts b/src/tools/script.ts index beaef94bf..128363bdc 100644 --- a/src/tools/script.ts +++ b/src/tools/script.ts @@ -3,7 +3,7 @@ * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ -import type {JSHandle} from 'puppeteer-core'; +import type {Frame, JSHandle, Page} from 'puppeteer-core'; import {zod} from '../third_party/modelcontextprotocol-sdk/index.js'; @@ -45,15 +45,29 @@ Example with arguments: \`(el) => { .describe(`An optional list of arguments to pass to the function.`), }, handler: async (request, response, context) => { - const page = context.getSelectedPage(); - const fn = await page.evaluateHandle(`(${request.params.function})`); - const args: Array> = [fn]; + const args: Array> = []; try { + const frames = new Set(); for (const el of request.params.args ?? []) { - args.push(await context.getElementByUid(el.uid)); + const handle = await context.getElementByUid(el.uid); + frames.add(handle.frame); + args.push(handle); } + let pageOrFrame: Page | Frame; + // We can't evaluate the element handle across frames + if (frames.size > 1) { + throw new Error( + "Elements from different frames can't be evaluated together.", + ); + } else { + pageOrFrame = [...frames.values()][0] ?? context.getSelectedPage(); + } + const fn = await pageOrFrame.evaluateHandle( + `(${request.params.function})`, + ); + args.unshift(fn); await context.waitForEventsAfterAction(async () => { - const result = await page.evaluate( + const result = await pageOrFrame.evaluate( async (fn, ...args) => { // @ts-expect-error no types. return JSON.stringify(await fn(...args)); @@ -66,9 +80,7 @@ Example with arguments: \`(el) => { response.appendResponseLine('```'); }); } finally { - Promise.allSettled(args.map(arg => arg.dispose())).catch(() => { - // Ignore errors - }); + void Promise.allSettled(args.map(arg => arg.dispose())); } }, }); diff --git a/tests/tools/script.test.ts b/tests/tools/script.test.ts index bad9a902b..a31cff1c4 100644 --- a/tests/tools/script.test.ts +++ b/tests/tools/script.test.ts @@ -7,9 +7,12 @@ import assert from 'node:assert'; import {describe, it} from 'node:test'; import {evaluateScript} from '../../src/tools/script.js'; +import {serverHooks} from '../server.js'; import {html, withBrowser} from '../utils.js'; describe('script', () => { + const server = serverHooks(); + describe('browser_evaluate_script', () => { it('evaluates', async () => { await withBrowser(async (response, context) => { @@ -152,5 +155,36 @@ describe('script', () => { assert.strictEqual(JSON.parse(lineEvaluation), true); }); }); + + it('work for elements inside iframes', async () => { + server.addHtmlRoute( + '/iframe', + html`
`, + ); + server.addRoute('/main', async (_req, res) => { + res.write(html``); + res.end(); + }); + + await withBrowser(async (response, context) => { + const page = context.getSelectedPage(); + await page.goto(server.getRoute('/main')); + await context.createTextSnapshot(); + await evaluateScript.handler( + { + params: { + function: String((element: Element) => { + return element.textContent; + }), + args: [{uid: '1_3'}], + }, + }, + response, + context, + ); + const lineEvaluation = response.responseLines.at(2)!; + assert.strictEqual(JSON.parse(lineEvaluation), 'I am iframe button'); + }); + }); }); }); From b443033000e46a992ea7fa071af0f9ec304b9ea7 Mon Sep 17 00:00:00 2001 From: zyzyzyryxy <31672205+zyzyzyryxy@users.noreply.github.com> Date: Tue, 21 Oct 2025 11:31:06 +0200 Subject: [PATCH 002/456] refactor: bundle puppeteer-core (#417) Continue speeding up boot time. --------- Co-authored-by: Piotr Paulski --- package.json | 1 - rollup.config.mjs | 50 ++++++++++++++++++------ src/DevToolsConnectionAdapter.ts | 4 +- src/McpContext.ts | 51 +++++++++++++++++++++---- src/McpResponse.ts | 6 ++- src/PageCollector.ts | 2 +- src/WaitForHelper.ts | 2 +- src/browser.ts | 4 +- src/formatters/networkFormatter.ts | 5 ++- src/third_party/puppeteer-core/index.ts | 9 +++++ src/tools/ToolDefinition.ts | 11 +++++- src/tools/console.ts | 3 +- src/tools/emulation.ts | 3 +- src/tools/input.ts | 3 +- src/tools/network.ts | 3 +- src/tools/performance.ts | 3 +- src/tools/screenshot.ts | 3 +- src/tools/script.ts | 6 ++- src/tools/snapshot.ts | 18 +-------- tests/utils.ts | 4 +- 20 files changed, 128 insertions(+), 63 deletions(-) create mode 100644 src/third_party/puppeteer-core/index.ts diff --git a/package.json b/package.json index 10e165f87..b0f02da38 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,6 @@ "dependencies": { "core-js": "3.46.0", "debug": "4.4.3", - "puppeteer-core": "^24.24.1", "yargs": "18.0.0" }, "devDependencies": { diff --git a/rollup.config.mjs b/rollup.config.mjs index b03ee2762..9ca5ef30b 100644 --- a/rollup.config.mjs +++ b/rollup.config.mjs @@ -29,11 +29,31 @@ import license from 'rollup-plugin-license'; const isProduction = process.env.NODE_ENV === 'production'; -/** @type {import('rollup').RollupOptions} */ -const sdk = { - input: './build/src/third_party/modelcontextprotocol-sdk/index.js', +const allowedLicenses = [ + 'MIT', + 'Apache 2.0', + 'Apache-2.0', + 'BSD-3-Clause', + 'BSD-2-Clause', + 'ISC', + '0BSD', +]; + +/** + * @param {string} wrapperIndexPath + * @param {import('rollup').OutputOptions} [extraOutputOptions={}] + * @param {string[]} [external=[]] + * @returns {import('rollup').RollupOptions} + */ +const bundleDependency = ( + wrapperIndexPath, + extraOutputOptions = {}, + external = [], +) => ({ + input: wrapperIndexPath, output: { - file: './build/src/third_party/modelcontextprotocol-sdk/index.js', + ...extraOutputOptions, + file: wrapperIndexPath, sourcemap: !isProduction, format: 'esm', }, @@ -48,18 +68,14 @@ const sdk = { thirdParty: { allow: { test: dependency => { - let allowed_licenses = ['MIT', 'Apache 2.0', 'BSD-2-Clause', 'ISC']; - return allowed_licenses.includes(dependency.license); + return allowedLicenses.includes(dependency.license); }, failOnUnlicensed: true, failOnViolation: true, }, output: { file: path.join( - 'build', - 'src', - 'third_party', - 'modelcontextprotocol-sdk', + path.dirname(wrapperIndexPath), 'THIRD_PARTY_NOTICES', ), template(dependencies) { @@ -90,6 +106,16 @@ const sdk = { json(), nodeResolve(), ], -}; + external, +}); -export default [sdk]; +export default [ + bundleDependency('./build/src/third_party/modelcontextprotocol-sdk/index.js'), + bundleDependency( + './build/src/third_party/puppeteer-core/index.js', + { + inlineDynamicImports: true, + }, + ['./bidi.js', '../bidi/bidi.js'], + ), +]; diff --git a/src/DevToolsConnectionAdapter.ts b/src/DevToolsConnectionAdapter.ts index 814dc7f60..ec07d1a72 100644 --- a/src/DevToolsConnectionAdapter.ts +++ b/src/DevToolsConnectionAdapter.ts @@ -4,10 +4,10 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {type ConnectionTransport} from 'puppeteer-core'; - import {Connection} from '../node_modules/chrome-devtools-frontend/front_end/core/protocol_client/InspectorBackend.js'; +import {type ConnectionTransport} from './third_party/puppeteer-core/index.js'; + /** * Allows a puppeteer {@link ConnectionTransport} to act like a DevTools {@link Connection}. */ diff --git a/src/McpContext.ts b/src/McpContext.ts index 29170568d..4f7641d50 100644 --- a/src/McpContext.ts +++ b/src/McpContext.ts @@ -8,6 +8,10 @@ import os from 'node:os'; import path from 'node:path'; import type {Debugger} from 'debug'; + +import type {ListenerMap} from './PageCollector.js'; +import {NetworkCollector, PageCollector} from './PageCollector.js'; +import {Locator} from './third_party/puppeteer-core/index.js'; import type { Browser, ConsoleMessage, @@ -17,10 +21,7 @@ import type { Page, SerializedAXNode, PredefinedNetworkConditions, -} from 'puppeteer-core'; - -import type {ListenerMap} from './PageCollector.js'; -import {NetworkCollector, PageCollector} from './PageCollector.js'; +} from './third_party/puppeteer-core/index.js'; import {listPages} from './tools/pages.js'; import {takeSnapshot} from './tools/snapshot.js'; import {CLOSE_PAGE_ERROR} from './tools/ToolDefinition.js'; @@ -91,9 +92,16 @@ export class McpContext implements Context { #nextSnapshotId = 1; #traceResults: TraceResult[] = []; - private constructor(browser: Browser, logger: Debugger) { + #locatorClass: typeof Locator; + + private constructor( + browser: Browser, + logger: Debugger, + locatorClass: typeof Locator, + ) { this.browser = browser; this.logger = logger; + this.#locatorClass = locatorClass; this.#networkCollector = new NetworkCollector(this.browser); @@ -122,8 +130,13 @@ export class McpContext implements Context { await this.#consoleCollector.init(); } - static async from(browser: Browser, logger: Debugger) { - const context = new McpContext(browser, logger); + static async from( + browser: Browser, + logger: Debugger, + /* Let tests use unbundled Locator class to avoid overly strict checks within puppeteer that fail when mixing bundled and unbundled class instances */ + locatorClass: typeof Locator = Locator, + ) { + const context = new McpContext(browser, logger, locatorClass); await context.#init(); return context; } @@ -428,4 +441,28 @@ export class McpContext implements Context { getNetworkRequestStableId(request: HTTPRequest): number { return this.#networkCollector.getIdForResource(request); } + + waitForTextOnPage({ + text, + timeout, + }: { + text: string; + timeout?: number | undefined; + }): Promise { + const page = this.getSelectedPage(); + const frames = page.frames(); + + const locator = this.#locatorClass.race( + frames.flatMap(frame => [ + frame.locator(`aria/${text}`), + frame.locator(`text/${text}`), + ]), + ); + + if (timeout) { + locator.setTimeout(timeout); + } + + return locator.wait(); + } } diff --git a/src/McpResponse.ts b/src/McpResponse.ts index 8a1b6dc94..31bbdcc5a 100644 --- a/src/McpResponse.ts +++ b/src/McpResponse.ts @@ -3,8 +3,6 @@ * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ -import type {ConsoleMessage, ResourceType} from 'puppeteer-core'; - import type {ConsoleMessageData} from './formatters/consoleFormatter.js'; import { formatConsoleEventShort, @@ -23,6 +21,10 @@ import type { ImageContent, TextContent, } from './third_party/modelcontextprotocol-sdk/index.js'; +import type { + ConsoleMessage, + ResourceType, +} from './third_party/puppeteer-core/index.js'; import {handleDialog} from './tools/pages.js'; import type {ImageContentData, Response} from './tools/ToolDefinition.js'; import {paginate} from './utils/pagination.js'; diff --git a/src/PageCollector.ts b/src/PageCollector.ts index b6b3d6798..44d1363f8 100644 --- a/src/PageCollector.ts +++ b/src/PageCollector.ts @@ -11,7 +11,7 @@ import { type HTTPRequest, type Page, type PageEvents, -} from 'puppeteer-core'; +} from './third_party/puppeteer-core/index.js'; export type ListenerMap = { [K in keyof EventMap]?: (event: EventMap[K]) => void; diff --git a/src/WaitForHelper.ts b/src/WaitForHelper.ts index 62cc83f03..a18f6448a 100644 --- a/src/WaitForHelper.ts +++ b/src/WaitForHelper.ts @@ -3,10 +3,10 @@ * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ -import type {Page, Protocol} from 'puppeteer-core'; import type {CdpPage} from 'puppeteer-core/internal/cdp/Page.js'; import {logger} from './logger.js'; +import type {Page, Protocol} from './third_party/puppeteer-core/index.js'; export class WaitForHelper { #abortController = new AbortController(); diff --git a/src/browser.ts b/src/browser.ts index d76d5a9f5..47afe5a93 100644 --- a/src/browser.ts +++ b/src/browser.ts @@ -13,8 +13,8 @@ import type { ChromeReleaseChannel, LaunchOptions, Target, -} from 'puppeteer-core'; -import puppeteer from 'puppeteer-core'; +} from './third_party/puppeteer-core/index.js'; +import {puppeteer} from './third_party/puppeteer-core/index.js'; let browser: Browser | undefined; diff --git a/src/formatters/networkFormatter.ts b/src/formatters/networkFormatter.ts index 572797339..b3466e8c1 100644 --- a/src/formatters/networkFormatter.ts +++ b/src/formatters/networkFormatter.ts @@ -6,7 +6,10 @@ import {isUtf8} from 'node:buffer'; -import type {HTTPRequest, HTTPResponse} from 'puppeteer-core'; +import type { + HTTPRequest, + HTTPResponse, +} from '../third_party/puppeteer-core/index.js'; const BODY_CONTEXT_SIZE_LIMIT = 10000; diff --git a/src/third_party/puppeteer-core/index.ts b/src/third_party/puppeteer-core/index.ts new file mode 100644 index 000000000..92ae88ae7 --- /dev/null +++ b/src/third_party/puppeteer-core/index.ts @@ -0,0 +1,9 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +export {Locator, PredefinedNetworkConditions} from 'puppeteer-core'; +export {default as puppeteer} from 'puppeteer-core'; +export type * from 'puppeteer-core'; diff --git a/src/tools/ToolDefinition.ts b/src/tools/ToolDefinition.ts index e90264a7e..19415dd27 100644 --- a/src/tools/ToolDefinition.ts +++ b/src/tools/ToolDefinition.ts @@ -4,10 +4,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type {Dialog, ElementHandle, Page} from 'puppeteer-core'; - import type {TextSnapshotNode} from '../McpContext.js'; import {zod} from '../third_party/modelcontextprotocol-sdk/index.js'; +import type { + Dialog, + ElementHandle, + Page, +} from '../third_party/puppeteer-core/index.js'; import type {TraceResult} from '../trace-processing/parse.js'; import type {PaginationOptions} from '../utils/types.js'; @@ -93,6 +96,10 @@ export type Context = Readonly<{ filename: string, ): Promise<{filename: string}>; waitForEventsAfterAction(action: () => Promise): Promise; + waitForTextOnPage(params: { + text: string; + timeout?: number | undefined; + }): Promise; }>; export function defineTool( diff --git a/src/tools/console.ts b/src/tools/console.ts index 3f8967eb9..8be953c89 100644 --- a/src/tools/console.ts +++ b/src/tools/console.ts @@ -4,9 +4,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type {ConsoleMessageType} from 'puppeteer-core'; - import {zod} from '../third_party/modelcontextprotocol-sdk/index.js'; +import type {ConsoleMessageType} from '../third_party/puppeteer-core/index.js'; import {ToolCategories} from './categories.js'; import {defineTool} from './ToolDefinition.js'; diff --git a/src/tools/emulation.ts b/src/tools/emulation.ts index d65ac5e89..9287c00d6 100644 --- a/src/tools/emulation.ts +++ b/src/tools/emulation.ts @@ -4,9 +4,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {PredefinedNetworkConditions} from 'puppeteer-core'; - import {zod} from '../third_party/modelcontextprotocol-sdk/index.js'; +import {PredefinedNetworkConditions} from '../third_party/puppeteer-core/index.js'; import {ToolCategories} from './categories.js'; import {defineTool} from './ToolDefinition.js'; diff --git a/src/tools/input.ts b/src/tools/input.ts index ffb1cbb7c..973eb829f 100644 --- a/src/tools/input.ts +++ b/src/tools/input.ts @@ -4,10 +4,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type {ElementHandle} from 'puppeteer-core'; - import type {McpContext, TextSnapshotNode} from '../McpContext.js'; import {zod} from '../third_party/modelcontextprotocol-sdk/index.js'; +import type {ElementHandle} from '../third_party/puppeteer-core/index.js'; import {ToolCategories} from './categories.js'; import {defineTool} from './ToolDefinition.js'; diff --git a/src/tools/network.ts b/src/tools/network.ts index 0dbc7f7e7..d47fa9960 100644 --- a/src/tools/network.ts +++ b/src/tools/network.ts @@ -4,9 +4,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type {ResourceType} from 'puppeteer-core'; - import {zod} from '../third_party/modelcontextprotocol-sdk/index.js'; +import type {ResourceType} from '../third_party/puppeteer-core/index.js'; import {ToolCategories} from './categories.js'; import {defineTool} from './ToolDefinition.js'; diff --git a/src/tools/performance.ts b/src/tools/performance.ts index 6c172fbb9..a76f30d5a 100644 --- a/src/tools/performance.ts +++ b/src/tools/performance.ts @@ -4,10 +4,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type {Page} from 'puppeteer-core'; - import {logger} from '../logger.js'; import {zod} from '../third_party/modelcontextprotocol-sdk/index.js'; +import type {Page} from '../third_party/puppeteer-core/index.js'; import type {InsightName} from '../trace-processing/parse.js'; import { getInsightOutput, diff --git a/src/tools/screenshot.ts b/src/tools/screenshot.ts index 991fbd2b9..e053186f5 100644 --- a/src/tools/screenshot.ts +++ b/src/tools/screenshot.ts @@ -4,9 +4,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type {ElementHandle, Page} from 'puppeteer-core'; - import {zod} from '../third_party/modelcontextprotocol-sdk/index.js'; +import type {ElementHandle, Page} from '../third_party/puppeteer-core/index.js'; import {ToolCategories} from './categories.js'; import {defineTool} from './ToolDefinition.js'; diff --git a/src/tools/script.ts b/src/tools/script.ts index 128363bdc..6f08af08a 100644 --- a/src/tools/script.ts +++ b/src/tools/script.ts @@ -3,9 +3,13 @@ * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ -import type {Frame, JSHandle, Page} from 'puppeteer-core'; import {zod} from '../third_party/modelcontextprotocol-sdk/index.js'; +import type { + Frame, + JSHandle, + Page, +} from '../third_party/puppeteer-core/index.js'; import {ToolCategories} from './categories.js'; import {defineTool} from './ToolDefinition.js'; diff --git a/src/tools/snapshot.ts b/src/tools/snapshot.ts index fb774f78f..0cf548b41 100644 --- a/src/tools/snapshot.ts +++ b/src/tools/snapshot.ts @@ -4,8 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {Locator} from 'puppeteer-core'; - import {zod} from '../third_party/modelcontextprotocol-sdk/index.js'; import {ToolCategories} from './categories.js'; @@ -44,21 +42,7 @@ export const waitFor = defineTool({ ...timeoutSchema, }, handler: async (request, response, context) => { - const page = context.getSelectedPage(); - const frames = page.frames(); - - const locator = Locator.race( - frames.flatMap(frame => [ - frame.locator(`aria/${request.params.text}`), - frame.locator(`text/${request.params.text}`), - ]), - ); - - if (request.params.timeout) { - locator.setTimeout(request.params.timeout); - } - - await locator.wait(); + await context.waitForTextOnPage(request.params); response.appendResponseLine( `Element with text "${request.params.text}" found.`, diff --git a/tests/utils.ts b/tests/utils.ts index 6c8744fb0..7adaaf533 100644 --- a/tests/utils.ts +++ b/tests/utils.ts @@ -5,7 +5,7 @@ */ import logger from 'debug'; import type {Browser} from 'puppeteer'; -import puppeteer from 'puppeteer'; +import puppeteer, {Locator} from 'puppeteer'; import type {Frame, HTTPRequest, HTTPResponse} from 'puppeteer-core'; import {McpContext} from '../src/McpContext.js'; @@ -36,7 +36,7 @@ export async function withBrowser( }), ); const response = new McpResponse(); - const context = await McpContext.from(browser, logger('test')); + const context = await McpContext.from(browser, logger('test'), Locator); await cb(response, context); } From 7d47d6b2f40bf08def29de3ca37b1a4a28ce6777 Mon Sep 17 00:00:00 2001 From: Alex Rudenko Date: Tue, 21 Oct 2025 14:16:28 +0200 Subject: [PATCH 003/456] fix: indicate when request and response bodies are not available (#446) --- src/formatters/networkFormatter.ts | 6 ++---- tests/formatters/networkFormatter.test.ts | 8 ++++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/formatters/networkFormatter.ts b/src/formatters/networkFormatter.ts index b3466e8c1..0d8189bab 100644 --- a/src/formatters/networkFormatter.ts +++ b/src/formatters/networkFormatter.ts @@ -68,8 +68,7 @@ export async function getFormattedResponseBody( return ``; } catch { - // buffer() call might fail with CDP exception, in this case we don't print anything in the context - return; + return ``; } } @@ -91,8 +90,7 @@ export async function getFormattedRequestBody( return `${getSizeLimitedString(fetchData, sizeLimit)}`; } } catch { - // fetchPostData() call might fail with CDP exception, in this case we don't print anything in the context - return; + return ``; } } diff --git a/tests/formatters/networkFormatter.test.ts b/tests/formatters/networkFormatter.test.ts index e9608f507..e22b01406 100644 --- a/tests/formatters/networkFormatter.test.ts +++ b/tests/formatters/networkFormatter.test.ts @@ -115,7 +115,7 @@ describe('networkFormatter', () => { assert.strictEqual(result, 'test'); }); - it('shows empty string when no postData available', async () => { + it('shows not available when no postData available', async () => { const request = getMockRequest({ hasPostData: false, }); @@ -164,7 +164,7 @@ describe('networkFormatter', () => { assert.strictEqual(result, 'some text that is lo... '); }); - it('shows nothing on exception', async () => { + it('shows not available on exception', async () => { const request = getMockRequest({ hasPostData: true, postData: undefined, @@ -173,7 +173,7 @@ describe('networkFormatter', () => { const result = await getFormattedRequestBody(request, 200); - assert.strictEqual(result, undefined); + assert.strictEqual(result, ''); }); }); @@ -232,7 +232,7 @@ describe('networkFormatter', () => { const result = await getFormattedResponseBody(response, 200); - assert.strictEqual(result, undefined); + assert.strictEqual(result, ''); }); }); }); From 165cf9c70b7f91dc116558547a870281f29da710 Mon Sep 17 00:00:00 2001 From: Nikolay Vitkov <34244704+Lightning00Blade@users.noreply.github.com> Date: Tue, 21 Oct 2025 14:25:08 +0200 Subject: [PATCH 004/456] feat: expose previous navigations to the MCP (#419) We should consider changing the order of element shown in the from the collectors to be show newest first rather then the current oldest first approach. Closes https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/88 --- docs/tool-reference.md | 1 + src/McpContext.ts | 21 ++++++- src/McpResponse.ts | 7 ++- src/PageCollector.ts | 26 +++++++-- src/tools/ToolDefinition.ts | 1 + src/tools/network.ts | 6 ++ tests/PageCollector.test.ts | 36 ++++++++++++ tests/tools/network.test.js.snapshot | 36 ++++++++++++ tests/tools/network.test.ts | 82 +++++++++++++++++++++++++++- tests/tools/snapshot.test.ts | 4 +- tests/utils.ts | 23 ++++++++ 11 files changed, 229 insertions(+), 14 deletions(-) create mode 100644 tests/tools/network.test.js.snapshot diff --git a/docs/tool-reference.md b/docs/tool-reference.md index 68c1f9b39..d7cbc2f45 100644 --- a/docs/tool-reference.md +++ b/docs/tool-reference.md @@ -269,6 +269,7 @@ **Parameters:** +- **includePreviousNavigations** (boolean) _(optional)_: Whether to include requests from previous navigations. - **pageIdx** (integer) _(optional)_: Page number to return (0-based). When omitted, returns the first page. - **pageSize** (integer) _(optional)_: Maximum number of requests to return. When omitted, returns all requests. - **resourceTypes** (array) _(optional)_: Filter requests to only return requests of the specified resource types. When omitted or empty, returns all requests. diff --git a/src/McpContext.ts b/src/McpContext.ts index 4f7641d50..dd9186a6e 100644 --- a/src/McpContext.ts +++ b/src/McpContext.ts @@ -141,9 +141,9 @@ export class McpContext implements Context { return context; } - getNetworkRequests(): HTTPRequest[] { + getNetworkRequests(includePreviousNavigations?: boolean): HTTPRequest[] { const page = this.getSelectedPage(); - return this.#networkCollector.getData(page); + return this.#networkCollector.getData(page, includePreviousNavigations); } getConsoleData(): Array { @@ -465,4 +465,21 @@ export class McpContext implements Context { return locator.wait(); } + + /** + * We need to ignore favicon request as they make our test flaky + */ + async setUpNetworkCollectorForTesting() { + this.#networkCollector = new NetworkCollector(this.browser, collect => { + return { + request: req => { + if (req.url().includes('favicon.ico')) { + return; + } + collect(req); + }, + } as ListenerMap; + }); + await this.#networkCollector.init(); + } } diff --git a/src/McpResponse.ts b/src/McpResponse.ts index 31bbdcc5a..03a814658 100644 --- a/src/McpResponse.ts +++ b/src/McpResponse.ts @@ -42,6 +42,7 @@ export class McpResponse implements Response { include: boolean; pagination?: PaginationOptions; resourceTypes?: ResourceType[]; + includePreviousNavigations?: boolean; }; #consoleDataOptions?: { include: boolean; @@ -62,6 +63,7 @@ export class McpResponse implements Response { value: boolean, options?: PaginationOptions & { resourceTypes?: ResourceType[]; + includePreviousNavigations?: boolean; }, ): void { if (!value) { @@ -79,6 +81,7 @@ export class McpResponse implements Response { } : undefined, resourceTypes: options?.resourceTypes, + includePreviousNavigations: options?.includePreviousNavigations, }; } @@ -346,7 +349,9 @@ Call ${handleDialog.name} to handle it before continuing.`); response.push(...this.#formatConsoleData(data.consoleData)); if (this.#networkRequestsOptions?.include) { - let requests = context.getNetworkRequests(); + let requests = context.getNetworkRequests( + this.#networkRequestsOptions?.includePreviousNavigations, + ); // Apply resource type filtering if specified if (this.#networkRequestsOptions.resourceTypes?.length) { diff --git a/src/PageCollector.ts b/src/PageCollector.ts index 44d1363f8..39168dc8b 100644 --- a/src/PageCollector.ts +++ b/src/PageCollector.ts @@ -133,12 +133,21 @@ export class PageCollector { this.storage.delete(page); } - getData(page: Page): T[] { + getData(page: Page, includePreviousNavigations?: boolean): T[] { const navigations = this.storage.get(page); if (!navigations) { return []; } - return navigations[0]; + + if (!includePreviousNavigations) { + return navigations[0]; + } + + const data: T[] = []; + for (let index = this.#maxNavigationSaved; index >= 0; index--) { + data.push(...navigations[index]); + } + return data; } getIdForResource(resource: WithSymbolId): number { @@ -164,14 +173,19 @@ export class PageCollector { } export class NetworkCollector extends PageCollector { - constructor(browser: Browser) { - super(browser, collect => { + constructor( + browser: Browser, + listeners: ( + collector: (item: HTTPRequest) => void, + ) => ListenerMap = collect => { return { request: req => { collect(req); }, } as ListenerMap; - }); + }, + ) { + super(browser, listeners); } override splitAfterNavigation(page: Page) { const navigations = this.storage.get(page) ?? []; @@ -190,7 +204,7 @@ export class NetworkCollector extends PageCollector { // Keep all requests since the last navigation request including that // navigation request itself. // Keep the reference - if (lastRequestIdx) { + if (lastRequestIdx !== -1) { const fromCurrentNavigation = requests.splice(lastRequestIdx); navigations.unshift(fromCurrentNavigation); } else { diff --git a/src/tools/ToolDefinition.ts b/src/tools/ToolDefinition.ts index 19415dd27..966cf77b6 100644 --- a/src/tools/ToolDefinition.ts +++ b/src/tools/ToolDefinition.ts @@ -53,6 +53,7 @@ export interface Response { value: boolean, options?: PaginationOptions & { resourceTypes?: string[]; + includePreviousNavigations?: boolean; }, ): void; setIncludeConsoleData( diff --git a/src/tools/network.ts b/src/tools/network.ts index d47fa9960..d684d05b4 100644 --- a/src/tools/network.ts +++ b/src/tools/network.ts @@ -62,12 +62,18 @@ export const listNetworkRequests = defineTool({ .describe( 'Filter requests to only return requests of the specified resource types. When omitted or empty, returns all requests.', ), + includePreviousNavigations: zod + .boolean() + .default(false) + .optional() + .describe('Whether to include requests from previous navigations.'), }, handler: async (request, response) => { response.setIncludeNetworkRequests(true, { pageSize: request.params.pageSize, pageIdx: request.params.pageIdx, resourceTypes: request.params.resourceTypes, + includePreviousNavigations: request.params.includePreviousNavigations, }); }, }); diff --git a/tests/PageCollector.test.ts b/tests/PageCollector.test.ts index a916fdc24..5da1d31a1 100644 --- a/tests/PageCollector.test.ts +++ b/tests/PageCollector.test.ts @@ -250,4 +250,40 @@ describe('NetworkCollector', () => { assert.equal(collector.getData(page)[0], navRequest); assert.equal(collector.getData(page)[1], request2); }); + + it('correctly picks up after multiple back to back navigations', async () => { + const browser = getMockBrowser(); + const page = (await browser.pages())[0]; + const mainFrame = page.mainFrame(); + const navRequest = getMockRequest({ + navigationRequest: true, + frame: page.mainFrame(), + }); + const navRequest2 = getMockRequest({ + navigationRequest: true, + frame: page.mainFrame(), + }); + const request = getMockRequest(); + + const collector = new NetworkCollector(browser); + await collector.init(); + page.emit('request', navRequest); + assert.equal(collector.getData(page)[0], navRequest); + + page.emit('framenavigated', mainFrame); + assert.equal(collector.getData(page).length, 1); + assert.equal(collector.getData(page)[0], navRequest); + + page.emit('request', navRequest2); + assert.equal(collector.getData(page).length, 2); + assert.equal(collector.getData(page)[0], navRequest); + assert.equal(collector.getData(page)[1], navRequest2); + + page.emit('framenavigated', mainFrame); + assert.equal(collector.getData(page).length, 1); + assert.equal(collector.getData(page)[0], navRequest2); + + page.emit('request', request); + assert.equal(collector.getData(page).length, 2); + }); }); diff --git a/tests/tools/network.test.js.snapshot b/tests/tools/network.test.js.snapshot new file mode 100644 index 000000000..0a74003c1 --- /dev/null +++ b/tests/tools/network.test.js.snapshot @@ -0,0 +1,36 @@ +exports[`network > network_get_request > should get request from previous navigations 1`] = ` +# get_request response +## Request http://localhost:/one +Status: [success - 200] +### Request Headers +- accept-language:en-US,en;q=0.9 +- upgrade-insecure-requests:1 +- user-agent: +- sec-ch-ua:"Chromium";v="", "Not?A_Brand";v="8" +- sec-ch-ua-mobile:?0 +- sec-ch-ua-platform:"" +### Response Headers +- connection:keep-alive +- content-length:239 +- content-type:text/html; charset=utf-8 +- date: +- keep-alive:timeout=5 +### Response Body + +`; + +exports[`network > network_list_requests > list requests form current navigations only 1`] = ` +# list_request response +## Network requests +Showing 1-1 of 1 (Page 1 of 1). +reqid=3 GET http://localhost:/three [success - 200] +`; + +exports[`network > network_list_requests > list requests from previous navigations 1`] = ` +# list_request response +## Network requests +Showing 1-3 of 3 (Page 1 of 1). +reqid=1 GET http://localhost:/one [success - 200] +reqid=2 GET http://localhost:/two [success - 200] +reqid=3 GET http://localhost:/three [success - 200] +`; diff --git a/tests/tools/network.test.ts b/tests/tools/network.test.ts index fa6439dd1..c5f666b91 100644 --- a/tests/tools/network.test.ts +++ b/tests/tools/network.test.ts @@ -10,9 +10,11 @@ import { getNetworkRequest, listNetworkRequests, } from '../../src/tools/network.js'; -import {withBrowser} from '../utils.js'; +import {serverHooks} from '../server.js'; +import {html, withBrowser, stabilizeResponseOutput} from '../utils.js'; describe('network', () => { + const server = serverHooks(); describe('network_list_requests', () => { it('list requests', async () => { await withBrowser(async (response, context) => { @@ -21,23 +23,72 @@ describe('network', () => { assert.strictEqual(response.networkRequestsPageIdx, undefined); }); }); + + it('list requests form current navigations only', async t => { + server.addHtmlRoute('/one', html`
First
`); + server.addHtmlRoute('/two', html`
Second
`); + server.addHtmlRoute('/three', html`
Third
`); + + await withBrowser(async (response, context) => { + await context.setUpNetworkCollectorForTesting(); + const page = context.getSelectedPage(); + await page.goto(server.getRoute('/one')); + await page.goto(server.getRoute('/two')); + await page.goto(server.getRoute('/three')); + await listNetworkRequests.handler( + { + params: {}, + }, + response, + context, + ); + const responseData = await response.handle('list_request', context); + t.assert.snapshot?.(stabilizeResponseOutput(responseData[0].text)); + }); + }); + + it('list requests from previous navigations', async t => { + server.addHtmlRoute('/one', html`
First
`); + server.addHtmlRoute('/two', html`
Second
`); + server.addHtmlRoute('/three', html`
Third
`); + + await withBrowser(async (response, context) => { + await context.setUpNetworkCollectorForTesting(); + const page = context.getSelectedPage(); + await page.goto(server.getRoute('/one')); + await page.goto(server.getRoute('/two')); + await page.goto(server.getRoute('/three')); + await listNetworkRequests.handler( + { + params: { + includePreviousNavigations: true, + }, + }, + response, + context, + ); + const responseData = await response.handle('list_request', context); + t.assert.snapshot?.(stabilizeResponseOutput(responseData[0].text)); + }); + }); }); describe('network_get_request', () => { it('attaches request', async () => { await withBrowser(async (response, context) => { - const page = await context.getSelectedPage(); + const page = context.getSelectedPage(); await page.goto('data:text/html,
Hello MCP
'); await getNetworkRequest.handler( {params: {reqid: 1}}, response, context, ); + assert.equal(response.attachedNetworkRequestId, 1); }); }); it('should not add the request list', async () => { await withBrowser(async (response, context) => { - const page = await context.getSelectedPage(); + const page = context.getSelectedPage(); await page.goto('data:text/html,
Hello MCP
'); await getNetworkRequest.handler( {params: {reqid: 1}}, @@ -47,5 +98,30 @@ describe('network', () => { assert(!response.includeNetworkRequests); }); }); + it('should get request from previous navigations', async t => { + server.addHtmlRoute('/one', html`
First
`); + server.addHtmlRoute('/two', html`
Second
`); + server.addHtmlRoute('/three', html`
Third
`); + + await withBrowser(async (response, context) => { + await context.setUpNetworkCollectorForTesting(); + const page = context.getSelectedPage(); + await page.goto(server.getRoute('/one')); + await page.goto(server.getRoute('/two')); + await page.goto(server.getRoute('/three')); + await getNetworkRequest.handler( + { + params: { + reqid: 1, + }, + }, + response, + context, + ); + const responseData = await response.handle('get_request', context); + + t.assert.snapshot?.(stabilizeResponseOutput(responseData[0].text)); + }); + }); }); }); diff --git a/tests/tools/snapshot.test.ts b/tests/tools/snapshot.test.ts index 31857ff5a..718ea998c 100644 --- a/tests/tools/snapshot.test.ts +++ b/tests/tools/snapshot.test.ts @@ -21,7 +21,7 @@ describe('snapshot', () => { describe('browser_wait_for', () => { it('should work', async () => { await withBrowser(async (response, context) => { - const page = await context.getSelectedPage(); + const page = context.getSelectedPage(); await page.setContent( html`
Hello
World
`, @@ -98,7 +98,7 @@ describe('snapshot', () => { it('should work with iframe content', async () => { await withBrowser(async (response, context) => { - const page = await context.getSelectedPage(); + const page = context.getSelectedPage(); await page.setContent( html`

Top level

diff --git a/tests/utils.ts b/tests/utils.ts index 7adaaf533..17e86ca24 100644 --- a/tests/utils.ts +++ b/tests/utils.ts @@ -130,3 +130,26 @@ export function html( `; } + +export function stabilizeResponseOutput(text: unknown) { + if (typeof text !== 'string') { + throw new Error('Input must be string'); + } + let output = text; + const dateRegEx = /.{3}, \d{2} .{3} \d{4} \d{2}:\d{2}:\d{2} [A-Z]{3}/g; + output = output.replaceAll(dateRegEx, ''); + + const localhostRegEx = /http:\/\/localhost:\d{5}\//g; + output = output.replaceAll(localhostRegEx, 'http://localhost:/'); + + const userAgentRegEx = /user-agent:.*\n/g; + output = output.replaceAll(userAgentRegEx, 'user-agent:\n'); + + const chUaRegEx = /sec-ch-ua:"Chromium";v="\d{3}"/g; + output = output.replaceAll(chUaRegEx, 'sec-ch-ua:"Chromium";v=""'); + + // sec-ch-ua-platform:"Linux" + const chUaPlatformRegEx = /sec-ch-ua-platform:"[a-zA-Z]*"/g; + output = output.replaceAll(chUaPlatformRegEx, 'sec-ch-ua-platform:""'); + return output; +} From 68fc890feab27973702b4469127fc336078c6ce2 Mon Sep 17 00:00:00 2001 From: Alex Rudenko Date: Tue, 21 Oct 2025 14:46:26 +0200 Subject: [PATCH 005/456] chore(deps): bump puppeteer (#447) --- package-lock.json | 118 +++++++++++++++++++++++++++++++++++++--------- package.json | 2 +- 2 files changed, 96 insertions(+), 24 deletions(-) diff --git a/package-lock.json b/package-lock.json index b24808746..6cc2e886e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,6 @@ "dependencies": { "core-js": "3.46.0", "debug": "4.4.3", - "puppeteer-core": "^24.24.1", "yargs": "18.0.0" }, "bin": { @@ -37,7 +36,7 @@ "eslint-plugin-import": "^2.32.0", "globals": "^16.4.0", "prettier": "^3.6.2", - "puppeteer": "24.25.0", + "puppeteer": "24.26.0", "rollup": "4.52.5", "rollup-plugin-cleanup": "^3.2.1", "rollup-plugin-license": "^3.6.0", @@ -461,6 +460,7 @@ "version": "2.10.12", "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.10.12.tgz", "integrity": "sha512-mP9iLFZwH+FapKJLeA7/fLqOlSUwYpMwjR1P5J23qd4e7qGJwecJccJqHYrjw33jmIZYV4dtiTHPD/J+1e7cEw==", + "dev": true, "license": "Apache-2.0", "dependencies": { "debug": "^4.4.3", @@ -482,6 +482,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -491,6 +492,7 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, "license": "ISC", "dependencies": { "string-width": "^4.2.0", @@ -505,12 +507,14 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, "license": "MIT" }, "node_modules/@puppeteer/browsers/node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -525,6 +529,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -537,6 +542,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -554,6 +560,7 @@ "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, "license": "MIT", "dependencies": { "cliui": "^8.0.1", @@ -572,6 +579,7 @@ "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, "license": "ISC", "engines": { "node": ">=12" @@ -1054,6 +1062,7 @@ "version": "0.23.0", "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", + "dev": true, "license": "MIT" }, "node_modules/@tybys/wasm-util": { @@ -1126,7 +1135,7 @@ "version": "24.8.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.8.1.tgz", "integrity": "sha512-alv65KGRadQVfVcG69MuB4IzdYVpRwMG/mq8KWOaoOdyY617P5ivaDiMCGOFDWD2sAn5Q0mR3mRtUOgm99hL9Q==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "undici-types": "~7.14.0" @@ -1177,6 +1186,7 @@ "version": "2.10.3", "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -1715,6 +1725,7 @@ "version": "7.1.4", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, "license": "MIT", "engines": { "node": ">= 14" @@ -1753,6 +1764,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -1907,6 +1919,7 @@ "version": "0.13.4", "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", + "dev": true, "license": "MIT", "dependencies": { "tslib": "^2.0.1" @@ -1945,6 +1958,7 @@ "version": "1.7.3", "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.7.3.tgz", "integrity": "sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q==", + "dev": true, "license": "Apache-2.0", "peerDependencies": { "react-native-b4a": "*" @@ -1966,6 +1980,7 @@ "version": "2.8.0", "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.0.tgz", "integrity": "sha512-AOhh6Bg5QmFIXdViHbMc2tLDsBIRxdkIaIddPslJF9Z5De3APBScuqGP2uThXnIpqFrgoxMNC6km7uXNIMLHXA==", + "dev": true, "license": "Apache-2.0", "peerDependencies": { "bare-abort-controller": "*" @@ -1980,6 +1995,7 @@ "version": "4.4.11", "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.4.11.tgz", "integrity": "sha512-Bejmm9zRMvMTRoHS+2adgmXw1ANZnCNx+B5dgZpGwlP1E3x6Yuxea8RToddHUbWtVV0iUMWqsgZr8+jcgUI2SA==", + "dev": true, "license": "Apache-2.0", "optional": true, "dependencies": { @@ -2005,6 +2021,7 @@ "version": "3.6.2", "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.6.2.tgz", "integrity": "sha512-T+V1+1srU2qYNBmJCXZkUY5vQ0B4FSlL3QDROnKQYOqeiQR8UbjNHlPa+TIbM4cuidiN9GaTaOZgSEgsvPbh5A==", + "dev": true, "license": "Apache-2.0", "optional": true, "engines": { @@ -2015,6 +2032,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.0.tgz", "integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==", + "dev": true, "license": "Apache-2.0", "optional": true, "dependencies": { @@ -2025,6 +2043,7 @@ "version": "2.7.0", "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.7.0.tgz", "integrity": "sha512-oyXQNicV1y8nc2aKffH+BUHFRXmx6VrPzlnaEvMhram0nPBrKcEdcyBg5r08D0i8VxngHFAiVyn1QKXpSG0B8A==", + "dev": true, "license": "Apache-2.0", "optional": true, "dependencies": { @@ -2044,9 +2063,10 @@ } }, "node_modules/bare-url": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.3.0.tgz", - "integrity": "sha512-c+RCqMSZbkz97Mw1LWR0gcOqwK82oyYKfLoHJ8k13ybi1+I80ffdDzUy0TdAburdrR/kI0/VuN8YgEnJqX+Nyw==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.3.1.tgz", + "integrity": "sha512-v2yl0TnaZTdEnelkKtXZGnotiV6qATBlnNuUMrHl6v9Lmmrh9mw9RYyImPU7/4RahumSwQS1k2oKXcRfXcbjJw==", + "dev": true, "license": "Apache-2.0", "optional": true, "dependencies": { @@ -2057,6 +2077,7 @@ "version": "5.0.5", "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", + "dev": true, "license": "MIT", "engines": { "node": ">=10.0.0" @@ -2110,6 +2131,7 @@ "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, "license": "MIT", "engines": { "node": "*" @@ -2210,9 +2232,10 @@ "license": "BSD-3-Clause" }, "node_modules/chromium-bidi": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-9.1.0.tgz", - "integrity": "sha512-rlUzQ4WzIAWdIbY/viPShhZU2n21CxDUgazXVbw4Hu1MwaeUSEksSeM6DqPgpRjCLXRk702AVRxJxoOz0dw4OA==", + "version": "10.5.1", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-10.5.1.tgz", + "integrity": "sha512-rlj6OyhKhVTnk4aENcUme3Jl9h+cq4oXu4AzBcvr8RMmT6BR4a3zSNT9dbIfXr9/BS6ibzRyDhowuw4n2GgzsQ==", + "dev": true, "license": "Apache-2.0", "dependencies": { "mitt": "^3.0.1", @@ -2240,6 +2263,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -2252,6 +2276,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, "license": "MIT" }, "node_modules/commenting": { @@ -2389,6 +2414,7 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", + "dev": true, "license": "MIT", "engines": { "node": ">= 14" @@ -2522,6 +2548,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", + "dev": true, "license": "MIT", "dependencies": { "ast-types": "^0.13.4", @@ -2546,6 +2573,7 @@ "version": "0.0.1508733", "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1508733.tgz", "integrity": "sha512-QJ1R5gtck6nDcdM+nlsaJXcelPEI7ZxSMw1ujHpO1c4+9l+Nue5qlebi9xO1Z2MGr92bFOQTW7/rrheh5hHxDg==", + "dev": true, "license": "BSD-3-Clause" }, "node_modules/diff": { @@ -2613,6 +2641,7 @@ "version": "1.4.5", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dev": true, "license": "MIT", "dependencies": { "once": "^1.4.0" @@ -2820,6 +2849,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "dev": true, "license": "BSD-2-Clause", "dependencies": { "esprima": "^4.0.1", @@ -3171,6 +3201,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, "license": "BSD-2-Clause", "bin": { "esparse": "bin/esparse.js", @@ -3210,6 +3241,7 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=4.0" @@ -3226,6 +3258,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" @@ -3245,6 +3278,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", + "dev": true, "license": "Apache-2.0", "dependencies": { "bare-events": "^2.7.0" @@ -3336,6 +3370,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, "license": "BSD-2-Clause", "dependencies": { "debug": "^4.1.1", @@ -3363,6 +3398,7 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "dev": true, "license": "MIT" }, "node_modules/fast-glob": { @@ -3423,6 +3459,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, "license": "MIT", "dependencies": { "pend": "~1.2.0" @@ -3694,6 +3731,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, "license": "MIT", "dependencies": { "pump": "^3.0.0" @@ -3740,6 +3778,7 @@ "version": "6.0.5", "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.5.tgz", "integrity": "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==", + "dev": true, "license": "MIT", "dependencies": { "basic-ftp": "^5.0.2", @@ -3928,6 +3967,7 @@ "version": "7.0.2", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, "license": "MIT", "dependencies": { "agent-base": "^7.1.0", @@ -3941,6 +3981,7 @@ "version": "7.0.6", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, "license": "MIT", "dependencies": { "agent-base": "^7.1.2", @@ -4026,6 +4067,7 @@ "version": "10.0.1", "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==", + "dev": true, "license": "MIT", "engines": { "node": ">= 12" @@ -4223,6 +4265,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -4648,6 +4691,7 @@ "version": "7.18.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, "license": "ISC", "engines": { "node": ">=12" @@ -4786,6 +4830,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "dev": true, "license": "MIT" }, "node_modules/moment": { @@ -4841,6 +4886,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4.0" @@ -4970,6 +5016,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, "license": "ISC", "dependencies": { "wrappy": "1" @@ -5047,6 +5094,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz", "integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==", + "dev": true, "license": "MIT", "dependencies": { "@tootallnate/quickjs-emscripten": "^0.23.0", @@ -5066,6 +5114,7 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", + "dev": true, "license": "MIT", "dependencies": { "degenerator": "^5.0.0", @@ -5172,6 +5221,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true, "license": "MIT" }, "node_modules/perf-regexes": { @@ -5254,6 +5304,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.4.0" @@ -5277,6 +5328,7 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz", "integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==", + "dev": true, "license": "MIT", "dependencies": { "agent-base": "^7.1.2", @@ -5296,12 +5348,14 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true, "license": "MIT" }, "node_modules/pump": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "dev": true, "license": "MIT", "dependencies": { "end-of-stream": "^1.1.0", @@ -5319,18 +5373,18 @@ } }, "node_modules/puppeteer": { - "version": "24.25.0", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.25.0.tgz", - "integrity": "sha512-P3rUaom2w/Ubrnz3v3kSbxGkN7SpbtQeGRPb7iO86Bv/dAz2WUmGQBHr37W/Rp1fbAocMvu0rHFbCIJvjiNhGw==", + "version": "24.26.0", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.26.0.tgz", + "integrity": "sha512-5F4A3dGeuw0jJOR3hGV969TO8oi+JG2TzqxAFvH+gxzxh0ILZmiFskWPlhcWQh5s9HAtgZyktOBDqs6zOi7IKA==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { "@puppeteer/browsers": "2.10.12", - "chromium-bidi": "9.1.0", + "chromium-bidi": "10.5.1", "cosmiconfig": "^9.0.0", "devtools-protocol": "0.0.1508733", - "puppeteer-core": "24.25.0", + "puppeteer-core": "24.26.0", "typed-query-selector": "^2.12.0" }, "bin": { @@ -5341,17 +5395,18 @@ } }, "node_modules/puppeteer-core": { - "version": "24.25.0", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.25.0.tgz", - "integrity": "sha512-8Xs6q3Ut+C8y7sAaqjIhzv1QykGWG4gc2mEZ2mYE7siZFuRp4xQVehOf8uQKSQAkeL7jXUs3mknEeiqnRqUKvQ==", + "version": "24.26.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.26.0.tgz", + "integrity": "sha512-l3aMYhTdSzazZ14rfpNAPGhnYHsd8mwduqybhu5aO/OR+d24P/V/eo8XTB3GB2yX2ZWf9GLAVcx8nnVPFZpP/A==", + "dev": true, "license": "Apache-2.0", "dependencies": { "@puppeteer/browsers": "2.10.12", - "chromium-bidi": "9.1.0", + "chromium-bidi": "10.5.1", "debug": "^4.4.3", "devtools-protocol": "0.0.1508733", "typed-query-selector": "^2.12.0", - "webdriver-bidi-protocol": "0.3.7", + "webdriver-bidi-protocol": "0.3.8", "ws": "^8.18.3" }, "engines": { @@ -5486,6 +5541,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -5770,6 +5826,7 @@ "version": "7.7.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -6004,6 +6061,7 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true, "license": "MIT", "engines": { "node": ">= 6.0.0", @@ -6014,6 +6072,7 @@ "version": "2.8.7", "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", + "dev": true, "license": "MIT", "dependencies": { "ip-address": "^10.0.1", @@ -6028,6 +6087,7 @@ "version": "8.0.5", "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", + "dev": true, "license": "MIT", "dependencies": { "agent-base": "^7.1.2", @@ -6042,6 +6102,7 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, "license": "BSD-3-Clause", "optional": true, "engines": { @@ -6160,6 +6221,7 @@ "version": "2.23.0", "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.23.0.tgz", "integrity": "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==", + "dev": true, "license": "MIT", "dependencies": { "events-universal": "^1.0.0", @@ -6311,6 +6373,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.1.tgz", "integrity": "sha512-LZA0oaPOc2fVo82Txf3gw+AkEd38szODlptMYejQUhndHMLQ9M059uXR+AfS7DNo0NpINvSqDsvyaCrBVkptWg==", + "dev": true, "license": "MIT", "dependencies": { "pump": "^3.0.0", @@ -6325,6 +6388,7 @@ "version": "3.1.7", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", + "dev": true, "license": "MIT", "dependencies": { "b4a": "^1.6.4", @@ -6336,6 +6400,7 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", + "dev": true, "license": "Apache-2.0", "dependencies": { "b4a": "^1.6.4" @@ -6411,6 +6476,7 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, "license": "0BSD" }, "node_modules/type-check": { @@ -6533,6 +6599,7 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.0.tgz", "integrity": "sha512-SbklCd1F0EiZOyPiW192rrHZzZ5sBijB6xM+cpmrwDqObvdtunOHHIk9fCGsoK5JVIYXoyEp4iEdE3upFH3PAg==", + "dev": true, "license": "MIT" }, "node_modules/typescript": { @@ -6596,7 +6663,7 @@ "version": "7.14.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.14.0.tgz", "integrity": "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/unpipe": { @@ -6665,9 +6732,10 @@ } }, "node_modules/webdriver-bidi-protocol": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/webdriver-bidi-protocol/-/webdriver-bidi-protocol-0.3.7.tgz", - "integrity": "sha512-wIx5Gu/LLTeexxilpk8WxU2cpGAKlfbWRO5h+my6EMD1k5PYqM1qQO1MHUFf4f3KRnhBvpbZU7VkizAgeSEf7g==", + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/webdriver-bidi-protocol/-/webdriver-bidi-protocol-0.3.8.tgz", + "integrity": "sha512-21Yi2GhGntMc671vNBCjiAeEVknXjVRoyu+k+9xOMShu+ZQfpGQwnBqbNz/Sv4GXZ6JmutlPAi2nIJcrymAWuQ==", + "dev": true, "license": "Apache-2.0" }, "node_modules/which": { @@ -6818,12 +6886,14 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, "license": "ISC" }, "node_modules/ws": { "version": "8.18.3", "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "dev": true, "license": "MIT", "engines": { "node": ">=10.0.0" @@ -6880,6 +6950,7 @@ "version": "2.10.0", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, "license": "MIT", "dependencies": { "buffer-crc32": "~0.2.3", @@ -6903,6 +6974,7 @@ "version": "3.25.76", "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" diff --git a/package.json b/package.json index b0f02da38..19d3b2413 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,7 @@ "eslint-plugin-import": "^2.32.0", "globals": "^16.4.0", "prettier": "^3.6.2", - "puppeteer": "24.25.0", + "puppeteer": "24.26.0", "rollup": "4.52.5", "rollup-plugin-cleanup": "^3.2.1", "rollup-plugin-license": "^3.6.0", From bc81582632be5cd87d8681748532c1d52e91004f Mon Sep 17 00:00:00 2001 From: Alex Rudenko Date: Tue, 21 Oct 2025 15:36:49 +0200 Subject: [PATCH 006/456] ci: create separate devDependency PRs for puppeteer and chrome-devtools-frontend (#448) --- .github/dependabot.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 63c22745e..d5de32cac 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -10,15 +10,13 @@ updates: groups: dependencies: dependency-type: production - exclude-patterns: - - 'puppeteer*' - - 'chrome-devtools-frontend' patterns: - '*' dev-dependencies: dependency-type: development exclude-patterns: - 'puppeteer*' + - 'chrome-devtools-frontend' patterns: - '*' puppeteer: From 4a897f1b0d285064c547eb7992ab70e2fe18e7d0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Oct 2025 17:27:34 +0200 Subject: [PATCH 007/456] chore(deps-dev): bump the dev-dependencies group with 4 updates (#449) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the dev-dependencies group with 4 updates: [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node), [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin), [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) and [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint). Updates `@types/node` from 24.8.1 to 24.9.1
Commits

Updates `@typescript-eslint/eslint-plugin` from 8.46.1 to 8.46.2
Release notes

Sourced from @​typescript-eslint/eslint-plugin's releases.

v8.46.2

8.46.2 (2025-10-20)

🩹 Fixes

  • eslint-plugin: [prefer-optional-chain] skip optional chaining when it could change the result (#11702)
  • typescript-estree: forbid invalid modifiers in object methods (#11689)

❤️ Thank You

You can read about our versioning strategy and releases on our website.

Changelog

Sourced from @​typescript-eslint/eslint-plugin's changelog.

8.46.2 (2025-10-20)

🩹 Fixes

  • eslint-plugin: [prefer-optional-chain] skip optional chaining when it could change the result (#11702)

❤️ Thank You

  • mdm317

You can read about our versioning strategy and releases on our website.

Commits
  • 55ca033 chore(release): publish 8.46.2
  • 698e7a8 fix(eslint-plugin): [prefer-optional-chain] skip optional chaining when it co...
  • See full diff in compare view

Updates `@typescript-eslint/parser` from 8.46.1 to 8.46.2
Release notes

Sourced from @​typescript-eslint/parser's releases.

v8.46.2

8.46.2 (2025-10-20)

🩹 Fixes

  • eslint-plugin: [prefer-optional-chain] skip optional chaining when it could change the result (#11702)
  • typescript-estree: forbid invalid modifiers in object methods (#11689)

❤️ Thank You

You can read about our versioning strategy and releases on our website.

Changelog

Sourced from @​typescript-eslint/parser's changelog.

8.46.2 (2025-10-20)

This was a version bump only for parser to align it with other projects, there were no code changes.

You can read about our versioning strategy and releases on our website.

Commits

Updates `typescript-eslint` from 8.46.1 to 8.46.2
Release notes

Sourced from typescript-eslint's releases.

v8.46.2

8.46.2 (2025-10-20)

🩹 Fixes

  • eslint-plugin: [prefer-optional-chain] skip optional chaining when it could change the result (#11702)
  • typescript-estree: forbid invalid modifiers in object methods (#11689)

❤️ Thank You

You can read about our versioning strategy and releases on our website.

Changelog

Sourced from typescript-eslint's changelog.

8.46.2 (2025-10-20)

This was a version bump only for typescript-eslint to align it with other projects, there were no code changes.

You can read about our versioning strategy and releases on our website.

Commits

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 136 +++++++++++++++++++++++----------------------- 1 file changed, 68 insertions(+), 68 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6cc2e886e..fe709a408 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1132,13 +1132,13 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "24.8.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.8.1.tgz", - "integrity": "sha512-alv65KGRadQVfVcG69MuB4IzdYVpRwMG/mq8KWOaoOdyY617P5ivaDiMCGOFDWD2sAn5Q0mR3mRtUOgm99hL9Q==", + "version": "24.9.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.9.1.tgz", + "integrity": "sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~7.14.0" + "undici-types": "~7.16.0" } }, "node_modules/@types/resolve": { @@ -1194,17 +1194,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.46.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.1.tgz", - "integrity": "sha512-rUsLh8PXmBjdiPY+Emjz9NX2yHvhS11v0SR6xNJkm5GM1MO9ea/1GoDKlHHZGrOJclL/cZ2i/vRUYVtjRhrHVQ==", + "version": "8.46.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.2.tgz", + "integrity": "sha512-ZGBMToy857/NIPaaCucIUQgqueOiq7HeAKkhlvqVV4lm089zUFW6ikRySx2v+cAhKeUCPuWVHeimyk6Dw1iY3w==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.46.1", - "@typescript-eslint/type-utils": "8.46.1", - "@typescript-eslint/utils": "8.46.1", - "@typescript-eslint/visitor-keys": "8.46.1", + "@typescript-eslint/scope-manager": "8.46.2", + "@typescript-eslint/type-utils": "8.46.2", + "@typescript-eslint/utils": "8.46.2", + "@typescript-eslint/visitor-keys": "8.46.2", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -1218,22 +1218,22 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.46.1", + "@typescript-eslint/parser": "^8.43.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/parser": { - "version": "8.46.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.46.1.tgz", - "integrity": "sha512-6JSSaBZmsKvEkbRUkf7Zj7dru/8ZCrJxAqArcLaVMee5907JdtEbKGsZ7zNiIm/UAkpGUkaSMZEXShnN2D1HZA==", + "version": "8.46.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.46.2.tgz", + "integrity": "sha512-BnOroVl1SgrPLywqxyqdJ4l3S2MsKVLDVxZvjI1Eoe8ev2r3kGDo+PcMihNmDE+6/KjkTubSJnmqGZZjQSBq/g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.46.1", - "@typescript-eslint/types": "8.46.1", - "@typescript-eslint/typescript-estree": "8.46.1", - "@typescript-eslint/visitor-keys": "8.46.1", + "@typescript-eslint/scope-manager": "8.46.2", + "@typescript-eslint/types": "8.46.2", + "@typescript-eslint/typescript-estree": "8.46.2", + "@typescript-eslint/visitor-keys": "8.46.2", "debug": "^4.3.4" }, "engines": { @@ -1249,14 +1249,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.46.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.46.1.tgz", - "integrity": "sha512-FOIaFVMHzRskXr5J4Jp8lFVV0gz5ngv3RHmn+E4HYxSJ3DgDzU7fVI1/M7Ijh1zf6S7HIoaIOtln1H5y8V+9Zg==", + "version": "8.46.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.46.2.tgz", + "integrity": "sha512-PULOLZ9iqwI7hXcmL4fVfIsBi6AN9YxRc0frbvmg8f+4hQAjQ5GYNKK0DIArNo+rOKmR/iBYwkpBmnIwin4wBg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.46.1", - "@typescript-eslint/types": "^8.46.1", + "@typescript-eslint/tsconfig-utils": "^8.46.2", + "@typescript-eslint/types": "^8.46.2", "debug": "^4.3.4" }, "engines": { @@ -1271,14 +1271,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.46.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.46.1.tgz", - "integrity": "sha512-weL9Gg3/5F0pVQKiF8eOXFZp8emqWzZsOJuWRUNtHT+UNV2xSJegmpCNQHy37aEQIbToTq7RHKhWvOsmbM680A==", + "version": "8.46.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.46.2.tgz", + "integrity": "sha512-LF4b/NmGvdWEHD2H4MsHD8ny6JpiVNDzrSZr3CsckEgCbAGZbYM4Cqxvi9L+WqDMT+51Ozy7lt2M+d0JLEuBqA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.46.1", - "@typescript-eslint/visitor-keys": "8.46.1" + "@typescript-eslint/types": "8.46.2", + "@typescript-eslint/visitor-keys": "8.46.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1289,9 +1289,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.46.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.46.1.tgz", - "integrity": "sha512-X88+J/CwFvlJB+mK09VFqx5FE4H5cXD+H/Bdza2aEWkSb8hnWIQorNcscRl4IEo1Cz9VI/+/r/jnGWkbWPx54g==", + "version": "8.46.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.46.2.tgz", + "integrity": "sha512-a7QH6fw4S57+F5y2FIxxSDyi5M4UfGF+Jl1bCGd7+L4KsaUY80GsiF/t0UoRFDHAguKlBaACWJRmdrc6Xfkkag==", "dev": true, "license": "MIT", "engines": { @@ -1306,15 +1306,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.46.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.46.1.tgz", - "integrity": "sha512-+BlmiHIiqufBxkVnOtFwjah/vrkF4MtKKvpXrKSPLCkCtAp8H01/VV43sfqA98Od7nJpDcFnkwgyfQbOG0AMvw==", + "version": "8.46.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.46.2.tgz", + "integrity": "sha512-HbPM4LbaAAt/DjxXaG9yiS9brOOz6fabal4uvUmaUYe6l3K1phQDMQKBRUrr06BQkxkvIZVVHttqiybM9nJsLA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.46.1", - "@typescript-eslint/typescript-estree": "8.46.1", - "@typescript-eslint/utils": "8.46.1", + "@typescript-eslint/types": "8.46.2", + "@typescript-eslint/typescript-estree": "8.46.2", + "@typescript-eslint/utils": "8.46.2", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -1331,9 +1331,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.46.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.46.1.tgz", - "integrity": "sha512-C+soprGBHwWBdkDpbaRC4paGBrkIXxVlNohadL5o0kfhsXqOC6GYH2S/Obmig+I0HTDl8wMaRySwrfrXVP8/pQ==", + "version": "8.46.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.46.2.tgz", + "integrity": "sha512-lNCWCbq7rpg7qDsQrd3D6NyWYu+gkTENkG5IKYhUIcxSb59SQC/hEQ+MrG4sTgBVghTonNWq42bA/d4yYumldQ==", "dev": true, "license": "MIT", "engines": { @@ -1345,16 +1345,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.46.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.46.1.tgz", - "integrity": "sha512-uIifjT4s8cQKFQ8ZBXXyoUODtRoAd7F7+G8MKmtzj17+1UbdzFl52AzRyZRyKqPHhgzvXunnSckVu36flGy8cg==", + "version": "8.46.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.46.2.tgz", + "integrity": "sha512-f7rW7LJ2b7Uh2EiQ+7sza6RDZnajbNbemn54Ob6fRwQbgcIn+GWfyuHDHRYgRoZu1P4AayVScrRW+YfbTvPQoQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.46.1", - "@typescript-eslint/tsconfig-utils": "8.46.1", - "@typescript-eslint/types": "8.46.1", - "@typescript-eslint/visitor-keys": "8.46.1", + "@typescript-eslint/project-service": "8.46.2", + "@typescript-eslint/tsconfig-utils": "8.46.2", + "@typescript-eslint/types": "8.46.2", + "@typescript-eslint/visitor-keys": "8.46.2", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -1374,16 +1374,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.46.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.46.1.tgz", - "integrity": "sha512-vkYUy6LdZS7q1v/Gxb2Zs7zziuXN0wxqsetJdeZdRe/f5dwJFglmuvZBfTUivCtjH725C1jWCDfpadadD95EDQ==", + "version": "8.46.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.46.2.tgz", + "integrity": "sha512-sExxzucx0Tud5tE0XqR0lT0psBQvEpnpiul9XbGUB1QwpWJJAps1O/Z7hJxLGiZLBKMCutjTzDgmd1muEhBnVg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.46.1", - "@typescript-eslint/types": "8.46.1", - "@typescript-eslint/typescript-estree": "8.46.1" + "@typescript-eslint/scope-manager": "8.46.2", + "@typescript-eslint/types": "8.46.2", + "@typescript-eslint/typescript-estree": "8.46.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1398,13 +1398,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.46.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.46.1.tgz", - "integrity": "sha512-ptkmIf2iDkNUjdeu2bQqhFPV1m6qTnFFjg7PPDjxKWaMaP0Z6I9l30Jr3g5QqbZGdw8YdYvLp+XnqnWWZOg/NA==", + "version": "8.46.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.46.2.tgz", + "integrity": "sha512-tUFMXI4gxzzMXt4xpGJEsBsTox0XbNQ1y94EwlD/CuZwFcQP79xfQqMhau9HsRc/J0cAPA/HZt1dZPtGn9V/7w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.46.1", + "@typescript-eslint/types": "8.46.2", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -6617,16 +6617,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.46.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.46.1.tgz", - "integrity": "sha512-VHgijW803JafdSsDO8I761r3SHrgk4T00IdyQ+/UsthtgPRsBWQLqoSxOolxTpxRKi1kGXK0bSz4CoAc9ObqJA==", + "version": "8.46.2", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.46.2.tgz", + "integrity": "sha512-vbw8bOmiuYNdzzV3lsiWv6sRwjyuKJMQqWulBOU7M0RrxedXledX8G8kBbQeiOYDnTfiXz0Y4081E1QMNB6iQg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.46.1", - "@typescript-eslint/parser": "8.46.1", - "@typescript-eslint/typescript-estree": "8.46.1", - "@typescript-eslint/utils": "8.46.1" + "@typescript-eslint/eslint-plugin": "8.46.2", + "@typescript-eslint/parser": "8.46.2", + "@typescript-eslint/typescript-estree": "8.46.2", + "@typescript-eslint/utils": "8.46.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6660,9 +6660,9 @@ } }, "node_modules/undici-types": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.14.0.tgz", - "integrity": "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", "dev": true, "license": "MIT" }, From 4c65f59cf9f62662cf903fbbd19b67a8828d674a Mon Sep 17 00:00:00 2001 From: Nikolay Vitkov <34244704+Lightning00Blade@users.noreply.github.com> Date: Tue, 21 Oct 2025 18:53:30 +0200 Subject: [PATCH 008/456] fix: retrieve data correctly with fewer than 3 navigations (#451) Also adds test for navigation --- src/PageCollector.ts | 5 +++- tests/PageCollector.test.ts | 32 +++++++++++++++++++++++ tests/tools/network.test.js.snapshot | 9 +++++++ tests/tools/network.test.ts | 38 ++++++++++++++++++++++++++++ tests/tools/script.test.ts | 5 +--- 5 files changed, 84 insertions(+), 5 deletions(-) diff --git a/src/PageCollector.ts b/src/PageCollector.ts index 39168dc8b..16841149a 100644 --- a/src/PageCollector.ts +++ b/src/PageCollector.ts @@ -144,8 +144,11 @@ export class PageCollector { } const data: T[] = []; + for (let index = this.#maxNavigationSaved; index >= 0; index--) { - data.push(...navigations[index]); + if (navigations[index]) { + data.push(...navigations[index]); + } } return data; } diff --git a/tests/PageCollector.test.ts b/tests/PageCollector.test.ts index 5da1d31a1..9cd7c4230 100644 --- a/tests/PageCollector.test.ts +++ b/tests/PageCollector.test.ts @@ -286,4 +286,36 @@ describe('NetworkCollector', () => { page.emit('request', request); assert.equal(collector.getData(page).length, 2); }); + + it('works with previous navigations', async () => { + const browser = getMockBrowser(); + const page = (await browser.pages())[0]; + const mainFrame = page.mainFrame(); + const navRequest = getMockRequest({ + navigationRequest: true, + frame: page.mainFrame(), + }); + const navRequest2 = getMockRequest({ + navigationRequest: true, + frame: page.mainFrame(), + }); + const request = getMockRequest(); + + const collector = new NetworkCollector(browser); + await collector.init(); + page.emit('request', navRequest); + assert.equal(collector.getData(page, true).length, 1); + + page.emit('framenavigated', mainFrame); + assert.equal(collector.getData(page, true).length, 1); + + page.emit('request', navRequest2); + assert.equal(collector.getData(page, true).length, 2); + + page.emit('framenavigated', mainFrame); + assert.equal(collector.getData(page, true).length, 2); + + page.emit('request', request); + assert.equal(collector.getData(page, true).length, 3); + }); }); diff --git a/tests/tools/network.test.js.snapshot b/tests/tools/network.test.js.snapshot index 0a74003c1..df4002ba0 100644 --- a/tests/tools/network.test.js.snapshot +++ b/tests/tools/network.test.js.snapshot @@ -34,3 +34,12 @@ reqid=1 GET http://localhost:/one [success - 200] reqid=2 GET http://localhost:/two [success - 200] reqid=3 GET http://localhost:/three [success - 200] `; + +exports[`network > network_list_requests > list requests from previous navigations from redirects 1`] = ` +# list_request response +## Network requests +Showing 1-3 of 3 (Page 1 of 1). +reqid=1 GET http://localhost:/redirect [failed - 302] +reqid=2 GET http://localhost:/redirected [success - 200] +reqid=3 GET http://localhost:/redirected-page [success - 200] +`; diff --git a/tests/tools/network.test.ts b/tests/tools/network.test.ts index c5f666b91..69af7fb4b 100644 --- a/tests/tools/network.test.ts +++ b/tests/tools/network.test.ts @@ -71,6 +71,44 @@ describe('network', () => { t.assert.snapshot?.(stabilizeResponseOutput(responseData[0].text)); }); }); + + it('list requests from previous navigations from redirects', async t => { + server.addRoute('/redirect', async (_req, res) => { + res.writeHead(302, { + Location: server.getRoute('/redirected'), + }); + res.end(); + }); + + server.addHtmlRoute( + '/redirected', + html``, + ); + + server.addHtmlRoute( + '/redirected-page', + html`
I was redirected 2 times
`, + ); + + await withBrowser(async (response, context) => { + await context.setUpNetworkCollectorForTesting(); + const page = context.getSelectedPage(); + await page.goto(server.getRoute('/redirect')); + await listNetworkRequests.handler( + { + params: { + includePreviousNavigations: true, + }, + }, + response, + context, + ); + const responseData = await response.handle('list_request', context); + t.assert.snapshot?.(stabilizeResponseOutput(responseData[0].text)); + }); + }); }); describe('network_get_request', () => { it('attaches request', async () => { diff --git a/tests/tools/script.test.ts b/tests/tools/script.test.ts index a31cff1c4..981ca92e1 100644 --- a/tests/tools/script.test.ts +++ b/tests/tools/script.test.ts @@ -161,10 +161,7 @@ describe('script', () => { '/iframe', html`
`, ); - server.addRoute('/main', async (_req, res) => { - res.write(html``); - res.end(); - }); + server.addHtmlRoute('/main', html``); await withBrowser(async (response, context) => { const page = context.getSelectedPage(); From 914b980113353fd41b301da397aa45975090487a Mon Sep 17 00:00:00 2001 From: zyzyzyryxy <31672205+zyzyzyryxy@users.noreply.github.com> Date: Tue, 21 Oct 2025 19:44:31 +0200 Subject: [PATCH 009/456] refactor: bundle all dependencies together (#450) Final bundling step for improved startup times. --------- Co-authored-by: Piotr Paulski --- package-lock.json | 26 ++++++++++++++----- package.json | 10 +++---- rollup.config.mjs | 22 +++++++++++++--- src/DevToolsConnectionAdapter.ts | 2 +- src/McpContext.ts | 7 +++-- src/McpResponse.ts | 8 +++--- src/PageCollector.ts | 2 +- src/WaitForHelper.ts | 4 +-- src/browser.ts | 4 +-- src/cli.ts | 5 ++-- src/formatters/networkFormatter.ts | 5 +--- src/logger.ts | 2 +- src/main.ts | 2 +- src/polyfill.ts | 4 +-- src/third_party/index.ts | 26 +++++++++++++++++++ .../modelcontextprotocol-sdk/index.ts | 15 ----------- src/third_party/puppeteer-core/index.ts | 9 ------- src/tools/ToolDefinition.ts | 8 ++---- src/tools/console.ts | 4 +-- src/tools/emulation.ts | 3 +-- src/tools/input.ts | 4 +-- src/tools/network.ts | 4 +-- src/tools/pages.ts | 2 +- src/tools/performance.ts | 4 +-- src/tools/screenshot.ts | 4 +-- src/tools/script.ts | 8 ++---- src/tools/snapshot.ts | 2 +- 27 files changed, 103 insertions(+), 93 deletions(-) create mode 100644 src/third_party/index.ts delete mode 100644 src/third_party/modelcontextprotocol-sdk/index.ts delete mode 100644 src/third_party/puppeteer-core/index.ts diff --git a/package-lock.json b/package-lock.json index fe709a408..76d2bc001 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,11 +8,6 @@ "name": "chrome-devtools-mcp", "version": "0.8.1", "license": "Apache-2.0", - "dependencies": { - "core-js": "3.46.0", - "debug": "4.4.3", - "yargs": "18.0.0" - }, "bin": { "chrome-devtools-mcp": "build/src/index.js" }, @@ -31,6 +26,8 @@ "@typescript-eslint/eslint-plugin": "^8.43.0", "@typescript-eslint/parser": "^8.43.0", "chrome-devtools-frontend": "1.0.1524741", + "core-js": "3.46.0", + "debug": "4.4.3", "eslint": "^9.35.0", "eslint-import-resolver-typescript": "^4.4.4", "eslint-plugin-import": "^2.32.0", @@ -42,7 +39,8 @@ "rollup-plugin-license": "^3.6.0", "sinon": "^21.0.0", "typescript": "^5.9.2", - "typescript-eslint": "^8.43.0" + "typescript-eslint": "^8.43.0", + "yargs": "18.0.0" }, "engines": { "node": "^20.19.0 || ^22.12.0 || >=23" @@ -1752,6 +1750,7 @@ "version": "6.2.2", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -2249,6 +2248,7 @@ "version": "9.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", + "dev": true, "license": "ISC", "dependencies": { "string-width": "^7.2.0", @@ -2347,6 +2347,7 @@ "version": "3.46.0", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.46.0.tgz", "integrity": "sha512-vDMm9B0xnqqZ8uSBpZ8sNtRtOdmfShrvT6h2TuQGLs0Is+cR0DYbj/KWP6ALVNbWPpqA/qPLoOuppJN07humpA==", + "dev": true, "hasInstallScript": true, "license": "MIT", "funding": { @@ -2478,6 +2479,7 @@ "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -2625,6 +2627,7 @@ "version": "10.6.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, "license": "MIT" }, "node_modules/encodeurl": { @@ -2820,6 +2823,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -3671,6 +3675,7 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" @@ -3680,6 +3685,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz", "integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==", + "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -4847,6 +4853,7 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, "license": "MIT" }, "node_modules/napi-postinstall": { @@ -6233,6 +6240,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^10.3.0", @@ -6309,6 +6317,7 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" @@ -6857,6 +6866,7 @@ "version": "9.0.2", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^6.2.1", @@ -6874,6 +6884,7 @@ "version": "6.2.3", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -6915,6 +6926,7 @@ "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, "license": "ISC", "engines": { "node": ">=10" @@ -6924,6 +6936,7 @@ "version": "18.0.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz", "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==", + "dev": true, "license": "MIT", "dependencies": { "cliui": "^9.0.1", @@ -6941,6 +6954,7 @@ "version": "22.0.0", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", + "dev": true, "license": "ISC", "engines": { "node": "^20.19.0 || ^22.12.0 || >=23" diff --git a/package.json b/package.json index 19d3b2413..a71b47ffa 100644 --- a/package.json +++ b/package.json @@ -37,11 +37,6 @@ }, "homepage": "https://github.com/ChromeDevTools/chrome-devtools-mcp#readme", "mcpName": "io.github.ChromeDevTools/chrome-devtools-mcp", - "dependencies": { - "core-js": "3.46.0", - "debug": "4.4.3", - "yargs": "18.0.0" - }, "devDependencies": { "@eslint/js": "^9.35.0", "@modelcontextprotocol/sdk": "1.20.1", @@ -57,6 +52,8 @@ "@typescript-eslint/eslint-plugin": "^8.43.0", "@typescript-eslint/parser": "^8.43.0", "chrome-devtools-frontend": "1.0.1524741", + "core-js": "3.46.0", + "debug": "4.4.3", "eslint": "^9.35.0", "eslint-import-resolver-typescript": "^4.4.4", "eslint-plugin-import": "^2.32.0", @@ -68,7 +65,8 @@ "rollup-plugin-license": "^3.6.0", "sinon": "^21.0.0", "typescript": "^5.9.2", - "typescript-eslint": "^8.43.0" + "typescript-eslint": "^8.43.0", + "yargs": "18.0.0" }, "engines": { "node": "^20.19.0 || ^22.12.0 || >=23" diff --git a/rollup.config.mjs b/rollup.config.mjs index 9ca5ef30b..0fdfc9443 100644 --- a/rollup.config.mjs +++ b/rollup.config.mjs @@ -42,7 +42,7 @@ const allowedLicenses = [ /** * @param {string} wrapperIndexPath * @param {import('rollup').OutputOptions} [extraOutputOptions={}] - * @param {string[]} [external=[]] + * @param {import('rollup').ExternalOption} [external=[]] * @returns {import('rollup').RollupOptions} */ const bundleDependency = ( @@ -110,12 +110,26 @@ const bundleDependency = ( }); export default [ - bundleDependency('./build/src/third_party/modelcontextprotocol-sdk/index.js'), bundleDependency( - './build/src/third_party/puppeteer-core/index.js', + './build/src/third_party/index.js', { inlineDynamicImports: true, }, - ['./bidi.js', '../bidi/bidi.js'], + (source, importer, _isResolved) => { + if ( + source === 'yargs' && + importer && + importer.includes('puppeteer-core') + ) { + return true; + } + + const existingExternals = ['./bidi.js', '../bidi/bidi.js']; + if (existingExternals.includes(source)) { + return true; + } + + return false; + }, ), ]; diff --git a/src/DevToolsConnectionAdapter.ts b/src/DevToolsConnectionAdapter.ts index ec07d1a72..5a6491e3e 100644 --- a/src/DevToolsConnectionAdapter.ts +++ b/src/DevToolsConnectionAdapter.ts @@ -6,7 +6,7 @@ import {Connection} from '../node_modules/chrome-devtools-frontend/front_end/core/protocol_client/InspectorBackend.js'; -import {type ConnectionTransport} from './third_party/puppeteer-core/index.js'; +import {type ConnectionTransport} from './third_party/index.js'; /** * Allows a puppeteer {@link ConnectionTransport} to act like a DevTools {@link Connection}. diff --git a/src/McpContext.ts b/src/McpContext.ts index dd9186a6e..fd5e75369 100644 --- a/src/McpContext.ts +++ b/src/McpContext.ts @@ -7,21 +7,20 @@ import fs from 'node:fs/promises'; import os from 'node:os'; import path from 'node:path'; -import type {Debugger} from 'debug'; - import type {ListenerMap} from './PageCollector.js'; import {NetworkCollector, PageCollector} from './PageCollector.js'; -import {Locator} from './third_party/puppeteer-core/index.js'; +import {Locator} from './third_party/index.js'; import type { Browser, ConsoleMessage, + Debugger, Dialog, ElementHandle, HTTPRequest, Page, SerializedAXNode, PredefinedNetworkConditions, -} from './third_party/puppeteer-core/index.js'; +} from './third_party/index.js'; import {listPages} from './tools/pages.js'; import {takeSnapshot} from './tools/snapshot.js'; import {CLOSE_PAGE_ERROR} from './tools/ToolDefinition.js'; diff --git a/src/McpResponse.ts b/src/McpResponse.ts index 03a814658..f937519cc 100644 --- a/src/McpResponse.ts +++ b/src/McpResponse.ts @@ -17,14 +17,12 @@ import { } from './formatters/networkFormatter.js'; import {formatA11ySnapshot} from './formatters/snapshotFormatter.js'; import type {McpContext} from './McpContext.js'; -import type { - ImageContent, - TextContent, -} from './third_party/modelcontextprotocol-sdk/index.js'; import type { ConsoleMessage, + ImageContent, ResourceType, -} from './third_party/puppeteer-core/index.js'; + TextContent, +} from './third_party/index.js'; import {handleDialog} from './tools/pages.js'; import type {ImageContentData, Response} from './tools/ToolDefinition.js'; import {paginate} from './utils/pagination.js'; diff --git a/src/PageCollector.ts b/src/PageCollector.ts index 16841149a..89c7c3154 100644 --- a/src/PageCollector.ts +++ b/src/PageCollector.ts @@ -11,7 +11,7 @@ import { type HTTPRequest, type Page, type PageEvents, -} from './third_party/puppeteer-core/index.js'; +} from './third_party/index.js'; export type ListenerMap = { [K in keyof EventMap]?: (event: EventMap[K]) => void; diff --git a/src/WaitForHelper.ts b/src/WaitForHelper.ts index a18f6448a..7a5edaf4e 100644 --- a/src/WaitForHelper.ts +++ b/src/WaitForHelper.ts @@ -3,10 +3,8 @@ * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ -import type {CdpPage} from 'puppeteer-core/internal/cdp/Page.js'; - import {logger} from './logger.js'; -import type {Page, Protocol} from './third_party/puppeteer-core/index.js'; +import type {Page, Protocol, CdpPage} from './third_party/index.js'; export class WaitForHelper { #abortController = new AbortController(); diff --git a/src/browser.ts b/src/browser.ts index 47afe5a93..1947651b9 100644 --- a/src/browser.ts +++ b/src/browser.ts @@ -13,8 +13,8 @@ import type { ChromeReleaseChannel, LaunchOptions, Target, -} from './third_party/puppeteer-core/index.js'; -import {puppeteer} from './third_party/puppeteer-core/index.js'; +} from './third_party/index.js'; +import {puppeteer} from './third_party/index.js'; let browser: Browser | undefined; diff --git a/src/cli.ts b/src/cli.ts index 89c8097d0..54c832825 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -4,9 +4,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type {Options as YargsOptions} from 'yargs'; -import yargs from 'yargs'; -import {hideBin} from 'yargs/helpers'; +import type {YargsOptions} from './third_party/index.js'; +import {yargs, hideBin} from './third_party/index.js'; export const cliOptions = { browserUrl: { diff --git a/src/formatters/networkFormatter.ts b/src/formatters/networkFormatter.ts index 0d8189bab..3c1abe58c 100644 --- a/src/formatters/networkFormatter.ts +++ b/src/formatters/networkFormatter.ts @@ -6,10 +6,7 @@ import {isUtf8} from 'node:buffer'; -import type { - HTTPRequest, - HTTPResponse, -} from '../third_party/puppeteer-core/index.js'; +import type {HTTPRequest, HTTPResponse} from '../third_party/index.js'; const BODY_CONTEXT_SIZE_LIMIT = 10000; diff --git a/src/logger.ts b/src/logger.ts index f939e4cd9..072ece389 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -5,7 +5,7 @@ */ import fs from 'node:fs'; -import debug from 'debug'; +import {debug} from './third_party/index.js'; const mcpDebugNamespace = 'mcp:log'; diff --git a/src/main.ts b/src/main.ts index df526946b..c1fb8303b 100644 --- a/src/main.ts +++ b/src/main.ts @@ -18,7 +18,7 @@ import { StdioServerTransport, type CallToolResult, SetLevelRequestSchema, -} from './third_party/modelcontextprotocol-sdk/index.js'; +} from './third_party/index.js'; import * as consoleTools from './tools/console.js'; import * as emulationTools from './tools/emulation.js'; import * as inputTools from './tools/input.js'; diff --git a/src/polyfill.ts b/src/polyfill.ts index 0484a02f3..26ddfb710 100644 --- a/src/polyfill.ts +++ b/src/polyfill.ts @@ -4,5 +4,5 @@ * SPDX-License-Identifier: Apache-2.0 */ -import 'core-js/modules/es.promise.with-resolvers.js'; -import 'core-js/proposals/iterator-helpers.js'; +// polyfills are now bundled with all other dependencies +import './third_party/index.js'; diff --git a/src/third_party/index.ts b/src/third_party/index.ts new file mode 100644 index 000000000..c9995d15e --- /dev/null +++ b/src/third_party/index.ts @@ -0,0 +1,26 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ +import 'core-js/modules/es.promise.with-resolvers.js'; +import 'core-js/proposals/iterator-helpers.js'; + +export type {Options as YargsOptions} from 'yargs'; +export {default as yargs} from 'yargs'; +export {hideBin} from 'yargs/helpers'; +export {debug} from 'debug'; +export type {Debugger} from 'debug'; +export {McpServer} from '@modelcontextprotocol/sdk/server/mcp.js'; +export {StdioServerTransport} from '@modelcontextprotocol/sdk/server/stdio.js'; +export { + type CallToolResult, + SetLevelRequestSchema, + type ImageContent, + type TextContent, +} from '@modelcontextprotocol/sdk/types.js'; +export {z as zod} from 'zod'; +export {Locator, PredefinedNetworkConditions} from 'puppeteer-core'; +export {default as puppeteer} from 'puppeteer-core'; +export type * from 'puppeteer-core'; +export type {CdpPage} from 'puppeteer-core/internal/cdp/Page.js'; diff --git a/src/third_party/modelcontextprotocol-sdk/index.ts b/src/third_party/modelcontextprotocol-sdk/index.ts deleted file mode 100644 index 6366490f1..000000000 --- a/src/third_party/modelcontextprotocol-sdk/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * @license - * Copyright 2025 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -export {McpServer} from '@modelcontextprotocol/sdk/server/mcp.js'; -export {StdioServerTransport} from '@modelcontextprotocol/sdk/server/stdio.js'; -export { - type CallToolResult, - SetLevelRequestSchema, - type ImageContent, - type TextContent, -} from '@modelcontextprotocol/sdk/types.js'; -export {z as zod} from 'zod'; diff --git a/src/third_party/puppeteer-core/index.ts b/src/third_party/puppeteer-core/index.ts deleted file mode 100644 index 92ae88ae7..000000000 --- a/src/third_party/puppeteer-core/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * @license - * Copyright 2025 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -export {Locator, PredefinedNetworkConditions} from 'puppeteer-core'; -export {default as puppeteer} from 'puppeteer-core'; -export type * from 'puppeteer-core'; diff --git a/src/tools/ToolDefinition.ts b/src/tools/ToolDefinition.ts index 966cf77b6..0e0691be5 100644 --- a/src/tools/ToolDefinition.ts +++ b/src/tools/ToolDefinition.ts @@ -5,12 +5,8 @@ */ import type {TextSnapshotNode} from '../McpContext.js'; -import {zod} from '../third_party/modelcontextprotocol-sdk/index.js'; -import type { - Dialog, - ElementHandle, - Page, -} from '../third_party/puppeteer-core/index.js'; +import {zod} from '../third_party/index.js'; +import type {Dialog, ElementHandle, Page} from '../third_party/index.js'; import type {TraceResult} from '../trace-processing/parse.js'; import type {PaginationOptions} from '../utils/types.js'; diff --git a/src/tools/console.ts b/src/tools/console.ts index 8be953c89..2a56efb78 100644 --- a/src/tools/console.ts +++ b/src/tools/console.ts @@ -4,8 +4,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {zod} from '../third_party/modelcontextprotocol-sdk/index.js'; -import type {ConsoleMessageType} from '../third_party/puppeteer-core/index.js'; +import {zod} from '../third_party/index.js'; +import type {ConsoleMessageType} from '../third_party/index.js'; import {ToolCategories} from './categories.js'; import {defineTool} from './ToolDefinition.js'; diff --git a/src/tools/emulation.ts b/src/tools/emulation.ts index 9287c00d6..eceaa53bb 100644 --- a/src/tools/emulation.ts +++ b/src/tools/emulation.ts @@ -4,8 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {zod} from '../third_party/modelcontextprotocol-sdk/index.js'; -import {PredefinedNetworkConditions} from '../third_party/puppeteer-core/index.js'; +import {zod, PredefinedNetworkConditions} from '../third_party/index.js'; import {ToolCategories} from './categories.js'; import {defineTool} from './ToolDefinition.js'; diff --git a/src/tools/input.ts b/src/tools/input.ts index 973eb829f..78a1372a6 100644 --- a/src/tools/input.ts +++ b/src/tools/input.ts @@ -5,8 +5,8 @@ */ import type {McpContext, TextSnapshotNode} from '../McpContext.js'; -import {zod} from '../third_party/modelcontextprotocol-sdk/index.js'; -import type {ElementHandle} from '../third_party/puppeteer-core/index.js'; +import {zod} from '../third_party/index.js'; +import type {ElementHandle} from '../third_party/index.js'; import {ToolCategories} from './categories.js'; import {defineTool} from './ToolDefinition.js'; diff --git a/src/tools/network.ts b/src/tools/network.ts index d684d05b4..f31bd27a4 100644 --- a/src/tools/network.ts +++ b/src/tools/network.ts @@ -4,8 +4,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {zod} from '../third_party/modelcontextprotocol-sdk/index.js'; -import type {ResourceType} from '../third_party/puppeteer-core/index.js'; +import {zod} from '../third_party/index.js'; +import type {ResourceType} from '../third_party/index.js'; import {ToolCategories} from './categories.js'; import {defineTool} from './ToolDefinition.js'; diff --git a/src/tools/pages.ts b/src/tools/pages.ts index ac6756d25..a1c39bee8 100644 --- a/src/tools/pages.ts +++ b/src/tools/pages.ts @@ -5,7 +5,7 @@ */ import {logger} from '../logger.js'; -import {zod} from '../third_party/modelcontextprotocol-sdk/index.js'; +import {zod} from '../third_party/index.js'; import {ToolCategories} from './categories.js'; import {CLOSE_PAGE_ERROR, defineTool, timeoutSchema} from './ToolDefinition.js'; diff --git a/src/tools/performance.ts b/src/tools/performance.ts index a76f30d5a..8b5874990 100644 --- a/src/tools/performance.ts +++ b/src/tools/performance.ts @@ -5,8 +5,8 @@ */ import {logger} from '../logger.js'; -import {zod} from '../third_party/modelcontextprotocol-sdk/index.js'; -import type {Page} from '../third_party/puppeteer-core/index.js'; +import {zod} from '../third_party/index.js'; +import type {Page} from '../third_party/index.js'; import type {InsightName} from '../trace-processing/parse.js'; import { getInsightOutput, diff --git a/src/tools/screenshot.ts b/src/tools/screenshot.ts index e053186f5..91afa25d1 100644 --- a/src/tools/screenshot.ts +++ b/src/tools/screenshot.ts @@ -4,8 +4,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {zod} from '../third_party/modelcontextprotocol-sdk/index.js'; -import type {ElementHandle, Page} from '../third_party/puppeteer-core/index.js'; +import {zod} from '../third_party/index.js'; +import type {ElementHandle, Page} from '../third_party/index.js'; import {ToolCategories} from './categories.js'; import {defineTool} from './ToolDefinition.js'; diff --git a/src/tools/script.ts b/src/tools/script.ts index 6f08af08a..70115fc37 100644 --- a/src/tools/script.ts +++ b/src/tools/script.ts @@ -4,12 +4,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {zod} from '../third_party/modelcontextprotocol-sdk/index.js'; -import type { - Frame, - JSHandle, - Page, -} from '../third_party/puppeteer-core/index.js'; +import {zod} from '../third_party/index.js'; +import type {Frame, JSHandle, Page} from '../third_party/index.js'; import {ToolCategories} from './categories.js'; import {defineTool} from './ToolDefinition.js'; diff --git a/src/tools/snapshot.ts b/src/tools/snapshot.ts index 0cf548b41..05df1734a 100644 --- a/src/tools/snapshot.ts +++ b/src/tools/snapshot.ts @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {zod} from '../third_party/modelcontextprotocol-sdk/index.js'; +import {zod} from '../third_party/index.js'; import {ToolCategories} from './categories.js'; import {defineTool, timeoutSchema} from './ToolDefinition.js'; From 6f243620391f0c608f51d464257cf3222d653e9e Mon Sep 17 00:00:00 2001 From: Nikolay Vitkov <34244704+Lightning00Blade@users.noreply.github.com> Date: Wed, 22 Oct 2025 01:03:01 +0200 Subject: [PATCH 010/456] feat: support previous navigation for Console messages (#452) --- docs/tool-reference.md | 1 + src/McpContext.ts | 6 ++++-- src/McpResponse.ts | 7 ++++++- src/tools/ToolDefinition.ts | 1 + src/tools/console.ts | 6 ++++++ 5 files changed, 18 insertions(+), 3 deletions(-) diff --git a/docs/tool-reference.md b/docs/tool-reference.md index d7cbc2f45..c300c341b 100644 --- a/docs/tool-reference.md +++ b/docs/tool-reference.md @@ -314,6 +314,7 @@ so returned values have to JSON-serializable. **Parameters:** +- **includePreviousNavigations** (boolean) _(optional)_: Whether to include messages from previous navigations. - **pageIdx** (integer) _(optional)_: Page number to return (0-based). When omitted, returns the first page. - **pageSize** (integer) _(optional)_: Maximum number of messages to return. When omitted, returns all requests. - **types** (array) _(optional)_: Filter messages to only return messages of the specified resource types. When omitted or empty, returns all messages. diff --git a/src/McpContext.ts b/src/McpContext.ts index fd5e75369..07f107e6f 100644 --- a/src/McpContext.ts +++ b/src/McpContext.ts @@ -145,9 +145,11 @@ export class McpContext implements Context { return this.#networkCollector.getData(page, includePreviousNavigations); } - getConsoleData(): Array { + getConsoleData( + includePreviousNavigations?: boolean, + ): Array { const page = this.getSelectedPage(); - return this.#consoleCollector.getData(page); + return this.#consoleCollector.getData(page, includePreviousNavigations); } getConsoleMessageStableId(message: ConsoleMessage | Error): number { diff --git a/src/McpResponse.ts b/src/McpResponse.ts index f937519cc..584a2d49e 100644 --- a/src/McpResponse.ts +++ b/src/McpResponse.ts @@ -46,6 +46,7 @@ export class McpResponse implements Response { include: boolean; pagination?: PaginationOptions; types?: string[]; + includePreviousNavigations?: boolean; }; setIncludePages(value: boolean): void { @@ -87,6 +88,7 @@ export class McpResponse implements Response { value: boolean, options?: PaginationOptions & { types?: string[]; + includePreviousNavigations?: boolean; }, ): void { if (!value) { @@ -104,6 +106,7 @@ export class McpResponse implements Response { } : undefined, types: options?.types, + includePreviousNavigations: options?.includePreviousNavigations, }; } @@ -228,7 +231,9 @@ export class McpResponse implements Response { let consoleListData: ConsoleMessageData[] | undefined; if (this.#consoleDataOptions?.include) { - let messages = context.getConsoleData(); + let messages = context.getConsoleData( + this.#consoleDataOptions.includePreviousNavigations, + ); if (this.#consoleDataOptions.types?.length) { const normalizedTypes = new Set(this.#consoleDataOptions.types); diff --git a/src/tools/ToolDefinition.ts b/src/tools/ToolDefinition.ts index 0e0691be5..d36d473d0 100644 --- a/src/tools/ToolDefinition.ts +++ b/src/tools/ToolDefinition.ts @@ -56,6 +56,7 @@ export interface Response { value: boolean, options?: PaginationOptions & { types?: string[]; + includePreviousNavigations?: boolean; }, ): void; setIncludeSnapshot(value: boolean): void; diff --git a/src/tools/console.ts b/src/tools/console.ts index 2a56efb78..324e04a13 100644 --- a/src/tools/console.ts +++ b/src/tools/console.ts @@ -66,12 +66,18 @@ export const listConsoleMessages = defineTool({ .describe( 'Filter messages to only return messages of the specified resource types. When omitted or empty, returns all messages.', ), + includePreviousNavigations: zod + .boolean() + .default(false) + .optional() + .describe('Whether to include messages from previous navigations.'), }, handler: async (request, response) => { response.setIncludeConsoleData(true, { pageSize: request.params.pageSize, pageIdx: request.params.pageIdx, types: request.params.types, + includePreviousNavigations: request.params.includePreviousNavigations, }); }, }); From 0fe2b8a2b4d64b9da5f7d1adccc5425fd7cbec34 Mon Sep 17 00:00:00 2001 From: Alex Rudenko Date: Wed, 22 Oct 2025 12:32:16 +0200 Subject: [PATCH 011/456] feat: allow configuring tool categories (#454) This PR introduces a feature to disable some of the MCP server tool categories to tailor it to specific use cases. For example, ``` npx chrome-devtools-mcp@latest --no-category-emulation Disable tools in the emulation category npx chrome-devtools-mcp@latest --no-category-performance Disable tools in the performance category npx chrome-devtools-mcp@latest --no-category-network Disable tools in the network category ``` --- README.md | 15 +++++++++++++++ scripts/generate-docs.ts | 15 ++++++++------- src/cli.ts | 21 +++++++++++++++++++++ src/main.ts | 19 +++++++++++++++++++ src/tools/ToolDefinition.ts | 4 ++-- src/tools/categories.ts | 23 ++++++++++++++++------- src/tools/console.ts | 6 +++--- src/tools/emulation.ts | 6 +++--- src/tools/input.ts | 14 +++++++------- src/tools/network.ts | 6 +++--- src/tools/pages.ts | 18 +++++++++--------- src/tools/performance.ts | 8 ++++---- src/tools/screenshot.ts | 4 ++-- src/tools/script.ts | 4 ++-- src/tools/snapshot.ts | 6 +++--- tests/cli.test.ts | 35 +++++++++++++++++++++++++++++++++++ 16 files changed, 152 insertions(+), 52 deletions(-) diff --git a/README.md b/README.md index a4e8fab75..91faecdef 100644 --- a/README.md +++ b/README.md @@ -331,6 +331,21 @@ The Chrome DevTools MCP server supports the following configuration option: Additional arguments for Chrome. Only applies when Chrome is launched by chrome-devtools-mcp. - **Type:** array +- **`--categoryEmulation`** + Set to false to exlcude tools related to emulation. + - **Type:** boolean + - **Default:** `true` + +- **`--categoryPerformance`** + Set to false to exlcude tools related to performance. + - **Type:** boolean + - **Default:** `true` + +- **`--categoryNetwork`** + Set to false to exlcude tools related to network. + - **Type:** boolean + - **Default:** `true` + Pass them via the `args` property in the JSON configuration. For example: diff --git a/scripts/generate-docs.ts b/scripts/generate-docs.ts index aaba46317..7466ab939 100644 --- a/scripts/generate-docs.ts +++ b/scripts/generate-docs.ts @@ -11,7 +11,7 @@ import {StdioClientTransport} from '@modelcontextprotocol/sdk/client/stdio.js'; import type {Tool} from '@modelcontextprotocol/sdk/types.js'; import {cliOptions} from '../build/src/cli.js'; -import {ToolCategories} from '../build/src/tools/categories.js'; +import {ToolCategory, labels} from '../build/src/tools/categories.js'; const MCP_SERVER_PATH = 'build/src/index.js'; const OUTPUT_PATH = './docs/tool-reference.md'; @@ -21,7 +21,7 @@ const README_PATH = './README.md'; interface ToolWithAnnotations extends Tool { annotations?: { title?: string; - category?: ToolCategories; + category?: typeof ToolCategory; }; } @@ -67,7 +67,7 @@ function generateToolsTOC( for (const category of sortedCategories) { const categoryTools = categories[category]; - const categoryName = category; + const categoryName = labels[category]; toc += `- **${categoryName}** (${categoryTools.length} tools)\n`; // Sort tools within category for TOC @@ -209,7 +209,7 @@ async function generateToolDocumentation(): Promise { }); // Sort categories using the enum order - const categoryOrder = Object.values(ToolCategories); + const categoryOrder = Object.values(ToolCategory); const sortedCategories = Object.keys(categories).sort((a, b) => { const aIndex = categoryOrder.indexOf(a); const bIndex = categoryOrder.indexOf(b); @@ -223,8 +223,8 @@ async function generateToolDocumentation(): Promise { // Generate table of contents for (const category of sortedCategories) { const categoryTools = categories[category]; - const categoryName = category; - const anchorName = category.toLowerCase().replace(/\s+/g, '-'); + const categoryName = labels[category]; + const anchorName = categoryName.toLowerCase().replace(/\s+/g, '-'); markdown += `- **[${categoryName}](#${anchorName})** (${categoryTools.length} tools)\n`; // Sort tools within category for TOC @@ -239,8 +239,9 @@ async function generateToolDocumentation(): Promise { for (const category of sortedCategories) { const categoryTools = categories[category]; + const categoryName = labels[category]; - markdown += `## ${category}\n\n`; + markdown += `## ${categoryName}\n\n`; // Sort tools within category categoryTools.sort((a: Tool, b: Tool) => a.name.localeCompare(b.name)); diff --git a/src/cli.ts b/src/cli.ts index 54c832825..5e811ba25 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -139,6 +139,21 @@ export const cliOptions = { describe: 'Additional arguments for Chrome. Only applies when Chrome is launched by chrome-devtools-mcp.', }, + categoryEmulation: { + type: 'boolean', + default: true, + describe: 'Set to false to exlcude tools related to emulation.', + }, + categoryPerformance: { + type: 'boolean', + default: true, + describe: 'Set to false to exlcude tools related to performance.', + }, + categoryNetwork: { + type: 'boolean', + default: true, + describe: 'Set to false to exlcude tools related to network.', + }, } satisfies Record; export function parseArguments(version: string, argv = process.argv) { @@ -185,6 +200,12 @@ export function parseArguments(version: string, argv = process.argv) { `$0 --chrome-arg='--no-sandbox' --chrome-arg='--disable-setuid-sandbox'`, 'Launch Chrome without sandboxes. Use with caution.', ], + ['$0 --no-category-emulation', 'Disable tools in the emulation category'], + [ + '$0 --no-category-performance', + 'Disable tools in the performance category', + ], + ['$0 --no-category-network', 'Disable tools in the network category'], ]); return yargsInstance diff --git a/src/main.ts b/src/main.ts index c1fb8303b..44a08c715 100644 --- a/src/main.ts +++ b/src/main.ts @@ -19,6 +19,7 @@ import { type CallToolResult, SetLevelRequestSchema, } from './third_party/index.js'; +import {ToolCategory} from './tools/categories.js'; import * as consoleTools from './tools/console.js'; import * as emulationTools from './tools/emulation.js'; import * as inputTools from './tools/input.js'; @@ -96,6 +97,24 @@ Avoid sharing sensitive or personal information that you do not want to share wi const toolMutex = new Mutex(); function registerTool(tool: ToolDefinition): void { + if ( + tool.annotations.category === ToolCategory.EMULATION && + args.categoryEmulation === false + ) { + return; + } + if ( + tool.annotations.category === ToolCategory.PERFORMANCE && + args.categoryPerformance === false + ) { + return; + } + if ( + tool.annotations.category === ToolCategory.NETWORK && + args.categoryNetwork === false + ) { + return; + } server.registerTool( tool.name, { diff --git a/src/tools/ToolDefinition.ts b/src/tools/ToolDefinition.ts index d36d473d0..00b85a43e 100644 --- a/src/tools/ToolDefinition.ts +++ b/src/tools/ToolDefinition.ts @@ -10,7 +10,7 @@ import type {Dialog, ElementHandle, Page} from '../third_party/index.js'; import type {TraceResult} from '../trace-processing/parse.js'; import type {PaginationOptions} from '../utils/types.js'; -import type {ToolCategories} from './categories.js'; +import type {ToolCategory} from './categories.js'; export interface ToolDefinition< Schema extends zod.ZodRawShape = zod.ZodRawShape, @@ -19,7 +19,7 @@ export interface ToolDefinition< description: string; annotations: { title?: string; - category: ToolCategories; + category: ToolCategory; /** * If true, the tool does not modify its environment. */ diff --git a/src/tools/categories.ts b/src/tools/categories.ts index 084be6fef..f27a80361 100644 --- a/src/tools/categories.ts +++ b/src/tools/categories.ts @@ -4,11 +4,20 @@ * SPDX-License-Identifier: Apache-2.0 */ -export enum ToolCategories { - INPUT_AUTOMATION = 'Input automation', - NAVIGATION_AUTOMATION = 'Navigation automation', - EMULATION = 'Emulation', - PERFORMANCE = 'Performance', - NETWORK = 'Network', - DEBUGGING = 'Debugging', +export enum ToolCategory { + INPUT = 'input', + NAVIGATION = 'navigation', + EMULATION = 'emulation', + PERFORMANCE = 'performance', + NETWORK = 'network', + DEBUGGING = 'debugging', } + +export const labels = { + [ToolCategory.INPUT]: 'Input automation', + [ToolCategory.NAVIGATION]: 'Navigation automation', + [ToolCategory.EMULATION]: 'Emulation', + [ToolCategory.PERFORMANCE]: 'Performance', + [ToolCategory.NETWORK]: 'Network', + [ToolCategory.DEBUGGING]: 'Debugging', +}; diff --git a/src/tools/console.ts b/src/tools/console.ts index 324e04a13..7851930e2 100644 --- a/src/tools/console.ts +++ b/src/tools/console.ts @@ -7,7 +7,7 @@ import {zod} from '../third_party/index.js'; import type {ConsoleMessageType} from '../third_party/index.js'; -import {ToolCategories} from './categories.js'; +import {ToolCategory} from './categories.js'; import {defineTool} from './ToolDefinition.js'; const FILTERABLE_MESSAGE_TYPES: readonly [ @@ -40,7 +40,7 @@ export const listConsoleMessages = defineTool({ description: 'List all console messages for the currently selected page since the last navigation.', annotations: { - category: ToolCategories.DEBUGGING, + category: ToolCategory.DEBUGGING, readOnlyHint: true, }, schema: { @@ -86,7 +86,7 @@ export const getConsoleMessage = defineTool({ name: 'get_console_message', description: `Gets a console message by its ID. You can get all messages by calling ${listConsoleMessages.name}.`, annotations: { - category: ToolCategories.DEBUGGING, + category: ToolCategory.DEBUGGING, readOnlyHint: true, }, schema: { diff --git a/src/tools/emulation.ts b/src/tools/emulation.ts index eceaa53bb..2222e4e3e 100644 --- a/src/tools/emulation.ts +++ b/src/tools/emulation.ts @@ -6,7 +6,7 @@ import {zod, PredefinedNetworkConditions} from '../third_party/index.js'; -import {ToolCategories} from './categories.js'; +import {ToolCategory} from './categories.js'; import {defineTool} from './ToolDefinition.js'; const throttlingOptions: [string, ...string[]] = [ @@ -19,7 +19,7 @@ export const emulateNetwork = defineTool({ name: 'emulate_network', description: `Emulates network conditions such as throttling or offline mode on the selected page.`, annotations: { - category: ToolCategories.EMULATION, + category: ToolCategory.EMULATION, readOnlyHint: false, }, schema: { @@ -65,7 +65,7 @@ export const emulateCpu = defineTool({ name: 'emulate_cpu', description: `Emulates CPU throttling by slowing down the selected page's execution.`, annotations: { - category: ToolCategories.EMULATION, + category: ToolCategory.EMULATION, readOnlyHint: false, }, schema: { diff --git a/src/tools/input.ts b/src/tools/input.ts index 78a1372a6..9913ee560 100644 --- a/src/tools/input.ts +++ b/src/tools/input.ts @@ -8,14 +8,14 @@ import type {McpContext, TextSnapshotNode} from '../McpContext.js'; import {zod} from '../third_party/index.js'; import type {ElementHandle} from '../third_party/index.js'; -import {ToolCategories} from './categories.js'; +import {ToolCategory} from './categories.js'; import {defineTool} from './ToolDefinition.js'; export const click = defineTool({ name: 'click', description: `Clicks on the provided element`, annotations: { - category: ToolCategories.INPUT_AUTOMATION, + category: ToolCategory.INPUT, readOnlyHint: false, }, schema: { @@ -54,7 +54,7 @@ export const hover = defineTool({ name: 'hover', description: `Hover over the provided element`, annotations: { - category: ToolCategories.INPUT_AUTOMATION, + category: ToolCategory.INPUT, readOnlyHint: false, }, schema: { @@ -138,7 +138,7 @@ export const fill = defineTool({ name: 'fill', description: `Type text into a input, text area or select an option from a `); await page.focus('button'); - response.setIncludeSnapshot(true); + response.includeSnapshot(); const result = await response.handle('test', context); assert.equal(result[0].type, 'text'); assert.strictEqual( @@ -80,7 +83,7 @@ uid=1_0 RootWebArea />`, ); await page.focus('input'); - response.setIncludeSnapshot(true); + response.includeSnapshot(); const result = await response.handle('test', context); assert.equal(result[0].type, 'text'); assert.strictEqual( @@ -99,7 +102,9 @@ uid=1_0 RootWebArea "My test page" await withBrowser(async (response, context) => { const page = context.getSelectedPage(); await page.setContent(html``); - response.setIncludeSnapshot(true, true); + response.includeSnapshot({ + verbose: true, + }); const result = await response.handle('test', context); assert.equal(result[0].type, 'text'); assert.strictEqual( @@ -117,6 +122,42 @@ uid=1_0 RootWebArea "My test page" }); }); + it('saves snapshot to file', async () => { + const filePath = join(tmpdir(), 'test-screenshot.png'); + try { + await withBrowser(async (response, context) => { + const page = context.getSelectedPage(); + await page.setContent(html``); + response.includeSnapshot({ + verbose: true, + filePath, + }); + const result = await response.handle('test', context); + assert.equal(result[0].type, 'text'); + console.log(result[0].text); + assert.strictEqual( + result[0].text, + `# test response +## Page content +Saved snapshot to ${filePath}.`, + ); + }); + const content = await readFile(filePath, 'utf-8'); + assert.strictEqual( + content, + `uid=1_0 RootWebArea "My test page" + uid=1_1 ignored + uid=1_2 ignored + uid=1_3 complementary + uid=1_4 StaticText "test" + uid=1_5 InlineTextBox "test" +`, + ); + } finally { + await rm(filePath, {force: true}); + } + }); + it('adds throttling setting when it is not null', async () => { await withBrowser(async (response, context) => { context.setNetworkConditions('Slow 3G'); diff --git a/tests/tools/input.test.ts b/tests/tools/input.test.ts index 8a6211739..d819d80a5 100644 --- a/tests/tools/input.test.ts +++ b/tests/tools/input.test.ts @@ -427,7 +427,7 @@ describe('input', () => { ); assert.strictEqual(response.responseLines.length, 0); - assert.strictEqual(response.includeSnapshot, false); + assert.strictEqual(response.snapshotParams, undefined); await fs.unlink(testFilePath); }); From fc2f0586f49145229ae3269fe65f2f50536ed60a Mon Sep 17 00:00:00 2001 From: Nikolay Vitkov <34244704+Lightning00Blade@users.noreply.github.com> Date: Fri, 24 Oct 2025 12:51:42 +0200 Subject: [PATCH 020/456] chore: update bug template (#464) --- .github/ISSUE_TEMPLATE/01-bug.yml | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/01-bug.yml b/.github/ISSUE_TEMPLATE/01-bug.yml index 84e643875..357423ef0 100644 --- a/.github/ISSUE_TEMPLATE/01-bug.yml +++ b/.github/ISSUE_TEMPLATE/01-bug.yml @@ -31,15 +31,16 @@ body: - id: mcp-configuration type: textarea + render: json attributes: label: MCP configuration - - id: node-version + - id: chrome-devtools-mcp-version type: input attributes: - label: Node version - description: > - Please verify you have the minimal supported version listed in the README.md + label: Chrome DevTools MCP version + validations: + required: true - id: chrome-version type: input @@ -61,6 +62,13 @@ body: attributes: label: Chat log + - id: node-version + type: input + attributes: + label: Node version + description: > + Please verify you have the minimal supported version listed in the README.md + - id: operating-system type: dropdown attributes: @@ -68,6 +76,7 @@ body: description: What supported operating system are you running? options: - Windows + - Windows Subsystem for Linux (WSL) - macOS - Linux From a41e4407996b8090f8cccc85f6c4696006fc31ec Mon Sep 17 00:00:00 2001 From: Alex Rudenko Date: Fri, 24 Oct 2025 14:30:31 +0200 Subject: [PATCH 021/456] refactor: connect to DevTools targets by default (#466) in preparation for integration with DevTools, this PR moves the pages filtering to the MCP context. --------- Co-authored-by: Nikolay Vitkov <34244704+Lightning00Blade@users.noreply.github.com> --- src/McpContext.ts | 18 ++++++++++++++++-- src/browser.ts | 9 +++------ src/main.ts | 4 +++- tests/utils.ts | 9 ++++++++- 4 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src/McpContext.ts b/src/McpContext.ts index eb12e7da8..adee09212 100644 --- a/src/McpContext.ts +++ b/src/McpContext.ts @@ -39,6 +39,11 @@ export interface TextSnapshot { snapshotId: string; } +interface McpContextOptions { + // Whether the DevTools windows are exposed as pages. + devtools: boolean; +} + const DEFAULT_TIMEOUT = 5_000; const NAVIGATION_TIMEOUT = 10_000; @@ -92,15 +97,18 @@ export class McpContext implements Context { #traceResults: TraceResult[] = []; #locatorClass: typeof Locator; + #options: McpContextOptions; private constructor( browser: Browser, logger: Debugger, + options: McpContextOptions, locatorClass: typeof Locator, ) { this.browser = browser; this.logger = logger; this.#locatorClass = locatorClass; + this.#options = options; this.#networkCollector = new NetworkCollector(this.browser); @@ -132,10 +140,11 @@ export class McpContext implements Context { static async from( browser: Browser, logger: Debugger, + opts: McpContextOptions, /* Let tests use unbundled Locator class to avoid overly strict checks within puppeteer that fail when mixing bundled and unbundled class instances */ locatorClass: typeof Locator = Locator, ) { - const context = new McpContext(browser, logger, locatorClass); + const context = new McpContext(browser, logger, opts, locatorClass); await context.#init(); return context; } @@ -315,7 +324,12 @@ export class McpContext implements Context { * Creates a snapshot of the pages. */ async createPagesSnapshot(): Promise { - this.#pages = await this.browser.pages(); + this.#pages = (await this.browser.pages()).filter(page => { + if (page.url().startsWith('devtools://')) { + return this.#options.devtools; + } + return true; + }); return this.#pages; } diff --git a/src/browser.ts b/src/browser.ts index 1947651b9..88765a7d0 100644 --- a/src/browser.ts +++ b/src/browser.ts @@ -18,16 +18,13 @@ import {puppeteer} from './third_party/index.js'; let browser: Browser | undefined; -function makeTargetFilter(devtools: boolean) { +function makeTargetFilter() { const ignoredPrefixes = new Set([ 'chrome://', 'chrome-extension://', 'chrome-untrusted://', ]); - if (!devtools) { - ignoredPrefixes.add('devtools://'); - } return function targetFilter(target: Target): boolean { if (target.url() === 'chrome://newtab/') { return true; @@ -52,7 +49,7 @@ export async function ensureBrowserConnected(options: { } const connectOptions: Parameters[0] = { - targetFilter: makeTargetFilter(options.devtools), + targetFilter: makeTargetFilter(), defaultViewport: null, handleDevToolsAsPage: options.devtools, }; @@ -129,7 +126,7 @@ export async function launch(options: McpLaunchOptions): Promise { try { const browser = await puppeteer.launch({ channel: puppeteerChannel, - targetFilter: makeTargetFilter(options.devtools), + targetFilter: makeTargetFilter(), executablePath, defaultViewport: null, userDataDir, diff --git a/src/main.ts b/src/main.ts index 6dd783fbd..38f39cbe9 100644 --- a/src/main.ts +++ b/src/main.ts @@ -81,7 +81,9 @@ async function getContext(): Promise { }); if (context?.browser !== browser) { - context = await McpContext.from(browser, logger); + context = await McpContext.from(browser, logger, { + devtools, + }); } return context; } diff --git a/tests/utils.ts b/tests/utils.ts index 17e86ca24..0c071f90d 100644 --- a/tests/utils.ts +++ b/tests/utils.ts @@ -36,7 +36,14 @@ export async function withBrowser( }), ); const response = new McpResponse(); - const context = await McpContext.from(browser, logger('test'), Locator); + const context = await McpContext.from( + browser, + logger('test'), + { + devtools: false, + }, + Locator, + ); await cb(response, context); } From 66557071d42a7e6dcd6e4f6f340e0ffc51eae329 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Oct 2025 10:30:58 +0100 Subject: [PATCH 022/456] chore(deps-dev): bump chrome-devtools-frontend from 1.0.1532884 to 1.0.1534717 in the chrome-devtools-frontend group (#472) Bumps the chrome-devtools-frontend group with 1 update: [chrome-devtools-frontend](https://github.com/ChromeDevTools/devtools-frontend). Updates `chrome-devtools-frontend` from 1.0.1532884 to 1.0.1534717
Commits
  • 06129cc Update DevTools DEPS (trusted)
  • 5a5fdd6 [cleanup] Prefer assert.exist
  • e1ec444 Add targetOrigin parameter to postMessage in RehydratingConnection
  • 61f924e RPP: fix font family in screenshots
  • fd282ef Update DevTools DEPS (trusted)
  • 3e1f3a5 [Console Insights Teasers] UMA for teaser generation time
  • 350807c Reland "[cleanup] Remove old things from front_end/devtools_compatibility.js"
  • 18fa374 [Console Insights Teasers] UMA for model and GPU availability
  • 72fec1d Update Chrome (for Testing) PIN
  • d5701ce [cleanup] Remove WindowBoundsService
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=chrome-devtools-frontend&package-manager=npm_and_yarn&previous-version=1.0.1532884&new-version=1.0.1534717)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index da0c2647f..d5310edc2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,7 +25,7 @@ "@types/yargs": "^17.0.33", "@typescript-eslint/eslint-plugin": "^8.43.0", "@typescript-eslint/parser": "^8.43.0", - "chrome-devtools-frontend": "1.0.1532884", + "chrome-devtools-frontend": "1.0.1534717", "core-js": "3.46.0", "debug": "4.4.3", "eslint": "^9.35.0", @@ -2224,9 +2224,9 @@ } }, "node_modules/chrome-devtools-frontend": { - "version": "1.0.1532884", - "resolved": "https://registry.npmjs.org/chrome-devtools-frontend/-/chrome-devtools-frontend-1.0.1532884.tgz", - "integrity": "sha512-AX7J+CHhwKEgN3+8g3DEl60IiVTGxaC8lHM3X9UjfOSFvBFvSUnHXfZ2Dlc0xa+xQJgcuHlyounCSNkyBIYAoQ==", + "version": "1.0.1534717", + "resolved": "https://registry.npmjs.org/chrome-devtools-frontend/-/chrome-devtools-frontend-1.0.1534717.tgz", + "integrity": "sha512-+X86ZVMM9CyTY750CMPgfdL5vEq9VFPOjkhtQ2hhAKiIRSkJHH0VLj9A+Oeat4+Kl8ojkm1bZPjGvppPcc2+zw==", "dev": true, "license": "BSD-3-Clause" }, diff --git a/package.json b/package.json index 8859732dc..0fdb3a90e 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "@types/yargs": "^17.0.33", "@typescript-eslint/eslint-plugin": "^8.43.0", "@typescript-eslint/parser": "^8.43.0", - "chrome-devtools-frontend": "1.0.1532884", + "chrome-devtools-frontend": "1.0.1534717", "core-js": "3.46.0", "debug": "4.4.3", "eslint": "^9.35.0", From 1560ff23cad28ab63c1cf9fb1b961db886bc4a3e Mon Sep 17 00:00:00 2001 From: Alex Rudenko Date: Mon, 27 Oct 2025 10:53:34 +0100 Subject: [PATCH 023/456] refactor: detect DevTools page for each page (#467) --- src/DevtoolsUtils.ts | 51 ++++++++++++++++++++++++++ src/McpContext.ts | 54 ++++++++++++++++++++++++---- src/browser.ts | 4 +-- src/main.ts | 2 +- tests/DevtoolsUtils.test.ts | 71 +++++++++++++++++++++++++++++++++++++ tests/McpContext.test.ts | 18 ++++++++++ tests/utils.ts | 8 +++-- 7 files changed, 195 insertions(+), 13 deletions(-) create mode 100644 src/DevtoolsUtils.ts create mode 100644 tests/DevtoolsUtils.test.ts diff --git a/src/DevtoolsUtils.ts b/src/DevtoolsUtils.ts new file mode 100644 index 000000000..8b12b3a91 --- /dev/null +++ b/src/DevtoolsUtils.ts @@ -0,0 +1,51 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ +export function extractUrlLikeFromDevToolsTitle( + title: string, +): string | undefined { + const match = title.match(new RegExp(`DevTools - (.*)`)); + return match?.[1] ?? undefined; +} + +export function urlsEqual(url1: string, url2: string): boolean { + const normalizedUrl1 = normalizeUrl(url1); + const normalizedUrl2 = normalizeUrl(url2); + return normalizedUrl1 === normalizedUrl2; +} + +/** + * For the sake of the MCP server, when we determine if two URLs are equal we + * remove some parts: + * + * 1. We do not care about the protocol. + * 2. We do not care about trailing slashes. + * 3. We do not care about "www". + * + * For example, if the user types "record a trace on foo.com", we would want to + * match a tab in the connected Chrome instance that is showing "www.foo.com/" + */ +function normalizeUrl(url: string): string { + let result = url.trim(); + + // Remove protocols + if (result.startsWith('https://')) { + result = result.slice(8); + } else if (result.startsWith('http://')) { + result = result.slice(7); + } + + // Remove 'www.'. This ensures that we find the right URL regardless of if the user adds `www` or not. + if (result.startsWith('www.')) { + result = result.slice(4); + } + + // Remove trailing slash + if (result.endsWith('/')) { + result = result.slice(0, -1); + } + + return result; +} diff --git a/src/McpContext.ts b/src/McpContext.ts index adee09212..db7ab0028 100644 --- a/src/McpContext.ts +++ b/src/McpContext.ts @@ -7,6 +7,7 @@ import fs from 'node:fs/promises'; import os from 'node:os'; import path from 'node:path'; +import {extractUrlLikeFromDevToolsTitle, urlsEqual} from './DevtoolsUtils.js'; import type {ListenerMap} from './PageCollector.js'; import {NetworkCollector, PageCollector} from './PageCollector.js'; import {Locator} from './third_party/index.js'; @@ -40,8 +41,8 @@ export interface TextSnapshot { } interface McpContextOptions { - // Whether the DevTools windows are exposed as pages. - devtools: boolean; + // Whether the DevTools windows are exposed as pages for debugging of DevTools. + experimentalDevToolsDebugging: boolean; } const DEFAULT_TIMEOUT = 5_000; @@ -82,6 +83,7 @@ export class McpContext implements Context { // The most recent page state. #pages: Page[] = []; + #pageToDevToolsPage = new Map(); #selectedPageIdx = 0; // The most recent snapshot. #textSnapshot: TextSnapshot | null = null; @@ -324,19 +326,57 @@ export class McpContext implements Context { * Creates a snapshot of the pages. */ async createPagesSnapshot(): Promise { - this.#pages = (await this.browser.pages()).filter(page => { - if (page.url().startsWith('devtools://')) { - return this.#options.devtools; - } - return true; + const allPages = await this.browser.pages(); + + this.#pages = allPages.filter(page => { + // If we allow debugging DevTools windows, return all pages. + // If we are in regular mode, the user should only see non-DevTools page. + return ( + this.#options.experimentalDevToolsDebugging || + !page.url().startsWith('devtools://') + ); }); + + await this.#detectOpenDevToolsWindows(allPages); + return this.#pages; } + async #detectOpenDevToolsWindows(pages: Page[]) { + this.#pageToDevToolsPage = new Map(); + for (const devToolsPage of pages) { + if (devToolsPage.url().startsWith('devtools://')) { + try { + const data = await devToolsPage + // @ts-expect-error no types for _client(). + ._client() + .send('Target.getTargetInfo'); + const devtoolsPageTitle = data.targetInfo.title; + const urlLike = extractUrlLikeFromDevToolsTitle(devtoolsPageTitle); + if (!urlLike) { + continue; + } + // TODO: lookup without a loop. + for (const page of this.#pages) { + if (urlsEqual(page.url(), urlLike)) { + this.#pageToDevToolsPage.set(page, devToolsPage); + } + } + } catch { + // no-op + } + } + } + } + getPages(): Page[] { return this.#pages; } + getDevToolsPage(page: Page): Page | undefined { + return this.#pageToDevToolsPage.get(page); + } + /** * Creates a text snapshot of a page. */ diff --git a/src/browser.ts b/src/browser.ts index 88765a7d0..d0c99c7f2 100644 --- a/src/browser.ts +++ b/src/browser.ts @@ -51,7 +51,7 @@ export async function ensureBrowserConnected(options: { const connectOptions: Parameters[0] = { targetFilter: makeTargetFilter(), defaultViewport: null, - handleDevToolsAsPage: options.devtools, + handleDevToolsAsPage: true, }; if (options.wsEndpoint) { @@ -134,7 +134,7 @@ export async function launch(options: McpLaunchOptions): Promise { headless, args, acceptInsecureCerts: options.acceptInsecureCerts, - handleDevToolsAsPage: options.devtools, + handleDevToolsAsPage: true, }); if (options.logFile) { // FIXME: we are probably subscribing too late to catch startup logs. We diff --git a/src/main.ts b/src/main.ts index 38f39cbe9..3443cecc4 100644 --- a/src/main.ts +++ b/src/main.ts @@ -82,7 +82,7 @@ async function getContext(): Promise { if (context?.browser !== browser) { context = await McpContext.from(browser, logger, { - devtools, + experimentalDevToolsDebugging: devtools, }); } return context; diff --git a/tests/DevtoolsUtils.test.ts b/tests/DevtoolsUtils.test.ts new file mode 100644 index 000000000..2c1300841 --- /dev/null +++ b/tests/DevtoolsUtils.test.ts @@ -0,0 +1,71 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ +import assert from 'node:assert'; +import {describe, it} from 'node:test'; + +import { + extractUrlLikeFromDevToolsTitle, + urlsEqual, +} from '../src/DevtoolsUtils.js'; + +describe('extractUrlFromDevToolsTitle', () => { + it('deals with no trailing /', () => { + assert.strictEqual( + extractUrlLikeFromDevToolsTitle('DevTools - example.com'), + 'example.com', + ); + }); + it('deals with a trailing /', () => { + assert.strictEqual( + extractUrlLikeFromDevToolsTitle('DevTools - example.com/'), + 'example.com/', + ); + }); + it('deals with www', () => { + assert.strictEqual( + extractUrlLikeFromDevToolsTitle('DevTools - www.example.com/'), + 'www.example.com/', + ); + }); + it('deals with complex url', () => { + assert.strictEqual( + extractUrlLikeFromDevToolsTitle( + 'DevTools - www.example.com/path.html?a=b#3', + ), + 'www.example.com/path.html?a=b#3', + ); + }); +}); + +describe('urlsEqual', () => { + it('ignores trailing slashes', () => { + assert.strictEqual( + urlsEqual('https://google.com/', 'https://google.com'), + true, + ); + }); + + it('ignores www', () => { + assert.strictEqual( + urlsEqual('https://google.com/', 'https://www.google.com'), + true, + ); + }); + + it('ignores protocols', () => { + assert.strictEqual( + urlsEqual('https://google.com/', 'http://www.google.com'), + true, + ); + }); + + it('does not ignore other subdomains', () => { + assert.strictEqual( + urlsEqual('https://google.com/', 'https://photos.google.com'), + false, + ); + }); +}); diff --git a/tests/McpContext.test.ts b/tests/McpContext.test.ts index a054baba9..64e2110d2 100644 --- a/tests/McpContext.test.ts +++ b/tests/McpContext.test.ts @@ -78,4 +78,22 @@ describe('McpContext', () => { sinon.assert.calledWithExactly(stub, page, 2, 10); }); }); + + it('should should detect open DevTools pages', async () => { + await withBrowser( + async (_response, context) => { + const page = await context.newPage(); + // TODO: we do not know when the CLI flag to auto open DevTools will run + // so we need this until + // https://github.com/puppeteer/puppeteer/issues/14368 is there. + await new Promise(resolve => setTimeout(resolve, 5000)); + await context.createPagesSnapshot(); + assert.ok(context.getDevToolsPage(page)); + }, + { + autoOpenDevToos: true, + force: true, + }, + ); + }); }); diff --git a/tests/utils.ts b/tests/utils.ts index 0c071f90d..d366767d7 100644 --- a/tests/utils.ts +++ b/tests/utils.ts @@ -16,14 +16,16 @@ let browser: Browser | undefined; export async function withBrowser( cb: (response: McpResponse, context: McpContext) => Promise, - options: {debug?: boolean} = {}, + options: {debug?: boolean; autoOpenDevToos?: boolean; force?: boolean} = {}, ) { const {debug = false} = options; - if (!browser) { + if (!browser || options.force) { browser = await puppeteer.launch({ executablePath: process.env.PUPPETEER_EXECUTABLE_PATH, headless: !debug, defaultViewport: null, + devtools: options.autoOpenDevToos ?? false, + handleDevToolsAsPage: true, }); } const newPage = await browser.newPage(); @@ -40,7 +42,7 @@ export async function withBrowser( browser, logger('test'), { - devtools: false, + experimentalDevToolsDebugging: false, }, Locator, ); From 73be1b44247d03e2a7bc91e2ee59f76bc9795cdc Mon Sep 17 00:00:00 2001 From: Nikolay Vitkov <34244704+Lightning00Blade@users.noreply.github.com> Date: Mon, 27 Oct 2025 12:30:33 +0100 Subject: [PATCH 024/456] build: extract bundled in a separate config (#474) --- .github/dependabot.yml | 12 +++++++++--- src/McpContext.ts | 4 ++-- tests/McpContext.test.ts | 3 +-- tests/utils.ts | 26 +++++++++++++++----------- 4 files changed, 27 insertions(+), 18 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index d5de32cac..942421fa2 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -17,14 +17,20 @@ updates: exclude-patterns: - 'puppeteer*' - 'chrome-devtools-frontend' + - '@modelcontextprotocol/sdk' + - 'yargs' + - 'debug' + - 'core-js' patterns: - '*' - puppeteer: + bundled: patterns: - 'puppeteer*' - chrome-devtools-frontend: - patterns: - 'chrome-devtools-frontend' + - '@modelcontextprotocol/sdk' + - 'yargs' + - 'debug' + - 'core-js' - package-ecosystem: github-actions directory: / schedule: diff --git a/src/McpContext.ts b/src/McpContext.ts index db7ab0028..586c2ba71 100644 --- a/src/McpContext.ts +++ b/src/McpContext.ts @@ -362,8 +362,8 @@ export class McpContext implements Context { this.#pageToDevToolsPage.set(page, devToolsPage); } } - } catch { - // no-op + } catch (error) { + this.logger('Issue occurred while trying to find DevTools', error); } } } diff --git a/tests/McpContext.test.ts b/tests/McpContext.test.ts index 64e2110d2..573271179 100644 --- a/tests/McpContext.test.ts +++ b/tests/McpContext.test.ts @@ -91,8 +91,7 @@ describe('McpContext', () => { assert.ok(context.getDevToolsPage(page)); }, { - autoOpenDevToos: true, - force: true, + autoOpenDevTools: true, }, ); }); diff --git a/tests/utils.ts b/tests/utils.ts index d366767d7..92834db6d 100644 --- a/tests/utils.ts +++ b/tests/utils.ts @@ -12,21 +12,25 @@ import {McpContext} from '../src/McpContext.js'; import {McpResponse} from '../src/McpResponse.js'; import {stableIdSymbol} from '../src/PageCollector.js'; -let browser: Browser | undefined; +const browsers = new Map(); export async function withBrowser( cb: (response: McpResponse, context: McpContext) => Promise, - options: {debug?: boolean; autoOpenDevToos?: boolean; force?: boolean} = {}, + options: {debug?: boolean; autoOpenDevTools?: boolean} = {}, ) { - const {debug = false} = options; - if (!browser || options.force) { - browser = await puppeteer.launch({ - executablePath: process.env.PUPPETEER_EXECUTABLE_PATH, - headless: !debug, - defaultViewport: null, - devtools: options.autoOpenDevToos ?? false, - handleDevToolsAsPage: true, - }); + const launchOptions = { + executablePath: process.env.PUPPETEER_EXECUTABLE_PATH, + headless: !options.debug, + defaultViewport: null, + devtools: options.autoOpenDevTools ?? false, + handleDevToolsAsPage: true, + }; + const key = JSON.stringify(launchOptions); + + let browser = browsers.get(key); + if (!browser) { + browser = await puppeteer.launch(launchOptions); + browsers.set(key, browser); } const newPage = await browser.newPage(); // Close other pages. From 376f57c972f43bf680ab6f46738558f83ee8bf83 Mon Sep 17 00:00:00 2001 From: Nikolay Vitkov <34244704+Lightning00Blade@users.noreply.github.com> Date: Mon, 27 Oct 2025 13:32:47 +0100 Subject: [PATCH 025/456] test: use html helper (#478) --- tests/McpContext.test.ts | 11 ++- tests/McpResponse.test.ts | 11 ++- tests/tools/input.test.ts | 136 ++++++++++++++++++++++++-------------- 3 files changed, 104 insertions(+), 54 deletions(-) diff --git a/tests/McpContext.test.ts b/tests/McpContext.test.ts index 573271179..63887a05a 100644 --- a/tests/McpContext.test.ts +++ b/tests/McpContext.test.ts @@ -10,14 +10,19 @@ import sinon from 'sinon'; import type {TraceResult} from '../src/trace-processing/parse.js'; -import {withBrowser} from './utils.js'; +import {html, withBrowser} from './utils.js'; describe('McpContext', () => { it('list pages', async () => { await withBrowser(async (_response, context) => { const page = context.getSelectedPage(); - await page.setContent(` -`); + await page.setContent( + html``, + ); await context.createTextSnapshot(); assert.ok(await context.getElementByUid('1_1')); await context.createTextSnapshot(); diff --git a/tests/McpResponse.test.ts b/tests/McpResponse.test.ts index 29dad971d..1c6678d54 100644 --- a/tests/McpResponse.test.ts +++ b/tests/McpResponse.test.ts @@ -54,8 +54,13 @@ Testing 2`, it('returns correctly formatted snapshot for a simple tree', async () => { await withBrowser(async (response, context) => { const page = context.getSelectedPage(); - await page.setContent(` -`); + await page.setContent( + html``, + ); await page.focus('button'); response.includeSnapshot(); const result = await response.handle('test', context); @@ -64,7 +69,7 @@ Testing 2`, result[0].text, `# test response ## Page content -uid=1_0 RootWebArea +uid=1_0 RootWebArea "My test page" uid=1_1 button "Click me" focusable focused uid=1_2 textbox value="Input" `, diff --git a/tests/tools/input.test.ts b/tests/tools/input.test.ts index d819d80a5..b6f39622a 100644 --- a/tests/tools/input.test.ts +++ b/tests/tools/input.test.ts @@ -29,7 +29,7 @@ describe('input', () => { await withBrowser(async (response, context) => { const page = context.getSelectedPage(); await page.setContent( - ``, ); await context.createTextSnapshot(); await click.handler( @@ -53,7 +53,9 @@ describe('input', () => { await withBrowser(async (response, context) => { const page = context.getSelectedPage(); await page.setContent( - ``, ); await context.createTextSnapshot(); await click.handler( @@ -158,7 +160,7 @@ describe('input', () => { await withBrowser(async (response, context) => { const page = context.getSelectedPage(); await page.setContent( - ``, ); await context.createTextSnapshot(); await hover.handler( @@ -184,7 +186,7 @@ describe('input', () => { it('fills out an input', async () => { await withBrowser(async (response, context) => { const page = context.getSelectedPage(); - await page.setContent(``); + await page.setContent(html``); await context.createTextSnapshot(); await fill.handler( { @@ -209,7 +211,10 @@ describe('input', () => { await withBrowser(async (response, context) => { const page = context.getSelectedPage(); await page.setContent( - ``, + html``, ); await context.createTextSnapshot(); await fill.handler( @@ -239,25 +244,35 @@ describe('input', () => { it('drags one element onto another', async () => { await withBrowser(async (response, context) => { const page = context.getSelectedPage(); - await page.setContent(` -
drag me
-
-
-`); + await page.setContent( + html`
drag me
+
+
+ `, + ); await context.createTextSnapshot(); await drag.handler( { @@ -283,12 +298,24 @@ describe('input', () => { it('successfully fills out the form', async () => { await withBrowser(async (response, context) => { const page = context.getSelectedPage(); - await page.setContent(` -
- - - -
`); + await page.setContent( + html`
+ + + +
`, + ); await context.createTextSnapshot(); await fillForm.handler( { @@ -335,10 +362,14 @@ describe('input', () => { await withBrowser(async (response, context) => { const page = context.getSelectedPage(); - await page.setContent(` -
- -
`); + await page.setContent( + html`
+ +
`, + ); await context.createTextSnapshot(); await uploadFile.handler( { @@ -366,14 +397,21 @@ describe('input', () => { await withBrowser(async (response, context) => { const page = context.getSelectedPage(); - await page.setContent(` - - -`); + await page.setContent( + html` + + `, + ); await context.createTextSnapshot(); await uploadFile.handler( { @@ -406,7 +444,7 @@ describe('input', () => { await withBrowser(async (response, context) => { const page = context.getSelectedPage(); - await page.setContent(`
Not a file input
`); + await page.setContent(html`
Not a file input
`); await context.createTextSnapshot(); await assert.rejects( @@ -465,11 +503,13 @@ describe('input', () => { it('processes press_key', async () => { await withBrowser(async (response, context) => { const page = context.getSelectedPage(); - await page.setContent(``); + await page.setContent( + html``, + ); await context.createTextSnapshot(); await pressKey.handler( From 04deff21a43b217b799a7bd5be7927e726f015e4 Mon Sep 17 00:00:00 2001 From: Nikolay Vitkov <34244704+Lightning00Blade@users.noreply.github.com> Date: Mon, 27 Oct 2025 13:41:04 +0100 Subject: [PATCH 026/456] test: add Puppeteer config (#479) Effectively this means we won't download chrome-headless-shell by default --- eslint.config.mjs | 1 + puppeteer.config.cjs | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 puppeteer.config.cjs diff --git a/eslint.config.mjs b/eslint.config.mjs index 2dcb3cecb..3a17c16d0 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -29,6 +29,7 @@ export default defineConfig([ projectService: { allowDefaultProject: [ '.prettierrc.cjs', + 'puppeteer.config.cjs', 'eslint.config.mjs', 'rollup.config.mjs', ], diff --git a/puppeteer.config.cjs b/puppeteer.config.cjs new file mode 100644 index 000000000..d162fc0fd --- /dev/null +++ b/puppeteer.config.cjs @@ -0,0 +1,20 @@ +/** + * @license + * Copyright 2025 Google Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @type {import("puppeteer").Configuration} + */ +module.exports = { + chrome: { + skipDownload: false, + }, + ['chrome-headless-shell']: { + skipDownload: true, + }, + firefox: { + skipDownload: true, + }, +}; From 02e0765293116ce09acc7ee35eb1e5b6b1270e5c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Oct 2025 12:50:23 +0000 Subject: [PATCH 027/456] chore(deps-dev): bump the dev-dependencies group across 1 directory with 2 updates (#480) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the dev-dependencies group with 2 updates in the / directory: [@rollup/plugin-commonjs](https://github.com/rollup/plugins/tree/HEAD/packages/commonjs) and [@types/yargs](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/yargs). Updates `@rollup/plugin-commonjs` from 28.0.8 to 28.0.9
Changelog

Sourced from @​rollup/plugin-commonjs's changelog.

v28.0.9

2025-10-24

Bugfixes

  • fix: handle node: builtins with strictRequires: auto (#1930)
Commits

Updates `@types/yargs` from 17.0.33 to 17.0.34
Commits

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index d5310edc2..1017ebb85 100644 --- a/package-lock.json +++ b/package-lock.json @@ -584,9 +584,9 @@ } }, "node_modules/@rollup/plugin-commonjs": { - "version": "28.0.8", - "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-28.0.8.tgz", - "integrity": "sha512-o1Ug9PxYsF61R7/NXO/GgMZZproLd/WH2XA53Tp9ppf6bU1lMlTtC/gUM6zM3mesi2E0rypk+PNtVrELREyWEQ==", + "version": "28.0.9", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-28.0.9.tgz", + "integrity": "sha512-PIR4/OHZ79romx0BVVll/PkwWpJ7e5lsqFa3gFfcrFPWwLXLV39JVUzQV9RKjWerE7B845Hqjj9VYlQeieZ2dA==", "dev": true, "license": "MIT", "dependencies": { @@ -1164,9 +1164,9 @@ "license": "MIT" }, "node_modules/@types/yargs": { - "version": "17.0.33", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", - "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "version": "17.0.34", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.34.tgz", + "integrity": "sha512-KExbHVa92aJpw9WDQvzBaGVE2/Pz+pLZQloT2hjL8IqsZnV62rlPOYvNnLmf/L2dyllfVUOVBj64M0z/46eR2A==", "dev": true, "license": "MIT", "dependencies": { From 43897afd1d18e98e1db7a0c6cbc831e9c1e1547d Mon Sep 17 00:00:00 2001 From: Nikolay Vitkov <34244704+Lightning00Blade@users.noreply.github.com> Date: Mon, 27 Oct 2025 14:04:35 +0100 Subject: [PATCH 028/456] test: switch to pipes (#482) --- tests/utils.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/utils.ts b/tests/utils.ts index 92834db6d..03cf84f91 100644 --- a/tests/utils.ts +++ b/tests/utils.ts @@ -6,7 +6,12 @@ import logger from 'debug'; import type {Browser} from 'puppeteer'; import puppeteer, {Locator} from 'puppeteer'; -import type {Frame, HTTPRequest, HTTPResponse} from 'puppeteer-core'; +import type { + Frame, + HTTPRequest, + HTTPResponse, + LaunchOptions, +} from 'puppeteer-core'; import {McpContext} from '../src/McpContext.js'; import {McpResponse} from '../src/McpResponse.js'; @@ -18,11 +23,12 @@ export async function withBrowser( cb: (response: McpResponse, context: McpContext) => Promise, options: {debug?: boolean; autoOpenDevTools?: boolean} = {}, ) { - const launchOptions = { + const launchOptions: LaunchOptions = { executablePath: process.env.PUPPETEER_EXECUTABLE_PATH, headless: !options.debug, defaultViewport: null, devtools: options.autoOpenDevTools ?? false, + pipe: true, handleDevToolsAsPage: true, }; const key = JSON.stringify(launchOptions); From e8394ab1133eaede5d57efd5f05c012498edd50b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Oct 2025 14:08:01 +0000 Subject: [PATCH 029/456] chore(deps-dev): bump the bundled group with 2 updates (#481) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the bundled group with 2 updates: [@modelcontextprotocol/sdk](https://github.com/modelcontextprotocol/typescript-sdk) and [puppeteer](https://github.com/puppeteer/puppeteer). Updates `@modelcontextprotocol/sdk` from 1.20.1 to 1.20.2
Commits

Updates `puppeteer` from 24.26.0 to 24.26.1
Release notes

Sourced from puppeteer's releases.

puppeteer-core: v24.26.1

24.26.1 (2025-10-22)

🛠️ Fixes

puppeteer: v24.26.1

24.26.1 (2025-10-22)

♻️ Chores

  • puppeteer: Synchronize puppeteer versions

Dependencies

  • The following workspace dependencies were updated
    • dependencies
      • puppeteer-core bumped from 24.26.0 to 24.26.1
Changelog

Sourced from puppeteer's changelog.

24.26.1 (2025-10-22)

♻️ Chores

  • puppeteer: Synchronize puppeteer versions

Dependencies

  • The following workspace dependencies were updated
    • dependencies
      • puppeteer-core bumped from 24.26.0 to 24.26.1

🛠️ Fixes

Commits

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
--------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Nikolay Vitkov --- package-lock.json | 36 ++++++++-------- package.json | 4 +- tests/McpResponse.test.js.snapshot | 45 +++++++++++++++++++ tests/McpResponse.test.ts | 69 ++++++++---------------------- tests/utils.ts | 3 ++ 5 files changed, 85 insertions(+), 72 deletions(-) create mode 100644 tests/McpResponse.test.js.snapshot diff --git a/package-lock.json b/package-lock.json index 1017ebb85..36a0c2b4e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ }, "devDependencies": { "@eslint/js": "^9.35.0", - "@modelcontextprotocol/sdk": "1.20.1", + "@modelcontextprotocol/sdk": "1.20.2", "@rollup/plugin-commonjs": "^28.0.8", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^16.0.3", @@ -33,7 +33,7 @@ "eslint-plugin-import": "^2.32.0", "globals": "^16.4.0", "prettier": "^3.6.2", - "puppeteer": "24.26.0", + "puppeteer": "24.26.1", "rollup": "4.52.5", "rollup-plugin-cleanup": "^3.2.1", "rollup-plugin-license": "^3.6.0", @@ -380,9 +380,9 @@ "license": "MIT" }, "node_modules/@modelcontextprotocol/sdk": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.20.1.tgz", - "integrity": "sha512-j/P+yuxXfgxb+mW7OEoRCM3G47zCTDqUPivJo/VzpjbG8I9csTXtOprCf5FfOfHK4whOJny0aHuBEON+kS7CCA==", + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.20.2.tgz", + "integrity": "sha512-6rqTdFt67AAAzln3NOKsXRmv5ZzPkgbfaebKBqUbts7vK1GZudqnrun5a8d3M/h955cam9RHZ6Jb4Y1XhnmFPg==", "dev": true, "license": "MIT", "dependencies": { @@ -1976,9 +1976,9 @@ "license": "MIT" }, "node_modules/bare-events": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.0.tgz", - "integrity": "sha512-AOhh6Bg5QmFIXdViHbMc2tLDsBIRxdkIaIddPslJF9Z5De3APBScuqGP2uThXnIpqFrgoxMNC6km7uXNIMLHXA==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.1.tgz", + "integrity": "sha512-oxSAxTS1hRfnyit2CL5QpAOS5ixfBjj6ex3yTNvXyY/kE719jQ/IjuESJBK2w5v4wwQRAHGseVJXx9QBYOtFGQ==", "dev": true, "license": "Apache-2.0", "peerDependencies": { @@ -1991,9 +1991,9 @@ } }, "node_modules/bare-fs": { - "version": "4.4.11", - "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.4.11.tgz", - "integrity": "sha512-Bejmm9zRMvMTRoHS+2adgmXw1ANZnCNx+B5dgZpGwlP1E3x6Yuxea8RToddHUbWtVV0iUMWqsgZr8+jcgUI2SA==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.5.0.tgz", + "integrity": "sha512-GljgCjeupKZJNetTqxKaQArLK10vpmK28or0+RwWjEl5Rk+/xG3wkpmkv+WrcBm3q1BwHKlnhXzR8O37kcvkXQ==", "dev": true, "license": "Apache-2.0", "optional": true, @@ -5380,9 +5380,9 @@ } }, "node_modules/puppeteer": { - "version": "24.26.0", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.26.0.tgz", - "integrity": "sha512-5F4A3dGeuw0jJOR3hGV969TO8oi+JG2TzqxAFvH+gxzxh0ILZmiFskWPlhcWQh5s9HAtgZyktOBDqs6zOi7IKA==", + "version": "24.26.1", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.26.1.tgz", + "integrity": "sha512-3RG2UqclzMFolM2fS4bN8t5/EjZ0VwEoAGVxG8PMGeprjLzj+x0U4auH7MQ4B6ftW+u1JUnTTN8ab4ABPdl4mA==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", @@ -5391,7 +5391,7 @@ "chromium-bidi": "10.5.1", "cosmiconfig": "^9.0.0", "devtools-protocol": "0.0.1508733", - "puppeteer-core": "24.26.0", + "puppeteer-core": "24.26.1", "typed-query-selector": "^2.12.0" }, "bin": { @@ -5402,9 +5402,9 @@ } }, "node_modules/puppeteer-core": { - "version": "24.26.0", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.26.0.tgz", - "integrity": "sha512-l3aMYhTdSzazZ14rfpNAPGhnYHsd8mwduqybhu5aO/OR+d24P/V/eo8XTB3GB2yX2ZWf9GLAVcx8nnVPFZpP/A==", + "version": "24.26.1", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.26.1.tgz", + "integrity": "sha512-YHZdo3chJ5b9pTYVnuDuoI3UX/tWJFJyRZvkLbThGy6XeHWC+0KI8iN0UMCkvde5l/YOk3huiVZ/PvwgSbwdrA==", "dev": true, "license": "Apache-2.0", "dependencies": { diff --git a/package.json b/package.json index 0fdb3a90e..9689137aa 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "mcpName": "io.github.ChromeDevTools/chrome-devtools-mcp", "devDependencies": { "@eslint/js": "^9.35.0", - "@modelcontextprotocol/sdk": "1.20.1", + "@modelcontextprotocol/sdk": "1.20.2", "@rollup/plugin-commonjs": "^28.0.8", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^16.0.3", @@ -59,7 +59,7 @@ "eslint-plugin-import": "^2.32.0", "globals": "^16.4.0", "prettier": "^3.6.2", - "puppeteer": "24.26.0", + "puppeteer": "24.26.1", "rollup": "4.52.5", "rollup-plugin-cleanup": "^3.2.1", "rollup-plugin-license": "^3.6.0", diff --git a/tests/McpResponse.test.js.snapshot b/tests/McpResponse.test.js.snapshot new file mode 100644 index 000000000..5f4794985 --- /dev/null +++ b/tests/McpResponse.test.js.snapshot @@ -0,0 +1,45 @@ +exports[`McpResponse > returns correctly formatted snapshot for a simple tree 1`] = ` +# test response +## Page content +uid=1_0 RootWebArea "My test page" url="about:blank" + uid=1_1 button "Click me" focusable focused + uid=1_2 textbox value="Input" + +`; + +exports[`McpResponse > returns values for textboxes 1`] = ` +# test response +## Page content +uid=1_0 RootWebArea "My test page" url="about:blank" + uid=1_1 StaticText "username" + uid=1_2 textbox "username" focusable focused value="mcp" + +`; + +exports[`McpResponse > returns verbose snapshot 1`] = ` +# test response +## Page content +uid=1_0 RootWebArea "My test page" url="about:blank" + uid=1_1 ignored + uid=1_2 ignored + uid=1_3 complementary + uid=1_4 StaticText "test" + uid=1_5 InlineTextBox "test" + +`; + +exports[`McpResponse > saves snapshot to file 1`] = ` +# test response +## Page content +Saved snapshot to +`; + +exports[`McpResponse > saves snapshot to file 2`] = ` +uid=1_0 RootWebArea "My test page" url="about:blank" + uid=1_1 ignored + uid=1_2 ignored + uid=1_3 complementary + uid=1_4 StaticText "test" + uid=1_5 InlineTextBox "test" + +`; diff --git a/tests/McpResponse.test.ts b/tests/McpResponse.test.ts index 1c6678d54..1de5ab863 100644 --- a/tests/McpResponse.test.ts +++ b/tests/McpResponse.test.ts @@ -9,7 +9,13 @@ import {tmpdir} from 'node:os'; import {join} from 'node:path'; import {describe, it} from 'node:test'; -import {getMockRequest, getMockResponse, html, withBrowser} from './utils.js'; +import { + getMockRequest, + getMockResponse, + html, + stabilizeResponseOutput, + withBrowser, +} from './utils.js'; describe('McpResponse', () => { it('list pages', async () => { @@ -51,7 +57,7 @@ Testing 2`, }); }); - it('returns correctly formatted snapshot for a simple tree', async () => { + it('returns correctly formatted snapshot for a simple tree', async t => { await withBrowser(async (response, context) => { const page = context.getSelectedPage(); await page.setContent( @@ -65,19 +71,11 @@ Testing 2`, response.includeSnapshot(); const result = await response.handle('test', context); assert.equal(result[0].type, 'text'); - assert.strictEqual( - result[0].text, - `# test response -## Page content -uid=1_0 RootWebArea "My test page" - uid=1_1 button "Click me" focusable focused - uid=1_2 textbox value="Input" -`, - ); + t.assert.snapshot?.(result[0].text); }); }); - it('returns values for textboxes', async () => { + it('returns values for textboxes', async t => { await withBrowser(async (response, context) => { const page = context.getSelectedPage(); await page.setContent( @@ -91,19 +89,11 @@ uid=1_0 RootWebArea "My test page" response.includeSnapshot(); const result = await response.handle('test', context); assert.equal(result[0].type, 'text'); - assert.strictEqual( - result[0].text, - `# test response -## Page content -uid=1_0 RootWebArea "My test page" - uid=1_1 StaticText "username" - uid=1_2 textbox "username" focusable focused value="mcp" -`, - ); + t.assert.snapshot?.(result[0].text); }); }); - it('returns verbose snapshot', async () => { + it('returns verbose snapshot', async t => { await withBrowser(async (response, context) => { const page = context.getSelectedPage(); await page.setContent(html``); @@ -112,22 +102,11 @@ uid=1_0 RootWebArea "My test page" }); const result = await response.handle('test', context); assert.equal(result[0].type, 'text'); - assert.strictEqual( - result[0].text, - `# test response -## Page content -uid=1_0 RootWebArea "My test page" - uid=1_1 ignored - uid=1_2 ignored - uid=1_3 complementary - uid=1_4 StaticText "test" - uid=1_5 InlineTextBox "test" -`, - ); + t.assert.snapshot?.(result[0].text); }); }); - it('saves snapshot to file', async () => { + it('saves snapshot to file', async t => { const filePath = join(tmpdir(), 'test-screenshot.png'); try { await withBrowser(async (response, context) => { @@ -139,25 +118,10 @@ uid=1_0 RootWebArea "My test page" }); const result = await response.handle('test', context); assert.equal(result[0].type, 'text'); - console.log(result[0].text); - assert.strictEqual( - result[0].text, - `# test response -## Page content -Saved snapshot to ${filePath}.`, - ); + t.assert.snapshot?.(stabilizeResponseOutput(result[0].text)); }); const content = await readFile(filePath, 'utf-8'); - assert.strictEqual( - content, - `uid=1_0 RootWebArea "My test page" - uid=1_1 ignored - uid=1_2 ignored - uid=1_3 complementary - uid=1_4 StaticText "test" - uid=1_5 InlineTextBox "test" -`, - ); + t.assert.snapshot?.(stabilizeResponseOutput(content)); } finally { await rm(filePath, {force: true}); } @@ -492,6 +456,7 @@ No requests found.`, ]; }; const result = await response.handle('test', context); + assert.strictEqual( result[0].text, `# test response diff --git a/tests/utils.ts b/tests/utils.ts index 03cf84f91..6dca14a71 100644 --- a/tests/utils.ts +++ b/tests/utils.ts @@ -170,5 +170,8 @@ export function stabilizeResponseOutput(text: unknown) { // sec-ch-ua-platform:"Linux" const chUaPlatformRegEx = /sec-ch-ua-platform:"[a-zA-Z]*"/g; output = output.replaceAll(chUaPlatformRegEx, 'sec-ch-ua-platform:""'); + + const savedSnapshot = /Saved snapshot to (.*)/g; + output = output.replaceAll(savedSnapshot, 'Saved snapshot to '); return output; } From b30bdd6e17419b8da74bbf9d09f4e3edf21570f1 Mon Sep 17 00:00:00 2001 From: Nikolay Vitkov <34244704+Lightning00Blade@users.noreply.github.com> Date: Mon, 27 Oct 2025 16:02:03 +0100 Subject: [PATCH 030/456] test: use snapshots for McpResponse (#483) --- tests/McpResponse.test.js.snapshot | 131 +++++++++++++++++++++ tests/McpResponse.test.ts | 178 ++++++----------------------- 2 files changed, 163 insertions(+), 146 deletions(-) diff --git a/tests/McpResponse.test.js.snapshot b/tests/McpResponse.test.js.snapshot index 5f4794985..79d43b8fc 100644 --- a/tests/McpResponse.test.js.snapshot +++ b/tests/McpResponse.test.js.snapshot @@ -1,3 +1,91 @@ +exports[`McpResponse > add network request when attached 1`] = ` +# test response +## Request http://example.com +Status: [pending] +### Request Headers +- content-size:10 +## Network requests +Showing 1-1 of 1 (Page 1 of 1). +reqid=1 GET http://example.com [pending] +`; + +exports[`McpResponse > add network request when attached with POST data 1`] = ` +# test response +## Request http://example.com +Status: [success - 200] +### Request Headers +- content-size:10 +### Request Body +{"request":"body"} +### Response Headers +- Content-Type:application/json +### Response Body +{"response":"body"} +## Network requests +Showing 1-1 of 1 (Page 1 of 1). +reqid=1 POST http://example.com [success - 200] +`; + +exports[`McpResponse > add network requests when setting is true 1`] = ` +# test response +## Network requests +Showing 1-2 of 2 (Page 1 of 1). +reqid=1 GET http://example.com [pending] +reqid=2 GET http://example.com [pending] +`; + +exports[`McpResponse > adds a message when no console messages exist 1`] = ` +# test response +## Console messages + +`; + +exports[`McpResponse > adds a prompt dialog 1`] = ` +# test response +# Open dialog +prompt: message (default value: "default"). +Call handle_dialog to handle it before continuing. +`; + +exports[`McpResponse > adds an alert dialog 1`] = ` +# test response +# Open dialog +alert: message. +Call handle_dialog to handle it before continuing. +`; + +exports[`McpResponse > adds console messages when the setting is true 1`] = ` +# test response +## Console messages +Showing 1-1 of 1 (Page 1 of 1). +msgid=1 [log] Hello from the test (1 args) +`; + +exports[`McpResponse > adds cpu throttling setting when it is over 1 1`] = ` +# test response +## CPU emulation +Emulating: 4x slowdown +`; + +exports[`McpResponse > adds throttling setting when it is not null 1`] = ` +# test response +## Network emulation +Emulating: Slow 3G +Default navigation timeout set to 100000 ms +`; + +exports[`McpResponse > allows response text lines to be added 1`] = ` +# test response +Testing 1 +Testing 2 +`; + +exports[`McpResponse > list pages 1`] = ` +# test response +## Pages +0: about:blank [selected] +`; + exports[`McpResponse > returns correctly formatted snapshot for a simple tree 1`] = ` # test response ## Page content @@ -43,3 +131,46 @@ uid=1_0 RootWebArea "My test page" url="about:blank" uid=1_5 InlineTextBox "test" `; + +exports[`McpResponse network request filtering > filters network requests by resource type 1`] = ` +# test response +## Network requests +Showing 1-2 of 2 (Page 1 of 1). +reqid=1 GET http://example.com [pending] +reqid=1 GET http://example.com [pending] +`; + +exports[`McpResponse network request filtering > filters network requests by single resource type 1`] = ` +# test response +## Network requests +Showing 1-1 of 1 (Page 1 of 1). +reqid=1 GET http://example.com [pending] +`; + +exports[`McpResponse network request filtering > shows all requests when empty resourceTypes array is provided 1`] = ` +# test response +## Network requests +Showing 1-5 of 5 (Page 1 of 1). +reqid=1 GET http://example.com [pending] +reqid=1 GET http://example.com [pending] +reqid=1 GET http://example.com [pending] +reqid=1 GET http://example.com [pending] +reqid=1 GET http://example.com [pending] +`; + +exports[`McpResponse network request filtering > shows all requests when no filters are provided 1`] = ` +# test response +## Network requests +Showing 1-5 of 5 (Page 1 of 1). +reqid=1 GET http://example.com [pending] +reqid=1 GET http://example.com [pending] +reqid=1 GET http://example.com [pending] +reqid=1 GET http://example.com [pending] +reqid=1 GET http://example.com [pending] +`; + +exports[`McpResponse network request filtering > shows no requests when filter matches nothing 1`] = ` +# test response +## Network requests +No requests found. +`; diff --git a/tests/McpResponse.test.ts b/tests/McpResponse.test.ts index 1de5ab863..4cf522525 100644 --- a/tests/McpResponse.test.ts +++ b/tests/McpResponse.test.ts @@ -18,32 +18,22 @@ import { } from './utils.js'; describe('McpResponse', () => { - it('list pages', async () => { + it('list pages', async t => { await withBrowser(async (response, context) => { response.setIncludePages(true); const result = await response.handle('test', context); assert.equal(result[0].type, 'text'); - assert.deepStrictEqual( - result[0].text, - `# test response -## Pages -0: about:blank [selected]`, - ); + t.assert.snapshot?.(result[0].text); }); }); - it('allows response text lines to be added', async () => { + it('allows response text lines to be added', async t => { await withBrowser(async (response, context) => { response.appendResponseLine('Testing 1'); response.appendResponseLine('Testing 2'); const result = await response.handle('test', context); assert.equal(result[0].type, 'text'); - assert.deepStrictEqual( - result[0].text, - `# test response -Testing 1 -Testing 2`, - ); + t.assert.snapshot?.(result[0].text); }); }); @@ -127,18 +117,12 @@ Testing 2`, } }); - it('adds throttling setting when it is not null', async () => { + it('adds throttling setting when it is not null', async t => { await withBrowser(async (response, context) => { context.setNetworkConditions('Slow 3G'); const result = await response.handle('test', context); assert.equal(result[0].type, 'text'); - assert.strictEqual( - result[0].text, - `# test response -## Network emulation -Emulating: Slow 3G -Default navigation timeout set to 100000 ms`, - ); + t.assert.snapshot?.(result[0].text); }); }); @@ -161,16 +145,11 @@ Default navigation timeout set to 100000 ms`, }); }); - it('adds cpu throttling setting when it is over 1', async () => { + it('adds cpu throttling setting when it is over 1', async t => { await withBrowser(async (response, context) => { context.setCpuThrottlingRate(4); const result = await response.handle('test', context); - assert.strictEqual( - result[0].text, - `# test response -## CPU emulation -Emulating: 4x slowdown`, - ); + t.assert.snapshot?.(result[0].text); }); }); @@ -182,7 +161,7 @@ Emulating: 4x slowdown`, }); }); - it('adds a prompt dialog', async () => { + it('adds a prompt dialog', async t => { await withBrowser(async (response, context) => { const page = context.getSelectedPage(); const dialogPromise = new Promise(resolve => { @@ -196,17 +175,11 @@ Emulating: 4x slowdown`, await dialogPromise; const result = await response.handle('test', context); await context.getDialog()?.dismiss(); - assert.strictEqual( - result[0].text, - `# test response -# Open dialog -prompt: message (default value: "default"). -Call handle_dialog to handle it before continuing.`, - ); + t.assert.snapshot?.(result[0].text); }); }); - it('adds an alert dialog', async () => { + it('adds an alert dialog', async t => { await withBrowser(async (response, context) => { const page = context.getSelectedPage(); const dialogPromise = new Promise(resolve => { @@ -220,31 +193,18 @@ Call handle_dialog to handle it before continuing.`, await dialogPromise; const result = await response.handle('test', context); await context.getDialog()?.dismiss(); - assert.strictEqual( - result[0].text, - `# test response -# Open dialog -alert: message. -Call handle_dialog to handle it before continuing.`, - ); + t.assert.snapshot?.(result[0].text); }); }); - it('add network requests when setting is true', async () => { + it('add network requests when setting is true', async t => { await withBrowser(async (response, context) => { response.setIncludeNetworkRequests(true); context.getNetworkRequests = () => { return [getMockRequest({stableId: 1}), getMockRequest({stableId: 2})]; }; const result = await response.handle('test', context); - assert.strictEqual( - result[0].text, - `# test response -## Network requests -Showing 1-2 of 2 (Page 1 of 1). -reqid=1 GET http://example.com [pending] -reqid=2 GET http://example.com [pending]`, - ); + t.assert.snapshot?.(result[0].text); }); }); @@ -259,7 +219,7 @@ reqid=2 GET http://example.com [pending]`, }); }); - it('add network request when attached with POST data', async () => { + it('add network request when attached with POST data', async t => { await withBrowser(async (response, context) => { response.setIncludeNetworkRequests(true); const httpResponse = getMockResponse(); @@ -287,27 +247,11 @@ reqid=2 GET http://example.com [pending]`, const result = await response.handle('test', context); - assert.strictEqual( - result[0].text, - `# test response -## Request http://example.com -Status: [success - 200] -### Request Headers -- content-size:10 -### Request Body -${JSON.stringify({request: 'body'})} -### Response Headers -- Content-Type:application/json -### Response Body -${JSON.stringify({response: 'body'})} -## Network requests -Showing 1-1 of 1 (Page 1 of 1). -reqid=1 POST http://example.com [success - 200]`, - ); + t.assert.snapshot?.(result[0].text); }); }); - it('add network request when attached', async () => { + it('add network request when attached', async t => { await withBrowser(async (response, context) => { response.setIncludeNetworkRequests(true); const request = getMockRequest(); @@ -319,21 +263,11 @@ reqid=1 POST http://example.com [success - 200]`, }; response.attachNetworkRequest(1); const result = await response.handle('test', context); - assert.strictEqual( - result[0].text, - `# test response -## Request http://example.com -Status: [pending] -### Request Headers -- content-size:10 -## Network requests -Showing 1-1 of 1 (Page 1 of 1). -reqid=1 GET http://example.com [pending]`, - ); + t.assert.snapshot?.(result[0].text); }); }); - it('adds console messages when the setting is true', async () => { + it('adds console messages when the setting is true', async t => { await withBrowser(async (response, context) => { response.setIncludeConsoleData(true); const page = context.getSelectedPage(); @@ -348,32 +282,22 @@ reqid=1 GET http://example.com [pending]`, await consoleMessagePromise; const result = await response.handle('test', context); assert.ok(result[0].text); - // Cannot check the full text because it contains local file path - assert.ok( - result[0].text.toString().startsWith(`# test response -## Console messages`), - ); - assert.ok(result[0].text.toString().includes('Hello from the test')); + t.assert.snapshot?.(result[0].text); }); }); - it('adds a message when no console messages exist', async () => { + it('adds a message when no console messages exist', async t => { await withBrowser(async (response, context) => { response.setIncludeConsoleData(true); const result = await response.handle('test', context); assert.ok(result[0].text); - assert.strictEqual( - result[0].text.toString(), - `# test response -## Console messages -`, - ); + t.assert.snapshot?.(result[0].text); }); }); }); describe('McpResponse network request filtering', () => { - it('filters network requests by resource type', async () => { + it('filters network requests by resource type', async t => { await withBrowser(async (response, context) => { response.setIncludeNetworkRequests(true, { resourceTypes: ['script', 'stylesheet'], @@ -387,18 +311,11 @@ describe('McpResponse network request filtering', () => { ]; }; const result = await response.handle('test', context); - assert.strictEqual( - result[0].text, - `# test response -## Network requests -Showing 1-2 of 2 (Page 1 of 1). -reqid=1 GET http://example.com [pending] -reqid=1 GET http://example.com [pending]`, - ); + t.assert.snapshot?.(result[0].text); }); }); - it('filters network requests by single resource type', async () => { + it('filters network requests by single resource type', async t => { await withBrowser(async (response, context) => { response.setIncludeNetworkRequests(true, { resourceTypes: ['image'], @@ -411,17 +328,11 @@ reqid=1 GET http://example.com [pending]`, ]; }; const result = await response.handle('test', context); - assert.strictEqual( - result[0].text, - `# test response -## Network requests -Showing 1-1 of 1 (Page 1 of 1). -reqid=1 GET http://example.com [pending]`, - ); + t.assert.snapshot?.(result[0].text); }); }); - it('shows no requests when filter matches nothing', async () => { + it('shows no requests when filter matches nothing', async t => { await withBrowser(async (response, context) => { response.setIncludeNetworkRequests(true, { resourceTypes: ['font'], @@ -434,16 +345,11 @@ reqid=1 GET http://example.com [pending]`, ]; }; const result = await response.handle('test', context); - assert.strictEqual( - result[0].text, - `# test response -## Network requests -No requests found.`, - ); + t.assert.snapshot?.(result[0].text); }); }); - it('shows all requests when no filters are provided', async () => { + it('shows all requests when no filters are provided', async t => { await withBrowser(async (response, context) => { response.setIncludeNetworkRequests(true); context.getNetworkRequests = () => { @@ -457,21 +363,11 @@ No requests found.`, }; const result = await response.handle('test', context); - assert.strictEqual( - result[0].text, - `# test response -## Network requests -Showing 1-5 of 5 (Page 1 of 1). -reqid=1 GET http://example.com [pending] -reqid=1 GET http://example.com [pending] -reqid=1 GET http://example.com [pending] -reqid=1 GET http://example.com [pending] -reqid=1 GET http://example.com [pending]`, - ); + t.assert.snapshot?.(result[0].text); }); }); - it('shows all requests when empty resourceTypes array is provided', async () => { + it('shows all requests when empty resourceTypes array is provided', async t => { await withBrowser(async (response, context) => { response.setIncludeNetworkRequests(true, { resourceTypes: [], @@ -486,17 +382,7 @@ reqid=1 GET http://example.com [pending]`, ]; }; const result = await response.handle('test', context); - assert.strictEqual( - result[0].text, - `# test response -## Network requests -Showing 1-5 of 5 (Page 1 of 1). -reqid=1 GET http://example.com [pending] -reqid=1 GET http://example.com [pending] -reqid=1 GET http://example.com [pending] -reqid=1 GET http://example.com [pending] -reqid=1 GET http://example.com [pending]`, - ); + t.assert.snapshot?.(result[0].text); }); }); }); From 40e1753d2e874bb22005dbebdb551da304a80033 Mon Sep 17 00:00:00 2001 From: Nikolay Vitkov <34244704+Lightning00Blade@users.noreply.github.com> Date: Mon, 27 Oct 2025 16:38:40 +0100 Subject: [PATCH 031/456] docs: remove unnecessary replace (#475) --- scripts/generate-docs.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/scripts/generate-docs.ts b/scripts/generate-docs.ts index 7466ab939..9a18c8192 100644 --- a/scripts/generate-docs.ts +++ b/scripts/generate-docs.ts @@ -44,7 +44,7 @@ function addCrossLinks(text: string, tools: ToolWithAnnotations[]): string { for (const toolName of sortedToolNames) { // Create regex to match tool name (case insensitive, word boundaries) - const regex = new RegExp(`\\b${toolName.replace(/_/g, '_')}\\b`, 'gi'); + const regex = new RegExp(`\\b${toolName}\\b`, 'gi'); result = result.replace(regex, match => { // Only create link if the match isn't already inside a link @@ -274,7 +274,8 @@ async function generateToolDocumentation(): Promise { const propertyNames = Object.keys(properties).sort(); for (const propName of propertyNames) { - const prop = properties[propName] as string; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const prop = properties[propName] as any; const isRequired = required.includes(propName); const requiredText = isRequired ? ' **(required)**' @@ -282,7 +283,7 @@ async function generateToolDocumentation(): Promise { let typeInfo = prop.type || 'unknown'; if (prop.enum) { - typeInfo = `enum: ${prop.enum.map(v => `"${v}"`).join(', ')}`; + typeInfo = `enum: ${prop.enum.map((v: string) => `"${v}"`).join(', ')}`; } markdown += `- **${propName}** (${typeInfo})${requiredText}`; From 796aed72b7126ed4332888ffbc06d6cb678265ef Mon Sep 17 00:00:00 2001 From: Alex Rudenko Date: Tue, 28 Oct 2025 10:52:09 +0100 Subject: [PATCH 032/456] feat: detect network requests inspected in DevTools UI (#477) If you manually inspect a network request in the DevTools UI, the Chrome DevTools MCP server is now able to detect it and allow you to refer to the selected request. The approach was tested in a couple of models. In the list of the network requests we have a short indication about which request is selected in DevTools UI. We refer to the DevTools as "DevTools UI" to disambiguate from the DevTools MCP server. --- docs/tool-reference.md | 5 +-- src/McpContext.ts | 44 +++++++++++++++++++++-- src/McpResponse.ts | 5 +++ src/PageCollector.ts | 28 +++++++++++---- src/formatters/networkFormatter.ts | 3 +- src/main.ts | 1 + src/tools/ToolDefinition.ts | 2 ++ src/tools/network.ts | 25 ++++++++++--- tests/formatters/networkFormatter.test.ts | 10 ++++++ 9 files changed, 107 insertions(+), 16 deletions(-) diff --git a/docs/tool-reference.md b/docs/tool-reference.md index 7e51181bc..b9bc6d286 100644 --- a/docs/tool-reference.md +++ b/docs/tool-reference.md @@ -255,11 +255,12 @@ ### `get_network_request` -**Description:** Gets a network request by URL. You can get all requests by calling [`list_network_requests`](#list_network_requests). +**Description:** Gets a network request by reqid. You can get all requests by calling [`list_network_requests`](#list_network_requests). +Get the request currently selected in the DevTools UI by ommitting reqid **Parameters:** -- **reqid** (number) **(required)**: The reqid of a request on the page from the listed network requests +- **reqid** (number) _(optional)_: The reqid of the network request. If omitted, looks up the current request selected in DevTools UI. --- diff --git a/src/McpContext.ts b/src/McpContext.ts index 586c2ba71..ceae35136 100644 --- a/src/McpContext.ts +++ b/src/McpContext.ts @@ -337,12 +337,13 @@ export class McpContext implements Context { ); }); - await this.#detectOpenDevToolsWindows(allPages); + await this.detectOpenDevToolsWindows(); return this.#pages; } - async #detectOpenDevToolsWindows(pages: Page[]) { + async detectOpenDevToolsWindows() { + const pages = await this.browser.pages(); this.#pageToDevToolsPage = new Map(); for (const devToolsPage of pages) { if (devToolsPage.url().startsWith('devtools://')) { @@ -377,6 +378,45 @@ export class McpContext implements Context { return this.#pageToDevToolsPage.get(page); } + async getDevToolsData(): Promise { + try { + const selectedPage = this.getSelectedPage(); + const devtoolsPage = this.getDevToolsPage(selectedPage); + if (devtoolsPage) { + const cdpRequestId = await devtoolsPage.evaluate(async () => { + // @ts-expect-error no types + const UI = await import('/bundled/ui/legacy/legacy.js'); + // @ts-expect-error no types + const SDK = await import('/bundled/core/sdk/sdk.js'); + const request = UI.Context.Context.instance().flavor( + SDK.NetworkRequest.NetworkRequest, + ); + return request?.requestId(); + }); + if (!cdpRequestId) { + this.logger('no context request'); + return; + } + const request = this.#networkCollector.find(selectedPage, request => { + // @ts-expect-error id is internal. + return request.id === cdpRequestId; + }); + if (!request) { + this.logger('no collected request for ' + cdpRequestId); + return; + } + return { + requestId: this.#networkCollector.getIdForResource(request), + }; + } else { + this.logger('no devtools page deteched'); + } + } catch (err) { + this.logger('error getting devtools data', err); + } + return; + } + /** * Creates a text snapshot of a page. */ diff --git a/src/McpResponse.ts b/src/McpResponse.ts index 24f2b3f8a..6798c90b3 100644 --- a/src/McpResponse.ts +++ b/src/McpResponse.ts @@ -44,6 +44,7 @@ export class McpResponse implements Response { pagination?: PaginationOptions; resourceTypes?: ResourceType[]; includePreservedRequests?: boolean; + networkRequestIdInDevToolsUI?: number; }; #consoleDataOptions?: { include: boolean; @@ -67,6 +68,7 @@ export class McpResponse implements Response { options?: PaginationOptions & { resourceTypes?: ResourceType[]; includePreservedRequests?: boolean; + networkRequestIdInDevToolsUI?: number; }, ): void { if (!value) { @@ -85,6 +87,7 @@ export class McpResponse implements Response { : undefined, resourceTypes: options?.resourceTypes, includePreservedRequests: options?.includePreservedRequests, + networkRequestIdInDevToolsUI: options?.networkRequestIdInDevToolsUI, }; } @@ -391,6 +394,8 @@ Call ${handleDialog.name} to handle it before continuing.`); getShortDescriptionForRequest( request, context.getNetworkRequestStableId(request), + context.getNetworkRequestStableId(request) === + this.#networkRequestsOptions?.networkRequestIdInDevToolsUI, ), ); } diff --git a/src/PageCollector.ts b/src/PageCollector.ts index 8cb433501..22b9c7a82 100644 --- a/src/PageCollector.ts +++ b/src/PageCollector.ts @@ -163,16 +163,32 @@ export class PageCollector { throw new Error('No requests found for selected page'); } - for (const navigation of navigations) { - for (const collected of navigation) { - if (collected[stableIdSymbol] === stableId) { - return collected; - } - } + const item = this.find(page, item => item[stableIdSymbol] === stableId); + + if (item) { + return item; } throw new Error('Request not found for selected page'); } + + find( + page: Page, + filter: (item: WithSymbolId) => boolean, + ): WithSymbolId | undefined { + const navigations = this.storage.get(page); + if (!navigations) { + return; + } + + for (const navigation of navigations) { + const item = navigation.find(filter); + if (item) { + return item; + } + } + return; + } } export class NetworkCollector extends PageCollector { diff --git a/src/formatters/networkFormatter.ts b/src/formatters/networkFormatter.ts index 3c1abe58c..d3945130a 100644 --- a/src/formatters/networkFormatter.ts +++ b/src/formatters/networkFormatter.ts @@ -13,9 +13,10 @@ const BODY_CONTEXT_SIZE_LIMIT = 10000; export function getShortDescriptionForRequest( request: HTTPRequest, id: number, + selectedInDevToolsUI = false, ): string { // TODO truncate the URL - return `reqid=${id} ${request.method()} ${request.url()} ${getStatusFromRequest(request)}`; + return `reqid=${id} ${request.method()} ${request.url()} ${getStatusFromRequest(request)}${selectedInDevToolsUI ? ` [selected in DevTools UI]` : ''}`; } export function getStatusFromRequest(request: HTTPRequest): string { diff --git a/src/main.ts b/src/main.ts index 3443cecc4..9dad338e9 100644 --- a/src/main.ts +++ b/src/main.ts @@ -129,6 +129,7 @@ function registerTool(tool: ToolDefinition): void { try { logger(`${tool.name} request: ${JSON.stringify(params, null, ' ')}`); const context = await getContext(); + await context.detectOpenDevToolsWindows(); const response = new McpResponse(); await tool.handler( { diff --git a/src/tools/ToolDefinition.ts b/src/tools/ToolDefinition.ts index 68f292603..8df0f720c 100644 --- a/src/tools/ToolDefinition.ts +++ b/src/tools/ToolDefinition.ts @@ -55,6 +55,7 @@ export interface Response { options?: PaginationOptions & { resourceTypes?: string[]; includePreservedRequests?: boolean; + networkRequestIdInDevToolsUI?: number; }, ): void; setIncludeConsoleData( @@ -102,6 +103,7 @@ export type Context = Readonly<{ text: string; timeout?: number | undefined; }): Promise; + getDevToolsData(): Promise; }>; export function defineTool( diff --git a/src/tools/network.ts b/src/tools/network.ts index fe51f72f2..1df945500 100644 --- a/src/tools/network.ts +++ b/src/tools/network.ts @@ -70,19 +70,22 @@ export const listNetworkRequests = defineTool({ 'Set to true to return the preserved requests over the last 3 navigations.', ), }, - handler: async (request, response) => { + handler: async (request, response, context) => { + const data = await context.getDevToolsData(); response.setIncludeNetworkRequests(true, { pageSize: request.params.pageSize, pageIdx: request.params.pageIdx, resourceTypes: request.params.resourceTypes, includePreservedRequests: request.params.includePreservedRequests, + networkRequestIdInDevToolsUI: data?.requestId, }); }, }); export const getNetworkRequest = defineTool({ name: 'get_network_request', - description: `Gets a network request by URL. You can get all requests by calling ${listNetworkRequests.name}.`, + description: `Gets a network request by reqid. You can get all requests by calling ${listNetworkRequests.name}. +Get the request currently selected in the DevTools UI by ommitting reqid`, annotations: { category: ToolCategory.NETWORK, readOnlyHint: true, @@ -90,11 +93,23 @@ export const getNetworkRequest = defineTool({ schema: { reqid: zod .number() + .optional() .describe( - 'The reqid of a request on the page from the listed network requests', + 'The reqid of the network request. If omitted, looks up the current request selected in DevTools UI.', ), }, - handler: async (request, response, _context) => { - response.attachNetworkRequest(request.params.reqid); + handler: async (request, response, context) => { + if (request.params.reqid) { + response.attachNetworkRequest(request.params.reqid); + } else { + const data = await context.getDevToolsData(); + if (data?.requestId) { + response.attachNetworkRequest(data?.requestId); + } else { + response.appendResponseLine( + `Nothing is currently selected in DevTools UI.`, + ); + } + } }, }); diff --git a/tests/formatters/networkFormatter.test.ts b/tests/formatters/networkFormatter.test.ts index e22b01406..2bd1bbd64 100644 --- a/tests/formatters/networkFormatter.test.ts +++ b/tests/formatters/networkFormatter.test.ts @@ -71,6 +71,16 @@ describe('networkFormatter', () => { 'reqid=1 GET http://example.com [failed - Error in Network]', ); }); + + it('marks requests selected in DevTools UI', async () => { + const request = getMockRequest(); + const result = getShortDescriptionForRequest(request, 1, true); + + assert.equal( + result, + 'reqid=1 GET http://example.com [pending] [selected in DevTools UI]', + ); + }); }); describe('getFormattedHeaderValue', () => { From e5653b05160fba52a83068296ba57bd0213d60f2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 29 Oct 2025 08:18:31 +0000 Subject: [PATCH 033/456] chore(deps-dev): bump the bundled group with 2 updates (#490) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the bundled group with 2 updates: [chrome-devtools-frontend](https://github.com/ChromeDevTools/devtools-frontend) and [puppeteer](https://github.com/puppeteer/puppeteer). Updates `chrome-devtools-frontend` from 1.0.1534717 to 1.0.1536371
Commits
  • f9114c7 Update Chrome (for Testing) PIN
  • 420b33e [RPP] In trace_app, ignore frames not in the primary main frame
  • 02e4714 Add option for Tooltip to be triggered by both hover and click
  • a53e59d Restructure BFCache view to make adopting UI eng vision easier
  • 02896d3 Add placeholder for Keybind recording
  • b8c6a36 [Console Insights Teasers] Update KnownContextValues
  • ddf2fed Roll browser-protocol
  • ffaf345 Add unit test for Widget.focus() behavior
  • a8f58dd Update DevTools DEPS (trusted)
  • d8a5a91 Make setDefaultFocusedElement use autofocus attribute
  • Additional commits viewable in compare view

Updates `puppeteer` from 24.26.1 to 24.27.0
Release notes

Sourced from puppeteer's releases.

puppeteer-core: v24.27.0

24.27.0 (2025-10-29)

🎉 Features

🛠️ Fixes

📄 Documentation

puppeteer: v24.27.0

24.27.0 (2025-10-29)

🎉 Features

♻️ Chores

Dependencies

  • The following workspace dependencies were updated
    • dependencies
      • puppeteer-core bumped from 24.26.1 to 24.27.0
Changelog

Sourced from puppeteer's changelog.

24.27.0 (2025-10-29)

🎉 Features

♻️ Chores

Dependencies

  • The following workspace dependencies were updated
    • dependencies
      • puppeteer-core bumped from 24.26.1 to 24.27.0

🛠️ Fixes

📄 Documentation

Commits

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 34 ++++++++++++++-------------- package.json | 4 ++-- src/formatters/snapshotFormatter.ts | 9 +++++++- tests/tools/network.test.js.snapshot | 2 +- 4 files changed, 28 insertions(+), 21 deletions(-) diff --git a/package-lock.json b/package-lock.json index 36a0c2b4e..e0c95dd9a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,7 +25,7 @@ "@types/yargs": "^17.0.33", "@typescript-eslint/eslint-plugin": "^8.43.0", "@typescript-eslint/parser": "^8.43.0", - "chrome-devtools-frontend": "1.0.1534717", + "chrome-devtools-frontend": "1.0.1536371", "core-js": "3.46.0", "debug": "4.4.3", "eslint": "^9.35.0", @@ -33,7 +33,7 @@ "eslint-plugin-import": "^2.32.0", "globals": "^16.4.0", "prettier": "^3.6.2", - "puppeteer": "24.26.1", + "puppeteer": "24.27.0", "rollup": "4.52.5", "rollup-plugin-cleanup": "^3.2.1", "rollup-plugin-license": "^3.6.0", @@ -2224,9 +2224,9 @@ } }, "node_modules/chrome-devtools-frontend": { - "version": "1.0.1534717", - "resolved": "https://registry.npmjs.org/chrome-devtools-frontend/-/chrome-devtools-frontend-1.0.1534717.tgz", - "integrity": "sha512-+X86ZVMM9CyTY750CMPgfdL5vEq9VFPOjkhtQ2hhAKiIRSkJHH0VLj9A+Oeat4+Kl8ojkm1bZPjGvppPcc2+zw==", + "version": "1.0.1536371", + "resolved": "https://registry.npmjs.org/chrome-devtools-frontend/-/chrome-devtools-frontend-1.0.1536371.tgz", + "integrity": "sha512-LfK+KgW5EoxMrhlO/hyuRGhihkkfwpxTWVNfTJbsg8kX1sx4cIjelaWPlWvuAKg1/1M6yZECV6yFajdiHguj1A==", "dev": true, "license": "BSD-3-Clause" }, @@ -2572,9 +2572,9 @@ } }, "node_modules/devtools-protocol": { - "version": "0.0.1508733", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1508733.tgz", - "integrity": "sha512-QJ1R5gtck6nDcdM+nlsaJXcelPEI7ZxSMw1ujHpO1c4+9l+Nue5qlebi9xO1Z2MGr92bFOQTW7/rrheh5hHxDg==", + "version": "0.0.1521046", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1521046.tgz", + "integrity": "sha512-vhE6eymDQSKWUXwwA37NtTTVEzjtGVfDr3pRbsWEQ5onH/Snp2c+2xZHWJJawG/0hCCJLRGt4xVtEVUVILol4w==", "dev": true, "license": "BSD-3-Clause" }, @@ -5380,9 +5380,9 @@ } }, "node_modules/puppeteer": { - "version": "24.26.1", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.26.1.tgz", - "integrity": "sha512-3RG2UqclzMFolM2fS4bN8t5/EjZ0VwEoAGVxG8PMGeprjLzj+x0U4auH7MQ4B6ftW+u1JUnTTN8ab4ABPdl4mA==", + "version": "24.27.0", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.27.0.tgz", + "integrity": "sha512-eEcAFGxmHRSrk74DVkFAMAwfj4l3Ak8avBuA2bZaAoocY1+Fb9WLS3I7jlOc/tIOU7EmGLiDdVP08R44wADpHw==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", @@ -5390,8 +5390,8 @@ "@puppeteer/browsers": "2.10.12", "chromium-bidi": "10.5.1", "cosmiconfig": "^9.0.0", - "devtools-protocol": "0.0.1508733", - "puppeteer-core": "24.26.1", + "devtools-protocol": "0.0.1521046", + "puppeteer-core": "24.27.0", "typed-query-selector": "^2.12.0" }, "bin": { @@ -5402,16 +5402,16 @@ } }, "node_modules/puppeteer-core": { - "version": "24.26.1", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.26.1.tgz", - "integrity": "sha512-YHZdo3chJ5b9pTYVnuDuoI3UX/tWJFJyRZvkLbThGy6XeHWC+0KI8iN0UMCkvde5l/YOk3huiVZ/PvwgSbwdrA==", + "version": "24.27.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.27.0.tgz", + "integrity": "sha512-yubwj2XXmTM3wRIpbhO5nCjbByPgpFHlgrsD4IK+gMPqO7/a5FfnoSXDKjmqi8A2M1Ewusz0rTI/r+IN0GU0MA==", "dev": true, "license": "Apache-2.0", "dependencies": { "@puppeteer/browsers": "2.10.12", "chromium-bidi": "10.5.1", "debug": "^4.4.3", - "devtools-protocol": "0.0.1508733", + "devtools-protocol": "0.0.1521046", "typed-query-selector": "^2.12.0", "webdriver-bidi-protocol": "0.3.8", "ws": "^8.18.3" diff --git a/package.json b/package.json index 9689137aa..9a0c085ef 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "@types/yargs": "^17.0.33", "@typescript-eslint/eslint-plugin": "^8.43.0", "@typescript-eslint/parser": "^8.43.0", - "chrome-devtools-frontend": "1.0.1534717", + "chrome-devtools-frontend": "1.0.1536371", "core-js": "3.46.0", "debug": "4.4.3", "eslint": "^9.35.0", @@ -59,7 +59,7 @@ "eslint-plugin-import": "^2.32.0", "globals": "^16.4.0", "prettier": "^3.6.2", - "puppeteer": "24.26.1", + "puppeteer": "24.27.0", "rollup": "4.52.5", "rollup-plugin-cleanup": "^3.2.1", "rollup-plugin-license": "^3.6.0", diff --git a/src/formatters/snapshotFormatter.ts b/src/formatters/snapshotFormatter.ts index 004359376..e77d7e293 100644 --- a/src/formatters/snapshotFormatter.ts +++ b/src/formatters/snapshotFormatter.ts @@ -35,7 +35,14 @@ function getAttributes(serializedAXNodeRoot: TextSnapshotNode): string[] { attributes.push(`"${serializedAXNodeRoot.name}"`); } - const excluded = new Set(['id', 'role', 'name', 'elementHandle', 'children']); + const excluded = new Set([ + 'id', + 'role', + 'name', + 'elementHandle', + 'children', + 'backendNodeId', + ]); const booleanPropertyMap: Record = { disabled: 'disableable', diff --git a/tests/tools/network.test.js.snapshot b/tests/tools/network.test.js.snapshot index df4002ba0..789e682d7 100644 --- a/tests/tools/network.test.js.snapshot +++ b/tests/tools/network.test.js.snapshot @@ -6,7 +6,7 @@ Status: [success - 200] - accept-language:en-US,en;q=0.9 - upgrade-insecure-requests:1 - user-agent: -- sec-ch-ua:"Chromium";v="", "Not?A_Brand";v="8" +- sec-ch-ua:"Not_A Brand";v="99", "Chromium";v="142" - sec-ch-ua-mobile:?0 - sec-ch-ua-platform:"" ### Response Headers From 8e56307d623fe3651262287b30544ed70426b0b8 Mon Sep 17 00:00:00 2001 From: Alex Rudenko Date: Wed, 29 Oct 2025 10:32:27 +0100 Subject: [PATCH 034/456] feat: an option to ignore cache on reload (#485) This CL implements an option to ignore cache on reload. Since the configuration is passed as-is we rely on tests in Puppeteer to verify the cache behavior. Closes https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/374 --- docs/tool-reference.md | 1 + src/tools/pages.ts | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/tool-reference.md b/docs/tool-reference.md index b9bc6d286..f9d70d5e1 100644 --- a/docs/tool-reference.md +++ b/docs/tool-reference.md @@ -149,6 +149,7 @@ **Parameters:** +- **ignoreCache** (boolean) _(optional)_: Whether to ignore cache on reload. - **timeout** (integer) _(optional)_: Maximum wait time in milliseconds. If set to 0, the default timeout will be used. - **type** (enum: "url", "back", "forward", "reload") _(optional)_: Navigate the page by URL, back or forward in history, or reload. - **url** (string) _(optional)_: Target URL (only type=url) diff --git a/src/tools/pages.ts b/src/tools/pages.ts index d907ffad4..d77a36765 100644 --- a/src/tools/pages.ts +++ b/src/tools/pages.ts @@ -112,6 +112,10 @@ export const navigatePage = defineTool({ 'Navigate the page by URL, back or forward in history, or reload.', ), url: zod.string().optional().describe('Target URL (only type=url)'), + ignoreCache: zod + .boolean() + .optional() + .describe('Whether to ignore cache on reload.'), ...timeoutSchema, }, handler: async (request, response, context) => { @@ -171,7 +175,10 @@ export const navigatePage = defineTool({ break; case 'reload': try { - await page.reload(options); + await page.reload({ + ...options, + ignoreCache: request.params.ignoreCache, + }); response.appendResponseLine(`Successfully reloaded the page.`); } catch (error) { response.appendResponseLine( From 17b2edca7c6b73f29c1d556546ac110a328168c7 Mon Sep 17 00:00:00 2001 From: Alex Rudenko Date: Wed, 29 Oct 2025 11:00:43 +0100 Subject: [PATCH 035/456] build: do not bundle by default (#488) The CI jobs will still bundle by default but I think the risk of the differences between the bundled and unbundled version is fairly low for local development. --- .github/workflows/publish-to-npm-on-tag.yml | 8 ++++---- .github/workflows/run-tests.yml | 2 +- package.json | 3 ++- src/third_party/index.ts | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/workflows/publish-to-npm-on-tag.yml b/.github/workflows/publish-to-npm-on-tag.yml index 007474e8a..297990090 100644 --- a/.github/workflows/publish-to-npm-on-tag.yml +++ b/.github/workflows/publish-to-npm-on-tag.yml @@ -43,8 +43,8 @@ jobs: - name: Install dependencies run: npm ci - - name: Build - run: npm run build + - name: Build and bundle + run: npm run bundle env: NODE_ENV: 'production' @@ -76,8 +76,8 @@ jobs: - name: Install dependencies run: npm ci - - name: Build - run: npm run build + - name: Build and bundle + run: npm run bundle env: NODE_ENV: 'production' diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 52d85e563..5649d2ca7 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -41,7 +41,7 @@ jobs: run: npm ci - name: Build - run: npm run build + run: npm run bundle - name: Set up Node.js uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0 diff --git a/package.json b/package.json index 9a0c085ef..c08de50ca 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,8 @@ "main": "index.js", "scripts": { "clean": "node -e \"require('fs').rmSync('build', {recursive: true, force: true})\"", - "build": "npm run clean && tsc && node --experimental-strip-types --no-warnings=ExperimentalWarning scripts/post-build.ts && rollup -c rollup.config.mjs", + "bundle": "npm run clean && npm run build && rollup -c rollup.config.mjs", + "build": "tsc && node --experimental-strip-types --no-warnings=ExperimentalWarning scripts/post-build.ts", "typecheck": "tsc --noEmit", "format": "eslint --cache --fix . && prettier --write --cache .", "check-format": "eslint --cache . && prettier --check --cache .;", diff --git a/src/third_party/index.ts b/src/third_party/index.ts index c9995d15e..49ef09c59 100644 --- a/src/third_party/index.ts +++ b/src/third_party/index.ts @@ -9,7 +9,7 @@ import 'core-js/proposals/iterator-helpers.js'; export type {Options as YargsOptions} from 'yargs'; export {default as yargs} from 'yargs'; export {hideBin} from 'yargs/helpers'; -export {debug} from 'debug'; +export {default as debug} from 'debug'; export type {Debugger} from 'debug'; export {McpServer} from '@modelcontextprotocol/sdk/server/mcp.js'; export {StdioServerTransport} from '@modelcontextprotocol/sdk/server/stdio.js'; From f0e167568770edc233d49faff97b956122d4b0ff Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 29 Oct 2025 10:19:52 +0000 Subject: [PATCH 036/456] chore(deps-dev): bump @types/node from 24.9.1 to 24.9.2 in the dev-dependencies group (#489) Bumps the dev-dependencies group with 1 update: [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node). Updates `@types/node` from 24.9.1 to 24.9.2
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=@types/node&package-manager=npm_and_yarn&previous-version=24.9.1&new-version=24.9.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index e0c95dd9a..e6d568255 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1130,9 +1130,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "24.9.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.9.1.tgz", - "integrity": "sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg==", + "version": "24.9.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.9.2.tgz", + "integrity": "sha512-uWN8YqxXxqFMX2RqGOrumsKeti4LlmIMIyV0lgut4jx7KQBcBiW6vkDtIBvHnHIquwNfJhk8v2OtmO8zXWHfPA==", "dev": true, "license": "MIT", "dependencies": { From 63a5d824c2d914c9007e2b837fa292f5ba74ceed Mon Sep 17 00:00:00 2001 From: Jack Franklin Date: Wed, 29 Oct 2025 11:33:36 +0100 Subject: [PATCH 037/456] docs: add Windsurf to the editor config README (#493) --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 78c37c182..dfb127251 100644 --- a/README.md +++ b/README.md @@ -219,6 +219,12 @@ Go to `Settings | AI | Manage MCP Servers` -> `+ Add` to [add an MCP Server](htt +
+ Windsurf + Follow the configure MCP guide + using the standard config from above. +
+ ### Your first prompt Enter the following prompt in your MCP Client to check if everything is working: From 60fd66e5bc275c4cdac3951d6acda8369cdc881a Mon Sep 17 00:00:00 2001 From: Sebastian Benz Date: Wed, 29 Oct 2025 21:10:53 +0100 Subject: [PATCH 038/456] chore: new bug report template not showing up (#496) Removed JSON rendering for MCP configuration field. --- .github/ISSUE_TEMPLATE/01-bug.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/01-bug.yml b/.github/ISSUE_TEMPLATE/01-bug.yml index 357423ef0..844727ea6 100644 --- a/.github/ISSUE_TEMPLATE/01-bug.yml +++ b/.github/ISSUE_TEMPLATE/01-bug.yml @@ -31,7 +31,6 @@ body: - id: mcp-configuration type: textarea - render: json attributes: label: MCP configuration From c06f4522ee8f762b59c60c2fd23a0deaaa544766 Mon Sep 17 00:00:00 2001 From: Alex Rudenko Date: Thu, 30 Oct 2025 09:50:36 +0100 Subject: [PATCH 039/456] refactor: merge emulate tools into one (#494) Merges emulateCpu and emulateNetwork into a single tool to reduce token consumption. Changes descriptions to be more concise. --- README.md | 5 +- docs/tool-reference.md | 22 +++----- src/tools/emulation.ts | 94 ++++++++++++++++------------------- tests/tools/emulation.test.ts | 36 +++++++------- 4 files changed, 69 insertions(+), 88 deletions(-) diff --git a/README.md b/README.md index dfb127251..63a8a7cc0 100644 --- a/README.md +++ b/README.md @@ -260,9 +260,8 @@ If you run into any issues, checkout our [troubleshooting guide](./docs/troubles - [`new_page`](docs/tool-reference.md#new_page) - [`select_page`](docs/tool-reference.md#select_page) - [`wait_for`](docs/tool-reference.md#wait_for) -- **Emulation** (3 tools) - - [`emulate_cpu`](docs/tool-reference.md#emulate_cpu) - - [`emulate_network`](docs/tool-reference.md#emulate_network) +- **Emulation** (2 tools) + - [`emulate`](docs/tool-reference.md#emulate) - [`resize_page`](docs/tool-reference.md#resize_page) - **Performance** (3 tools) - [`performance_analyze_insight`](docs/tool-reference.md#performance_analyze_insight) diff --git a/docs/tool-reference.md b/docs/tool-reference.md index f9d70d5e1..2fa445b2c 100644 --- a/docs/tool-reference.md +++ b/docs/tool-reference.md @@ -18,9 +18,8 @@ - [`new_page`](#new_page) - [`select_page`](#select_page) - [`wait_for`](#wait_for) -- **[Emulation](#emulation)** (3 tools) - - [`emulate_cpu`](#emulate_cpu) - - [`emulate_network`](#emulate_network) +- **[Emulation](#emulation)** (2 tools) + - [`emulate`](#emulate) - [`resize_page`](#resize_page) - **[Performance](#performance)** (3 tools) - [`performance_analyze_insight`](#performance_analyze_insight) @@ -190,23 +189,14 @@ ## Emulation -### `emulate_cpu` +### `emulate` -**Description:** Emulates CPU throttling by slowing down the selected page's execution. +**Description:** Emulates various features on the selected page. **Parameters:** -- **throttlingRate** (number) **(required)**: The CPU throttling rate representing the slowdown factor 1-20x. Set the rate to 1 to disable throttling - ---- - -### `emulate_network` - -**Description:** Emulates network conditions such as throttling or offline mode on the selected page. - -**Parameters:** - -- **throttlingOption** (enum: "No emulation", "Offline", "Slow 3G", "Fast 3G", "Slow 4G", "Fast 4G") **(required)**: The network throttling option to emulate. Available throttling options are: No emulation, Offline, Slow 3G, Fast 3G, Slow 4G, Fast 4G. Set to "No emulation" to disable. Set to "Offline" to simulate offline network conditions. +- **cpuThrottlingRate** (number) _(optional)_: Represents the CPU slowdown factor. Set the rate to 1 to disable throttling. If omitted, throttling remains unchanged. +- **networkConditions** (enum: "No emulation", "Offline", "Slow 3G", "Fast 3G", "Slow 4G", "Fast 4G") _(optional)_: Throttle network. Set to "No emulation" to disable. If omitted, conditions remain unchanged. --- diff --git a/src/tools/emulation.ts b/src/tools/emulation.ts index 2222e4e3e..4b88eead7 100644 --- a/src/tools/emulation.ts +++ b/src/tools/emulation.ts @@ -15,73 +15,65 @@ const throttlingOptions: [string, ...string[]] = [ ...Object.keys(PredefinedNetworkConditions), ]; -export const emulateNetwork = defineTool({ - name: 'emulate_network', - description: `Emulates network conditions such as throttling or offline mode on the selected page.`, +export const emulate = defineTool({ + name: 'emulate', + description: `Emulates various features on the selected page.`, annotations: { category: ToolCategory.EMULATION, readOnlyHint: false, }, schema: { - throttlingOption: zod + networkConditions: zod .enum(throttlingOptions) + .optional() .describe( - `The network throttling option to emulate. Available throttling options are: ${throttlingOptions.join(', ')}. Set to "No emulation" to disable. Set to "Offline" to simulate offline network conditions.`, + `Throttle network. Set to "No emulation" to disable. If omitted, conditions remain unchanged.`, ), - }, - handler: async (request, _response, context) => { - const page = context.getSelectedPage(); - const conditions = request.params.throttlingOption; - - if (conditions === 'No emulation') { - await page.emulateNetworkConditions(null); - context.setNetworkConditions(null); - return; - } - - if (conditions === 'Offline') { - await page.emulateNetworkConditions({ - offline: true, - download: 0, - upload: 0, - latency: 0, - }); - context.setNetworkConditions('Offline'); - return; - } - - if (conditions in PredefinedNetworkConditions) { - const networkCondition = - PredefinedNetworkConditions[ - conditions as keyof typeof PredefinedNetworkConditions - ]; - await page.emulateNetworkConditions(networkCondition); - context.setNetworkConditions(conditions); - } - }, -}); - -export const emulateCpu = defineTool({ - name: 'emulate_cpu', - description: `Emulates CPU throttling by slowing down the selected page's execution.`, - annotations: { - category: ToolCategory.EMULATION, - readOnlyHint: false, - }, - schema: { - throttlingRate: zod + cpuThrottlingRate: zod .number() .min(1) .max(20) + .optional() .describe( - 'The CPU throttling rate representing the slowdown factor 1-20x. Set the rate to 1 to disable throttling', + 'Represents the CPU slowdown factor. Set the rate to 1 to disable throttling. If omitted, throttling remains unchanged.', ), }, handler: async (request, _response, context) => { const page = context.getSelectedPage(); - const {throttlingRate} = request.params; + const networkConditions = request.params.networkConditions; + const cpuThrottlingRate = request.params.cpuThrottlingRate; - await page.emulateCPUThrottling(throttlingRate); - context.setCpuThrottlingRate(throttlingRate); + if (networkConditions) { + if (networkConditions === 'No emulation') { + await page.emulateNetworkConditions(null); + context.setNetworkConditions(null); + return; + } + + if (networkConditions === 'Offline') { + await page.emulateNetworkConditions({ + offline: true, + download: 0, + upload: 0, + latency: 0, + }); + context.setNetworkConditions('Offline'); + return; + } + + if (networkConditions in PredefinedNetworkConditions) { + const networkCondition = + PredefinedNetworkConditions[ + networkConditions as keyof typeof PredefinedNetworkConditions + ]; + await page.emulateNetworkConditions(networkCondition); + context.setNetworkConditions(networkConditions); + } + } + + if (cpuThrottlingRate) { + await page.emulateCPUThrottling(cpuThrottlingRate); + context.setCpuThrottlingRate(cpuThrottlingRate); + } }, }); diff --git a/tests/tools/emulation.test.ts b/tests/tools/emulation.test.ts index 0b64e4847..92ad52a56 100644 --- a/tests/tools/emulation.test.ts +++ b/tests/tools/emulation.test.ts @@ -6,17 +6,17 @@ import assert from 'node:assert'; import {describe, it} from 'node:test'; -import {emulateCpu, emulateNetwork} from '../../src/tools/emulation.js'; +import {emulate} from '../../src/tools/emulation.js'; import {withBrowser} from '../utils.js'; describe('emulation', () => { describe('network', () => { it('emulates offline network conditions', async () => { await withBrowser(async (response, context) => { - await emulateNetwork.handler( + await emulate.handler( { params: { - throttlingOption: 'Offline', + networkConditions: 'Offline', }, }, response, @@ -26,12 +26,12 @@ describe('emulation', () => { assert.strictEqual(context.getNetworkConditions(), 'Offline'); }); }); - it('emulates network throttling when the throttling option is valid ', async () => { + it('emulates network throttling when the throttling option is valid', async () => { await withBrowser(async (response, context) => { - await emulateNetwork.handler( + await emulate.handler( { params: { - throttlingOption: 'Slow 3G', + networkConditions: 'Slow 3G', }, }, response, @@ -44,10 +44,10 @@ describe('emulation', () => { it('disables network emulation', async () => { await withBrowser(async (response, context) => { - await emulateNetwork.handler( + await emulate.handler( { params: { - throttlingOption: 'No emulation', + networkConditions: 'No emulation', }, }, response, @@ -60,10 +60,10 @@ describe('emulation', () => { it('does not set throttling when the network throttling is not one of the predefined options', async () => { await withBrowser(async (response, context) => { - await emulateNetwork.handler( + await emulate.handler( { params: { - throttlingOption: 'Slow 11G', + networkConditions: 'Slow 11G', }, }, response, @@ -77,10 +77,10 @@ describe('emulation', () => { it('report correctly for the currently selected page', async () => { await withBrowser(async (response, context) => { await context.newPage(); - await emulateNetwork.handler( + await emulate.handler( { params: { - throttlingOption: 'Slow 3G', + networkConditions: 'Slow 3G', }, }, response, @@ -99,10 +99,10 @@ describe('emulation', () => { describe('cpu', () => { it('emulates cpu throttling when the rate is valid (1-20x)', async () => { await withBrowser(async (response, context) => { - await emulateCpu.handler( + await emulate.handler( { params: { - throttlingRate: 4, + cpuThrottlingRate: 4, }, }, response, @@ -116,10 +116,10 @@ describe('emulation', () => { it('disables cpu throttling', async () => { await withBrowser(async (response, context) => { context.setCpuThrottlingRate(4); // Set it to something first. - await emulateCpu.handler( + await emulate.handler( { params: { - throttlingRate: 1, + cpuThrottlingRate: 1, }, }, response, @@ -133,10 +133,10 @@ describe('emulation', () => { it('report correctly for the currently selected page', async () => { await withBrowser(async (response, context) => { await context.newPage(); - await emulateCpu.handler( + await emulate.handler( { params: { - throttlingRate: 4, + cpuThrottlingRate: 4, }, }, response, From aae8c0fab051f962888a993dd08f1bf55233148a Mon Sep 17 00:00:00 2001 From: Alex Rudenko Date: Thu, 30 Oct 2025 10:58:38 +0100 Subject: [PATCH 040/456] build: remove outdated prepare.ts steps (#501) --- scripts/prepare.ts | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/scripts/prepare.ts b/scripts/prepare.ts index 07dc11046..dc7756dea 100644 --- a/scripts/prepare.ts +++ b/scripts/prepare.ts @@ -7,15 +7,12 @@ import {rm} from 'node:fs/promises'; import {resolve} from 'node:path'; -import {sed} from './sed.ts'; - const projectRoot = process.cwd(); const filesToRemove = [ 'node_modules/chrome-devtools-frontend/package.json', 'node_modules/chrome-devtools-frontend/front_end/models/trace/lantern/testing', 'node_modules/chrome-devtools-frontend/front_end/third_party/intl-messageformat/package/package.json', - 'node_modules/chrome-devtools-frontend/front_end/third_party/codemirror.next/codemirror.next.js', ]; async function main() { @@ -30,28 +27,6 @@ async function main() { process.exit(1); } } - // TODO: remove once https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/7072054 is available. - sed( - 'node_modules/chrome-devtools-frontend/front_end/core/sdk/NetworkManager.ts', - `declare global { - // TS typedefs are not up to date - interface URLPattern { - hash: string; - hostname: string; - password: string; - pathname: string; - port: string; - protocol: string; - search: string; - username: string; - hasRegExpGroups: boolean; - test(url: string): boolean; - } - /* eslint-disable-next-line @typescript-eslint/naming-convention */ - var URLPattern: {prototype: URLPattern, new (input: string): URLPattern}; -}`, - '', - ); console.log('Clean up of chrome-devtools-frontend complete.'); } From 2f448e84ea8d3a44687c74b3577edf882ef2c19f Mon Sep 17 00:00:00 2001 From: Alex Rudenko Date: Thu, 30 Oct 2025 11:52:09 +0100 Subject: [PATCH 041/456] fix: improve get_network_request description (#500) Closes https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/497 --- docs/tool-reference.md | 5 ++--- src/formatters/networkFormatter.ts | 2 +- src/tools/network.ts | 7 +++---- tests/formatters/networkFormatter.test.ts | 2 +- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/docs/tool-reference.md b/docs/tool-reference.md index 2fa445b2c..b5106dcf6 100644 --- a/docs/tool-reference.md +++ b/docs/tool-reference.md @@ -246,12 +246,11 @@ ### `get_network_request` -**Description:** Gets a network request by reqid. You can get all requests by calling [`list_network_requests`](#list_network_requests). -Get the request currently selected in the DevTools UI by ommitting reqid +**Description:** Gets a network request by an optional reqid, if omitted returns the currently selected request in the DevTools Network panel. **Parameters:** -- **reqid** (number) _(optional)_: The reqid of the network request. If omitted, looks up the current request selected in DevTools UI. +- **reqid** (number) _(optional)_: The reqid of the network request. If omitted returns the currently selected request in the DevTools Network panel. --- diff --git a/src/formatters/networkFormatter.ts b/src/formatters/networkFormatter.ts index d3945130a..74ba11842 100644 --- a/src/formatters/networkFormatter.ts +++ b/src/formatters/networkFormatter.ts @@ -16,7 +16,7 @@ export function getShortDescriptionForRequest( selectedInDevToolsUI = false, ): string { // TODO truncate the URL - return `reqid=${id} ${request.method()} ${request.url()} ${getStatusFromRequest(request)}${selectedInDevToolsUI ? ` [selected in DevTools UI]` : ''}`; + return `reqid=${id} ${request.method()} ${request.url()} ${getStatusFromRequest(request)}${selectedInDevToolsUI ? ` [selected in the DevTools Network panel]` : ''}`; } export function getStatusFromRequest(request: HTTPRequest): string { diff --git a/src/tools/network.ts b/src/tools/network.ts index 1df945500..e2923c426 100644 --- a/src/tools/network.ts +++ b/src/tools/network.ts @@ -84,8 +84,7 @@ export const listNetworkRequests = defineTool({ export const getNetworkRequest = defineTool({ name: 'get_network_request', - description: `Gets a network request by reqid. You can get all requests by calling ${listNetworkRequests.name}. -Get the request currently selected in the DevTools UI by ommitting reqid`, + description: `Gets a network request by an optional reqid, if omitted returns the currently selected request in the DevTools Network panel.`, annotations: { category: ToolCategory.NETWORK, readOnlyHint: true, @@ -95,7 +94,7 @@ Get the request currently selected in the DevTools UI by ommitting reqid`, .number() .optional() .describe( - 'The reqid of the network request. If omitted, looks up the current request selected in DevTools UI.', + 'The reqid of the network request. If omitted returns the currently selected request in the DevTools Network panel.', ), }, handler: async (request, response, context) => { @@ -107,7 +106,7 @@ Get the request currently selected in the DevTools UI by ommitting reqid`, response.attachNetworkRequest(data?.requestId); } else { response.appendResponseLine( - `Nothing is currently selected in DevTools UI.`, + `Nothing is currently selected in the DevTools Network panel.`, ); } } diff --git a/tests/formatters/networkFormatter.test.ts b/tests/formatters/networkFormatter.test.ts index 2bd1bbd64..b6edd56ad 100644 --- a/tests/formatters/networkFormatter.test.ts +++ b/tests/formatters/networkFormatter.test.ts @@ -78,7 +78,7 @@ describe('networkFormatter', () => { assert.equal( result, - 'reqid=1 GET http://example.com [pending] [selected in DevTools UI]', + 'reqid=1 GET http://example.com [pending] [selected in the DevTools Network panel]', ); }); }); From cca5ff471c2d2c663e63ade1e2ea58f9a7f5a2cd Mon Sep 17 00:00:00 2001 From: Alex Rudenko Date: Fri, 31 Oct 2025 09:26:24 +0100 Subject: [PATCH 042/456] fix: work around NTP iframes causing hangs (#504) Drive-by: some logging --- src/McpContext.ts | 2 ++ src/browser.ts | 7 +++++++ src/main.ts | 1 + 3 files changed, 10 insertions(+) diff --git a/src/McpContext.ts b/src/McpContext.ts index ceae35136..47c5030a7 100644 --- a/src/McpContext.ts +++ b/src/McpContext.ts @@ -343,11 +343,13 @@ export class McpContext implements Context { } async detectOpenDevToolsWindows() { + this.logger('Detecting open DevTools windows'); const pages = await this.browser.pages(); this.#pageToDevToolsPage = new Map(); for (const devToolsPage of pages) { if (devToolsPage.url().startsWith('devtools://')) { try { + this.logger('Calling getTargetInfo for ' + devToolsPage.url()); const data = await devToolsPage // @ts-expect-error no types for _client(). ._client() diff --git a/src/browser.ts b/src/browser.ts index d0c99c7f2..f3f3d9e11 100644 --- a/src/browser.ts +++ b/src/browser.ts @@ -8,6 +8,7 @@ import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; +import {logger} from './logger.js'; import type { Browser, ChromeReleaseChannel, @@ -29,6 +30,10 @@ function makeTargetFilter() { if (target.url() === 'chrome://newtab/') { return true; } + if (target.url().startsWith('https://ogs.google.com/widget/app/so')) { + // Some special frame on the NTP that is not picked up by CDP-auto-attach. + return false; + } for (const prefix of ignoredPrefixes) { if (target.url().startsWith(prefix)) { return false; @@ -65,7 +70,9 @@ export async function ensureBrowserConnected(options: { throw new Error('Either browserURL or wsEndpoint must be provided'); } + logger('Connecting Puppeteer to ', JSON.stringify(connectOptions)); browser = await puppeteer.connect(connectOptions); + logger('Connected Puppeteer'); return browser; } diff --git a/src/main.ts b/src/main.ts index 9dad338e9..395f07a85 100644 --- a/src/main.ts +++ b/src/main.ts @@ -129,6 +129,7 @@ function registerTool(tool: ToolDefinition): void { try { logger(`${tool.name} request: ${JSON.stringify(params, null, ' ')}`); const context = await getContext(); + logger(`${tool.name} context: resolved`); await context.detectOpenDevToolsWindows(); const response = new McpResponse(); await tool.handler( From 4a83574961d8d6b974037db56fc8bdbbb91f79b6 Mon Sep 17 00:00:00 2001 From: Alex Rudenko Date: Fri, 31 Oct 2025 11:07:10 +0100 Subject: [PATCH 043/456] feat: fetch DOM node selected in the DevTools Elements panel (#486) Refs https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/129 --- docs/tool-reference.md | 3 +- src/McpContext.ts | 94 ++++++++++++++++------ src/McpResponse.ts | 19 ++++- src/formatters/snapshotFormatter.ts | 21 +++-- src/tools/ToolDefinition.ts | 17 +++- src/tools/network.ts | 14 +++- src/tools/snapshot.ts | 3 +- tests/formatters/snapshotFormatter.test.ts | 18 ++--- 8 files changed, 137 insertions(+), 52 deletions(-) diff --git a/docs/tool-reference.md b/docs/tool-reference.md index b5106dcf6..72a41da7c 100644 --- a/docs/tool-reference.md +++ b/docs/tool-reference.md @@ -329,7 +329,8 @@ so returned values have to JSON-serializable. ### `take_snapshot` **Description:** Take a text snapshot of the currently selected page based on the a11y tree. The snapshot lists page elements along with a unique -identifier (uid). Always use the latest snapshot. Prefer taking a snapshot over taking a screenshot. +identifier (uid). Always use the latest snapshot. Prefer taking a snapshot over taking a screenshot. The snapshot indicates the element selected +in the DevTools Elements panel (if any). **Parameters:** diff --git a/src/McpContext.ts b/src/McpContext.ts index 47c5030a7..59fc72b1b 100644 --- a/src/McpContext.ts +++ b/src/McpContext.ts @@ -25,12 +25,13 @@ import type { import {listPages} from './tools/pages.js'; import {takeSnapshot} from './tools/snapshot.js'; import {CLOSE_PAGE_ERROR} from './tools/ToolDefinition.js'; -import type {Context} from './tools/ToolDefinition.js'; +import type {Context, DevToolsData} from './tools/ToolDefinition.js'; import type {TraceResult} from './trace-processing/parse.js'; import {WaitForHelper} from './WaitForHelper.js'; export interface TextSnapshotNode extends SerializedAXNode { id: string; + backendNodeId?: number; children: TextSnapshotNode[]; } @@ -38,6 +39,7 @@ export interface TextSnapshot { root: TextSnapshotNode; idToNode: Map; snapshotId: string; + selectedElementUid?: string; } interface McpContextOptions { @@ -151,6 +153,42 @@ export class McpContext implements Context { return context; } + resolveCdpRequestId(cdpRequestId: string): number | undefined { + const selectedPage = this.getSelectedPage(); + if (!cdpRequestId) { + this.logger('no network request'); + return; + } + const request = this.#networkCollector.find(selectedPage, request => { + // @ts-expect-error id is internal. + return request.id === cdpRequestId; + }); + if (!request) { + this.logger('no network request for ' + cdpRequestId); + return; + } + return this.#networkCollector.getIdForResource(request); + } + + resolveCdpElementId(cdpBackendNodeId: number): string | undefined { + if (!cdpBackendNodeId) { + this.logger('no cdpBackendNodeId'); + return; + } + // TODO: index by backendNodeId instead. + const queue = [this.#textSnapshot?.root]; + while (queue.length) { + const current = queue.pop()!; + if (current.backendNodeId === cdpBackendNodeId) { + return current.id; + } + for (const child of current.children) { + queue.push(child); + } + } + return; + } + getNetworkRequests(includePreservedRequests?: boolean): HTTPRequest[] { const page = this.getSelectedPage(); return this.#networkCollector.getData(page, includePreservedRequests); @@ -380,12 +418,17 @@ export class McpContext implements Context { return this.#pageToDevToolsPage.get(page); } - async getDevToolsData(): Promise { + async getDevToolsData(): Promise { try { + this.logger('Getting DevTools UI data'); const selectedPage = this.getSelectedPage(); const devtoolsPage = this.getDevToolsPage(selectedPage); - if (devtoolsPage) { - const cdpRequestId = await devtoolsPage.evaluate(async () => { + if (!devtoolsPage) { + this.logger('No DevTools page detected'); + return {}; + } + const {cdpRequestId, cdpBackendNodeId} = await devtoolsPage.evaluate( + async () => { // @ts-expect-error no types const UI = await import('/bundled/ui/legacy/legacy.js'); // @ts-expect-error no types @@ -393,36 +436,29 @@ export class McpContext implements Context { const request = UI.Context.Context.instance().flavor( SDK.NetworkRequest.NetworkRequest, ); - return request?.requestId(); - }); - if (!cdpRequestId) { - this.logger('no context request'); - return; - } - const request = this.#networkCollector.find(selectedPage, request => { - // @ts-expect-error id is internal. - return request.id === cdpRequestId; - }); - if (!request) { - this.logger('no collected request for ' + cdpRequestId); - return; - } - return { - requestId: this.#networkCollector.getIdForResource(request), - }; - } else { - this.logger('no devtools page deteched'); - } + const node = UI.Context.Context.instance().flavor( + SDK.DOMModel.DOMNode, + ); + return { + cdpRequestId: request?.requestId(), + cdpBackendNodeId: node?.backendNodeId(), + }; + }, + ); + return {cdpBackendNodeId, cdpRequestId}; } catch (err) { this.logger('error getting devtools data', err); } - return; + return {}; } /** * Creates a text snapshot of a page. */ - async createTextSnapshot(verbose = false): Promise { + async createTextSnapshot( + verbose = false, + devtoolsData: DevToolsData | undefined = undefined, + ): Promise { const page = this.getSelectedPage(); const rootNode = await page.accessibility.snapshot({ includeIframes: true, @@ -465,6 +501,12 @@ export class McpContext implements Context { snapshotId: String(snapshotId), idToNode, }; + const data = devtoolsData ?? (await this.getDevToolsData()); + if (data?.cdpBackendNodeId) { + this.#textSnapshot.selectedElementUid = this.resolveCdpElementId( + data?.cdpBackendNodeId, + ); + } } getTextSnapshot(): TextSnapshot | null { diff --git a/src/McpResponse.ts b/src/McpResponse.ts index 6798c90b3..b5ea74aa4 100644 --- a/src/McpResponse.ts +++ b/src/McpResponse.ts @@ -15,7 +15,7 @@ import { getShortDescriptionForRequest, getStatusFromRequest, } from './formatters/networkFormatter.js'; -import {formatA11ySnapshot} from './formatters/snapshotFormatter.js'; +import {formatSnapshotNode} from './formatters/snapshotFormatter.js'; import type {McpContext} from './McpContext.js'; import type { ConsoleMessage, @@ -25,6 +25,7 @@ import type { } from './third_party/index.js'; import {handleDialog} from './tools/pages.js'; import type { + DevToolsData, ImageContentData, Response, SnapshotParams, @@ -52,6 +53,11 @@ export class McpResponse implements Response { types?: string[]; includePreservedMessages?: boolean; }; + #devToolsData?: DevToolsData; + + attachDevToolsData(data: DevToolsData): void { + this.#devToolsData = data; + } setIncludePages(value: boolean): void { this.#includePages = value; @@ -179,17 +185,22 @@ export class McpResponse implements Response { let formattedSnapshot: string | undefined; if (this.#snapshotParams) { - await context.createTextSnapshot(this.#snapshotParams.verbose); + await context.createTextSnapshot( + this.#snapshotParams.verbose, + this.#devToolsData, + ); const snapshot = context.getTextSnapshot(); if (snapshot) { if (this.#snapshotParams.filePath) { await context.saveFile( - new TextEncoder().encode(formatA11ySnapshot(snapshot.root)), + new TextEncoder().encode( + formatSnapshotNode(snapshot.root, snapshot), + ), this.#snapshotParams.filePath, ); formattedSnapshot = `Saved snapshot to ${this.#snapshotParams.filePath}.`; } else { - formattedSnapshot = formatA11ySnapshot(snapshot.root); + formattedSnapshot = formatSnapshotNode(snapshot.root, snapshot); } } } diff --git a/src/formatters/snapshotFormatter.ts b/src/formatters/snapshotFormatter.ts index e77d7e293..42d567e50 100644 --- a/src/formatters/snapshotFormatter.ts +++ b/src/formatters/snapshotFormatter.ts @@ -3,19 +3,26 @@ * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ -import type {TextSnapshotNode} from '../McpContext.js'; +import type {TextSnapshot, TextSnapshotNode} from '../McpContext.js'; -export function formatA11ySnapshot( - serializedAXNodeRoot: TextSnapshotNode, +export function formatSnapshotNode( + root: TextSnapshotNode, + snapshot?: TextSnapshot, depth = 0, ): string { let result = ''; - const attributes = getAttributes(serializedAXNodeRoot); - const line = ' '.repeat(depth * 2) + attributes.join(' ') + '\n'; + const attributes = getAttributes(root); + const line = + ' '.repeat(depth * 2) + + attributes.join(' ') + + (root.id === snapshot?.selectedElementUid + ? ' [selected in the DevTools Elements panel]' + : '') + + '\n'; result += line; - for (const child of serializedAXNodeRoot.children) { - result += formatA11ySnapshot(child, depth + 1); + for (const child of root.children) { + result += formatSnapshotNode(child, snapshot, depth + 1); } return result; diff --git a/src/tools/ToolDefinition.ts b/src/tools/ToolDefinition.ts index 8df0f720c..9651718bf 100644 --- a/src/tools/ToolDefinition.ts +++ b/src/tools/ToolDefinition.ts @@ -47,6 +47,11 @@ export interface SnapshotParams { filePath?: string; } +export interface DevToolsData { + cdpRequestId?: string; + cdpBackendNodeId?: number; +} + export interface Response { appendResponseLine(value: string): void; setIncludePages(value: boolean): void; @@ -69,6 +74,8 @@ export interface Response { attachImage(value: ImageContentData): void; attachNetworkRequest(reqid: number): void; attachConsoleMessage(msgid: number): void; + // Allows re-using DevTools data queried by some tools. + attachDevToolsData(data: DevToolsData): void; } /** @@ -103,7 +110,15 @@ export type Context = Readonly<{ text: string; timeout?: number | undefined; }): Promise; - getDevToolsData(): Promise; + getDevToolsData(): Promise; + /** + * Returns a reqid for a cdpRequestId. + */ + resolveCdpRequestId(cdpRequestId: string): number | undefined; + /** + * Returns a reqid for a cdpRequestId. + */ + resolveCdpElementId(cdpBackendNodeId: number): string | undefined; }>; export function defineTool( diff --git a/src/tools/network.ts b/src/tools/network.ts index e2923c426..d3b6c1fe6 100644 --- a/src/tools/network.ts +++ b/src/tools/network.ts @@ -72,12 +72,16 @@ export const listNetworkRequests = defineTool({ }, handler: async (request, response, context) => { const data = await context.getDevToolsData(); + response.attachDevToolsData(data); + const reqid = data?.cdpRequestId + ? context.resolveCdpRequestId(data.cdpRequestId) + : undefined; response.setIncludeNetworkRequests(true, { pageSize: request.params.pageSize, pageIdx: request.params.pageIdx, resourceTypes: request.params.resourceTypes, includePreservedRequests: request.params.includePreservedRequests, - networkRequestIdInDevToolsUI: data?.requestId, + networkRequestIdInDevToolsUI: reqid, }); }, }); @@ -102,8 +106,12 @@ export const getNetworkRequest = defineTool({ response.attachNetworkRequest(request.params.reqid); } else { const data = await context.getDevToolsData(); - if (data?.requestId) { - response.attachNetworkRequest(data?.requestId); + response.attachDevToolsData(data); + const reqid = data?.cdpRequestId + ? context.resolveCdpRequestId(data.cdpRequestId) + : undefined; + if (reqid) { + response.attachNetworkRequest(reqid); } else { response.appendResponseLine( `Nothing is currently selected in the DevTools Network panel.`, diff --git a/src/tools/snapshot.ts b/src/tools/snapshot.ts index 0187f7fc7..21099f141 100644 --- a/src/tools/snapshot.ts +++ b/src/tools/snapshot.ts @@ -12,7 +12,8 @@ import {defineTool, timeoutSchema} from './ToolDefinition.js'; export const takeSnapshot = defineTool({ name: 'take_snapshot', description: `Take a text snapshot of the currently selected page based on the a11y tree. The snapshot lists page elements along with a unique -identifier (uid). Always use the latest snapshot. Prefer taking a snapshot over taking a screenshot.`, +identifier (uid). Always use the latest snapshot. Prefer taking a snapshot over taking a screenshot. The snapshot indicates the element selected +in the DevTools Elements panel (if any).`, annotations: { category: ToolCategory.DEBUGGING, // Not read-only due to filePath param. diff --git a/tests/formatters/snapshotFormatter.test.ts b/tests/formatters/snapshotFormatter.test.ts index 47b7d2880..1232929f3 100644 --- a/tests/formatters/snapshotFormatter.test.ts +++ b/tests/formatters/snapshotFormatter.test.ts @@ -9,12 +9,12 @@ import {describe, it} from 'node:test'; import type {ElementHandle} from 'puppeteer-core'; -import {formatA11ySnapshot} from '../../src/formatters/snapshotFormatter.js'; +import {formatSnapshotNode} from '../../src/formatters/snapshotFormatter.js'; import type {TextSnapshotNode} from '../../src/McpContext.js'; describe('snapshotFormatter', () => { it('formats a snapshot with value properties', () => { - const snapshot: TextSnapshotNode = { + const node: TextSnapshotNode = { id: '1_1', role: 'textbox', name: 'textbox', @@ -35,7 +35,7 @@ describe('snapshotFormatter', () => { }, }; - const formatted = formatA11ySnapshot(snapshot); + const formatted = formatSnapshotNode(node); assert.strictEqual( formatted, `uid=1_1 textbox "textbox" value="value" @@ -45,7 +45,7 @@ describe('snapshotFormatter', () => { }); it('formats a snapshot with boolean properties', () => { - const snapshot: TextSnapshotNode = { + const node: TextSnapshotNode = { id: '1_1', role: 'button', name: 'button', @@ -66,7 +66,7 @@ describe('snapshotFormatter', () => { }, }; - const formatted = formatA11ySnapshot(snapshot); + const formatted = formatSnapshotNode(node); assert.strictEqual( formatted, `uid=1_1 button "button" disableable disabled @@ -76,7 +76,7 @@ describe('snapshotFormatter', () => { }); it('formats a snapshot with checked properties', () => { - const snapshot: TextSnapshotNode = { + const node: TextSnapshotNode = { id: '1_1', role: 'checkbox', name: 'checkbox', @@ -97,7 +97,7 @@ describe('snapshotFormatter', () => { }, }; - const formatted = formatA11ySnapshot(snapshot); + const formatted = formatSnapshotNode(node); assert.strictEqual( formatted, `uid=1_1 checkbox "checkbox" checked @@ -107,7 +107,7 @@ describe('snapshotFormatter', () => { }); it('formats a snapshot with multiple different type attributes', () => { - const snapshot: TextSnapshotNode = { + const node: TextSnapshotNode = { id: '1_1', role: 'root', name: 'root', @@ -139,7 +139,7 @@ describe('snapshotFormatter', () => { }, }; - const formatted = formatA11ySnapshot(snapshot); + const formatted = formatSnapshotNode(node); assert.strictEqual( formatted, `uid=1_1 root "root" From 3da8d9f5a35964418e8e92238c1060a65f88a30b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Nov 2025 08:22:45 +0100 Subject: [PATCH 044/456] chore(deps-dev): bump the dev-dependencies group with 4 updates (#507) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the dev-dependencies group with 4 updates: [@eslint/js](https://github.com/eslint/eslint/tree/HEAD/packages/js), [@rollup/plugin-commonjs](https://github.com/rollup/plugins/tree/HEAD/packages/commonjs), [eslint](https://github.com/eslint/eslint) and [globals](https://github.com/sindresorhus/globals). Updates `@eslint/js` from 9.38.0 to 9.39.0
Release notes

Sourced from @​eslint/js's releases.

v9.39.0

Features

  • cc57d87 feat: update error loc to key in no-dupe-class-members (#20259) (Tanuj Kanti)
  • 126552f feat: update error location in for-direction and no-dupe-args (#20258) (Tanuj Kanti)
  • 167d097 feat: update complexity rule to highlight only static block header (#20245) (jaymarvelz)

Bug Fixes

  • 15f5c7c fix: forward traversal step.args to visitors (#20253) (jaymarvelz)
  • 5a1a534 fix: allow JSDoc comments in object-shorthand rule (#20167) (Nitin Kumar)
  • e86b813 fix: Use more types from @​eslint/core (#20257) (Nicholas C. Zakas)
  • 927272d fix: correct Scope typings (#20198) (jaymarvelz)
  • 37f76d9 fix: use AST.Program type for Program node (#20244) (Francesco Trotta)
  • ae07f0b fix: unify timing report for concurrent linting (#20188) (jaymarvelz)
  • b165d47 fix: correct Rule typings (#20199) (jaymarvelz)
  • fb97cda fix: improve error message for missing fix function in suggestions (#20218) (jaymarvelz)

Documentation

  • d3e81e3 docs: Always recommend to include a files property (#20158) (Percy Ma)
  • 0f0385f docs: use consistent naming recommendation (#20250) (Alex M. Spieslechner)
  • a3b1456 docs: Update README (GitHub Actions Bot)
  • cf5f2dd docs: fix correct tag of no-useless-constructor (#20255) (Tanuj Kanti)
  • 10b995c docs: add TS options and examples for nofunc in no-use-before-define (#20249) (Tanuj Kanti)
  • 2584187 docs: remove repetitive word in comment (#20242) (reddaisyy)
  • 637216b docs: update CLI flags migration instructions (#20238) (jaymarvelz)
  • e7cda3b docs: Update README (GitHub Actions Bot)
  • 7b9446f docs: handle empty flags sections on the feature flags page (#20222) (sethamus)

Chores

  • dfe3c1b chore: update @eslint/js version to 9.39.0 (#20270) (Francesco Trotta)
  • 2375a6d chore: package.json update for @​eslint/js release (Jenkins)
  • a1f4e52 chore: update @eslint dependencies (#20265) (Francesco Trotta)
  • c7d3229 chore: update dependency @​eslint/core to ^0.17.0 (#20256) (renovate[bot])
  • 27549bc chore: update fuzz testing to not error if code sample minimizer fails (#20252) (Milos Djermanovic)
  • a1370ee ci: bump actions/setup-node from 5 to 6 (#20230) (dependabot[bot])
  • 9e7fad4 chore: add script to auto-generate eslint:recommended configuration (#20208) (唯然)
Commits
  • 2375a6d chore: package.json update for @​eslint/js release
  • 9e7fad4 chore: add script to auto-generate eslint:recommended configuration (#20208)
  • See full diff in compare view

Updates `@rollup/plugin-commonjs` from 28.0.9 to 29.0.0
Changelog

Sourced from @​rollup/plugin-commonjs's changelog.

v29.0.0

2025-10-30

Breaking Changes

  • feat!: revert #1909 and add requireNodeBuiltins option (#1937)
Commits

Updates `eslint` from 9.38.0 to 9.39.0
Release notes

Sourced from eslint's releases.

v9.39.0

Features

  • cc57d87 feat: update error loc to key in no-dupe-class-members (#20259) (Tanuj Kanti)
  • 126552f feat: update error location in for-direction and no-dupe-args (#20258) (Tanuj Kanti)
  • 167d097 feat: update complexity rule to highlight only static block header (#20245) (jaymarvelz)

Bug Fixes

  • 15f5c7c fix: forward traversal step.args to visitors (#20253) (jaymarvelz)
  • 5a1a534 fix: allow JSDoc comments in object-shorthand rule (#20167) (Nitin Kumar)
  • e86b813 fix: Use more types from @​eslint/core (#20257) (Nicholas C. Zakas)
  • 927272d fix: correct Scope typings (#20198) (jaymarvelz)
  • 37f76d9 fix: use AST.Program type for Program node (#20244) (Francesco Trotta)
  • ae07f0b fix: unify timing report for concurrent linting (#20188) (jaymarvelz)
  • b165d47 fix: correct Rule typings (#20199) (jaymarvelz)
  • fb97cda fix: improve error message for missing fix function in suggestions (#20218) (jaymarvelz)

Documentation

  • d3e81e3 docs: Always recommend to include a files property (#20158) (Percy Ma)
  • 0f0385f docs: use consistent naming recommendation (#20250) (Alex M. Spieslechner)
  • a3b1456 docs: Update README (GitHub Actions Bot)
  • cf5f2dd docs: fix correct tag of no-useless-constructor (#20255) (Tanuj Kanti)
  • 10b995c docs: add TS options and examples for nofunc in no-use-before-define (#20249) (Tanuj Kanti)
  • 2584187 docs: remove repetitive word in comment (#20242) (reddaisyy)
  • 637216b docs: update CLI flags migration instructions (#20238) (jaymarvelz)
  • e7cda3b docs: Update README (GitHub Actions Bot)
  • 7b9446f docs: handle empty flags sections on the feature flags page (#20222) (sethamus)

Chores

  • dfe3c1b chore: update @eslint/js version to 9.39.0 (#20270) (Francesco Trotta)
  • 2375a6d chore: package.json update for @​eslint/js release (Jenkins)
  • a1f4e52 chore: update @eslint dependencies (#20265) (Francesco Trotta)
  • c7d3229 chore: update dependency @​eslint/core to ^0.17.0 (#20256) (renovate[bot])
  • 27549bc chore: update fuzz testing to not error if code sample minimizer fails (#20252) (Milos Djermanovic)
  • a1370ee ci: bump actions/setup-node from 5 to 6 (#20230) (dependabot[bot])
  • 9e7fad4 chore: add script to auto-generate eslint:recommended configuration (#20208) (唯然)
Commits

Updates `globals` from 16.4.0 to 16.5.0
Release notes

Sourced from globals's releases.

v16.5.0

  • Update globals (2025-11-01) (#316) 6d441ca
  • Add Vue, Svelte, and Astro globals (#314) ea31521

https://github.com/sindresorhus/globals/compare/v16.4.0...v16.5.0

Commits

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 56 +++++++++++++++++++++++------------------------ package.json | 2 +- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/package-lock.json b/package-lock.json index e6d568255..6adea978e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "devDependencies": { "@eslint/js": "^9.35.0", "@modelcontextprotocol/sdk": "1.20.2", - "@rollup/plugin-commonjs": "^28.0.8", + "@rollup/plugin-commonjs": "^29.0.0", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^16.0.3", "@stylistic/eslint-plugin": "^5.4.0", @@ -187,22 +187,22 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.1.tgz", - "integrity": "sha512-csZAzkNhsgwb0I/UAV6/RGFTbiakPCf0ZrGmrIxQpYvGZ00PhTkSnyKNolphgIvmnJeGw6rcGVEXfTzUnFuEvw==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.16.0" + "@eslint/core": "^0.17.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/core": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.16.0.tgz", - "integrity": "sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q==", + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -284,9 +284,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.38.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.38.0.tgz", - "integrity": "sha512-UZ1VpFvXf9J06YG9xQBdnzU+kthors6KjhMAl6f4gH4usHyh31rUf2DLGInT8RFYIReYXNSydgPY0V2LuWgl7A==", + "version": "9.39.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.0.tgz", + "integrity": "sha512-BIhe0sW91JGPiaF1mOuPy5v8NflqfjIcDNpC+LbW9f609WVRX1rArrhi6Z2ymvrAry9jw+5POTj4t2t62o8Bmw==", "dev": true, "license": "MIT", "engines": { @@ -307,13 +307,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.0.tgz", - "integrity": "sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.16.0", + "@eslint/core": "^0.17.0", "levn": "^0.4.1" }, "engines": { @@ -584,9 +584,9 @@ } }, "node_modules/@rollup/plugin-commonjs": { - "version": "28.0.9", - "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-28.0.9.tgz", - "integrity": "sha512-PIR4/OHZ79romx0BVVll/PkwWpJ7e5lsqFa3gFfcrFPWwLXLV39JVUzQV9RKjWerE7B845Hqjj9VYlQeieZ2dA==", + "version": "29.0.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-29.0.0.tgz", + "integrity": "sha512-U2YHaxR2cU/yAiwKJtJRhnyLk7cifnQw0zUpISsocBDoHDJn+HTV74ABqnwr5bEgWUwFZC9oFL6wLe21lHu5eQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2872,20 +2872,20 @@ } }, "node_modules/eslint": { - "version": "9.38.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.38.0.tgz", - "integrity": "sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw==", + "version": "9.39.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.0.tgz", + "integrity": "sha512-iy2GE3MHrYTL5lrCtMZ0X1KLEKKUjmK0kzwcnefhR66txcEmXZD2YWgR5GNdcEwkNx3a0siYkSvl0vIC+Svjmg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.1", - "@eslint/config-helpers": "^0.4.1", - "@eslint/core": "^0.16.0", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.38.0", - "@eslint/plugin-kit": "^0.4.0", + "@eslint/js": "9.39.0", + "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -3809,9 +3809,9 @@ } }, "node_modules/globals": { - "version": "16.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz", - "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==", + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz", + "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==", "dev": true, "license": "MIT", "engines": { diff --git a/package.json b/package.json index c08de50ca..f2a4b5a6d 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "devDependencies": { "@eslint/js": "^9.35.0", "@modelcontextprotocol/sdk": "1.20.2", - "@rollup/plugin-commonjs": "^28.0.8", + "@rollup/plugin-commonjs": "^29.0.0", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^16.0.3", "@stylistic/eslint-plugin": "^5.4.0", From 8854a3400c3a6b84c761bf8ed82769fc2dec7366 Mon Sep 17 00:00:00 2001 From: Kilian Valkhof Date: Mon, 3 Nov 2025 14:21:34 +0100 Subject: [PATCH 045/456] docs: fix typos in README.md exlcude -> exclude (#513) drive-by PR to fix some typos I spotted in the readme. --- README.md | 6 +++--- src/cli.ts | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 63a8a7cc0..54086fbf5 100644 --- a/README.md +++ b/README.md @@ -337,17 +337,17 @@ The Chrome DevTools MCP server supports the following configuration option: - **Type:** array - **`--categoryEmulation`** - Set to false to exlcude tools related to emulation. + Set to false to exclude tools related to emulation. - **Type:** boolean - **Default:** `true` - **`--categoryPerformance`** - Set to false to exlcude tools related to performance. + Set to false to exclude tools related to performance. - **Type:** boolean - **Default:** `true` - **`--categoryNetwork`** - Set to false to exlcude tools related to network. + Set to false to exclude tools related to network. - **Type:** boolean - **Default:** `true` diff --git a/src/cli.ts b/src/cli.ts index 5e811ba25..b7d7306b2 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -142,17 +142,17 @@ export const cliOptions = { categoryEmulation: { type: 'boolean', default: true, - describe: 'Set to false to exlcude tools related to emulation.', + describe: 'Set to false to exclude tools related to emulation.', }, categoryPerformance: { type: 'boolean', default: true, - describe: 'Set to false to exlcude tools related to performance.', + describe: 'Set to false to exclude tools related to performance.', }, categoryNetwork: { type: 'boolean', default: true, - describe: 'Set to false to exlcude tools related to network.', + describe: 'Set to false to exclude tools related to network.', }, } satisfies Record; From a2ddb398766308ae52c745abfcc545bb39d6a9ae Mon Sep 17 00:00:00 2001 From: Alex Rudenko Date: Mon, 3 Nov 2025 14:28:28 +0100 Subject: [PATCH 046/456] chore(deps): bump puppeteer (#516) --- package-lock.json | 32 ++++++++++++++++---------------- package.json | 2 +- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6adea978e..69b5731b2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,7 +33,7 @@ "eslint-plugin-import": "^2.32.0", "globals": "^16.4.0", "prettier": "^3.6.2", - "puppeteer": "24.27.0", + "puppeteer": "24.28.0", "rollup": "4.52.5", "rollup-plugin-cleanup": "^3.2.1", "rollup-plugin-license": "^3.6.0", @@ -455,9 +455,9 @@ } }, "node_modules/@puppeteer/browsers": { - "version": "2.10.12", - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.10.12.tgz", - "integrity": "sha512-mP9iLFZwH+FapKJLeA7/fLqOlSUwYpMwjR1P5J23qd4e7qGJwecJccJqHYrjw33jmIZYV4dtiTHPD/J+1e7cEw==", + "version": "2.10.13", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.10.13.tgz", + "integrity": "sha512-a9Ruw3j3qlnB5a/zHRTkruppynxqaeE4H9WNj5eYGRWqw0ZauZ23f4W2ARf3hghF5doozyD+CRtt7XSYuYRI/Q==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -2062,9 +2062,9 @@ } }, "node_modules/bare-url": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.3.1.tgz", - "integrity": "sha512-v2yl0TnaZTdEnelkKtXZGnotiV6qATBlnNuUMrHl6v9Lmmrh9mw9RYyImPU7/4RahumSwQS1k2oKXcRfXcbjJw==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.3.2.tgz", + "integrity": "sha512-ZMq4gd9ngV5aTMa5p9+UfY0b3skwhHELaDkhEHetMdX0LRkW9kzaym4oo/Eh+Ghm0CCDuMTsRIGM/ytUc1ZYmw==", "dev": true, "license": "Apache-2.0", "optional": true, @@ -5380,18 +5380,18 @@ } }, "node_modules/puppeteer": { - "version": "24.27.0", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.27.0.tgz", - "integrity": "sha512-eEcAFGxmHRSrk74DVkFAMAwfj4l3Ak8avBuA2bZaAoocY1+Fb9WLS3I7jlOc/tIOU7EmGLiDdVP08R44wADpHw==", + "version": "24.28.0", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.28.0.tgz", + "integrity": "sha512-KLRGFNCGmXJpocEBbEIoHJB0vNRZLQNBjl5ExXEv0z7MIU+qqVEQcfWTyat+qxPDk/wZvSf+b30cQqAfWxX0zg==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@puppeteer/browsers": "2.10.12", + "@puppeteer/browsers": "2.10.13", "chromium-bidi": "10.5.1", "cosmiconfig": "^9.0.0", "devtools-protocol": "0.0.1521046", - "puppeteer-core": "24.27.0", + "puppeteer-core": "24.28.0", "typed-query-selector": "^2.12.0" }, "bin": { @@ -5402,13 +5402,13 @@ } }, "node_modules/puppeteer-core": { - "version": "24.27.0", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.27.0.tgz", - "integrity": "sha512-yubwj2XXmTM3wRIpbhO5nCjbByPgpFHlgrsD4IK+gMPqO7/a5FfnoSXDKjmqi8A2M1Ewusz0rTI/r+IN0GU0MA==", + "version": "24.28.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.28.0.tgz", + "integrity": "sha512-QpAqaYgeZHF5/xAZ4jAOzsU+l0Ed4EJoWkRdfw8rNqmSN7itcdYeCJaSPQ0s5Pyn/eGNC4xNevxbgY+5bzNllw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@puppeteer/browsers": "2.10.12", + "@puppeteer/browsers": "2.10.13", "chromium-bidi": "10.5.1", "debug": "^4.4.3", "devtools-protocol": "0.0.1521046", diff --git a/package.json b/package.json index f2a4b5a6d..a391fcae4 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "eslint-plugin-import": "^2.32.0", "globals": "^16.4.0", "prettier": "^3.6.2", - "puppeteer": "24.27.0", + "puppeteer": "24.28.0", "rollup": "4.52.5", "rollup-plugin-cleanup": "^3.2.1", "rollup-plugin-license": "^3.6.0", From 6af913348f8cc321e5ba2306cae36589df3d468a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Nov 2025 08:00:39 +0000 Subject: [PATCH 047/456] chore(deps-dev): bump chrome-devtools-frontend from 1.0.1536371 to 1.0.1538310 in the bundled group (#508) Bumps the bundled group with 1 update: [chrome-devtools-frontend](https://github.com/ChromeDevTools/devtools-frontend). Updates `chrome-devtools-frontend` from 1.0.1536371 to 1.0.1538310
Commits
  • 0339e4b Adopt UI eng vision in the OriginTrialTokenRows
  • 93bd5da Update DevTools DEPS (trusted)
  • 346f3df Add throttling indications in the performance panel
  • a000135 Update .env description
  • e828c4b Update Chrome (for Testing) PIN
  • ff94562 [eslint] Enforce custom elements naming
  • e47cf44 Add code for creating request and calling generateCode
  • d31e1d7 Speculation Rules: move NOT_TRIGGERED to bottom when sorted
  • e76b255 Mostly migrate RequestTimingView.ts
  • ff3c303 Use unqualified Lit identifiers and call update render function names in the ...
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=chrome-devtools-frontend&package-manager=npm_and_yarn&previous-version=1.0.1536371&new-version=1.0.1538310)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
--------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Alex Rudenko --- package-lock.json | 8 ++++---- package.json | 2 +- tests/tools/performance.test.js.snapshot | 11 ++++++++++- tests/trace-processing/parse.test.js.snapshot | 11 ++++++++++- 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 69b5731b2..385ee351e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,7 +25,7 @@ "@types/yargs": "^17.0.33", "@typescript-eslint/eslint-plugin": "^8.43.0", "@typescript-eslint/parser": "^8.43.0", - "chrome-devtools-frontend": "1.0.1536371", + "chrome-devtools-frontend": "1.0.1538310", "core-js": "3.46.0", "debug": "4.4.3", "eslint": "^9.35.0", @@ -2224,9 +2224,9 @@ } }, "node_modules/chrome-devtools-frontend": { - "version": "1.0.1536371", - "resolved": "https://registry.npmjs.org/chrome-devtools-frontend/-/chrome-devtools-frontend-1.0.1536371.tgz", - "integrity": "sha512-LfK+KgW5EoxMrhlO/hyuRGhihkkfwpxTWVNfTJbsg8kX1sx4cIjelaWPlWvuAKg1/1M6yZECV6yFajdiHguj1A==", + "version": "1.0.1538310", + "resolved": "https://registry.npmjs.org/chrome-devtools-frontend/-/chrome-devtools-frontend-1.0.1538310.tgz", + "integrity": "sha512-e9/CODQDqxIAJ8LZEbhUJEs2PVSupuJMgs6nwdjeQuT/yrHHbGVUiYXtfso4Vlv338P+qSsm6J+1jUm5WayeEA==", "dev": true, "license": "BSD-3-Clause" }, diff --git a/package.json b/package.json index a391fcae4..3c2b88861 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "@types/yargs": "^17.0.33", "@typescript-eslint/eslint-plugin": "^8.43.0", "@typescript-eslint/parser": "^8.43.0", - "chrome-devtools-frontend": "1.0.1536371", + "chrome-devtools-frontend": "1.0.1538310", "core-js": "3.46.0", "debug": "4.4.3", "eslint": "^9.35.0", diff --git a/tests/tools/performance.test.js.snapshot b/tests/tools/performance.test.js.snapshot index f38c86ac4..0ca8cc3f1 100644 --- a/tests/tools/performance.test.js.snapshot +++ b/tests/tools/performance.test.js.snapshot @@ -53,9 +53,18 @@ exports[`performance > performance_stop_trace > returns the high level summary o The performance trace has been stopped. ## Summary of Performance trace findings: URL: https://web.dev/ -Bounds: {min: 122410994891, max: 122416385853} +Trace bounds: {min: 122410994891, max: 122416385853} CPU throttling: none Network throttling: none + +# Available insight sets + +The following is a list of insight sets. An insight set covers a specific part of the trace, split by navigations. The insights within each insight set are specific to that part of the trace. Be sure to consider the insight set id and bounds when calling functions. If no specific insight set or navigation is mentioned, assume the user is referring to the first one. + +## insight set id: 8463DF94CD61B265B664E7F768183DE3 + +URL: https://web.dev/ +Bounds: {min: 122410996889, max: 122416385853} Metrics (lab / observed): - LCP: 129 ms, event: (eventKey: r-6063, ts: 122411126100), nodeId: 7 - LCP breakdown: diff --git a/tests/trace-processing/parse.test.js.snapshot b/tests/trace-processing/parse.test.js.snapshot index 5d26a47ed..f8d8115cc 100644 --- a/tests/trace-processing/parse.test.js.snapshot +++ b/tests/trace-processing/parse.test.js.snapshot @@ -1,9 +1,18 @@ exports[`Trace parsing > can format results of a trace 1`] = ` ## Summary of Performance trace findings: URL: https://web.dev/ -Bounds: {min: 122410994891, max: 122416385853} +Trace bounds: {min: 122410994891, max: 122416385853} CPU throttling: none Network throttling: none + +# Available insight sets + +The following is a list of insight sets. An insight set covers a specific part of the trace, split by navigations. The insights within each insight set are specific to that part of the trace. Be sure to consider the insight set id and bounds when calling functions. If no specific insight set or navigation is mentioned, assume the user is referring to the first one. + +## insight set id: 8463DF94CD61B265B664E7F768183DE3 + +URL: https://web.dev/ +Bounds: {min: 122410996889, max: 122416385853} Metrics (lab / observed): - LCP: 129 ms, event: (eventKey: r-6063, ts: 122411126100), nodeId: 7 - LCP breakdown: From 36504d29caf637b2d7bf231204c0478b54220c83 Mon Sep 17 00:00:00 2001 From: Connor Clark Date: Tue, 4 Nov 2025 00:18:04 -0800 Subject: [PATCH 048/456] feat: add insightSetId to performance_analyze_insight (#518) This should land with / after https://github.com/ChromeDevTools/chrome-devtools-mcp/pull/508 --- docs/tool-reference.md | 3 ++- src/tools/performance.ts | 8 +++++++- src/trace-processing/parse.ts | 22 ++++++---------------- tests/tools/performance.test.ts | 3 +++ 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/docs/tool-reference.md b/docs/tool-reference.md index 72a41da7c..0e126b8c3 100644 --- a/docs/tool-reference.md +++ b/docs/tool-reference.md @@ -215,11 +215,12 @@ ### `performance_analyze_insight` -**Description:** Provides more detailed information on a specific Performance Insight that was highlighted in the results of a trace recording. +**Description:** Provides more detailed information on a specific Performance Insight of an insight set that was highlighted in the results of a trace recording. **Parameters:** - **insightName** (string) **(required)**: The name of the Insight you want more information on. For example: "DocumentLatency" or "LCPBreakdown" +- **insightSetId** (string) **(required)**: The id for the specific insight set. Only use the ids given in the "Available insight sets" list. --- diff --git a/src/tools/performance.ts b/src/tools/performance.ts index 420a62132..a8b24903c 100644 --- a/src/tools/performance.ts +++ b/src/tools/performance.ts @@ -121,12 +121,17 @@ export const stopTrace = defineTool({ export const analyzeInsight = defineTool({ name: 'performance_analyze_insight', description: - 'Provides more detailed information on a specific Performance Insight that was highlighted in the results of a trace recording.', + 'Provides more detailed information on a specific Performance Insight of an insight set that was highlighted in the results of a trace recording.', annotations: { category: ToolCategory.PERFORMANCE, readOnlyHint: true, }, schema: { + insightSetId: zod + .string() + .describe( + 'The id for the specific insight set. Only use the ids given in the "Available insight sets" list.', + ), insightName: zod .string() .describe( @@ -144,6 +149,7 @@ export const analyzeInsight = defineTool({ const insightOutput = getInsightOutput( lastRecording, + request.params.insightSetId, request.params.insightName as InsightName, ); if ('error' in insightOutput) { diff --git a/src/trace-processing/parse.ts b/src/trace-processing/parse.ts index 23e195764..cbd79462e 100644 --- a/src/trace-processing/parse.ts +++ b/src/trace-processing/parse.ts @@ -97,6 +97,7 @@ export type InsightOutput = {output: string} | {error: string}; export function getInsightOutput( result: TraceResult, + insightSetId: string, insightName: InsightName, ): InsightOutput { if (!result.insights) { @@ -105,27 +106,16 @@ export function getInsightOutput( }; } - // Currently, we do not support inspecting traces with multiple navigations. We either: - // 1. Find Insights from the first navigation (common case: user records a trace with a page reload to test load performance) - // 2. Fall back to finding Insights not associated with a navigation (common case: user tests an interaction without a page load). - const mainNavigationId = - result.parsedTrace.data.Meta.mainFrameNavigations.at(0)?.args.data - ?.navigationId; - - const insightsForNav = result.insights.get( - mainNavigationId ?? TraceEngine.Types.Events.NO_NAVIGATION, - ); - - if (!insightsForNav) { + const insightSet = result.insights.get(insightSetId); + if (!insightSet) { return { - error: 'No Performance Insights for this trace.', + error: + 'No Performance Insights for the given insight set id. Only use ids given in the "Available insight sets" list.', }; } const matchingInsight = - insightName in insightsForNav.model - ? insightsForNav.model[insightName] - : null; + insightName in insightSet.model ? insightSet.model[insightName] : null; if (!matchingInsight) { return { error: `No Insight with the name ${insightName} found. Double check the name you provided is accurate and try again.`, diff --git a/tests/tools/performance.test.ts b/tests/tools/performance.test.ts index b8ac55338..0c3cb6584 100644 --- a/tests/tools/performance.test.ts +++ b/tests/tools/performance.test.ts @@ -158,6 +158,7 @@ describe('performance', () => { await analyzeInsight.handler( { params: { + insightSetId: '8463DF94CD61B265B664E7F768183DE3', insightName: 'LCPBreakdown', }, }, @@ -178,6 +179,7 @@ describe('performance', () => { await analyzeInsight.handler( { params: { + insightSetId: '8463DF94CD61B265B664E7F768183DE3', insightName: 'MadeUpInsightName', }, }, @@ -197,6 +199,7 @@ describe('performance', () => { await analyzeInsight.handler( { params: { + insightSetId: '8463DF94CD61B265B664E7F768183DE3', insightName: 'LCPBreakdown', }, }, From d90abd4e9e534417622d7f4676e9c3dbeb39ea8d Mon Sep 17 00:00:00 2001 From: Finnur Thorarinsson <79584015+finnur-thorarinsson@users.noreply.github.com> Date: Tue, 4 Nov 2025 15:00:20 +0000 Subject: [PATCH 049/456] fix: Augment fix to prevent stray OGS frames in NTP from causing hangs. (#521) The URL used before was a little too-specific, so it didn't catch frames that include `u/0/` in the URL (potentially to support multi users?). (original fix: https://github.com/ChromeDevTools/chrome-devtools-mcp/pull/504) --- src/browser.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/browser.ts b/src/browser.ts index f3f3d9e11..ec6c00c7c 100644 --- a/src/browser.ts +++ b/src/browser.ts @@ -30,7 +30,7 @@ function makeTargetFilter() { if (target.url() === 'chrome://newtab/') { return true; } - if (target.url().startsWith('https://ogs.google.com/widget/app/so')) { + if (target.url().startsWith('https://ogs.google.com/')) { // Some special frame on the NTP that is not picked up by CDP-auto-attach. return false; } From 01454d4617e744a68e2062f447f48fc8eb6f1375 Mon Sep 17 00:00:00 2001 From: Alex Rudenko Date: Wed, 5 Nov 2025 11:02:36 +0100 Subject: [PATCH 050/456] chore: experimental includeAllPages option (#517) --- src/McpContext.ts | 50 ++++++++++++++++++++++++++++---------------- src/PageCollector.ts | 8 +++++-- src/cli.ts | 6 ++++++ src/main.ts | 1 + 4 files changed, 45 insertions(+), 20 deletions(-) diff --git a/src/McpContext.ts b/src/McpContext.ts index 59fc72b1b..4e4da8789 100644 --- a/src/McpContext.ts +++ b/src/McpContext.ts @@ -45,6 +45,8 @@ export interface TextSnapshot { interface McpContextOptions { // Whether the DevTools windows are exposed as pages for debugging of DevTools. experimentalDevToolsDebugging: boolean; + // Whether all page-like targets are exposed as pages. + experimentalIncludeAllPages?: boolean; } const DEFAULT_TIMEOUT = 5_000; @@ -114,24 +116,32 @@ export class McpContext implements Context { this.#locatorClass = locatorClass; this.#options = options; - this.#networkCollector = new NetworkCollector(this.browser); + this.#networkCollector = new NetworkCollector( + this.browser, + undefined, + this.#options.experimentalIncludeAllPages, + ); - this.#consoleCollector = new PageCollector(this.browser, collect => { - return { - console: event => { - collect(event); - }, - pageerror: event => { - if (event instanceof Error) { + this.#consoleCollector = new PageCollector( + this.browser, + collect => { + return { + console: event => { collect(event); - } else { - const error = new Error(`${event}`); - error.stack = undefined; - collect(error); - } - }, - } as ListenerMap; - }); + }, + pageerror: event => { + if (event instanceof Error) { + collect(event); + } else { + const error = new Error(`${event}`); + error.stack = undefined; + collect(error); + } + }, + } as ListenerMap; + }, + this.#options.experimentalIncludeAllPages, + ); } async #init() { @@ -364,7 +374,9 @@ export class McpContext implements Context { * Creates a snapshot of the pages. */ async createPagesSnapshot(): Promise { - const allPages = await this.browser.pages(); + const allPages = await this.browser.pages( + this.#options.experimentalIncludeAllPages, + ); this.#pages = allPages.filter(page => { // If we allow debugging DevTools windows, return all pages. @@ -382,7 +394,9 @@ export class McpContext implements Context { async detectOpenDevToolsWindows() { this.logger('Detecting open DevTools windows'); - const pages = await this.browser.pages(); + const pages = await this.browser.pages( + this.#options.experimentalIncludeAllPages, + ); this.#pageToDevToolsPage = new Map(); for (const devToolsPage of pages) { if (devToolsPage.url().startsWith('devtools://')) { diff --git a/src/PageCollector.ts b/src/PageCollector.ts index 22b9c7a82..56d29c519 100644 --- a/src/PageCollector.ts +++ b/src/PageCollector.ts @@ -39,6 +39,7 @@ export class PageCollector { ) => ListenerMap; #listeners = new WeakMap(); #maxNavigationSaved = 3; + #includeAllPages?: boolean; /** * This maps a Page to a list of navigations with a sub-list @@ -50,13 +51,15 @@ export class PageCollector { constructor( browser: Browser, listeners: (collector: (item: T) => void) => ListenerMap, + includeAllPages?: boolean, ) { this.#browser = browser; this.#listenersInitializer = listeners; + this.#includeAllPages = includeAllPages; } async init() { - const pages = await this.#browser.pages(); + const pages = await this.#browser.pages(this.#includeAllPages); for (const page of pages) { this.#initializePage(page); } @@ -203,8 +206,9 @@ export class NetworkCollector extends PageCollector { }, } as ListenerMap; }, + includeAllPages?: boolean, ) { - super(browser, listeners); + super(browser, listeners, includeAllPages); } override splitAfterNavigation(page: Page) { const navigations = this.storage.get(page) ?? []; diff --git a/src/cli.ts b/src/cli.ts index b7d7306b2..5ce8673e7 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -134,6 +134,12 @@ export const cliOptions = { describe: 'Whether to enable automation over DevTools targets', hidden: true, }, + experimentalIncludeAllPages: { + type: 'boolean', + describe: + 'Whether to include all kinds of pages such as webviews or background pages as pages.', + hidden: true, + }, chromeArg: { type: 'array', describe: diff --git a/src/main.ts b/src/main.ts index 395f07a85..18b3a1d94 100644 --- a/src/main.ts +++ b/src/main.ts @@ -83,6 +83,7 @@ async function getContext(): Promise { if (context?.browser !== browser) { context = await McpContext.from(browser, logger, { experimentalDevToolsDebugging: devtools, + experimentalIncludeAllPages: args.experimentalIncludeAllPages, }); } return context; From 418934a7b9c885f88c775b7322c201ab6c1c5cce Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 5 Nov 2025 10:34:07 +0000 Subject: [PATCH 051/456] chore(deps-dev): bump the bundled group with 3 updates (#526) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the bundled group with 3 updates: [@modelcontextprotocol/sdk](https://github.com/modelcontextprotocol/typescript-sdk), [chrome-devtools-frontend](https://github.com/ChromeDevTools/devtools-frontend) and [puppeteer](https://github.com/puppeteer/puppeteer). Updates `@modelcontextprotocol/sdk` from 1.20.2 to 1.21.0
Release notes

Sourced from @​modelcontextprotocol/sdk's releases.

1.21.0

What's Changed

New Contributors

Full Changelog: https://github.com/modelcontextprotocol/typescript-sdk/compare/1.20.2...1.21.0

Commits
  • 783d53b chore: bump version to 1.21.0 for release (#1062)
  • 7387c44 Fix: Non-existent tool, disabled tool and inputSchema validation return MCP p...
  • 874aa27 fix: Prefer the token_endpoint_auth_method response from DCR registration ...
  • e74a358 Update metadata.ts (#1010)
  • 5a8fb39 feat: pluggable JSON schema validator providers (#1012)
  • See full diff in compare view

Updates `chrome-devtools-frontend` from 1.0.1538310 to 1.0.1539728
Commits
  • db6563f [docs] Add a screenshot test for LMI and remove it from component docs
  • 2e93ad7 [Cxx extension e2e] Disable DevToolsAiPromptApi
  • d813de5 Tooltip: improve positioning in fallback case
  • c2d3683 Let EsLint handle excluded files in PRESUBMIT
  • a83225f Update Chrome (for Testing) PIN
  • 2e32990 Update Chrome (for Testing) PIN
  • d429ba9 [docs] Remove icon button and issue panel related docs
  • 2f78c04 [cleanup] Prefixes for eslint-plugin-lit
  • 8327563 Refactor ElementsTreeElement to be more declarative
  • 85221e3 [node] Add regression test for node_app breakage
  • Additional commits viewable in compare view

Updates `puppeteer` from 24.28.0 to 24.29.0
Release notes

Sourced from puppeteer's releases.

puppeteer-core: v24.29.0

24.29.0 (2025-11-05)

🎉 Features

🛠️ Fixes

  • do not wait for all targets when connecting (#14395) (0029495)
  • tasks and session management in ExtensionTransport (#14400) (47c92d6)

puppeteer: v24.29.0

24.29.0 (2025-11-05)

♻️ Chores

  • puppeteer: Synchronize puppeteer versions

Dependencies

  • The following workspace dependencies were updated
    • dependencies
      • puppeteer-core bumped from 24.28.0 to 24.29.0
Changelog

Sourced from puppeteer's changelog.

24.29.0 (2025-11-05)

♻️ Chores

  • puppeteer: Synchronize puppeteer versions

Dependencies

  • The following workspace dependencies were updated
    • dependencies
      • puppeteer-core bumped from 24.28.0 to 24.29.0

🎉 Features

🛠️ Fixes

  • do not wait for all targets when connecting (#14395) (0029495)
  • tasks and session management in ExtensionTransport (#14400) (47c92d6)
Commits

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 136 ++++++++++++++++++++++++++++++++++++++++------ package.json | 6 +- 2 files changed, 122 insertions(+), 20 deletions(-) diff --git a/package-lock.json b/package-lock.json index 385ee351e..840e273f3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ }, "devDependencies": { "@eslint/js": "^9.35.0", - "@modelcontextprotocol/sdk": "1.20.2", + "@modelcontextprotocol/sdk": "1.21.0", "@rollup/plugin-commonjs": "^29.0.0", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^16.0.3", @@ -25,7 +25,7 @@ "@types/yargs": "^17.0.33", "@typescript-eslint/eslint-plugin": "^8.43.0", "@typescript-eslint/parser": "^8.43.0", - "chrome-devtools-frontend": "1.0.1538310", + "chrome-devtools-frontend": "1.0.1539728", "core-js": "3.46.0", "debug": "4.4.3", "eslint": "^9.35.0", @@ -33,7 +33,7 @@ "eslint-plugin-import": "^2.32.0", "globals": "^16.4.0", "prettier": "^3.6.2", - "puppeteer": "24.28.0", + "puppeteer": "24.29.0", "rollup": "4.52.5", "rollup-plugin-cleanup": "^3.2.1", "rollup-plugin-license": "^3.6.0", @@ -380,13 +380,14 @@ "license": "MIT" }, "node_modules/@modelcontextprotocol/sdk": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.20.2.tgz", - "integrity": "sha512-6rqTdFt67AAAzln3NOKsXRmv5ZzPkgbfaebKBqUbts7vK1GZudqnrun5a8d3M/h955cam9RHZ6Jb4Y1XhnmFPg==", + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.21.0.tgz", + "integrity": "sha512-YFBsXJMFCyI1zP98u7gezMFKX4lgu/XpoZJk7ufI6UlFKXLj2hAMUuRlQX/nrmIPOmhRrG6tw2OQ2ZA/ZlXYpQ==", "dev": true, "license": "MIT", "dependencies": { - "ajv": "^6.12.6", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", @@ -401,8 +402,40 @@ }, "engines": { "node": ">=18" + }, + "peerDependencies": { + "@cfworker/json-schema": "^4.1.1" + }, + "peerDependenciesMeta": { + "@cfworker/json-schema": { + "optional": true + } + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/@modelcontextprotocol/sdk/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, "node_modules/@napi-rs/wasm-runtime": { "version": "0.2.12", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", @@ -1746,6 +1779,48 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, "node_modules/ansi-regex": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", @@ -2224,9 +2299,9 @@ } }, "node_modules/chrome-devtools-frontend": { - "version": "1.0.1538310", - "resolved": "https://registry.npmjs.org/chrome-devtools-frontend/-/chrome-devtools-frontend-1.0.1538310.tgz", - "integrity": "sha512-e9/CODQDqxIAJ8LZEbhUJEs2PVSupuJMgs6nwdjeQuT/yrHHbGVUiYXtfso4Vlv338P+qSsm6J+1jUm5WayeEA==", + "version": "1.0.1539728", + "resolved": "https://registry.npmjs.org/chrome-devtools-frontend/-/chrome-devtools-frontend-1.0.1539728.tgz", + "integrity": "sha512-4+HoCLEq+DhJtE1BqU5Dj2+CeYvuYrpBov/Wg3DuMgpJJbn0l+ePcfPBLZCBskveo+/eltV27DM70Z2Rq0Zs0Q==", "dev": true, "license": "BSD-3-Clause" }, @@ -3449,6 +3524,23 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/fastq": { "version": "1.19.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", @@ -5380,9 +5472,9 @@ } }, "node_modules/puppeteer": { - "version": "24.28.0", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.28.0.tgz", - "integrity": "sha512-KLRGFNCGmXJpocEBbEIoHJB0vNRZLQNBjl5ExXEv0z7MIU+qqVEQcfWTyat+qxPDk/wZvSf+b30cQqAfWxX0zg==", + "version": "24.29.0", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.29.0.tgz", + "integrity": "sha512-1F8Jh32IxpIYvL1NflR/DyT6xPGM136M9I1noWYY24zOVt/q/zr0TJr/7rO6IUCO9+Np1twzqJQVyTC3QixbhQ==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", @@ -5391,7 +5483,7 @@ "chromium-bidi": "10.5.1", "cosmiconfig": "^9.0.0", "devtools-protocol": "0.0.1521046", - "puppeteer-core": "24.28.0", + "puppeteer-core": "24.29.0", "typed-query-selector": "^2.12.0" }, "bin": { @@ -5402,9 +5494,9 @@ } }, "node_modules/puppeteer-core": { - "version": "24.28.0", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.28.0.tgz", - "integrity": "sha512-QpAqaYgeZHF5/xAZ4jAOzsU+l0Ed4EJoWkRdfw8rNqmSN7itcdYeCJaSPQ0s5Pyn/eGNC4xNevxbgY+5bzNllw==", + "version": "24.29.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.29.0.tgz", + "integrity": "sha512-WTxTpGBJMETB4tQ8heMK9XozjpmCNfoA8iov8H65upwhoQpcosZWGTZkPFNndEjILww9IKg/mbTlg8BnZ8L6wQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -5554,6 +5646,16 @@ "node": ">=0.10.0" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve": { "version": "1.22.10", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", diff --git a/package.json b/package.json index 3c2b88861..720ca1020 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "mcpName": "io.github.ChromeDevTools/chrome-devtools-mcp", "devDependencies": { "@eslint/js": "^9.35.0", - "@modelcontextprotocol/sdk": "1.20.2", + "@modelcontextprotocol/sdk": "1.21.0", "@rollup/plugin-commonjs": "^29.0.0", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^16.0.3", @@ -52,7 +52,7 @@ "@types/yargs": "^17.0.33", "@typescript-eslint/eslint-plugin": "^8.43.0", "@typescript-eslint/parser": "^8.43.0", - "chrome-devtools-frontend": "1.0.1538310", + "chrome-devtools-frontend": "1.0.1539728", "core-js": "3.46.0", "debug": "4.4.3", "eslint": "^9.35.0", @@ -60,7 +60,7 @@ "eslint-plugin-import": "^2.32.0", "globals": "^16.4.0", "prettier": "^3.6.2", - "puppeteer": "24.28.0", + "puppeteer": "24.29.0", "rollup": "4.52.5", "rollup-plugin-cleanup": "^3.2.1", "rollup-plugin-license": "^3.6.0", From c03a4a47df6102f06f95d5ea885ac6786b591c0e Mon Sep 17 00:00:00 2001 From: Alex Rudenko Date: Wed, 5 Nov 2025 12:25:59 +0100 Subject: [PATCH 052/456] chore: remove special handling for target urls (#527) Fixed by https://github.com/puppeteer/puppeteer/pull/14400 --- src/browser.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/browser.ts b/src/browser.ts index ec6c00c7c..3abcbe00e 100644 --- a/src/browser.ts +++ b/src/browser.ts @@ -30,10 +30,6 @@ function makeTargetFilter() { if (target.url() === 'chrome://newtab/') { return true; } - if (target.url().startsWith('https://ogs.google.com/')) { - // Some special frame on the NTP that is not picked up by CDP-auto-attach. - return false; - } for (const prefix of ignoredPrefixes) { if (target.url().startsWith(prefix)) { return false; From b2f6d1be1ab598f557c7b6f060e72ae098d0b956 Mon Sep 17 00:00:00 2001 From: browser-automation-bot <133232582+browser-automation-bot@users.noreply.github.com> Date: Wed, 5 Nov 2025 12:38:10 +0100 Subject: [PATCH 053/456] chore(main): release chrome-devtools-mcp 0.10.0 (#460) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit :robot: I have created a release *beep* *boop* --- ## [0.10.0](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.9.0...chrome-devtools-mcp-v0.10.0) (2025-11-05) ### 🎉 Features * add a press_key tool ([#458](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/458)) ([b427392](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/b4273923928704e718e0a0f8b5cc86758416e994)) * add insightSetId to performance_analyze_insight ([#518](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/518)) ([36504d2](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/36504d29caf637b2d7bf231204c0478b54220c83)) * an option to ignore cache on reload ([#485](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/485)) ([8e56307](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/8e56307d623fe3651262287b30544ed70426b0b8)) * detect network requests inspected in DevTools UI ([#477](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/477)) ([796aed7](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/796aed72b7126ed4332888ffbc06d6cb678265ef)) * fetch DOM node selected in the DevTools Elements panel ([#486](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/486)) ([4a83574](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/4a83574961d8d6b974037db56fc8bdbbb91f79b6)) * support page reload ([#462](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/462)) ([d177087](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/d17708798194486b2571092aa67838085da7231e)) * support saving snapshots to file ([#463](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/463)) ([b0ce08a](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/b0ce08ae2ce422813fef3f28c18f2cb6c976d9fc)) ### 🛠️ Fixes * Augment fix to prevent stray OGS frames in NTP from causing hangs. ([#521](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/521)) ([d90abd4](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/d90abd4e9e534417622d7f4676e9c3dbeb39ea8d)) * improve get_network_request description ([#500](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/500)) ([2f448e8](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/2f448e84ea8d3a44687c74b3577edf882ef2c19f)) * work around NTP iframes causing hangs ([#504](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/504)) ([cca5ff4](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/cca5ff471c2d2c663e63ade1e2ea58f9a7f5a2cd)) ### 📄 Documentation * add Windsurf to the editor config README ([#493](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/493)) ([63a5d82](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/63a5d824c2d914c9007e2b837fa292f5ba74ceed)) * fix typos in README.md exlcude -> exclude ([#513](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/513)) ([8854a34](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/8854a3400c3a6b84c761bf8ed82769fc2dec7366)) * remove unnecessary replace ([#475](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/475)) ([40e1753](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/40e1753d2e874bb22005dbebdb551da304a80033)) ### ♻️ Chores * connect to DevTools targets by default ([#466](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/466)) ([a41e440](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/a41e4407996b8090f8cccc85f6c4696006fc31ec)) * detect DevTools page for each page ([#467](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/467)) ([1560ff2](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/1560ff23cad28ab63c1cf9fb1b961db886bc4a3e)) * merge emulate tools into one ([#494](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/494)) ([c06f452](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/c06f4522ee8f762b59c60c2fd23a0deaaa544766)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --- .release-please-manifest.json | 2 +- CHANGELOG.md | 34 ++++++++++++++++++++++++++++++++++ package-lock.json | 4 ++-- package.json | 2 +- server.json | 4 ++-- src/main.ts | 2 +- 6 files changed, 41 insertions(+), 7 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 76d5538a4..7d9b009db 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.9.0" + ".": "0.10.0" } diff --git a/CHANGELOG.md b/CHANGELOG.md index c7837a582..761f61997 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,39 @@ # Changelog +## [0.10.0](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.9.0...chrome-devtools-mcp-v0.10.0) (2025-11-05) + + +### 🎉 Features + +* add a press_key tool ([#458](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/458)) ([b427392](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/b4273923928704e718e0a0f8b5cc86758416e994)) +* add insightSetId to performance_analyze_insight ([#518](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/518)) ([36504d2](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/36504d29caf637b2d7bf231204c0478b54220c83)) +* an option to ignore cache on reload ([#485](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/485)) ([8e56307](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/8e56307d623fe3651262287b30544ed70426b0b8)) +* detect network requests inspected in DevTools UI ([#477](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/477)) ([796aed7](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/796aed72b7126ed4332888ffbc06d6cb678265ef)) +* fetch DOM node selected in the DevTools Elements panel ([#486](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/486)) ([4a83574](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/4a83574961d8d6b974037db56fc8bdbbb91f79b6)) +* support page reload ([#462](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/462)) ([d177087](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/d17708798194486b2571092aa67838085da7231e)) +* support saving snapshots to file ([#463](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/463)) ([b0ce08a](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/b0ce08ae2ce422813fef3f28c18f2cb6c976d9fc)) + + +### 🛠️ Fixes + +* Augment fix to prevent stray OGS frames in NTP from causing hangs. ([#521](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/521)) ([d90abd4](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/d90abd4e9e534417622d7f4676e9c3dbeb39ea8d)) +* improve get_network_request description ([#500](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/500)) ([2f448e8](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/2f448e84ea8d3a44687c74b3577edf882ef2c19f)) +* work around NTP iframes causing hangs ([#504](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/504)) ([cca5ff4](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/cca5ff471c2d2c663e63ade1e2ea58f9a7f5a2cd)) + + +### 📄 Documentation + +* add Windsurf to the editor config README ([#493](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/493)) ([63a5d82](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/63a5d824c2d914c9007e2b837fa292f5ba74ceed)) +* fix typos in README.md exlcude -> exclude ([#513](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/513)) ([8854a34](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/8854a3400c3a6b84c761bf8ed82769fc2dec7366)) +* remove unnecessary replace ([#475](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/475)) ([40e1753](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/40e1753d2e874bb22005dbebdb551da304a80033)) + + +### ♻️ Chores + +* connect to DevTools targets by default ([#466](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/466)) ([a41e440](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/a41e4407996b8090f8cccc85f6c4696006fc31ec)) +* detect DevTools page for each page ([#467](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/467)) ([1560ff2](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/1560ff23cad28ab63c1cf9fb1b961db886bc4a3e)) +* merge emulate tools into one ([#494](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/494)) ([c06f452](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/c06f4522ee8f762b59c60c2fd23a0deaaa544766)) + ## [0.9.0](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.8.1...chrome-devtools-mcp-v0.9.0) (2025-10-22) diff --git a/package-lock.json b/package-lock.json index 840e273f3..05577d383 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "chrome-devtools-mcp", - "version": "0.9.0", + "version": "0.10.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "chrome-devtools-mcp", - "version": "0.9.0", + "version": "0.10.0", "license": "Apache-2.0", "bin": { "chrome-devtools-mcp": "build/src/index.js" diff --git a/package.json b/package.json index 720ca1020..7d51dcbdd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "chrome-devtools-mcp", - "version": "0.9.0", + "version": "0.10.0", "description": "MCP server for Chrome DevTools", "type": "module", "bin": "./build/src/index.js", diff --git a/server.json b/server.json index a498bcf98..13e8a3b21 100644 --- a/server.json +++ b/server.json @@ -6,13 +6,13 @@ "url": "https://github.com/ChromeDevTools/chrome-devtools-mcp", "source": "github" }, - "version": "0.9.0", + "version": "0.10.0", "packages": [ { "registryType": "npm", "registryBaseUrl": "https://registry.npmjs.org", "identifier": "chrome-devtools-mcp", - "version": "0.9.0", + "version": "0.10.0", "transport": { "type": "stdio" }, diff --git a/src/main.ts b/src/main.ts index 18b3a1d94..80a1ec3f5 100644 --- a/src/main.ts +++ b/src/main.ts @@ -33,7 +33,7 @@ import type {ToolDefinition} from './tools/ToolDefinition.js'; // If moved update release-please config // x-release-please-start-version -const VERSION = '0.9.0'; +const VERSION = '0.10.0'; // x-release-please-end export const args = parseArguments(VERSION); From 39eac90ade75fde74f18cc088ebedb518c1be669 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 5 Nov 2025 17:35:48 +0100 Subject: [PATCH 054/456] chore(deps-dev): bump the dev-dependencies group with 6 updates (#525) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the dev-dependencies group with 6 updates: | Package | From | To | | --- | --- | --- | | [@eslint/js](https://github.com/eslint/eslint/tree/HEAD/packages/js) | `9.39.0` | `9.39.1` | | [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `24.9.2` | `24.10.0` | | [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) | `8.46.2` | `8.46.3` | | [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) | `8.46.2` | `8.46.3` | | [eslint](https://github.com/eslint/eslint) | `9.39.0` | `9.39.1` | | [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) | `8.46.2` | `8.46.3` | Updates `@eslint/js` from 9.39.0 to 9.39.1
Release notes

Sourced from @​eslint/js's releases.

v9.39.1

Bug Fixes

  • 650753e fix: Only pass node to JS lang visitor methods (#20283) (Nicholas C. Zakas)

Documentation

  • 51b51f4 docs: add a section on when to use extends vs cascading (#20268) (Tanuj Kanti)
  • b44d426 docs: Update README (GitHub Actions Bot)

Chores

  • 92db329 chore: update @eslint/js version to 9.39.1 (#20284) (Francesco Trotta)
  • c7ebefc chore: package.json update for @​eslint/js release (Jenkins)
  • 61778f6 chore: update eslint-config-eslint dependency @​eslint/js to ^9.39.0 (#20275) (renovate[bot])
  • d9ca2fc ci: Add rangeStrategy to eslint group in renovate config (#20266) (唯然)
  • 009e507 test: fix version tests for ESLint v10 (#20274) (Milos Djermanovic)
Commits

Updates `@types/node` from 24.9.2 to 24.10.0
Commits

Updates `@typescript-eslint/eslint-plugin` from 8.46.2 to 8.46.3
Release notes

Sourced from @​typescript-eslint/eslint-plugin's releases.

v8.46.3

8.46.3 (2025-11-03)

🩹 Fixes

  • eslint-plugin: [no-misused-promises] expand union type to retrieve target property (#11706)
  • eslint-plugin: [no-duplicate-enum-values] support signed numbers (#11722, #11723)

❤️ Thank You

You can read about our versioning strategy and releases on our website.

Changelog

Sourced from @​typescript-eslint/eslint-plugin's changelog.

8.46.3 (2025-11-03)

🩹 Fixes

  • eslint-plugin: [no-duplicate-enum-values] support signed numbers (#11722, #11723)
  • eslint-plugin: [no-misused-promises] expand union type to retrieve target property (#11706)

❤️ Thank You

You can read about our versioning strategy and releases on our website.

Commits
  • d9f3497 chore(release): publish 8.46.3
  • 26a9f94 fix(eslint-plugin): [no-duplicate-enum-values] support signed numbers (#11722...
  • b8219d1 fix(eslint-plugin): [no-misused-promises] expand union type to retrieve targe...
  • See full diff in compare view

Updates `@typescript-eslint/parser` from 8.46.2 to 8.46.3
Release notes

Sourced from @​typescript-eslint/parser's releases.

v8.46.3

8.46.3 (2025-11-03)

🩹 Fixes

  • eslint-plugin: [no-misused-promises] expand union type to retrieve target property (#11706)
  • eslint-plugin: [no-duplicate-enum-values] support signed numbers (#11722, #11723)

❤️ Thank You

You can read about our versioning strategy and releases on our website.

Changelog

Sourced from @​typescript-eslint/parser's changelog.

8.46.3 (2025-11-03)

This was a version bump only for parser to align it with other projects, there were no code changes.

You can read about our versioning strategy and releases on our website.

Commits

Updates `eslint` from 9.39.0 to 9.39.1
Release notes

Sourced from eslint's releases.

v9.39.1

Bug Fixes

  • 650753e fix: Only pass node to JS lang visitor methods (#20283) (Nicholas C. Zakas)

Documentation

  • 51b51f4 docs: add a section on when to use extends vs cascading (#20268) (Tanuj Kanti)
  • b44d426 docs: Update README (GitHub Actions Bot)

Chores

  • 92db329 chore: update @eslint/js version to 9.39.1 (#20284) (Francesco Trotta)
  • c7ebefc chore: package.json update for @​eslint/js release (Jenkins)
  • 61778f6 chore: update eslint-config-eslint dependency @​eslint/js to ^9.39.0 (#20275) (renovate[bot])
  • d9ca2fc ci: Add rangeStrategy to eslint group in renovate config (#20266) (唯然)
  • 009e507 test: fix version tests for ESLint v10 (#20274) (Milos Djermanovic)
Commits
  • e277281 9.39.1
  • 4cdf397 Build: changelog update for 9.39.1
  • 92db329 chore: update @eslint/js version to 9.39.1 (#20284)
  • c7ebefc chore: package.json update for @​eslint/js release
  • 650753e fix: Only pass node to JS lang visitor methods (#20283)
  • 51b51f4 docs: add a section on when to use extends vs cascading (#20268)
  • 61778f6 chore: update eslint-config-eslint dependency @​eslint/js to ^9.39.0 (#20275)
  • d9ca2fc ci: Add rangeStrategy to eslint group in renovate config (#20266)
  • 009e507 test: fix version tests for ESLint v10 (#20274)
  • b44d426 docs: Update README
  • See full diff in compare view

Updates `typescript-eslint` from 8.46.2 to 8.46.3
Release notes

Sourced from typescript-eslint's releases.

v8.46.3

8.46.3 (2025-11-03)

🩹 Fixes

  • eslint-plugin: [no-misused-promises] expand union type to retrieve target property (#11706)
  • eslint-plugin: [no-duplicate-enum-values] support signed numbers (#11722, #11723)

❤️ Thank You

You can read about our versioning strategy and releases on our website.

Changelog

Sourced from typescript-eslint's changelog.

8.46.3 (2025-11-03)

This was a version bump only for typescript-eslint to align it with other projects, there were no code changes.

You can read about our versioning strategy and releases on our website.

Commits

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 140 +++++++++++++++++++++++----------------------- 1 file changed, 70 insertions(+), 70 deletions(-) diff --git a/package-lock.json b/package-lock.json index 05577d383..2f473d103 100644 --- a/package-lock.json +++ b/package-lock.json @@ -284,9 +284,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.39.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.0.tgz", - "integrity": "sha512-BIhe0sW91JGPiaF1mOuPy5v8NflqfjIcDNpC+LbW9f609WVRX1rArrhi6Z2ymvrAry9jw+5POTj4t2t62o8Bmw==", + "version": "9.39.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.1.tgz", + "integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==", "dev": true, "license": "MIT", "engines": { @@ -1163,9 +1163,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "24.9.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.9.2.tgz", - "integrity": "sha512-uWN8YqxXxqFMX2RqGOrumsKeti4LlmIMIyV0lgut4jx7KQBcBiW6vkDtIBvHnHIquwNfJhk8v2OtmO8zXWHfPA==", + "version": "24.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.0.tgz", + "integrity": "sha512-qzQZRBqkFsYyaSWXuEHc2WR9c0a0CXwiE5FWUvn7ZM+vdy1uZLfCunD38UzhuB7YN/J11ndbDBcTmOdxJo9Q7A==", "dev": true, "license": "MIT", "dependencies": { @@ -1225,17 +1225,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.46.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.2.tgz", - "integrity": "sha512-ZGBMToy857/NIPaaCucIUQgqueOiq7HeAKkhlvqVV4lm089zUFW6ikRySx2v+cAhKeUCPuWVHeimyk6Dw1iY3w==", + "version": "8.46.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.3.tgz", + "integrity": "sha512-sbaQ27XBUopBkRiuY/P9sWGOWUW4rl8fDoHIUmLpZd8uldsTyB4/Zg6bWTegPoTLnKj9Hqgn3QD6cjPNB32Odw==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.46.2", - "@typescript-eslint/type-utils": "8.46.2", - "@typescript-eslint/utils": "8.46.2", - "@typescript-eslint/visitor-keys": "8.46.2", + "@typescript-eslint/scope-manager": "8.46.3", + "@typescript-eslint/type-utils": "8.46.3", + "@typescript-eslint/utils": "8.46.3", + "@typescript-eslint/visitor-keys": "8.46.3", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -1255,16 +1255,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.46.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.46.2.tgz", - "integrity": "sha512-BnOroVl1SgrPLywqxyqdJ4l3S2MsKVLDVxZvjI1Eoe8ev2r3kGDo+PcMihNmDE+6/KjkTubSJnmqGZZjQSBq/g==", + "version": "8.46.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.46.3.tgz", + "integrity": "sha512-6m1I5RmHBGTnUGS113G04DMu3CpSdxCAU/UvtjNWL4Nuf3MW9tQhiJqRlHzChIkhy6kZSAQmc+I1bcGjE3yNKg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.46.2", - "@typescript-eslint/types": "8.46.2", - "@typescript-eslint/typescript-estree": "8.46.2", - "@typescript-eslint/visitor-keys": "8.46.2", + "@typescript-eslint/scope-manager": "8.46.3", + "@typescript-eslint/types": "8.46.3", + "@typescript-eslint/typescript-estree": "8.46.3", + "@typescript-eslint/visitor-keys": "8.46.3", "debug": "^4.3.4" }, "engines": { @@ -1280,14 +1280,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.46.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.46.2.tgz", - "integrity": "sha512-PULOLZ9iqwI7hXcmL4fVfIsBi6AN9YxRc0frbvmg8f+4hQAjQ5GYNKK0DIArNo+rOKmR/iBYwkpBmnIwin4wBg==", + "version": "8.46.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.46.3.tgz", + "integrity": "sha512-Fz8yFXsp2wDFeUElO88S9n4w1I4CWDTXDqDr9gYvZgUpwXQqmZBr9+NTTql5R3J7+hrJZPdpiWaB9VNhAKYLuQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.46.2", - "@typescript-eslint/types": "^8.46.2", + "@typescript-eslint/tsconfig-utils": "^8.46.3", + "@typescript-eslint/types": "^8.46.3", "debug": "^4.3.4" }, "engines": { @@ -1302,14 +1302,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.46.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.46.2.tgz", - "integrity": "sha512-LF4b/NmGvdWEHD2H4MsHD8ny6JpiVNDzrSZr3CsckEgCbAGZbYM4Cqxvi9L+WqDMT+51Ozy7lt2M+d0JLEuBqA==", + "version": "8.46.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.46.3.tgz", + "integrity": "sha512-FCi7Y1zgrmxp3DfWfr+3m9ansUUFoy8dkEdeQSgA9gbm8DaHYvZCdkFRQrtKiedFf3Ha6VmoqoAaP68+i+22kg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.46.2", - "@typescript-eslint/visitor-keys": "8.46.2" + "@typescript-eslint/types": "8.46.3", + "@typescript-eslint/visitor-keys": "8.46.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1320,9 +1320,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.46.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.46.2.tgz", - "integrity": "sha512-a7QH6fw4S57+F5y2FIxxSDyi5M4UfGF+Jl1bCGd7+L4KsaUY80GsiF/t0UoRFDHAguKlBaACWJRmdrc6Xfkkag==", + "version": "8.46.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.46.3.tgz", + "integrity": "sha512-GLupljMniHNIROP0zE7nCcybptolcH8QZfXOpCfhQDAdwJ/ZTlcaBOYebSOZotpti/3HrHSw7D3PZm75gYFsOA==", "dev": true, "license": "MIT", "engines": { @@ -1337,15 +1337,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.46.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.46.2.tgz", - "integrity": "sha512-HbPM4LbaAAt/DjxXaG9yiS9brOOz6fabal4uvUmaUYe6l3K1phQDMQKBRUrr06BQkxkvIZVVHttqiybM9nJsLA==", + "version": "8.46.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.46.3.tgz", + "integrity": "sha512-ZPCADbr+qfz3aiTTYNNkCbUt+cjNwI/5McyANNrFBpVxPt7GqpEYz5ZfdwuFyGUnJ9FdDXbGODUu6iRCI6XRXw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.46.2", - "@typescript-eslint/typescript-estree": "8.46.2", - "@typescript-eslint/utils": "8.46.2", + "@typescript-eslint/types": "8.46.3", + "@typescript-eslint/typescript-estree": "8.46.3", + "@typescript-eslint/utils": "8.46.3", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -1362,9 +1362,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.46.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.46.2.tgz", - "integrity": "sha512-lNCWCbq7rpg7qDsQrd3D6NyWYu+gkTENkG5IKYhUIcxSb59SQC/hEQ+MrG4sTgBVghTonNWq42bA/d4yYumldQ==", + "version": "8.46.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.46.3.tgz", + "integrity": "sha512-G7Ok9WN/ggW7e/tOf8TQYMaxgID3Iujn231hfi0Pc7ZheztIJVpO44ekY00b7akqc6nZcvregk0Jpah3kep6hA==", "dev": true, "license": "MIT", "engines": { @@ -1376,16 +1376,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.46.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.46.2.tgz", - "integrity": "sha512-f7rW7LJ2b7Uh2EiQ+7sza6RDZnajbNbemn54Ob6fRwQbgcIn+GWfyuHDHRYgRoZu1P4AayVScrRW+YfbTvPQoQ==", + "version": "8.46.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.46.3.tgz", + "integrity": "sha512-f/NvtRjOm80BtNM5OQtlaBdM5BRFUv7gf381j9wygDNL+qOYSNOgtQ/DCndiYi80iIOv76QqaTmp4fa9hwI0OA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.46.2", - "@typescript-eslint/tsconfig-utils": "8.46.2", - "@typescript-eslint/types": "8.46.2", - "@typescript-eslint/visitor-keys": "8.46.2", + "@typescript-eslint/project-service": "8.46.3", + "@typescript-eslint/tsconfig-utils": "8.46.3", + "@typescript-eslint/types": "8.46.3", + "@typescript-eslint/visitor-keys": "8.46.3", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -1405,16 +1405,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.46.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.46.2.tgz", - "integrity": "sha512-sExxzucx0Tud5tE0XqR0lT0psBQvEpnpiul9XbGUB1QwpWJJAps1O/Z7hJxLGiZLBKMCutjTzDgmd1muEhBnVg==", + "version": "8.46.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.46.3.tgz", + "integrity": "sha512-VXw7qmdkucEx9WkmR3ld/u6VhRyKeiF1uxWwCy/iuNfokjJ7VhsgLSOTjsol8BunSw190zABzpwdNsze2Kpo4g==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.46.2", - "@typescript-eslint/types": "8.46.2", - "@typescript-eslint/typescript-estree": "8.46.2" + "@typescript-eslint/scope-manager": "8.46.3", + "@typescript-eslint/types": "8.46.3", + "@typescript-eslint/typescript-estree": "8.46.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1429,13 +1429,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.46.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.46.2.tgz", - "integrity": "sha512-tUFMXI4gxzzMXt4xpGJEsBsTox0XbNQ1y94EwlD/CuZwFcQP79xfQqMhau9HsRc/J0cAPA/HZt1dZPtGn9V/7w==", + "version": "8.46.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.46.3.tgz", + "integrity": "sha512-uk574k8IU0rOF/AjniX8qbLSGURJVUCeM5e4MIMKBFFi8weeiLrG1fyQejyLXQpRZbU/1BuQasleV/RfHC3hHg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.46.2", + "@typescript-eslint/types": "8.46.3", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -2947,9 +2947,9 @@ } }, "node_modules/eslint": { - "version": "9.39.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.0.tgz", - "integrity": "sha512-iy2GE3MHrYTL5lrCtMZ0X1KLEKKUjmK0kzwcnefhR66txcEmXZD2YWgR5GNdcEwkNx3a0siYkSvl0vIC+Svjmg==", + "version": "9.39.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.1.tgz", + "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==", "dev": true, "license": "MIT", "dependencies": { @@ -2959,7 +2959,7 @@ "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.39.0", + "@eslint/js": "9.39.1", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -6728,16 +6728,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.46.2", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.46.2.tgz", - "integrity": "sha512-vbw8bOmiuYNdzzV3lsiWv6sRwjyuKJMQqWulBOU7M0RrxedXledX8G8kBbQeiOYDnTfiXz0Y4081E1QMNB6iQg==", + "version": "8.46.3", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.46.3.tgz", + "integrity": "sha512-bAfgMavTuGo+8n6/QQDVQz4tZ4f7Soqg53RbrlZQEoAltYop/XR4RAts/I0BrO3TTClTSTFJ0wYbla+P8cEWJA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.46.2", - "@typescript-eslint/parser": "8.46.2", - "@typescript-eslint/typescript-estree": "8.46.2", - "@typescript-eslint/utils": "8.46.2" + "@typescript-eslint/eslint-plugin": "8.46.3", + "@typescript-eslint/parser": "8.46.3", + "@typescript-eslint/typescript-estree": "8.46.3", + "@typescript-eslint/utils": "8.46.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" From 7a095c699dc797ba805bce87d680ddb1513010ca Mon Sep 17 00:00:00 2001 From: Alex Rudenko Date: Fri, 7 Nov 2025 11:04:25 +0100 Subject: [PATCH 055/456] chore: add chrome devtools to a separate group (#534) --- .github/dependabot.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 942421fa2..41487df1c 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -23,10 +23,13 @@ updates: - 'core-js' patterns: - '*' + # breaks often so better to roll separetely. + bundled-devtools: + patterns: + - 'chrome-devtools-frontend' bundled: patterns: - 'puppeteer*' - - 'chrome-devtools-frontend' - '@modelcontextprotocol/sdk' - 'yargs' - 'debug' From b110e86a684bbbbf1c64216e291eb750079aad36 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 7 Nov 2025 10:09:24 +0000 Subject: [PATCH 056/456] chore(deps-dev): bump puppeteer from 24.29.0 to 24.29.1 in the bundled group across 1 directory (#535) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the bundled group with 1 update in the / directory: [puppeteer](https://github.com/puppeteer/puppeteer). Updates `puppeteer` from 24.29.0 to 24.29.1
Release notes

Sourced from puppeteer's releases.

puppeteer-core: v24.29.1

24.29.1 (2025-11-06)

🛠️ Fixes

puppeteer: v24.29.1

24.29.1 (2025-11-06)

♻️ Chores

  • puppeteer: Synchronize puppeteer versions

Dependencies

  • The following workspace dependencies were updated
    • dependencies
      • puppeteer-core bumped from 24.29.0 to 24.29.1
Changelog

Sourced from puppeteer's changelog.

24.29.1 (2025-11-06)

♻️ Chores

  • puppeteer: Synchronize puppeteer versions

Dependencies

  • The following workspace dependencies were updated
    • dependencies
      • puppeteer-core bumped from 24.29.0 to 24.29.1

🛠️ Fixes

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=puppeteer&package-manager=npm_and_yarn&previous-version=24.29.0&new-version=24.29.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 22 +++++++++++----------- package.json | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2f473d103..4a6e04c30 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,7 +33,7 @@ "eslint-plugin-import": "^2.32.0", "globals": "^16.4.0", "prettier": "^3.6.2", - "puppeteer": "24.29.0", + "puppeteer": "24.29.1", "rollup": "4.52.5", "rollup-plugin-cleanup": "^3.2.1", "rollup-plugin-license": "^3.6.0", @@ -2051,9 +2051,9 @@ "license": "MIT" }, "node_modules/bare-events": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.1.tgz", - "integrity": "sha512-oxSAxTS1hRfnyit2CL5QpAOS5ixfBjj6ex3yTNvXyY/kE719jQ/IjuESJBK2w5v4wwQRAHGseVJXx9QBYOtFGQ==", + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.2.tgz", + "integrity": "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==", "dev": true, "license": "Apache-2.0", "peerDependencies": { @@ -5472,9 +5472,9 @@ } }, "node_modules/puppeteer": { - "version": "24.29.0", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.29.0.tgz", - "integrity": "sha512-1F8Jh32IxpIYvL1NflR/DyT6xPGM136M9I1noWYY24zOVt/q/zr0TJr/7rO6IUCO9+Np1twzqJQVyTC3QixbhQ==", + "version": "24.29.1", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.29.1.tgz", + "integrity": "sha512-pX05JV1mMP+1N0vP3I4DOVwjMdpihv2LxQTtSfw6CUm5F0ZFLUFE/LSZ4yUWHYaM3C11Hdu+sgn7uY7teq5MYw==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", @@ -5483,7 +5483,7 @@ "chromium-bidi": "10.5.1", "cosmiconfig": "^9.0.0", "devtools-protocol": "0.0.1521046", - "puppeteer-core": "24.29.0", + "puppeteer-core": "24.29.1", "typed-query-selector": "^2.12.0" }, "bin": { @@ -5494,9 +5494,9 @@ } }, "node_modules/puppeteer-core": { - "version": "24.29.0", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.29.0.tgz", - "integrity": "sha512-WTxTpGBJMETB4tQ8heMK9XozjpmCNfoA8iov8H65upwhoQpcosZWGTZkPFNndEjILww9IKg/mbTlg8BnZ8L6wQ==", + "version": "24.29.1", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.29.1.tgz", + "integrity": "sha512-ErJ9qKCK+bdLvBa7QVSQTBSPm8KZbl1yC/WvhrZ0ut27hDf2QBzjDsn1IukzE1i1KtZ7NYGETOV4W1beoo9izA==", "dev": true, "license": "Apache-2.0", "dependencies": { diff --git a/package.json b/package.json index 7d51dcbdd..545353209 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "eslint-plugin-import": "^2.32.0", "globals": "^16.4.0", "prettier": "^3.6.2", - "puppeteer": "24.29.0", + "puppeteer": "24.29.1", "rollup": "4.52.5", "rollup-plugin-cleanup": "^3.2.1", "rollup-plugin-license": "^3.6.0", From 4724bbba9327fc162cd1f0372e608f6ebefc59cc Mon Sep 17 00:00:00 2001 From: Alex Rudenko Date: Fri, 7 Nov 2025 12:55:01 +0100 Subject: [PATCH 057/456] fix: avoid no page selected errors (#537) Uses object identity to track the selected page. This helps to be in sync with the browser state better. Closes https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/492 --------- Co-authored-by: Nikolay Vitkov <34244704+Lightning00Blade@users.noreply.github.com> --- src/McpContext.ts | 33 ++++++++++++++++++--------------- src/McpResponse.ts | 2 +- src/tools/ToolDefinition.ts | 3 ++- src/tools/pages.ts | 2 +- tests/tools/emulation.test.ts | 8 ++++---- tests/tools/pages.test.ts | 13 ++++++------- 6 files changed, 32 insertions(+), 29 deletions(-) diff --git a/src/McpContext.ts b/src/McpContext.ts index 4e4da8789..5853b7181 100644 --- a/src/McpContext.ts +++ b/src/McpContext.ts @@ -88,7 +88,7 @@ export class McpContext implements Context { // The most recent page state. #pages: Page[] = []; #pageToDevToolsPage = new Map(); - #selectedPageIdx = 0; + #selectedPage?: Page; // The most recent snapshot. #textSnapshot: TextSnapshot | null = null; #networkCollector: NetworkCollector; @@ -146,7 +146,6 @@ export class McpContext implements Context { async #init() { await this.createPagesSnapshot(); - this.setSelectedPageIdx(0); await this.#networkCollector.init(); await this.#consoleCollector.init(); } @@ -221,8 +220,8 @@ export class McpContext implements Context { async newPage(): Promise { const page = await this.browser.newPage(); - const pages = await this.createPagesSnapshot(); - this.setSelectedPageIdx(pages.indexOf(page)); + await this.createPagesSnapshot(); + this.selectPage(page); this.#networkCollector.addPage(page); this.#consoleCollector.addPage(page); return page; @@ -232,7 +231,6 @@ export class McpContext implements Context { throw new Error(CLOSE_PAGE_ERROR); } const page = this.getPageByIdx(pageIdx); - this.setSelectedPageIdx(0); await page.close({runBeforeUnload: false}); } @@ -283,7 +281,7 @@ export class McpContext implements Context { } getSelectedPage(): Page { - const page = this.#pages[this.#selectedPageIdx]; + const page = this.#selectedPage; if (!page) { throw new Error('No page selected'); } @@ -304,19 +302,20 @@ export class McpContext implements Context { return page; } - getSelectedPageIdx(): number { - return this.#selectedPageIdx; - } - #dialogHandler = (dialog: Dialog): void => { this.#dialog = dialog; }; - setSelectedPageIdx(idx: number): void { - const oldPage = this.getSelectedPage(); - oldPage.off('dialog', this.#dialogHandler); - this.#selectedPageIdx = idx; - const newPage = this.getSelectedPage(); + isPageSelected(page: Page): boolean { + return this.#selectedPage === page; + } + + selectPage(newPage: Page): void { + const oldPage = this.#selectedPage; + if (oldPage) { + oldPage.off('dialog', this.#dialogHandler); + } + this.#selectedPage = newPage; newPage.on('dialog', this.#dialogHandler); this.#updateSelectedPageTimeouts(); } @@ -387,6 +386,10 @@ export class McpContext implements Context { ); }); + if (!this.#selectedPage || this.#pages.indexOf(this.#selectedPage) === -1) { + this.selectPage(this.#pages[0]); + } + await this.detectOpenDevToolsWindows(); return this.#pages; diff --git a/src/McpResponse.ts b/src/McpResponse.ts index b5ea74aa4..da3d01dfb 100644 --- a/src/McpResponse.ts +++ b/src/McpResponse.ts @@ -362,7 +362,7 @@ Call ${handleDialog.name} to handle it before continuing.`); let idx = 0; for (const page of context.getPages()) { parts.push( - `${idx}: ${page.url()}${idx === context.getSelectedPageIdx() ? ' [selected]' : ''}`, + `${idx}: ${page.url()}${context.isPageSelected(page) ? ' [selected]' : ''}`, ); idx++; } diff --git a/src/tools/ToolDefinition.ts b/src/tools/ToolDefinition.ts index 9651718bf..8acebb564 100644 --- a/src/tools/ToolDefinition.ts +++ b/src/tools/ToolDefinition.ts @@ -90,9 +90,10 @@ export type Context = Readonly<{ getDialog(): Dialog | undefined; clearDialog(): void; getPageByIdx(idx: number): Page; + isPageSelected(page: Page): boolean; newPage(): Promise; closePage(pageIdx: number): Promise; - setSelectedPageIdx(idx: number): void; + selectPage(page: Page): void; getElementByUid(uid: string): Promise>; getAXNodeByUid(uid: string): TextSnapshotNode | undefined; setNetworkConditions(conditions: string | null): void; diff --git a/src/tools/pages.ts b/src/tools/pages.ts index d77a36765..f4638860f 100644 --- a/src/tools/pages.ts +++ b/src/tools/pages.ts @@ -40,7 +40,7 @@ export const selectPage = defineTool({ handler: async (request, response, context) => { const page = context.getPageByIdx(request.params.pageIdx); await page.bringToFront(); - context.setSelectedPageIdx(request.params.pageIdx); + context.selectPage(page); response.setIncludePages(true); }, }); diff --git a/tests/tools/emulation.test.ts b/tests/tools/emulation.test.ts index 92ad52a56..568631cc8 100644 --- a/tests/tools/emulation.test.ts +++ b/tests/tools/emulation.test.ts @@ -76,7 +76,6 @@ describe('emulation', () => { it('report correctly for the currently selected page', async () => { await withBrowser(async (response, context) => { - await context.newPage(); await emulate.handler( { params: { @@ -89,7 +88,8 @@ describe('emulation', () => { assert.strictEqual(context.getNetworkConditions(), 'Slow 3G'); - context.setSelectedPageIdx(0); + const page = await context.newPage(); + context.selectPage(page); assert.strictEqual(context.getNetworkConditions(), null); }); @@ -132,7 +132,6 @@ describe('emulation', () => { it('report correctly for the currently selected page', async () => { await withBrowser(async (response, context) => { - await context.newPage(); await emulate.handler( { params: { @@ -145,7 +144,8 @@ describe('emulation', () => { assert.strictEqual(context.getCpuThrottlingRate(), 4); - context.setSelectedPageIdx(0); + const page = await context.newPage(); + context.selectPage(page); assert.strictEqual(context.getCpuThrottlingRate(), 1); }); diff --git a/tests/tools/pages.test.ts b/tests/tools/pages.test.ts index cf482ae89..a4a76bcd7 100644 --- a/tests/tools/pages.test.ts +++ b/tests/tools/pages.test.ts @@ -31,13 +31,13 @@ describe('pages', () => { describe('new_page', () => { it('create a page', async () => { await withBrowser(async (response, context) => { - assert.strictEqual(context.getSelectedPageIdx(), 0); + assert.strictEqual(context.getPageByIdx(0), context.getSelectedPage()); await newPage.handler( {params: {url: 'about:blank'}}, response, context, ); - assert.strictEqual(context.getSelectedPageIdx(), 1); + assert.strictEqual(context.getPageByIdx(1), context.getSelectedPage()); assert.ok(response.includePages); }); }); @@ -46,7 +46,7 @@ describe('pages', () => { it('closes a page', async () => { await withBrowser(async (response, context) => { const page = await context.newPage(); - assert.strictEqual(context.getSelectedPageIdx(), 1); + assert.strictEqual(context.getPageByIdx(1), context.getSelectedPage()); assert.strictEqual(context.getPageByIdx(1), page); await closePage.handler({params: {pageIdx: 1}}, response, context); assert.ok(page.isClosed()); @@ -70,9 +70,9 @@ describe('pages', () => { it('selects a page', async () => { await withBrowser(async (response, context) => { await context.newPage(); - assert.strictEqual(context.getSelectedPageIdx(), 1); + assert.strictEqual(context.getPageByIdx(1), context.getSelectedPage()); await selectPage.handler({params: {pageIdx: 0}}, response, context); - assert.strictEqual(context.getSelectedPageIdx(), 0); + assert.strictEqual(context.getPageByIdx(0), context.getSelectedPage()); assert.ok(response.includePages); }); }); @@ -97,7 +97,7 @@ describe('pages', () => { it('throws an error if the page was closed not by the MCP server', async () => { await withBrowser(async (response, context) => { const page = await context.newPage(); - assert.strictEqual(context.getSelectedPageIdx(), 1); + assert.strictEqual(context.getPageByIdx(1), context.getSelectedPage()); assert.strictEqual(context.getPageByIdx(1), page); await page.close(); @@ -197,7 +197,6 @@ describe('pages', () => { describe('resize', () => { it('create a page', async () => { await withBrowser(async (response, context) => { - assert.strictEqual(context.getSelectedPageIdx(), 0); const page = context.getSelectedPage(); const resizePromise = page.evaluate(() => { return new Promise(resolve => { From c9d617007eb2fddffa44cc3ab238b7639ad284f5 Mon Sep 17 00:00:00 2001 From: browser-automation-bot <133232582+browser-automation-bot@users.noreply.github.com> Date: Fri, 7 Nov 2025 13:30:17 +0100 Subject: [PATCH 058/456] chore(main): release chrome-devtools-mcp 0.10.1 (#538) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit :robot: I have created a release *beep* *boop* --- ## [0.10.1](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.10.0...chrome-devtools-mcp-v0.10.1) (2025-11-07) ### 🛠️ Fixes * avoid no page selected errors ([#537](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/537)) ([4724bbb](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/4724bbba9327fc162cd1f0372e608f6ebefc59cc)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --- .release-please-manifest.json | 2 +- CHANGELOG.md | 7 +++++++ package-lock.json | 4 ++-- package.json | 2 +- server.json | 4 ++-- src/main.ts | 2 +- 6 files changed, 14 insertions(+), 7 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 7d9b009db..30b6d45ad 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.10.0" + ".": "0.10.1" } diff --git a/CHANGELOG.md b/CHANGELOG.md index 761f61997..b841f3b55 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [0.10.1](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.10.0...chrome-devtools-mcp-v0.10.1) (2025-11-07) + + +### 🛠️ Fixes + +* avoid no page selected errors ([#537](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/537)) ([4724bbb](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/4724bbba9327fc162cd1f0372e608f6ebefc59cc)) + ## [0.10.0](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.9.0...chrome-devtools-mcp-v0.10.0) (2025-11-05) diff --git a/package-lock.json b/package-lock.json index 4a6e04c30..5fa5029f9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "chrome-devtools-mcp", - "version": "0.10.0", + "version": "0.10.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "chrome-devtools-mcp", - "version": "0.10.0", + "version": "0.10.1", "license": "Apache-2.0", "bin": { "chrome-devtools-mcp": "build/src/index.js" diff --git a/package.json b/package.json index 545353209..04b4987bf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "chrome-devtools-mcp", - "version": "0.10.0", + "version": "0.10.1", "description": "MCP server for Chrome DevTools", "type": "module", "bin": "./build/src/index.js", diff --git a/server.json b/server.json index 13e8a3b21..7cb6d0ad6 100644 --- a/server.json +++ b/server.json @@ -6,13 +6,13 @@ "url": "https://github.com/ChromeDevTools/chrome-devtools-mcp", "source": "github" }, - "version": "0.10.0", + "version": "0.10.1", "packages": [ { "registryType": "npm", "registryBaseUrl": "https://registry.npmjs.org", "identifier": "chrome-devtools-mcp", - "version": "0.10.0", + "version": "0.10.1", "transport": { "type": "stdio" }, diff --git a/src/main.ts b/src/main.ts index 80a1ec3f5..80db16b66 100644 --- a/src/main.ts +++ b/src/main.ts @@ -33,7 +33,7 @@ import type {ToolDefinition} from './tools/ToolDefinition.js'; // If moved update release-please config // x-release-please-start-version -const VERSION = '0.10.0'; +const VERSION = '0.10.1'; // x-release-please-end export const args = parseArguments(VERSION); From 7453977272cc20676161eec9a22380e8ba7922bc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Nov 2025 10:39:39 +0100 Subject: [PATCH 059/456] chore(deps-dev): bump @modelcontextprotocol/sdk from 1.21.0 to 1.21.1 in the bundled group (#540) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the bundled group with 1 update: [@modelcontextprotocol/sdk](https://github.com/modelcontextprotocol/typescript-sdk). Updates `@modelcontextprotocol/sdk` from 1.21.0 to 1.21.1
Release notes

Sourced from @​modelcontextprotocol/sdk's releases.

1.21.1

What's Changed

New Contributors

Full Changelog: https://github.com/modelcontextprotocol/typescript-sdk/compare/1.21.0...1.21.1

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=@modelcontextprotocol/sdk&package-manager=npm_and_yarn&previous-version=1.21.0&new-version=1.21.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5fa5029f9..be5a7aadf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ }, "devDependencies": { "@eslint/js": "^9.35.0", - "@modelcontextprotocol/sdk": "1.21.0", + "@modelcontextprotocol/sdk": "1.21.1", "@rollup/plugin-commonjs": "^29.0.0", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^16.0.3", @@ -380,9 +380,9 @@ "license": "MIT" }, "node_modules/@modelcontextprotocol/sdk": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.21.0.tgz", - "integrity": "sha512-YFBsXJMFCyI1zP98u7gezMFKX4lgu/XpoZJk7ufI6UlFKXLj2hAMUuRlQX/nrmIPOmhRrG6tw2OQ2ZA/ZlXYpQ==", + "version": "1.21.1", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.21.1.tgz", + "integrity": "sha512-UyLFcJLDvUuZbGnaQqXFT32CpPpGj7VS19roLut6gkQVhb439xUzYWbsUvdI3ZPL+2hnFosuugtYWE0Mcs1rmQ==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 04b4987bf..68d0996d0 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "mcpName": "io.github.ChromeDevTools/chrome-devtools-mcp", "devDependencies": { "@eslint/js": "^9.35.0", - "@modelcontextprotocol/sdk": "1.21.0", + "@modelcontextprotocol/sdk": "1.21.1", "@rollup/plugin-commonjs": "^29.0.0", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^16.0.3", From 44080f2c489c46f5cf337dc5c2ed4a6fc837c54f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Nov 2025 12:53:48 +0100 Subject: [PATCH 060/456] chore(deps-dev): bump rollup from 4.52.5 to 4.53.1 in the dev-dependencies group (#539) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the dev-dependencies group with 1 update: [rollup](https://github.com/rollup/rollup). Updates `rollup` from 4.52.5 to 4.53.1
Release notes

Sourced from rollup's releases.

v4.53.1

4.53.1

2025-11-07

Bug Fixes

  • Fix install script (#6172)

Pull Requests

  • #6172: fix: move patch-package from postinstall to prepare script (@​mshima)

v4.53.0

4.53.0

2025-11-07

Features

  • Improve rendering performance by caching generated variable names (#5947)

Pull Requests

Changelog

Sourced from rollup's changelog.

4.53.1

2025-11-07

Bug Fixes

  • Fix install script (#6172)

Pull Requests

  • #6172: fix: move patch-package from postinstall to prepare script (@​mshima)

4.53.0

2025-11-07

Features

  • Improve rendering performance by caching generated variable names (#5947)

Pull Requests

Commits
  • e3bdcdf 4.53.1
  • 96b5453 fix: move patch-package from postinstall to prepare script (#6172)
  • ecff532 4.53.0
  • 05a6c01 refactor: store safe variable names in cache for subsequent usage (#5947)
  • 5cf4264 fix(deps): update swc monorepo (major) (#6166)
  • 75c4346 chore(deps): lock file maintenance minor/patch updates (#6167)
  • 1ba7efe chore(deps): update dependency @​rollup/plugin-alias to v6 (#6164)
  • e64d220 chore(deps): update dependency @​rollup/plugin-commonjs to v29 (#6165)
  • d96ffa9 fix(deps): lock file maintenance minor/patch updates (#6161)
  • 9a87f8e chore(deps): update dependency eslint-plugin-unicorn to v62 (#6159)
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=rollup&package-manager=npm_and_yarn&previous-version=4.52.5&new-version=4.53.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 184 +++++++++++++++++++++++----------------------- package.json | 2 +- 2 files changed, 93 insertions(+), 93 deletions(-) diff --git a/package-lock.json b/package-lock.json index be5a7aadf..c6f563375 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,7 +34,7 @@ "globals": "^16.4.0", "prettier": "^3.6.2", "puppeteer": "24.29.1", - "rollup": "4.52.5", + "rollup": "4.53.2", "rollup-plugin-cleanup": "^3.2.1", "rollup-plugin-license": "^3.6.0", "sinon": "^21.0.0", @@ -713,9 +713,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.5.tgz", - "integrity": "sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.2.tgz", + "integrity": "sha512-yDPzwsgiFO26RJA4nZo8I+xqzh7sJTZIWQOxn+/XOdPE31lAvLIYCKqjV+lNH/vxE2L2iH3plKxDCRK6i+CwhA==", "cpu": [ "arm" ], @@ -727,9 +727,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.5.tgz", - "integrity": "sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.2.tgz", + "integrity": "sha512-k8FontTxIE7b0/OGKeSN5B6j25EuppBcWM33Z19JoVT7UTXFSo3D9CdU39wGTeb29NO3XxpMNauh09B+Ibw+9g==", "cpu": [ "arm64" ], @@ -741,9 +741,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.5.tgz", - "integrity": "sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.2.tgz", + "integrity": "sha512-A6s4gJpomNBtJ2yioj8bflM2oogDwzUiMl2yNJ2v9E7++sHrSrsQ29fOfn5DM/iCzpWcebNYEdXpaK4tr2RhfQ==", "cpu": [ "arm64" ], @@ -755,9 +755,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.5.tgz", - "integrity": "sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.2.tgz", + "integrity": "sha512-e6XqVmXlHrBlG56obu9gDRPW3O3hLxpwHpLsBJvuI8qqnsrtSZ9ERoWUXtPOkY8c78WghyPHZdmPhHLWNdAGEw==", "cpu": [ "x64" ], @@ -769,9 +769,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.5.tgz", - "integrity": "sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.2.tgz", + "integrity": "sha512-v0E9lJW8VsrwPux5Qe5CwmH/CF/2mQs6xU1MF3nmUxmZUCHazCjLgYvToOk+YuuUqLQBio1qkkREhxhc656ViA==", "cpu": [ "arm64" ], @@ -783,9 +783,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.5.tgz", - "integrity": "sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.2.tgz", + "integrity": "sha512-ClAmAPx3ZCHtp6ysl4XEhWU69GUB1D+s7G9YjHGhIGCSrsg00nEGRRZHmINYxkdoJehde8VIsDC5t9C0gb6yqA==", "cpu": [ "x64" ], @@ -797,9 +797,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.5.tgz", - "integrity": "sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.2.tgz", + "integrity": "sha512-EPlb95nUsz6Dd9Qy13fI5kUPXNSljaG9FiJ4YUGU1O/Q77i5DYFW5KR8g1OzTcdZUqQQ1KdDqsTohdFVwCwjqg==", "cpu": [ "arm" ], @@ -811,9 +811,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.5.tgz", - "integrity": "sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.2.tgz", + "integrity": "sha512-BOmnVW+khAUX+YZvNfa0tGTEMVVEerOxN0pDk2E6N6DsEIa2Ctj48FOMfNDdrwinocKaC7YXUZ1pHlKpnkja/Q==", "cpu": [ "arm" ], @@ -825,9 +825,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.5.tgz", - "integrity": "sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.2.tgz", + "integrity": "sha512-Xt2byDZ+6OVNuREgBXr4+CZDJtrVso5woFtpKdGPhpTPHcNG7D8YXeQzpNbFRxzTVqJf7kvPMCub/pcGUWgBjA==", "cpu": [ "arm64" ], @@ -839,9 +839,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.5.tgz", - "integrity": "sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.2.tgz", + "integrity": "sha512-+LdZSldy/I9N8+klim/Y1HsKbJ3BbInHav5qE9Iy77dtHC/pibw1SR/fXlWyAk0ThnpRKoODwnAuSjqxFRDHUQ==", "cpu": [ "arm64" ], @@ -853,9 +853,9 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.5.tgz", - "integrity": "sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.2.tgz", + "integrity": "sha512-8ms8sjmyc1jWJS6WdNSA23rEfdjWB30LH8Wqj0Cqvv7qSHnvw6kgMMXRdop6hkmGPlyYBdRPkjJnj3KCUHV/uQ==", "cpu": [ "loong64" ], @@ -867,9 +867,9 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.5.tgz", - "integrity": "sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.2.tgz", + "integrity": "sha512-3HRQLUQbpBDMmzoxPJYd3W6vrVHOo2cVW8RUo87Xz0JPJcBLBr5kZ1pGcQAhdZgX9VV7NbGNipah1omKKe23/g==", "cpu": [ "ppc64" ], @@ -881,9 +881,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.5.tgz", - "integrity": "sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.2.tgz", + "integrity": "sha512-fMjKi+ojnmIvhk34gZP94vjogXNNUKMEYs+EDaB/5TG/wUkoeua7p7VCHnE6T2Tx+iaghAqQX8teQzcvrYpaQA==", "cpu": [ "riscv64" ], @@ -895,9 +895,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.5.tgz", - "integrity": "sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.2.tgz", + "integrity": "sha512-XuGFGU+VwUUV5kLvoAdi0Wz5Xbh2SrjIxCtZj6Wq8MDp4bflb/+ThZsVxokM7n0pcbkEr2h5/pzqzDYI7cCgLQ==", "cpu": [ "riscv64" ], @@ -909,9 +909,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.5.tgz", - "integrity": "sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.2.tgz", + "integrity": "sha512-w6yjZF0P+NGzWR3AXWX9zc0DNEGdtvykB03uhonSHMRa+oWA6novflo2WaJr6JZakG2ucsyb+rvhrKac6NIy+w==", "cpu": [ "s390x" ], @@ -923,9 +923,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.5.tgz", - "integrity": "sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.2.tgz", + "integrity": "sha512-yo8d6tdfdeBArzC7T/PnHd7OypfI9cbuZzPnzLJIyKYFhAQ8SvlkKtKBMbXDxe1h03Rcr7u++nFS7tqXz87Gtw==", "cpu": [ "x64" ], @@ -937,9 +937,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.5.tgz", - "integrity": "sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.2.tgz", + "integrity": "sha512-ah59c1YkCxKExPP8O9PwOvs+XRLKwh/mV+3YdKqQ5AMQ0r4M4ZDuOrpWkUaqO7fzAHdINzV9tEVu8vNw48z0lA==", "cpu": [ "x64" ], @@ -951,9 +951,9 @@ ] }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.5.tgz", - "integrity": "sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.2.tgz", + "integrity": "sha512-4VEd19Wmhr+Zy7hbUsFZ6YXEiP48hE//KPLCSVNY5RMGX2/7HZ+QkN55a3atM1C/BZCGIgqN+xrVgtdak2S9+A==", "cpu": [ "arm64" ], @@ -965,9 +965,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.5.tgz", - "integrity": "sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.2.tgz", + "integrity": "sha512-IlbHFYc/pQCgew/d5fslcy1KEaYVCJ44G8pajugd8VoOEI8ODhtb/j8XMhLpwHCMB3yk2J07ctup10gpw2nyMA==", "cpu": [ "arm64" ], @@ -979,9 +979,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.5.tgz", - "integrity": "sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.2.tgz", + "integrity": "sha512-lNlPEGgdUfSzdCWU176ku/dQRnA7W+Gp8d+cWv73jYrb8uT7HTVVxq62DUYxjbaByuf1Yk0RIIAbDzp+CnOTFg==", "cpu": [ "ia32" ], @@ -993,9 +993,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.5.tgz", - "integrity": "sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.2.tgz", + "integrity": "sha512-S6YojNVrHybQis2lYov1sd+uj7K0Q05NxHcGktuMMdIQ2VixGwAfbJ23NnlvvVV1bdpR2m5MsNBViHJKcA4ADw==", "cpu": [ "x64" ], @@ -1007,9 +1007,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.5.tgz", - "integrity": "sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.2.tgz", + "integrity": "sha512-k+/Rkcyx//P6fetPoLMb8pBeqJBNGx81uuf7iljX9++yNBVRDQgD04L+SVXmXmh5ZP4/WOp4mWF0kmi06PW2tA==", "cpu": [ "x64" ], @@ -5709,9 +5709,9 @@ } }, "node_modules/rollup": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.5.tgz", - "integrity": "sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.2.tgz", + "integrity": "sha512-MHngMYwGJVi6Fmnk6ISmnk7JAHRNF0UkuucA0CUW3N3a4KnONPEZz+vUanQP/ZC/iY1Qkf3bwPWzyY84wEks1g==", "dev": true, "license": "MIT", "dependencies": { @@ -5725,28 +5725,28 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.52.5", - "@rollup/rollup-android-arm64": "4.52.5", - "@rollup/rollup-darwin-arm64": "4.52.5", - "@rollup/rollup-darwin-x64": "4.52.5", - "@rollup/rollup-freebsd-arm64": "4.52.5", - "@rollup/rollup-freebsd-x64": "4.52.5", - "@rollup/rollup-linux-arm-gnueabihf": "4.52.5", - "@rollup/rollup-linux-arm-musleabihf": "4.52.5", - "@rollup/rollup-linux-arm64-gnu": "4.52.5", - "@rollup/rollup-linux-arm64-musl": "4.52.5", - "@rollup/rollup-linux-loong64-gnu": "4.52.5", - "@rollup/rollup-linux-ppc64-gnu": "4.52.5", - "@rollup/rollup-linux-riscv64-gnu": "4.52.5", - "@rollup/rollup-linux-riscv64-musl": "4.52.5", - "@rollup/rollup-linux-s390x-gnu": "4.52.5", - "@rollup/rollup-linux-x64-gnu": "4.52.5", - "@rollup/rollup-linux-x64-musl": "4.52.5", - "@rollup/rollup-openharmony-arm64": "4.52.5", - "@rollup/rollup-win32-arm64-msvc": "4.52.5", - "@rollup/rollup-win32-ia32-msvc": "4.52.5", - "@rollup/rollup-win32-x64-gnu": "4.52.5", - "@rollup/rollup-win32-x64-msvc": "4.52.5", + "@rollup/rollup-android-arm-eabi": "4.53.2", + "@rollup/rollup-android-arm64": "4.53.2", + "@rollup/rollup-darwin-arm64": "4.53.2", + "@rollup/rollup-darwin-x64": "4.53.2", + "@rollup/rollup-freebsd-arm64": "4.53.2", + "@rollup/rollup-freebsd-x64": "4.53.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.53.2", + "@rollup/rollup-linux-arm-musleabihf": "4.53.2", + "@rollup/rollup-linux-arm64-gnu": "4.53.2", + "@rollup/rollup-linux-arm64-musl": "4.53.2", + "@rollup/rollup-linux-loong64-gnu": "4.53.2", + "@rollup/rollup-linux-ppc64-gnu": "4.53.2", + "@rollup/rollup-linux-riscv64-gnu": "4.53.2", + "@rollup/rollup-linux-riscv64-musl": "4.53.2", + "@rollup/rollup-linux-s390x-gnu": "4.53.2", + "@rollup/rollup-linux-x64-gnu": "4.53.2", + "@rollup/rollup-linux-x64-musl": "4.53.2", + "@rollup/rollup-openharmony-arm64": "4.53.2", + "@rollup/rollup-win32-arm64-msvc": "4.53.2", + "@rollup/rollup-win32-ia32-msvc": "4.53.2", + "@rollup/rollup-win32-x64-gnu": "4.53.2", + "@rollup/rollup-win32-x64-msvc": "4.53.2", "fsevents": "~2.3.2" } }, diff --git a/package.json b/package.json index 68d0996d0..62d745748 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "globals": "^16.4.0", "prettier": "^3.6.2", "puppeteer": "24.29.1", - "rollup": "4.52.5", + "rollup": "4.53.2", "rollup-plugin-cleanup": "^3.2.1", "rollup-plugin-license": "^3.6.0", "sinon": "^21.0.0", From 247e1b6a718430be776e3b85dd96fc74b83346bb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Nov 2025 14:23:08 +0000 Subject: [PATCH 061/456] chore(deps-dev): bump chrome-devtools-frontend from 1.0.1539728 to 1.0.1541169 in the bundled-devtools group (#536) Bumps the bundled-devtools group with 1 update: [chrome-devtools-frontend](https://github.com/ChromeDevTools/devtools-frontend). Updates `chrome-devtools-frontend` from 1.0.1539728 to 1.0.1541169
Commits
  • 393435a Update Chrome (for Testing) PIN
  • 59cc38b Update DevTools DEPS (trusted)
  • 67a6315 [RPP] Simplify insight set id
  • 941f21a [AI] Present trace facts grouped by insight set
  • 8ae5a85 Revert from fetch back to http.request in resultsdb.ts
  • 774cfe7 Update Chrome (for Testing) PIN
  • 76cbb2c [eslint] Add eslint-plugin-lit
  • 92be0b6 Replace outline-width: 0 with outline-style: none
  • fa72d97 Move affected count updates out of list rendering
  • baec26c Roll browser-protocol
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=chrome-devtools-frontend&package-manager=npm_and_yarn&previous-version=1.0.1539728&new-version=1.0.1541169)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
--------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Alex Rudenko --- package-lock.json | 8 ++-- package.json | 2 +- tests/tools/performance.test.js.snapshot | 2 +- tests/tools/performance.test.ts | 4 +- tests/trace-processing/parse.test.js.snapshot | 2 +- tsconfig.json | 43 +++++++++++-------- 6 files changed, 33 insertions(+), 28 deletions(-) diff --git a/package-lock.json b/package-lock.json index c6f563375..4a4045fd9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,7 +25,7 @@ "@types/yargs": "^17.0.33", "@typescript-eslint/eslint-plugin": "^8.43.0", "@typescript-eslint/parser": "^8.43.0", - "chrome-devtools-frontend": "1.0.1539728", + "chrome-devtools-frontend": "1.0.1541552", "core-js": "3.46.0", "debug": "4.4.3", "eslint": "^9.35.0", @@ -2299,9 +2299,9 @@ } }, "node_modules/chrome-devtools-frontend": { - "version": "1.0.1539728", - "resolved": "https://registry.npmjs.org/chrome-devtools-frontend/-/chrome-devtools-frontend-1.0.1539728.tgz", - "integrity": "sha512-4+HoCLEq+DhJtE1BqU5Dj2+CeYvuYrpBov/Wg3DuMgpJJbn0l+ePcfPBLZCBskveo+/eltV27DM70Z2Rq0Zs0Q==", + "version": "1.0.1541552", + "resolved": "https://registry.npmjs.org/chrome-devtools-frontend/-/chrome-devtools-frontend-1.0.1541552.tgz", + "integrity": "sha512-a/tFvYOf0afjyfXGWNvH3mtPW43YY8E4w8VIflXl8l7xHQVdxGVlJEYgbfxBuXaXfWZiV1pvMxWmzknLEjRtgw==", "dev": true, "license": "BSD-3-Clause" }, diff --git a/package.json b/package.json index 62d745748..f23168ae4 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "@types/yargs": "^17.0.33", "@typescript-eslint/eslint-plugin": "^8.43.0", "@typescript-eslint/parser": "^8.43.0", - "chrome-devtools-frontend": "1.0.1539728", + "chrome-devtools-frontend": "1.0.1541552", "core-js": "3.46.0", "debug": "4.4.3", "eslint": "^9.35.0", diff --git a/tests/tools/performance.test.js.snapshot b/tests/tools/performance.test.js.snapshot index 0ca8cc3f1..0b31b5f19 100644 --- a/tests/tools/performance.test.js.snapshot +++ b/tests/tools/performance.test.js.snapshot @@ -61,7 +61,7 @@ Network throttling: none The following is a list of insight sets. An insight set covers a specific part of the trace, split by navigations. The insights within each insight set are specific to that part of the trace. Be sure to consider the insight set id and bounds when calling functions. If no specific insight set or navigation is mentioned, assume the user is referring to the first one. -## insight set id: 8463DF94CD61B265B664E7F768183DE3 +## insight set id: NAVIGATION_0 URL: https://web.dev/ Bounds: {min: 122410996889, max: 122416385853} diff --git a/tests/tools/performance.test.ts b/tests/tools/performance.test.ts index 0c3cb6584..32425788b 100644 --- a/tests/tools/performance.test.ts +++ b/tests/tools/performance.test.ts @@ -158,7 +158,7 @@ describe('performance', () => { await analyzeInsight.handler( { params: { - insightSetId: '8463DF94CD61B265B664E7F768183DE3', + insightSetId: 'NAVIGATION_0', insightName: 'LCPBreakdown', }, }, @@ -189,7 +189,7 @@ describe('performance', () => { assert.ok( response.responseLines .join('\n') - .match(/No Insight with the name MadeUpInsightName found./), + .match(/No Performance Insights for the given insight set id/), ); }); }); diff --git a/tests/trace-processing/parse.test.js.snapshot b/tests/trace-processing/parse.test.js.snapshot index f8d8115cc..bed739d88 100644 --- a/tests/trace-processing/parse.test.js.snapshot +++ b/tests/trace-processing/parse.test.js.snapshot @@ -9,7 +9,7 @@ Network throttling: none The following is a list of insight sets. An insight set covers a specific part of the trace, split by navigations. The insights within each insight set are specific to that part of the trace. Be sure to consider the insight set id and bounds when calling functions. If no specific insight set or navigation is mentioned, assume the user is referring to the first one. -## insight set id: 8463DF94CD61B265B664E7F768183DE3 +## insight set id: NAVIGATION_0 URL: https://web.dev/ Bounds: {min: 122410996889, max: 122416385853} diff --git a/tsconfig.json b/tsconfig.json index 11e5dfa9a..7ccc44480 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -26,37 +26,42 @@ "include": [ "src/**/*.ts", "tests/**/*.ts", - "node_modules/chrome-devtools-frontend/mcp/mcp.ts", + "node_modules/chrome-devtools-frontend/front_end/core/common", + "node_modules/chrome-devtools-frontend/front_end/core/host", + "node_modules/chrome-devtools-frontend/front_end/core/platform", + "node_modules/chrome-devtools-frontend/front_end/core/protocol_client", + "node_modules/chrome-devtools-frontend/front_end/core/root", + "node_modules/chrome-devtools-frontend/front_end/core/sdk", + "node_modules/chrome-devtools-frontend/front_end/foundation/foundation.ts", + "node_modules/chrome-devtools-frontend/front_end/foundation/Universe.ts", + "node_modules/chrome-devtools-frontend/front_end/generated", "node_modules/chrome-devtools-frontend/front_end/legacy/legacy-defs.d.ts", - "node_modules/chrome-devtools-frontend/front_end/models/trace", - "node_modules/chrome-devtools-frontend/front_end/models/logs", - "node_modules/chrome-devtools-frontend/front_end/models/text_utils", - "node_modules/chrome-devtools-frontend/front_end/models/network_time_calculator", - "node_modules/chrome-devtools-frontend/front_end/models/crux-manager", + "node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/NetworkRequestFormatter.ts", "node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.ts", "node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.ts", - "node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/NetworkRequestFormatter.ts", "node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/UnitFormatters.ts", "node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/performance", - "node_modules/chrome-devtools-frontend/front_end/models/trace_source_maps_resolver", - "node_modules/chrome-devtools-frontend/front_end/models/emulation", - "node_modules/chrome-devtools-frontend/front_end/models/stack_trace", "node_modules/chrome-devtools-frontend/front_end/models/bindings", + "node_modules/chrome-devtools-frontend/front_end/models/cpu_profile", + "node_modules/chrome-devtools-frontend/front_end/models/crux-manager", + "node_modules/chrome-devtools-frontend/front_end/models/emulation", "node_modules/chrome-devtools-frontend/front_end/models/formatter", "node_modules/chrome-devtools-frontend/front_end/models/geometry", + "node_modules/chrome-devtools-frontend/front_end/models/issues_manager", + "node_modules/chrome-devtools-frontend/front_end/models/logs", + "node_modules/chrome-devtools-frontend/front_end/models/network_time_calculator", "node_modules/chrome-devtools-frontend/front_end/models/source_map_scopes", + "node_modules/chrome-devtools-frontend/front_end/models/stack_trace", + "node_modules/chrome-devtools-frontend/front_end/models/text_utils", + "node_modules/chrome-devtools-frontend/front_end/models/trace_source_maps_resolver", + "node_modules/chrome-devtools-frontend/front_end/models/trace", "node_modules/chrome-devtools-frontend/front_end/models/workspace", - "node_modules/chrome-devtools-frontend/front_end/core/common", - "node_modules/chrome-devtools-frontend/front_end/core/sdk", - "node_modules/chrome-devtools-frontend/front_end/core/protocol_client", - "node_modules/chrome-devtools-frontend/front_end/core/host", - "node_modules/chrome-devtools-frontend/front_end/core/platform", - "node_modules/chrome-devtools-frontend/front_end/models/cpu_profile", - "node_modules/chrome-devtools-frontend/front_end/generated", + "node_modules/chrome-devtools-frontend/front_end/panels/issues/IssueAggregator.ts", "node_modules/chrome-devtools-frontend/front_end/third_party/legacy-javascript", + "node_modules/chrome-devtools-frontend/front_end/third_party/marked", "node_modules/chrome-devtools-frontend/front_end/third_party/source-map-scopes-codec", - "node_modules/chrome-devtools-frontend/front_end/core/root", - "node_modules/chrome-devtools-frontend/front_end/third_party/third-party-web" + "node_modules/chrome-devtools-frontend/front_end/third_party/third-party-web", + "node_modules/chrome-devtools-frontend/mcp/mcp.ts" ], "exclude": ["node_modules/chrome-devtools-frontend/**/*.test.ts"] } From 016e2fd6ee57447103f7385285dd503b5576a860 Mon Sep 17 00:00:00 2001 From: Eno Reyes Date: Tue, 11 Nov 2025 00:42:40 -0800 Subject: [PATCH 062/456] docs: add Factory CLI configuration to MCP clients (#523) This PR adds Factory CLI to the MCP client configuration section in the README. The Factory CLI section: - Follows the same format as other MCP clients - Is placed in alphabetical order (between Cursor and Gemini CLI) - Includes a link to the Factory documentation - Provides the command to add the Chrome DevTools MCP server using `droid mcp add` --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index 54086fbf5..3de17ff7b 100644 --- a/README.md +++ b/README.md @@ -152,6 +152,16 @@ Go to `Cursor Settings` -> `MCP` -> `New MCP Server`. Use the config provided ab +
+ Factory CLI +Use the Factory CLI to add the Chrome DevTools MCP server (guide): + +```bash +droid mcp add chrome-devtools "npx -y chrome-devtools-mcp@latest" +``` + +
+
Gemini CLI Install the Chrome DevTools MCP server using the Gemini CLI. From f74ddad29c352480741706c6883be71ae6c481f7 Mon Sep 17 00:00:00 2001 From: Alex Rudenko Date: Wed, 12 Nov 2025 09:49:53 +0100 Subject: [PATCH 063/456] chore: remove no-restricted-imports rule (#546) --- eslint.config.mjs | 16 ---------------- src/DevToolsConnectionAdapter.ts | 3 +-- 2 files changed, 1 insertion(+), 18 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index 3a17c16d0..2d7fb59f8 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -113,22 +113,6 @@ export default defineConfig([ '@stylistic/function-call-spacing': 'error', '@stylistic/semi': 'error', - - 'no-restricted-imports': [ - 'error', - { - patterns: [ - { - group: [ - '*/node_modules/chrome-devtools-frontend/*', - '!*/node_modules/chrome-devtools-frontend/mcp/mcp.js', - ], - message: - 'Import devtools-frontend code only from node_modules/chrome-devtools-frontend/mcp/mcp.js', - }, - ], - }, - ], }, }, { diff --git a/src/DevToolsConnectionAdapter.ts b/src/DevToolsConnectionAdapter.ts index 17c335a33..917575403 100644 --- a/src/DevToolsConnectionAdapter.ts +++ b/src/DevToolsConnectionAdapter.ts @@ -4,8 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -// eslint-disable-next-line no-restricted-imports -import {ConnectionTransport as DevToolsConnectionTransport} from '../node_modules/chrome-devtools-frontend/front_end/core/protocol_client/ConnectionTransport.js'; +import {ConnectionTransport as DevToolsConnectionTransport} from '../node_modules/chrome-devtools-frontend/mcp/mcp.js'; import {type ConnectionTransport} from './third_party/index.js'; From 2fd4cc9ac60587ff0ba3835d45fdafea68642a79 Mon Sep 17 00:00:00 2001 From: Nikolay Vitkov <34244704+Lightning00Blade@users.noreply.github.com> Date: Wed, 12 Nov 2025 11:15:11 +0100 Subject: [PATCH 064/456] chore: restrict DevTools imports (#547) --- eslint.config.mjs | 13 +++++++++++++ scripts/tsconfig.json | 2 ++ 2 files changed, 15 insertions(+) diff --git a/eslint.config.mjs b/eslint.config.mjs index 2d7fb59f8..04882b055 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -113,6 +113,19 @@ export default defineConfig([ '@stylistic/function-call-spacing': 'error', '@stylistic/semi': 'error', + + 'no-restricted-imports': [ + 'error', + { + patterns: [ + { + regex: '.*chrome-devtools-frontend/(?!mcp/mcp.js$).*', + message: + 'Import only the devtools-frontend code exported via node_modules/chrome-devtools-frontend/mcp/mcp.js', + }, + ], + }, + ], }, }, { diff --git a/scripts/tsconfig.json b/scripts/tsconfig.json index 199f9e994..c6c5981bf 100644 --- a/scripts/tsconfig.json +++ b/scripts/tsconfig.json @@ -15,6 +15,8 @@ "noFallthroughCasesInSwitch": true, "incremental": true, "allowJs": true, + "allowImportingTsExtensions": true, + "noEmit": true, "useUnknownInCatchVariables": false }, "include": ["./**/*.ts", "./**/*.js"] From d2c1f3e62d5d9152e4f17fc4d4e0b01e74acc5ab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Nov 2025 14:08:45 +0000 Subject: [PATCH 065/456] chore(deps-dev): bump puppeteer from 24.29.1 to 24.30.0 in the bundled group (#550) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the bundled group with 1 update: [puppeteer](https://github.com/puppeteer/puppeteer). Updates `puppeteer` from 24.29.1 to 24.30.0
Release notes

Sourced from puppeteer's releases.

puppeteer-core: v24.30.0

24.30.0 (2025-11-12)

🎉 Features

🛠️ Fixes

puppeteer: v24.30.0

24.30.0 (2025-11-12)

♻️ Chores

  • puppeteer: Synchronize puppeteer versions

Dependencies

  • The following workspace dependencies were updated
    • dependencies
      • puppeteer-core bumped from 24.29.1 to 24.30.0
Changelog

Sourced from puppeteer's changelog.

24.30.0 (2025-11-12)

♻️ Chores

  • puppeteer: Synchronize puppeteer versions

Dependencies

  • The following workspace dependencies were updated
    • dependencies
      • puppeteer-core bumped from 24.29.1 to 24.30.0

🎉 Features

🛠️ Fixes

Commits
  • 1afcd16 chore: release main (#14413)
  • 1d9881c feat: roll to Firefox 145.0 (#14418)
  • be07e5d fix: roll to Chrome 142.0.7444.162 (#14415)
  • e2c071c chore: disable flaky tests (#14414)
  • 66bf224 chore(deps-dev): bump the dev-dependencies group with 8 updates (#14406)
  • b923eec fix(cdp): update request with ExtraInfo if available (#14410)
  • 3131b24 chore: remove workaround for disabling Firefox Backup (#14411)
  • 53d0a66 chore(deps): bump docker/metadata-action from 5.8.0 to 5.9.0 in the all group...
  • 804d0ce chore(deps): bump node from 22ab967 to dcf0610 in /docker in the all grou...
  • bec9244 chore: bump chromium-bidi to 11.0.0 (#14404)
  • See full diff in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=puppeteer&package-manager=npm_and_yarn&previous-version=24.29.1&new-version=24.30.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
--------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Nikolay Vitkov --- package-lock.json | 38 ++++++++++++++-------------- package.json | 2 +- tests/tools/network.test.js.snapshot | 8 ++++++ tests/utils.ts | 4 +-- 4 files changed, 30 insertions(+), 22 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4a4045fd9..b5e6fe3d7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,7 +33,7 @@ "eslint-plugin-import": "^2.32.0", "globals": "^16.4.0", "prettier": "^3.6.2", - "puppeteer": "24.29.1", + "puppeteer": "24.30.0", "rollup": "4.53.2", "rollup-plugin-cleanup": "^3.2.1", "rollup-plugin-license": "^3.6.0", @@ -2066,9 +2066,9 @@ } }, "node_modules/bare-fs": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.5.0.tgz", - "integrity": "sha512-GljgCjeupKZJNetTqxKaQArLK10vpmK28or0+RwWjEl5Rk+/xG3wkpmkv+WrcBm3q1BwHKlnhXzR8O37kcvkXQ==", + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.5.1.tgz", + "integrity": "sha512-zGUCsm3yv/ePt2PHNbVxjjn0nNB1MkIaR4wOCxJ2ig5pCf5cCVAYJXVhQg/3OhhJV6DB1ts7Hv0oUaElc2TPQg==", "dev": true, "license": "Apache-2.0", "optional": true, @@ -2306,9 +2306,9 @@ "license": "BSD-3-Clause" }, "node_modules/chromium-bidi": { - "version": "10.5.1", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-10.5.1.tgz", - "integrity": "sha512-rlj6OyhKhVTnk4aENcUme3Jl9h+cq4oXu4AzBcvr8RMmT6BR4a3zSNT9dbIfXr9/BS6ibzRyDhowuw4n2GgzsQ==", + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-11.0.0.tgz", + "integrity": "sha512-cM3DI+OOb89T3wO8cpPSro80Q9eKYJ7hGVXoGS3GkDPxnYSqiv+6xwpIf6XERyJ9Tdsl09hmNmY94BkgZdVekw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -4162,9 +4162,9 @@ } }, "node_modules/ip-address": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", - "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", + "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", "dev": true, "license": "MIT", "engines": { @@ -5472,18 +5472,18 @@ } }, "node_modules/puppeteer": { - "version": "24.29.1", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.29.1.tgz", - "integrity": "sha512-pX05JV1mMP+1N0vP3I4DOVwjMdpihv2LxQTtSfw6CUm5F0ZFLUFE/LSZ4yUWHYaM3C11Hdu+sgn7uY7teq5MYw==", + "version": "24.30.0", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.30.0.tgz", + "integrity": "sha512-A5OtCi9WpiXBQgJ2vQiZHSyrAzQmO/WDsvghqlN4kgw21PhxA5knHUaUQq/N3EMt8CcvSS0RM+kmYLJmedR3TQ==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { "@puppeteer/browsers": "2.10.13", - "chromium-bidi": "10.5.1", + "chromium-bidi": "11.0.0", "cosmiconfig": "^9.0.0", "devtools-protocol": "0.0.1521046", - "puppeteer-core": "24.29.1", + "puppeteer-core": "24.30.0", "typed-query-selector": "^2.12.0" }, "bin": { @@ -5494,14 +5494,14 @@ } }, "node_modules/puppeteer-core": { - "version": "24.29.1", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.29.1.tgz", - "integrity": "sha512-ErJ9qKCK+bdLvBa7QVSQTBSPm8KZbl1yC/WvhrZ0ut27hDf2QBzjDsn1IukzE1i1KtZ7NYGETOV4W1beoo9izA==", + "version": "24.30.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.30.0.tgz", + "integrity": "sha512-2S3Smy0t0W4wJnNvDe7W0bE7wDmZjfZ3ljfMgJd6hn2Hq/f0jgN+x9PULZo2U3fu5UUIJ+JP8cNUGllu8P91Pg==", "dev": true, "license": "Apache-2.0", "dependencies": { "@puppeteer/browsers": "2.10.13", - "chromium-bidi": "10.5.1", + "chromium-bidi": "11.0.0", "debug": "^4.4.3", "devtools-protocol": "0.0.1521046", "typed-query-selector": "^2.12.0", diff --git a/package.json b/package.json index f23168ae4..77c45019c 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "eslint-plugin-import": "^2.32.0", "globals": "^16.4.0", "prettier": "^3.6.2", - "puppeteer": "24.29.1", + "puppeteer": "24.30.0", "rollup": "4.53.2", "rollup-plugin-cleanup": "^3.2.1", "rollup-plugin-license": "^3.6.0", diff --git a/tests/tools/network.test.js.snapshot b/tests/tools/network.test.js.snapshot index 789e682d7..af1314d63 100644 --- a/tests/tools/network.test.js.snapshot +++ b/tests/tools/network.test.js.snapshot @@ -9,6 +9,14 @@ Status: [success - 200] - sec-ch-ua:"Not_A Brand";v="99", "Chromium";v="142" - sec-ch-ua-mobile:?0 - sec-ch-ua-platform:"" +- accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 +- accept-encoding:gzip, deflate, br, zstd +- connection:keep-alive +- host:localhost: +- sec-fetch-dest:document +- sec-fetch-mode:navigate +- sec-fetch-site:none +- sec-fetch-user:?1 ### Response Headers - connection:keep-alive - content-length:239 diff --git a/tests/utils.ts b/tests/utils.ts index 6dca14a71..99f82b055 100644 --- a/tests/utils.ts +++ b/tests/utils.ts @@ -158,8 +158,8 @@ export function stabilizeResponseOutput(text: unknown) { const dateRegEx = /.{3}, \d{2} .{3} \d{4} \d{2}:\d{2}:\d{2} [A-Z]{3}/g; output = output.replaceAll(dateRegEx, ''); - const localhostRegEx = /http:\/\/localhost:\d{5}\//g; - output = output.replaceAll(localhostRegEx, 'http://localhost:/'); + const localhostRegEx = /localhost:\d{5}/g; + output = output.replaceAll(localhostRegEx, 'localhost:'); const userAgentRegEx = /user-agent:.*\n/g; output = output.replaceAll(userAgentRegEx, 'user-agent:\n'); From a6490e70a6dcbd54fd9c2fc8ac9263bf0058905f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Nov 2025 15:33:51 +0100 Subject: [PATCH 066/456] chore(deps-dev): bump chrome-devtools-frontend from 1.0.1541552 to 1.0.1543472 in the bundled-devtools group (#549) Bumps the bundled-devtools group with 1 update: [chrome-devtools-frontend](https://github.com/ChromeDevTools/devtools-frontend). Updates `chrome-devtools-frontend` from 1.0.1541552 to 1.0.1543472
Commits
  • 886fa52 Update DevTools DEPS (trusted)
  • de4c9bb Use unqualified lit identifiers and ScriptLocationLink in the FrameDetails View
  • efd29ef Select one CPU profile stream per thread by source
  • cfa5963 [workspace] Don't create IgnoreListManager lazily
  • d9fde31 Render each state separately in ConsoleInsight
  • 983a558 Store the IgnoreListManager singleton on the global DevToolsContext
  • b4dee74 [docs] Move toggle_fonts and toggle_dark_mode to static page
  • 058c823 Store the WorkspaceImpl singleton on the global DevToolsContext
  • b91eddb Pass 'Workspace' to 'DebuggerWorkspaceBinding' via constructor
  • 16e5de1 [cleanup] Remove dead code in Performance panel
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=chrome-devtools-frontend&package-manager=npm_and_yarn&previous-version=1.0.1541552&new-version=1.0.1543472)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index b5e6fe3d7..0b2dc9339 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,7 +25,7 @@ "@types/yargs": "^17.0.33", "@typescript-eslint/eslint-plugin": "^8.43.0", "@typescript-eslint/parser": "^8.43.0", - "chrome-devtools-frontend": "1.0.1541552", + "chrome-devtools-frontend": "1.0.1543472", "core-js": "3.46.0", "debug": "4.4.3", "eslint": "^9.35.0", @@ -2299,9 +2299,9 @@ } }, "node_modules/chrome-devtools-frontend": { - "version": "1.0.1541552", - "resolved": "https://registry.npmjs.org/chrome-devtools-frontend/-/chrome-devtools-frontend-1.0.1541552.tgz", - "integrity": "sha512-a/tFvYOf0afjyfXGWNvH3mtPW43YY8E4w8VIflXl8l7xHQVdxGVlJEYgbfxBuXaXfWZiV1pvMxWmzknLEjRtgw==", + "version": "1.0.1543472", + "resolved": "https://registry.npmjs.org/chrome-devtools-frontend/-/chrome-devtools-frontend-1.0.1543472.tgz", + "integrity": "sha512-K5vdi1Mp3cso/GbR9gelh/ByJBsE40BhqNwGtt5QlP2W+E+UGgCXna+pko8FFaauZROZFHetpTb4CweU4zLc9Q==", "dev": true, "license": "BSD-3-Clause" }, diff --git a/package.json b/package.json index 77c45019c..458248776 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "@types/yargs": "^17.0.33", "@typescript-eslint/eslint-plugin": "^8.43.0", "@typescript-eslint/parser": "^8.43.0", - "chrome-devtools-frontend": "1.0.1541552", + "chrome-devtools-frontend": "1.0.1543472", "core-js": "3.46.0", "debug": "4.4.3", "eslint": "^9.35.0", From 5f78b51a1cb8bbffba400f8da13b291f564c1ff1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Nov 2025 17:45:19 +0100 Subject: [PATCH 067/456] chore(deps-dev): bump the dev-dependencies group with 5 updates (#548) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the dev-dependencies group with 5 updates: | Package | From | To | | --- | --- | --- | | [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `24.10.0` | `24.10.1` | | [@types/sinon](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/sinon) | `17.0.4` | `20.0.0` | | [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) | `8.46.3` | `8.46.4` | | [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) | `8.46.3` | `8.46.4` | | [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) | `8.46.3` | `8.46.4` | Updates `@types/node` from 24.10.0 to 24.10.1
Commits

Updates `@types/sinon` from 17.0.4 to 20.0.0
Commits

Updates `@typescript-eslint/eslint-plugin` from 8.46.3 to 8.46.4
Release notes

Sourced from @​typescript-eslint/eslint-plugin's releases.

v8.46.4

8.46.4 (2025-11-10)

🩹 Fixes

  • eslint-plugin: [no-deprecated] fix double-report on computed literal identifiers (#11006, #10958)
  • eslint-plugin: handle override modifier in promise-function-async fixer (#11730)
  • parser: error when both projectService and project are set (#11333)

❤️ Thank You

You can read about our versioning strategy and releases on our website.

Changelog

Sourced from @​typescript-eslint/eslint-plugin's changelog.

8.46.4 (2025-11-10)

🩹 Fixes

  • parser: error when both projectService and project are set (#11333)
  • eslint-plugin: handle override modifier in promise-function-async fixer (#11730)
  • eslint-plugin: [no-deprecated] fix double-report on computed literal identifiers (#11006, #10958)

❤️ Thank You

You can read about our versioning strategy and releases on our website.

Commits
  • 843f144 chore(release): publish 8.46.4
  • 997e0c0 fix(parser): error when both projectService and project are set (#11333)
  • 7c6944e chore: fix typos (#11744)
  • 189a7f7 fix(eslint-plugin): handle override modifier in promise-function-async fixer ...
  • c779f3c fix(eslint-plugin): [no-deprecated] fix double-report on computed literal ide...
  • ea2ee6b chore(eslint-plugin): use correct type for return type of createValidator (...
  • See full diff in compare view

Updates `@typescript-eslint/parser` from 8.46.3 to 8.46.4
Release notes

Sourced from @​typescript-eslint/parser's releases.

v8.46.4

8.46.4 (2025-11-10)

🩹 Fixes

  • eslint-plugin: [no-deprecated] fix double-report on computed literal identifiers (#11006, #10958)
  • eslint-plugin: handle override modifier in promise-function-async fixer (#11730)
  • parser: error when both projectService and project are set (#11333)

❤️ Thank You

You can read about our versioning strategy and releases on our website.

Changelog

Sourced from @​typescript-eslint/parser's changelog.

8.46.4 (2025-11-10)

This was a version bump only for parser to align it with other projects, there were no code changes.

You can read about our versioning strategy and releases on our website.

Commits

Updates `typescript-eslint` from 8.46.3 to 8.46.4
Release notes

Sourced from typescript-eslint's releases.

v8.46.4

8.46.4 (2025-11-10)

🩹 Fixes

  • eslint-plugin: [no-deprecated] fix double-report on computed literal identifiers (#11006, #10958)
  • eslint-plugin: handle override modifier in promise-function-async fixer (#11730)
  • parser: error when both projectService and project are set (#11333)

❤️ Thank You

You can read about our versioning strategy and releases on our website.

Changelog

Sourced from typescript-eslint's changelog.

8.46.4 (2025-11-10)

This was a version bump only for typescript-eslint to align it with other projects, there were no code changes.

You can read about our versioning strategy and releases on our website.

Commits

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 134 +++++++++++++++++++++++----------------------- package.json | 2 +- 2 files changed, 68 insertions(+), 68 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0b2dc9339..a8d933cdc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,7 +21,7 @@ "@types/debug": "^4.1.12", "@types/filesystem": "^0.0.36", "@types/node": "^24.3.3", - "@types/sinon": "^17.0.4", + "@types/sinon": "^20.0.0", "@types/yargs": "^17.0.33", "@typescript-eslint/eslint-plugin": "^8.43.0", "@typescript-eslint/parser": "^8.43.0", @@ -1163,9 +1163,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "24.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.0.tgz", - "integrity": "sha512-qzQZRBqkFsYyaSWXuEHc2WR9c0a0CXwiE5FWUvn7ZM+vdy1uZLfCunD38UzhuB7YN/J11ndbDBcTmOdxJo9Q7A==", + "version": "24.10.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", + "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1180,9 +1180,9 @@ "license": "MIT" }, "node_modules/@types/sinon": { - "version": "17.0.4", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-17.0.4.tgz", - "integrity": "sha512-RHnIrhfPO3+tJT0s7cFaXGZvsL4bbR3/k7z3P312qMS4JaS2Tk+KiwiLx1S0rQ56ERj00u1/BtdyVd0FY+Pdew==", + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-20.0.0.tgz", + "integrity": "sha512-etYGUC6IEevDGSWvR9WrECRA01ucR2/Oi9XMBUAdV0g4bLkNf4HlZWGiGlDOq5lgwXRwcV+PSeKgFcW4QzzYOg==", "dev": true, "license": "MIT", "dependencies": { @@ -1225,17 +1225,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.46.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.3.tgz", - "integrity": "sha512-sbaQ27XBUopBkRiuY/P9sWGOWUW4rl8fDoHIUmLpZd8uldsTyB4/Zg6bWTegPoTLnKj9Hqgn3QD6cjPNB32Odw==", + "version": "8.46.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.4.tgz", + "integrity": "sha512-R48VhmTJqplNyDxCyqqVkFSZIx1qX6PzwqgcXn1olLrzxcSBDlOsbtcnQuQhNtnNiJ4Xe5gREI1foajYaYU2Vg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.46.3", - "@typescript-eslint/type-utils": "8.46.3", - "@typescript-eslint/utils": "8.46.3", - "@typescript-eslint/visitor-keys": "8.46.3", + "@typescript-eslint/scope-manager": "8.46.4", + "@typescript-eslint/type-utils": "8.46.4", + "@typescript-eslint/utils": "8.46.4", + "@typescript-eslint/visitor-keys": "8.46.4", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -1255,16 +1255,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.46.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.46.3.tgz", - "integrity": "sha512-6m1I5RmHBGTnUGS113G04DMu3CpSdxCAU/UvtjNWL4Nuf3MW9tQhiJqRlHzChIkhy6kZSAQmc+I1bcGjE3yNKg==", + "version": "8.46.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.46.4.tgz", + "integrity": "sha512-tK3GPFWbirvNgsNKto+UmB/cRtn6TZfyw0D6IKrW55n6Vbs7KJoZtI//kpTKzE/DUmmnAFD8/Ca46s7Obs92/w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.46.3", - "@typescript-eslint/types": "8.46.3", - "@typescript-eslint/typescript-estree": "8.46.3", - "@typescript-eslint/visitor-keys": "8.46.3", + "@typescript-eslint/scope-manager": "8.46.4", + "@typescript-eslint/types": "8.46.4", + "@typescript-eslint/typescript-estree": "8.46.4", + "@typescript-eslint/visitor-keys": "8.46.4", "debug": "^4.3.4" }, "engines": { @@ -1280,14 +1280,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.46.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.46.3.tgz", - "integrity": "sha512-Fz8yFXsp2wDFeUElO88S9n4w1I4CWDTXDqDr9gYvZgUpwXQqmZBr9+NTTql5R3J7+hrJZPdpiWaB9VNhAKYLuQ==", + "version": "8.46.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.46.4.tgz", + "integrity": "sha512-nPiRSKuvtTN+no/2N1kt2tUh/HoFzeEgOm9fQ6XQk4/ApGqjx0zFIIaLJ6wooR1HIoozvj2j6vTi/1fgAz7UYQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.46.3", - "@typescript-eslint/types": "^8.46.3", + "@typescript-eslint/tsconfig-utils": "^8.46.4", + "@typescript-eslint/types": "^8.46.4", "debug": "^4.3.4" }, "engines": { @@ -1302,14 +1302,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.46.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.46.3.tgz", - "integrity": "sha512-FCi7Y1zgrmxp3DfWfr+3m9ansUUFoy8dkEdeQSgA9gbm8DaHYvZCdkFRQrtKiedFf3Ha6VmoqoAaP68+i+22kg==", + "version": "8.46.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.46.4.tgz", + "integrity": "sha512-tMDbLGXb1wC+McN1M6QeDx7P7c0UWO5z9CXqp7J8E+xGcJuUuevWKxuG8j41FoweS3+L41SkyKKkia16jpX7CA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.46.3", - "@typescript-eslint/visitor-keys": "8.46.3" + "@typescript-eslint/types": "8.46.4", + "@typescript-eslint/visitor-keys": "8.46.4" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1320,9 +1320,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.46.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.46.3.tgz", - "integrity": "sha512-GLupljMniHNIROP0zE7nCcybptolcH8QZfXOpCfhQDAdwJ/ZTlcaBOYebSOZotpti/3HrHSw7D3PZm75gYFsOA==", + "version": "8.46.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.46.4.tgz", + "integrity": "sha512-+/XqaZPIAk6Cjg7NWgSGe27X4zMGqrFqZ8atJsX3CWxH/jACqWnrWI68h7nHQld0y+k9eTTjb9r+KU4twLoo9A==", "dev": true, "license": "MIT", "engines": { @@ -1337,15 +1337,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.46.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.46.3.tgz", - "integrity": "sha512-ZPCADbr+qfz3aiTTYNNkCbUt+cjNwI/5McyANNrFBpVxPt7GqpEYz5ZfdwuFyGUnJ9FdDXbGODUu6iRCI6XRXw==", + "version": "8.46.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.46.4.tgz", + "integrity": "sha512-V4QC8h3fdT5Wro6vANk6eojqfbv5bpwHuMsBcJUJkqs2z5XnYhJzyz9Y02eUmF9u3PgXEUiOt4w4KHR3P+z0PQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.46.3", - "@typescript-eslint/typescript-estree": "8.46.3", - "@typescript-eslint/utils": "8.46.3", + "@typescript-eslint/types": "8.46.4", + "@typescript-eslint/typescript-estree": "8.46.4", + "@typescript-eslint/utils": "8.46.4", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -1362,9 +1362,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.46.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.46.3.tgz", - "integrity": "sha512-G7Ok9WN/ggW7e/tOf8TQYMaxgID3Iujn231hfi0Pc7ZheztIJVpO44ekY00b7akqc6nZcvregk0Jpah3kep6hA==", + "version": "8.46.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.46.4.tgz", + "integrity": "sha512-USjyxm3gQEePdUwJBFjjGNG18xY9A2grDVGuk7/9AkjIF1L+ZrVnwR5VAU5JXtUnBL/Nwt3H31KlRDaksnM7/w==", "dev": true, "license": "MIT", "engines": { @@ -1376,16 +1376,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.46.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.46.3.tgz", - "integrity": "sha512-f/NvtRjOm80BtNM5OQtlaBdM5BRFUv7gf381j9wygDNL+qOYSNOgtQ/DCndiYi80iIOv76QqaTmp4fa9hwI0OA==", + "version": "8.46.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.46.4.tgz", + "integrity": "sha512-7oV2qEOr1d4NWNmpXLR35LvCfOkTNymY9oyW+lUHkmCno7aOmIf/hMaydnJBUTBMRCOGZh8YjkFOc8dadEoNGA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.46.3", - "@typescript-eslint/tsconfig-utils": "8.46.3", - "@typescript-eslint/types": "8.46.3", - "@typescript-eslint/visitor-keys": "8.46.3", + "@typescript-eslint/project-service": "8.46.4", + "@typescript-eslint/tsconfig-utils": "8.46.4", + "@typescript-eslint/types": "8.46.4", + "@typescript-eslint/visitor-keys": "8.46.4", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -1405,16 +1405,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.46.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.46.3.tgz", - "integrity": "sha512-VXw7qmdkucEx9WkmR3ld/u6VhRyKeiF1uxWwCy/iuNfokjJ7VhsgLSOTjsol8BunSw190zABzpwdNsze2Kpo4g==", + "version": "8.46.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.46.4.tgz", + "integrity": "sha512-AbSv11fklGXV6T28dp2Me04Uw90R2iJ30g2bgLz529Koehrmkbs1r7paFqr1vPCZi7hHwYxYtxfyQMRC8QaVSg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.46.3", - "@typescript-eslint/types": "8.46.3", - "@typescript-eslint/typescript-estree": "8.46.3" + "@typescript-eslint/scope-manager": "8.46.4", + "@typescript-eslint/types": "8.46.4", + "@typescript-eslint/typescript-estree": "8.46.4" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1429,13 +1429,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.46.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.46.3.tgz", - "integrity": "sha512-uk574k8IU0rOF/AjniX8qbLSGURJVUCeM5e4MIMKBFFi8weeiLrG1fyQejyLXQpRZbU/1BuQasleV/RfHC3hHg==", + "version": "8.46.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.46.4.tgz", + "integrity": "sha512-/++5CYLQqsO9HFGLI7APrxBJYo+5OCMpViuhV8q5/Qa3o5mMrF//eQHks+PXcsAVaLdn817fMuS7zqoXNNZGaw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.46.3", + "@typescript-eslint/types": "8.46.4", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -6728,16 +6728,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.46.3", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.46.3.tgz", - "integrity": "sha512-bAfgMavTuGo+8n6/QQDVQz4tZ4f7Soqg53RbrlZQEoAltYop/XR4RAts/I0BrO3TTClTSTFJ0wYbla+P8cEWJA==", + "version": "8.46.4", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.46.4.tgz", + "integrity": "sha512-KALyxkpYV5Ix7UhvjTwJXZv76VWsHG+NjNlt/z+a17SOQSiOcBdUXdbJdyXi7RPxrBFECtFOiPwUJQusJuCqrg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.46.3", - "@typescript-eslint/parser": "8.46.3", - "@typescript-eslint/typescript-estree": "8.46.3", - "@typescript-eslint/utils": "8.46.3" + "@typescript-eslint/eslint-plugin": "8.46.4", + "@typescript-eslint/parser": "8.46.4", + "@typescript-eslint/typescript-estree": "8.46.4", + "@typescript-eslint/utils": "8.46.4" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" diff --git a/package.json b/package.json index 458248776..a304668d7 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "@types/debug": "^4.1.12", "@types/filesystem": "^0.0.36", "@types/node": "^24.3.3", - "@types/sinon": "^17.0.4", + "@types/sinon": "^20.0.0", "@types/yargs": "^17.0.33", "@typescript-eslint/eslint-plugin": "^8.43.0", "@typescript-eslint/parser": "^8.43.0", From 53322defab7bb6c99a0ae947d659a2e580764597 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Nov 2025 08:58:01 +0100 Subject: [PATCH 068/456] chore(deps-dev): bump js-yaml from 4.1.0 to 4.1.1 (#558) Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 4.1.0 to 4.1.1.
Changelog

Sourced from js-yaml's changelog.

[4.1.1] - 2025-11-12

Security

  • Fix prototype pollution issue in yaml merge (<<) operator.
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=js-yaml&package-manager=npm_and_yarn&previous-version=4.1.0&new-version=4.1.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/ChromeDevTools/chrome-devtools-mcp/network/alerts).
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index a8d933cdc..15f8a430a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4671,9 +4671,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, "license": "MIT", "dependencies": { From 18fdf79e41af1372ef8f009957ef872a9bbba0b4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Nov 2025 08:01:32 +0000 Subject: [PATCH 069/456] chore(deps-dev): bump @types/yargs from 17.0.34 to 17.0.35 in the dev-dependencies group (#554) Bumps the dev-dependencies group with 1 update: [@types/yargs](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/yargs). Updates `@types/yargs` from 17.0.34 to 17.0.35
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=@types/yargs&package-manager=npm_and_yarn&previous-version=17.0.34&new-version=17.0.35)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 15f8a430a..a1f177a8f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1197,9 +1197,9 @@ "license": "MIT" }, "node_modules/@types/yargs": { - "version": "17.0.34", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.34.tgz", - "integrity": "sha512-KExbHVa92aJpw9WDQvzBaGVE2/Pz+pLZQloT2hjL8IqsZnV62rlPOYvNnLmf/L2dyllfVUOVBj64M0z/46eR2A==", + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", "dev": true, "license": "MIT", "dependencies": { From c42d81a4101e64fc4f2d6dce5d06470829f8ea80 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Nov 2025 08:06:51 +0000 Subject: [PATCH 070/456] chore(deps-dev): bump chrome-devtools-frontend from 1.0.1543472 to 1.0.1544076 in the bundled-devtools group (#555) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [//]: # (dependabot-start) ⚠️ **Dependabot is rebasing this PR** ⚠️ Rebasing might not happen immediately, so don't worry if this takes some time. Note: if you make any changes to this PR yourself, they will take precedence over the rebase. --- [//]: # (dependabot-end) Bumps the bundled-devtools group with 1 update: [chrome-devtools-frontend](https://github.com/ChromeDevTools/devtools-frontend). Updates `chrome-devtools-frontend` from 1.0.1543472 to 1.0.1544076
Commits
  • b83ad60 [sdk] Run RuntimeModel.test.ts in Node.js as well
  • b27cf10 Update DevTools DEPS (trusted)
  • ff42b23 Migrate Common.Linkifier client to the direct usage of DOMLinkifier
  • d01fd3c [cleanup] Remove imperative x-link generation
  • dd28425 Replace all non-printable characters with space, as these break cmd.exe comma...
  • 3a66d52 RPP: Hide Sources' Debugger sidebar in trace_app
  • 21dbf0a Display all @​font-* rules reported by backend
  • 04cca18 [cleanup] Some unused code around AI Assistance
  • c21b7de Add support for abort signal in Ai Code Generation using AIDA
  • baf3036 [sdk] Run TargetManager.test.ts in Node.js as well
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=chrome-devtools-frontend&package-manager=npm_and_yarn&previous-version=1.0.1543472&new-version=1.0.1544076)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index a1f177a8f..18f9d2349 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,7 +25,7 @@ "@types/yargs": "^17.0.33", "@typescript-eslint/eslint-plugin": "^8.43.0", "@typescript-eslint/parser": "^8.43.0", - "chrome-devtools-frontend": "1.0.1543472", + "chrome-devtools-frontend": "1.0.1544076", "core-js": "3.46.0", "debug": "4.4.3", "eslint": "^9.35.0", @@ -2299,9 +2299,9 @@ } }, "node_modules/chrome-devtools-frontend": { - "version": "1.0.1543472", - "resolved": "https://registry.npmjs.org/chrome-devtools-frontend/-/chrome-devtools-frontend-1.0.1543472.tgz", - "integrity": "sha512-K5vdi1Mp3cso/GbR9gelh/ByJBsE40BhqNwGtt5QlP2W+E+UGgCXna+pko8FFaauZROZFHetpTb4CweU4zLc9Q==", + "version": "1.0.1544076", + "resolved": "https://registry.npmjs.org/chrome-devtools-frontend/-/chrome-devtools-frontend-1.0.1544076.tgz", + "integrity": "sha512-sEhEZDPD3oqfY1MmpIhoZyoay4wPG9eq030Uv3B+faBshRpFj1ef5AjZv1yEtuv/OWDGZCWEmGRXJ+/K0mV3Cw==", "dev": true, "license": "BSD-3-Clause" }, diff --git a/package.json b/package.json index a304668d7..2c27fe34b 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "@types/yargs": "^17.0.33", "@typescript-eslint/eslint-plugin": "^8.43.0", "@typescript-eslint/parser": "^8.43.0", - "chrome-devtools-frontend": "1.0.1543472", + "chrome-devtools-frontend": "1.0.1544076", "core-js": "3.46.0", "debug": "4.4.3", "eslint": "^9.35.0", From c1e41189923e65063d3bbf1866ae89f19a9998fb Mon Sep 17 00:00:00 2001 From: Natasha Gorshunova <47688881+nattallius@users.noreply.github.com> Date: Mon, 17 Nov 2025 10:38:46 +0100 Subject: [PATCH 071/456] feat: report console issues in list_console_messages (#505) Co-authored-by: Natallia Harshunova Co-authored-by: Alex Rudenko --- scripts/post-build.ts | 11 ++- src/DevtoolsUtils.ts | 18 ++++ src/McpContext.ts | 19 +++-- src/McpResponse.ts | 92 +++++++++++++++------ src/PageCollector.ts | 99 ++++++++++++++++++++-- src/formatters/consoleFormatter.ts | 5 ++ src/issue-descriptions.ts | 49 +++++++++++ src/main.ts | 2 + src/tools/console.ts | 6 +- tests/PageCollector.test.ts | 128 +++++++++++++++++++++++++++-- tests/tools/console.test.ts | 29 ++++++- tsconfig.json | 5 +- 12 files changed, 413 insertions(+), 50 deletions(-) create mode 100644 src/issue-descriptions.ts diff --git a/scripts/post-build.ts b/scripts/post-build.ts index 5fa66d5b1..b520d6a07 100644 --- a/scripts/post-build.ts +++ b/scripts/post-build.ts @@ -63,7 +63,7 @@ export const i18n = { return str; }, lockedLazyString: () => {}, - getLazilyComputedLocalizedString: () => {}, + getLazilyComputedLocalizedString: () => ()=>{}, }; // TODO(jacktfranklin): once the DocumentLatency insight does not depend on @@ -169,6 +169,15 @@ export const hostConfig = {}; fs.copyFileSync(devtoolsLicenseFileSource, devtoolsLicenseFileDestination); copyThirdPartyLicenseFiles(); + copyDevToolsDescriptionFiles(); +} + +function copyDevToolsDescriptionFiles() { + const devtoolsIssuesDescriptionPath = + 'node_modules/chrome-devtools-frontend/front_end/models/issues_manager/descriptions'; + const sourceDir = path.join(process.cwd(), devtoolsIssuesDescriptionPath); + const destDir = path.join(BUILD_DIR, devtoolsIssuesDescriptionPath); + fs.cpSync(sourceDir, destDir, {recursive: true}); } main(); diff --git a/src/DevtoolsUtils.ts b/src/DevtoolsUtils.ts index 8b12b3a91..27067ec0b 100644 --- a/src/DevtoolsUtils.ts +++ b/src/DevtoolsUtils.ts @@ -3,6 +3,13 @@ * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ + +import { + type Issue, + type IssuesManagerEventTypes, + Common, +} from '../node_modules/chrome-devtools-frontend/mcp/mcp.js'; + export function extractUrlLikeFromDevToolsTitle( title: string, ): string | undefined { @@ -49,3 +56,14 @@ function normalizeUrl(url: string): string { return result; } + +/** + * A mock implementation of an issues manager that only implements the methods + * that are actually used by the IssuesAggregator + */ +export class FakeIssuesManager extends Common.ObjectWrapper + .ObjectWrapper { + issues(): Issue[] { + return []; + } +} diff --git a/src/McpContext.ts b/src/McpContext.ts index 5853b7181..76b70515b 100644 --- a/src/McpContext.ts +++ b/src/McpContext.ts @@ -7,9 +7,11 @@ import fs from 'node:fs/promises'; import os from 'node:os'; import path from 'node:path'; +import {type AggregatedIssue} from '../node_modules/chrome-devtools-frontend/mcp/mcp.js'; + import {extractUrlLikeFromDevToolsTitle, urlsEqual} from './DevtoolsUtils.js'; import type {ListenerMap} from './PageCollector.js'; -import {NetworkCollector, PageCollector} from './PageCollector.js'; +import {NetworkCollector, ConsoleCollector} from './PageCollector.js'; import {Locator} from './third_party/index.js'; import type { Browser, @@ -92,7 +94,7 @@ export class McpContext implements Context { // The most recent snapshot. #textSnapshot: TextSnapshot | null = null; #networkCollector: NetworkCollector; - #consoleCollector: PageCollector; + #consoleCollector: ConsoleCollector; #isRunningTrace = false; #networkConditionsMap = new WeakMap(); @@ -122,7 +124,7 @@ export class McpContext implements Context { this.#options.experimentalIncludeAllPages, ); - this.#consoleCollector = new PageCollector( + this.#consoleCollector = new ConsoleCollector( this.browser, collect => { return { @@ -138,6 +140,9 @@ export class McpContext implements Context { collect(error); } }, + issue: event => { + collect(event); + }, } as ListenerMap; }, this.#options.experimentalIncludeAllPages, @@ -205,16 +210,18 @@ export class McpContext implements Context { getConsoleData( includePreservedMessages?: boolean, - ): Array { + ): Array { const page = this.getSelectedPage(); return this.#consoleCollector.getData(page, includePreservedMessages); } - getConsoleMessageStableId(message: ConsoleMessage | Error): number { + getConsoleMessageStableId( + message: ConsoleMessage | Error | AggregatedIssue, + ): number { return this.#consoleCollector.getIdForResource(message); } - getConsoleMessageById(id: number): ConsoleMessage | Error { + getConsoleMessageById(id: number): ConsoleMessage | Error | AggregatedIssue { return this.#consoleCollector.getById(this.getSelectedPage(), id); } diff --git a/src/McpResponse.ts b/src/McpResponse.ts index da3d01dfb..737456d33 100644 --- a/src/McpResponse.ts +++ b/src/McpResponse.ts @@ -3,6 +3,12 @@ * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ +import { + AggregatedIssue, + Marked, + findTitleFromMarkdownAst, +} from '../node_modules/chrome-devtools-frontend/mcp/mcp.js'; + import type {ConsoleMessageData} from './formatters/consoleFormatter.js'; import { formatConsoleEventShort, @@ -16,6 +22,8 @@ import { getStatusFromRequest, } from './formatters/networkFormatter.js'; import {formatSnapshotNode} from './formatters/snapshotFormatter.js'; +import {getIssueDescription} from './issue-descriptions.js'; +import {logger} from './logger.js'; import type {McpContext} from './McpContext.js'; import type { ConsoleMessage, @@ -269,40 +277,70 @@ export class McpResponse implements Response { if ('type' in message) { return normalizedTypes.has(message.type()); } + if (message instanceof AggregatedIssue) { + return normalizedTypes.has('issue'); + } return normalizedTypes.has('error'); }); } - consoleListData = await Promise.all( - messages.map(async (item): Promise => { - const consoleMessageStableId = - context.getConsoleMessageStableId(item); - if ('args' in item) { - const consoleMessage = item as ConsoleMessage; + consoleListData = ( + await Promise.all( + messages.map(async (item): Promise => { + const consoleMessageStableId = + context.getConsoleMessageStableId(item); + if ('args' in item) { + const consoleMessage = item as ConsoleMessage; + return { + consoleMessageStableId, + type: consoleMessage.type(), + message: consoleMessage.text(), + args: await Promise.all( + consoleMessage.args().map(async arg => { + const stringArg = await arg.jsonValue().catch(() => { + // Ignore errors. + }); + return typeof stringArg === 'object' + ? JSON.stringify(stringArg) + : String(stringArg); + }), + ), + }; + } + if (item instanceof AggregatedIssue) { + const count = item.getAggregatedIssuesCount(); + const filename = item.getDescription()?.file; + const rawMarkdown = filename + ? getIssueDescription(filename) + : null; + if (!rawMarkdown) { + logger(`no markdown ${filename} found for issue:` + item.code); + return null; + } + const markdownAst = Marked.Marked.lexer(rawMarkdown); + const title = findTitleFromMarkdownAst(markdownAst); + if (!title) { + logger('cannot read issue title from ' + filename); + return null; + } + return { + consoleMessageStableId, + type: 'issue', + item, + message: title, + count, + args: [], + }; + } return { consoleMessageStableId, - type: consoleMessage.type(), - message: consoleMessage.text(), - args: await Promise.all( - consoleMessage.args().map(async arg => { - const stringArg = await arg.jsonValue().catch(() => { - // Ignore errors. - }); - return typeof stringArg === 'object' - ? JSON.stringify(stringArg) - : String(stringArg); - }), - ), + type: 'error', + message: (item as Error).message, + args: [], }; - } - return { - consoleMessageStableId, - type: 'error', - message: (item as Error).message, - args: [], - }; - }), - ); + }), + ) + ).filter(item => item !== null); } return this.format(toolName, context, { diff --git a/src/PageCollector.ts b/src/PageCollector.ts index 56d29c519..1212f996f 100644 --- a/src/PageCollector.ts +++ b/src/PageCollector.ts @@ -4,15 +4,30 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { + type AggregatedIssue, + IssueAggregatorEvents, + IssuesManagerEvents, + createIssuesFromProtocolIssue, + IssueAggregator, +} from '../node_modules/chrome-devtools-frontend/mcp/mcp.js'; + +import {FakeIssuesManager} from './DevtoolsUtils.js'; +import {logger} from './logger.js'; +import type {ConsoleMessage, Protocol} from './third_party/index.js'; import { type Browser, type Frame, type Handler, type HTTPRequest, type Page, - type PageEvents, + type PageEvents as PuppeteerPageEvents, } from './third_party/index.js'; +interface PageEvents extends PuppeteerPageEvents { + issue: AggregatedIssue; +} + export type ListenerMap = { [K in keyof EventMap]?: (event: EventMap[K]) => void; }; @@ -61,7 +76,7 @@ export class PageCollector { async init() { const pages = await this.#browser.pages(this.#includeAllPages); for (const page of pages) { - this.#initializePage(page); + this.addPage(page); } this.#browser.on('targetcreated', async target => { @@ -69,14 +84,14 @@ export class PageCollector { if (!page) { return; } - this.#initializePage(page); + this.addPage(page); }); this.#browser.on('targetdestroyed', async target => { const page = await target.page(); if (!page) { return; } - this.#cleanupPageDestroyed(page); + this.cleanupPageDestroyed(page); }); } @@ -88,7 +103,6 @@ export class PageCollector { if (this.storage.has(page)) { return; } - const idGenerator = createIdGenerator(); const storedLists: Array>> = [[]]; this.storage.set(page, storedLists); @@ -126,7 +140,7 @@ export class PageCollector { navigations.splice(this.#maxNavigationSaved); } - #cleanupPageDestroyed(page: Page) { + protected cleanupPageDestroyed(page: Page) { const listeners = this.#listeners.get(page); if (listeners) { for (const [name, listener] of Object.entries(listeners)) { @@ -147,7 +161,6 @@ export class PageCollector { } const data: T[] = []; - for (let index = this.#maxNavigationSaved; index >= 0; index--) { if (navigations[index]) { data.push(...navigations[index]); @@ -194,6 +207,78 @@ export class PageCollector { } } +export class ConsoleCollector extends PageCollector< + ConsoleMessage | Error | AggregatedIssue +> { + #seenIssueKeys = new WeakMap>(); + #issuesAggregators = new WeakMap(); + #mockIssuesManagers = new WeakMap(); + + override addPage(page: Page): void { + super.addPage(page); + void this.subscribeForIssues(page); + } + async subscribeForIssues(page: Page) { + if (this.#seenIssueKeys.has(page)) return; + + this.#seenIssueKeys.set(page, new Set()); + const mockManager = new FakeIssuesManager(); + const aggregator = new IssueAggregator(mockManager); + this.#mockIssuesManagers.set(page, mockManager); + this.#issuesAggregators.set(page, aggregator); + + aggregator.addEventListener( + IssueAggregatorEvents.AGGREGATED_ISSUE_UPDATED, + event => { + const withId = event.data as WithSymbolId; + // Emit aggregated issue only if it's a new one + if (withId[stableIdSymbol]) { + return; + } + page.emit('issue', event.data); + }, + ); + try { + const session = await page.createCDPSession(); + session.on('Audits.issueAdded', data => { + const inspectorIssue = + data.issue satisfies Protocol.Audits.InspectorIssue; + // @ts-expect-error Types of protocol from Puppeteer and CDP are incomparable for InspectorIssueCode, one is union, other is enum + const issue = createIssuesFromProtocolIssue(null, inspectorIssue)[0]; + if (!issue) { + logger('No issue mapping for for the issue: ', inspectorIssue.code); + return; + } + + const seenKeys = this.#seenIssueKeys.get(page)!; + const primaryKey = issue.primaryKey(); + if (seenKeys.has(primaryKey)) return; + seenKeys.add(primaryKey); + + const mockManager = this.#mockIssuesManagers.get(page); + if (!mockManager) return; + + mockManager.dispatchEventToListeners(IssuesManagerEvents.ISSUE_ADDED, { + issue, + // @ts-expect-error We don't care that issues model is null + issuesModel: null, + }); + }); + + await session.send('Audits.enable'); + } catch (e) { + logger('Error subscribing to issues', e); + } + } + + override cleanupPageDestroyed(page: Page) { + super.cleanupPageDestroyed(page); + this.#seenIssueKeys.delete(page); + this.#issuesAggregators.delete(page); + this.#mockIssuesManagers.delete(page); + } +} + export class NetworkCollector extends PageCollector { constructor( browser: Browser, diff --git a/src/formatters/consoleFormatter.ts b/src/formatters/consoleFormatter.ts index d80804560..e4eca9b1c 100644 --- a/src/formatters/consoleFormatter.ts +++ b/src/formatters/consoleFormatter.ts @@ -7,12 +7,17 @@ export interface ConsoleMessageData { consoleMessageStableId: number; type?: string; + item?: unknown; message?: string; + count?: number; args?: string[]; } // The short format for a console message, based on a previous format. export function formatConsoleEventShort(msg: ConsoleMessageData): string { + if (msg.type === 'issue') { + return `msgid=${msg.consoleMessageStableId} [${msg.type}] ${msg.message} (count: ${msg.count})`; + } return `msgid=${msg.consoleMessageStableId} [${msg.type}] ${msg.message} (${msg.args?.length ?? 0} args)`; } diff --git a/src/issue-descriptions.ts b/src/issue-descriptions.ts new file mode 100644 index 000000000..21485e4b1 --- /dev/null +++ b/src/issue-descriptions.ts @@ -0,0 +1,49 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as fs from 'node:fs'; +import * as path from 'node:path'; + +const DESCRIPTIONS_PATH = path.join( + import.meta.dirname, + '../node_modules/chrome-devtools-frontend/front_end/models/issues_manager/descriptions', +); + +let issueDescriptions: Record = {}; + +/** + * Reads all issue descriptions from the filesystem into memory. + */ +export async function loadIssueDescriptions(): Promise { + if (Object.keys(issueDescriptions).length > 0) { + return; + } + + const files = await fs.promises.readdir(DESCRIPTIONS_PATH); + const descriptions: Record = {}; + + for (const file of files) { + if (!file.endsWith('.md')) { + continue; + } + const content = await fs.promises.readFile( + path.join(DESCRIPTIONS_PATH, file), + 'utf-8', + ); + descriptions[file] = content; + } + + issueDescriptions = descriptions; +} + +/** + * Gets an issue description from the in-memory cache. + * @param fileName The file name of the issue description. + * @returns The description of the issue, or null if it doesn't exist. + */ +export function getIssueDescription(fileName: string): string | null { + return issueDescriptions[fileName] ?? null; +} diff --git a/src/main.ts b/src/main.ts index 80db16b66..779568764 100644 --- a/src/main.ts +++ b/src/main.ts @@ -9,6 +9,7 @@ import './polyfill.js'; import type {Channel} from './browser.js'; import {ensureBrowserConnected, ensureBrowserLaunched} from './browser.js'; import {parseArguments} from './cli.js'; +import {loadIssueDescriptions} from './issue-descriptions.js'; import {logger, saveLogsToFile} from './logger.js'; import {McpContext} from './McpContext.js'; import {McpResponse} from './McpResponse.js'; @@ -189,6 +190,7 @@ for (const tool of tools) { registerTool(tool); } +await loadIssueDescriptions(); const transport = new StdioServerTransport(); await server.connect(transport); logger('Chrome DevTools MCP Server connected'); diff --git a/src/tools/console.ts b/src/tools/console.ts index c45571e08..ee6720183 100644 --- a/src/tools/console.ts +++ b/src/tools/console.ts @@ -9,10 +9,11 @@ import type {ConsoleMessageType} from '../third_party/index.js'; import {ToolCategory} from './categories.js'; import {defineTool} from './ToolDefinition.js'; +type ConsoleResponseType = ConsoleMessageType | 'issue'; const FILTERABLE_MESSAGE_TYPES: readonly [ - ConsoleMessageType, - ...ConsoleMessageType[], + ConsoleResponseType, + ...ConsoleResponseType[], ] = [ 'log', 'debug', @@ -33,6 +34,7 @@ const FILTERABLE_MESSAGE_TYPES: readonly [ 'count', 'timeEnd', 'verbose', + 'issue', ]; export const listConsoleMessages = defineTool({ diff --git a/tests/PageCollector.test.ts b/tests/PageCollector.test.ts index 9cd7c4230..741712ef8 100644 --- a/tests/PageCollector.test.ts +++ b/tests/PageCollector.test.ts @@ -4,12 +4,26 @@ * SPDX-License-Identifier: Apache-2.0 */ import assert from 'node:assert'; -import {describe, it} from 'node:test'; - -import type {Browser, Frame, HTTPRequest, Page, Target} from 'puppeteer-core'; - +import {beforeEach, describe, it} from 'node:test'; + +import type { + Browser, + Frame, + HTTPRequest, + Page, + Target, + CDPSession, + Protocol, +} from 'puppeteer-core'; +import sinon from 'sinon'; + +import {AggregatedIssue} from '../node_modules/chrome-devtools-frontend/mcp/mcp.js'; import type {ListenerMap} from '../src/PageCollector.js'; -import {NetworkCollector, PageCollector} from '../src/PageCollector.js'; +import { + ConsoleCollector, + NetworkCollector, + PageCollector, +} from '../src/PageCollector.js'; import {getMockRequest} from './utils.js'; @@ -36,10 +50,19 @@ function mockListener() { function getMockPage(): Page { const mainFrame = {} as Frame; + const cdpSession = { + ...mockListener(), + send: () => { + // no-op + }, + }; return { mainFrame() { return mainFrame; }, + createCDPSession() { + return Promise.resolve(cdpSession as unknown as CDPSession); + }, ...mockListener(), } as Page; } @@ -319,3 +342,98 @@ describe('NetworkCollector', () => { assert.equal(collector.getData(page, true).length, 3); }); }); + +describe('ConsoleCollector', () => { + let issue: Protocol.Audits.InspectorIssue; + + beforeEach(() => { + issue = { + code: 'MixedContentIssue', + details: { + mixedContentIssueDetails: { + insecureURL: 'test.url', + resolutionStatus: 'MixedContentBlocked', + mainResourceURL: '', + }, + }, + }; + }); + + it('emits issues on page', async () => { + const browser = getMockBrowser(); + const page = (await browser.pages())[0]; + const cdpSession = await page.createCDPSession(); + const onIssuesListener = sinon.spy(); + + page.on('issue', onIssuesListener); + + const collector = new ConsoleCollector(browser, collect => { + return { + issue: issue => { + collect(issue as AggregatedIssue); + }, + } as ListenerMap; + }); + await collector.init(); + cdpSession.emit('Audits.issueAdded', {issue}); + sinon.assert.calledOnce(onIssuesListener); + + const issueArgument = onIssuesListener.getCall(0).args[0]; + assert(issueArgument instanceof AggregatedIssue); + }); + + it('collects issues', async () => { + const browser = getMockBrowser(); + const page = (await browser.pages())[0]; + const cdpSession = await page.createCDPSession(); + + const collector = new ConsoleCollector(browser, collect => { + return { + issue: issue => { + collect(issue as AggregatedIssue); + }, + } as ListenerMap; + }); + await collector.init(); + + const issue2 = { + code: 'ElementAccessibilityIssue' as const, + details: { + elementAccessibilityIssueDetails: { + nodeId: 1, + elementAccessibilityIssueReason: 'DisallowedSelectChild', + hasDisallowedAttributes: true, + }, + }, + } satisfies Protocol.Audits.InspectorIssue; + + cdpSession.emit('Audits.issueAdded', {issue}); + cdpSession.emit('Audits.issueAdded', {issue: issue2}); + const data = collector.getData(page); + assert.equal(data.length, 2); + }); + + it('filters duplicated issues', async () => { + const browser = getMockBrowser(); + const page = (await browser.pages())[0]; + const cdpSession = await page.createCDPSession(); + + const collector = new ConsoleCollector(browser, collect => { + return { + issue: issue => { + collect(issue as AggregatedIssue); + }, + } as ListenerMap; + }); + await collector.init(); + + cdpSession.emit('Audits.issueAdded', {issue}); + cdpSession.emit('Audits.issueAdded', {issue}); + const data = collector.getData(page); + assert.equal(data.length, 1); + const collectedIssue = data[0]; + assert(collectedIssue instanceof AggregatedIssue); + assert.equal(collectedIssue.code(), 'MixedContentIssue'); + assert.equal(collectedIssue.getAggregatedIssuesCount(), 1); + }); +}); diff --git a/tests/tools/console.test.ts b/tests/tools/console.test.ts index e94713e02..5a6f7b8ca 100644 --- a/tests/tools/console.test.ts +++ b/tests/tools/console.test.ts @@ -4,8 +4,9 @@ * SPDX-License-Identifier: Apache-2.0 */ import assert from 'node:assert'; -import {describe, it} from 'node:test'; +import {before, describe, it} from 'node:test'; +import {loadIssueDescriptions} from '../../src/issue-descriptions.js'; import { getConsoleMessage, listConsoleMessages, @@ -14,6 +15,10 @@ import {withBrowser} from '../utils.js'; describe('console', () => { describe('list_console_messages', () => { + before(async () => { + await loadIssueDescriptions(); + }); + it('list messages', async () => { await withBrowser(async (response, context) => { await listConsoleMessages.handler({params: {}}, response, context); @@ -36,6 +41,28 @@ describe('console', () => { }); }); + it('lists issues messages', async () => { + await withBrowser(async (response, context) => { + const page = await context.newPage(); + const issuePromise = new Promise(resolve => { + page.on('issue', () => { + resolve(); + }); + }); + + await page.setContent(''); + await issuePromise; + await listConsoleMessages.handler({params: {}}, response, context); + const formattedResponse = await response.handle('test', context); + const textContent = formattedResponse[0] as {text: string}; + assert.ok( + textContent.text.includes( + `msgid=1 [issue] An element doesn't have an autocomplete attribute (count: 1)`, + ), + ); + }); + }); + it('work with primitive unhandled errors', async () => { await withBrowser(async (response, context) => { const page = await context.newPage(); diff --git a/tsconfig.json b/tsconfig.json index 7ccc44480..d4e0eaf4f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -61,7 +61,10 @@ "node_modules/chrome-devtools-frontend/front_end/third_party/marked", "node_modules/chrome-devtools-frontend/front_end/third_party/source-map-scopes-codec", "node_modules/chrome-devtools-frontend/front_end/third_party/third-party-web", - "node_modules/chrome-devtools-frontend/mcp/mcp.ts" + "node_modules/chrome-devtools-frontend/mcp/mcp.ts", + "node_modules/chrome-devtools-frontend/front_end/models/issues_manager", + "node_modules/chrome-devtools-frontend/front_end/third_party/marked", + "node_modules/chrome-devtools-frontend/front_end/panels/issues/IssueAggregator.ts" ], "exclude": ["node_modules/chrome-devtools-frontend/**/*.test.ts"] } From 3b016f1a814b1a69750813548b3f35e79bfb6fef Mon Sep 17 00:00:00 2001 From: Alex Rudenko Date: Mon, 17 Nov 2025 12:25:51 +0100 Subject: [PATCH 072/456] refactor: simplify issue management (#564) follow-up for https://github.com/ChromeDevTools/chrome-devtools-mcp/pull/505 --- src/PageCollector.ts | 81 +++++++++++++++++-------------------- tests/PageCollector.test.ts | 15 +++++-- 2 files changed, 49 insertions(+), 47 deletions(-) diff --git a/src/PageCollector.ts b/src/PageCollector.ts index 1212f996f..62b28055c 100644 --- a/src/PageCollector.ts +++ b/src/PageCollector.ts @@ -14,7 +14,7 @@ import { import {FakeIssuesManager} from './DevtoolsUtils.js'; import {logger} from './logger.js'; -import type {ConsoleMessage, Protocol} from './third_party/index.js'; +import type {CDPSession, ConsoleMessage} from './third_party/index.js'; import { type Browser, type Frame, @@ -210,23 +210,18 @@ export class PageCollector { export class ConsoleCollector extends PageCollector< ConsoleMessage | Error | AggregatedIssue > { - #seenIssueKeys = new WeakMap>(); - #issuesAggregators = new WeakMap(); - #mockIssuesManagers = new WeakMap(); - override addPage(page: Page): void { + const subscribed = this.storage.has(page); super.addPage(page); - void this.subscribeForIssues(page); + if (!subscribed) { + void this.subscribeForIssues(page); + } } - async subscribeForIssues(page: Page) { - if (this.#seenIssueKeys.has(page)) return; - this.#seenIssueKeys.set(page, new Set()); + async subscribeForIssues(page: Page) { + const seenKeys = new Set(); const mockManager = new FakeIssuesManager(); const aggregator = new IssueAggregator(mockManager); - this.#mockIssuesManagers.set(page, mockManager); - this.#issuesAggregators.set(page, aggregator); - aggregator.addEventListener( IssueAggregatorEvents.AGGREGATED_ISSUE_UPDATED, event => { @@ -238,45 +233,45 @@ export class ConsoleCollector extends PageCollector< page.emit('issue', event.data); }, ); + try { - const session = await page.createCDPSession(); + // @ts-expect-error use existing CDP client (internal Puppeteer API). + const session = page._client() as CDPSession; session.on('Audits.issueAdded', data => { - const inspectorIssue = - data.issue satisfies Protocol.Audits.InspectorIssue; - // @ts-expect-error Types of protocol from Puppeteer and CDP are incomparable for InspectorIssueCode, one is union, other is enum - const issue = createIssuesFromProtocolIssue(null, inspectorIssue)[0]; - if (!issue) { - logger('No issue mapping for for the issue: ', inspectorIssue.code); - return; + try { + const inspectorIssue = data.issue; + // @ts-expect-error Types of protocol from Puppeteer and CDP are + // incomparable for InspectorIssueCode, one is union, other is enum. + const issue = createIssuesFromProtocolIssue(null, inspectorIssue)[0]; + if (!issue) { + logger('No issue mapping for for the issue: ', inspectorIssue.code); + return; + } + + const primaryKey = issue.primaryKey(); + if (seenKeys.has(primaryKey)) { + return; + } + seenKeys.add(primaryKey); + + mockManager.dispatchEventToListeners( + IssuesManagerEvents.ISSUE_ADDED, + { + issue, + // @ts-expect-error We don't care that issues model is null + issuesModel: null, + }, + ); + } catch (error) { + logger('Error creating a new issue', error); } - - const seenKeys = this.#seenIssueKeys.get(page)!; - const primaryKey = issue.primaryKey(); - if (seenKeys.has(primaryKey)) return; - seenKeys.add(primaryKey); - - const mockManager = this.#mockIssuesManagers.get(page); - if (!mockManager) return; - - mockManager.dispatchEventToListeners(IssuesManagerEvents.ISSUE_ADDED, { - issue, - // @ts-expect-error We don't care that issues model is null - issuesModel: null, - }); }); await session.send('Audits.enable'); - } catch (e) { - logger('Error subscribing to issues', e); + } catch (error) { + logger('Error subscribing to issues', error); } } - - override cleanupPageDestroyed(page: Page) { - super.cleanupPageDestroyed(page); - this.#seenIssueKeys.delete(page); - this.#issuesAggregators.delete(page); - this.#mockIssuesManagers.delete(page); - } } export class NetworkCollector extends PageCollector { diff --git a/tests/PageCollector.test.ts b/tests/PageCollector.test.ts index 741712ef8..e0b3a2a0c 100644 --- a/tests/PageCollector.test.ts +++ b/tests/PageCollector.test.ts @@ -64,7 +64,11 @@ function getMockPage(): Page { return Promise.resolve(cdpSession as unknown as CDPSession); }, ...mockListener(), - } as Page; + // @ts-expect-error internal API. + _client() { + return cdpSession; + }, + } satisfies Page; } function getMockBrowser(): Browser { @@ -362,7 +366,8 @@ describe('ConsoleCollector', () => { it('emits issues on page', async () => { const browser = getMockBrowser(); const page = (await browser.pages())[0]; - const cdpSession = await page.createCDPSession(); + // @ts-expect-error internal API. + const cdpSession = page._client(); const onIssuesListener = sinon.spy(); page.on('issue', onIssuesListener); @@ -385,7 +390,8 @@ describe('ConsoleCollector', () => { it('collects issues', async () => { const browser = getMockBrowser(); const page = (await browser.pages())[0]; - const cdpSession = await page.createCDPSession(); + // @ts-expect-error internal API. + const cdpSession = page._client(); const collector = new ConsoleCollector(browser, collect => { return { @@ -416,7 +422,8 @@ describe('ConsoleCollector', () => { it('filters duplicated issues', async () => { const browser = getMockBrowser(); const page = (await browser.pages())[0]; - const cdpSession = await page.createCDPSession(); + // @ts-expect-error internal API. + const cdpSession = page._client(); const collector = new ConsoleCollector(browser, collect => { return { From c3784d1990a926f651951e4eef05520c5c448964 Mon Sep 17 00:00:00 2001 From: Alex Rudenko Date: Mon, 17 Nov 2025 15:52:37 +0100 Subject: [PATCH 073/456] refactor: clear issue aggregator on page navigation (#565) - fixes a memory leak introduced previously (I could not pinpoint it but McpContext was retained for subsequent tests). - adds a test that issue continue being aggregated on reload. - moves page-specific logic to a class. --- src/McpContext.ts | 5 + src/PageCollector.ts | 209 +++++++++++++++++++++++++----------- tests/PageCollector.test.ts | 4 - tests/tools/console.test.ts | 47 +++++++- tests/utils.ts | 6 +- 5 files changed, 198 insertions(+), 73 deletions(-) diff --git a/src/McpContext.ts b/src/McpContext.ts index 76b70515b..1c3c988d2 100644 --- a/src/McpContext.ts +++ b/src/McpContext.ts @@ -155,6 +155,11 @@ export class McpContext implements Context { await this.#consoleCollector.init(); } + dispose() { + this.#networkCollector.dispose(); + this.#consoleCollector.dispose(); + } + static async from( browser: Browser, logger: Debugger, diff --git a/src/PageCollector.ts b/src/PageCollector.ts index 62b28055c..cff2148d6 100644 --- a/src/PageCollector.ts +++ b/src/PageCollector.ts @@ -4,8 +4,11 @@ * SPDX-License-Identifier: Apache-2.0 */ +import type { + AggregatedIssue, + Common, +} from '../node_modules/chrome-devtools-frontend/mcp/mcp.js'; import { - type AggregatedIssue, IssueAggregatorEvents, IssuesManagerEvents, createIssuesFromProtocolIssue, @@ -14,7 +17,12 @@ import { import {FakeIssuesManager} from './DevtoolsUtils.js'; import {logger} from './logger.js'; -import type {CDPSession, ConsoleMessage} from './third_party/index.js'; +import type { + CDPSession, + ConsoleMessage, + Protocol, + Target, +} from './third_party/index.js'; import { type Browser, type Frame, @@ -79,22 +87,31 @@ export class PageCollector { this.addPage(page); } - this.#browser.on('targetcreated', async target => { - const page = await target.page(); - if (!page) { - return; - } - this.addPage(page); - }); - this.#browser.on('targetdestroyed', async target => { - const page = await target.page(); - if (!page) { - return; - } - this.cleanupPageDestroyed(page); - }); + this.#browser.on('targetcreated', this.#onTargetCreated); + this.#browser.on('targetdestroyed', this.#onTargetDestroyed); + } + + dispose() { + this.#browser.off('targetcreated', this.#onTargetCreated); + this.#browser.off('targetdestroyed', this.#onTargetDestroyed); } + #onTargetCreated = async (target: Target) => { + const page = await target.page(); + if (!page) { + return; + } + this.addPage(page); + }; + + #onTargetDestroyed = async (target: Target) => { + const page = await target.page(); + if (!page) { + return; + } + this.cleanupPageDestroyed(page); + }; + public addPage(page: Page) { this.#initializePage(page); } @@ -210,68 +227,130 @@ export class PageCollector { export class ConsoleCollector extends PageCollector< ConsoleMessage | Error | AggregatedIssue > { + #subscribedPages = new WeakMap(); + override addPage(page: Page): void { - const subscribed = this.storage.has(page); super.addPage(page); - if (!subscribed) { - void this.subscribeForIssues(page); + if (!this.#subscribedPages.has(page)) { + const subscriber = new PageIssueSubscriber(page); + this.#subscribedPages.set(page, subscriber); + void subscriber.subscribe(); } } - async subscribeForIssues(page: Page) { - const seenKeys = new Set(); - const mockManager = new FakeIssuesManager(); - const aggregator = new IssueAggregator(mockManager); - aggregator.addEventListener( + protected override cleanupPageDestroyed(page: Page): void { + super.cleanupPageDestroyed(page); + this.#subscribedPages.get(page)?.unsubscribe(); + this.#subscribedPages.delete(page); + } +} + +class PageIssueSubscriber { + #issueManager = new FakeIssuesManager(); + #issueAggregator = new IssueAggregator(this.#issueManager); + #seenKeys = new Set(); + #seenIssues = new Set(); + #page: Page; + #session: CDPSession; + + constructor(page: Page) { + this.#page = page; + // @ts-expect-error use existing CDP client (internal Puppeteer API). + this.#session = this.#page._client() as CDPSession; + } + + #resetIssueAggregator() { + this.#issueManager = new FakeIssuesManager(); + if (this.#issueAggregator) { + this.#issueAggregator.removeEventListener( + IssueAggregatorEvents.AGGREGATED_ISSUE_UPDATED, + this.#onAggregatedissue, + ); + } + this.#issueAggregator = new IssueAggregator(this.#issueManager); + + this.#issueAggregator.addEventListener( IssueAggregatorEvents.AGGREGATED_ISSUE_UPDATED, - event => { - const withId = event.data as WithSymbolId; - // Emit aggregated issue only if it's a new one - if (withId[stableIdSymbol]) { - return; - } - page.emit('issue', event.data); - }, + this.#onAggregatedissue, ); + } + async subscribe() { + this.#resetIssueAggregator(); + this.#page.on('framenavigated', this.#onFrameNavigated); + this.#session.on('Audits.issueAdded', this.#onIssueAdded); try { - // @ts-expect-error use existing CDP client (internal Puppeteer API). - const session = page._client() as CDPSession; - session.on('Audits.issueAdded', data => { - try { - const inspectorIssue = data.issue; - // @ts-expect-error Types of protocol from Puppeteer and CDP are - // incomparable for InspectorIssueCode, one is union, other is enum. - const issue = createIssuesFromProtocolIssue(null, inspectorIssue)[0]; - if (!issue) { - logger('No issue mapping for for the issue: ', inspectorIssue.code); - return; - } - - const primaryKey = issue.primaryKey(); - if (seenKeys.has(primaryKey)) { - return; - } - seenKeys.add(primaryKey); - - mockManager.dispatchEventToListeners( - IssuesManagerEvents.ISSUE_ADDED, - { - issue, - // @ts-expect-error We don't care that issues model is null - issuesModel: null, - }, - ); - } catch (error) { - logger('Error creating a new issue', error); - } - }); - - await session.send('Audits.enable'); + await this.#session.send('Audits.enable'); } catch (error) { logger('Error subscribing to issues', error); } } + + unsubscribe() { + this.#seenKeys.clear(); + this.#seenIssues.clear(); + this.#page.off('framenavigated', this.#onFrameNavigated); + this.#session.off('Audits.issueAdded', this.#onIssueAdded); + if (this.#issueAggregator) { + this.#issueAggregator.removeEventListener( + IssueAggregatorEvents.AGGREGATED_ISSUE_UPDATED, + this.#onAggregatedissue, + ); + } + void this.#session.send('Audits.disable').catch(() => { + // might fail. + }); + } + + #onAggregatedissue = ( + event: Common.EventTarget.EventTargetEvent, + ) => { + if (this.#seenIssues.has(event.data)) { + return; + } + this.#seenIssues.add(event.data); + this.#page.emit('issue', event.data); + }; + + // On navigation, we reset issue aggregation. + #onFrameNavigated = (frame: Frame) => { + // Only split the storage on main frame navigation + if (frame !== frame.page().mainFrame()) { + return; + } + this.#seenKeys.clear(); + this.#seenIssues.clear(); + this.#resetIssueAggregator(); + }; + + #onIssueAdded = (data: Protocol.Audits.IssueAddedEvent) => { + try { + const inspectorIssue = data.issue; + // @ts-expect-error Types of protocol from Puppeteer and CDP are + // incomparable for InspectorIssueCode, one is union, other is enum. + const issue = createIssuesFromProtocolIssue(null, inspectorIssue)[0]; + if (!issue) { + logger('No issue mapping for for the issue: ', inspectorIssue.code); + return; + } + + const primaryKey = issue.primaryKey(); + if (this.#seenKeys.has(primaryKey)) { + return; + } + this.#seenKeys.add(primaryKey); + this.#issueManager.dispatchEventToListeners( + IssuesManagerEvents.ISSUE_ADDED, + { + issue, + // @ts-expect-error We don't care that issues model is null + issuesModel: null, + }, + ); + } catch (error) { + logger('Error creating a new issue', error); + } + }; } export class NetworkCollector extends PageCollector { diff --git a/tests/PageCollector.test.ts b/tests/PageCollector.test.ts index e0b3a2a0c..06438183e 100644 --- a/tests/PageCollector.test.ts +++ b/tests/PageCollector.test.ts @@ -12,7 +12,6 @@ import type { HTTPRequest, Page, Target, - CDPSession, Protocol, } from 'puppeteer-core'; import sinon from 'sinon'; @@ -60,9 +59,6 @@ function getMockPage(): Page { mainFrame() { return mainFrame; }, - createCDPSession() { - return Promise.resolve(cdpSession as unknown as CDPSession); - }, ...mockListener(), // @ts-expect-error internal API. _client() { diff --git a/tests/tools/console.test.ts b/tests/tools/console.test.ts index 5a6f7b8ca..770ccb4f0 100644 --- a/tests/tools/console.test.ts +++ b/tests/tools/console.test.ts @@ -41,15 +41,14 @@ describe('console', () => { }); }); - it('lists issues messages', async () => { + it('lists issues', async () => { await withBrowser(async (response, context) => { const page = await context.newPage(); const issuePromise = new Promise(resolve => { - page.on('issue', () => { + page.once('issue', () => { resolve(); }); }); - await page.setContent(''); await issuePromise; await listConsoleMessages.handler({params: {}}, response, context); @@ -63,6 +62,48 @@ describe('console', () => { }); }); + it('lists issues after a page reload', async () => { + await withBrowser(async (response, context) => { + const page = await context.newPage(); + const issuePromise = new Promise(resolve => { + page.once('issue', () => { + resolve(); + }); + }); + + await page.setContent(''); + await issuePromise; + await listConsoleMessages.handler({params: {}}, response, context); + { + const formattedResponse = await response.handle('test', context); + const textContent = formattedResponse[0] as {text: string}; + assert.ok( + textContent.text.includes( + `msgid=1 [issue] An element doesn't have an autocomplete attribute (count: 1)`, + ), + ); + } + + const anotherIssuePromise = new Promise(resolve => { + page.once('issue', () => { + resolve(); + }); + }); + await page.reload(); + await page.setContent(''); + await anotherIssuePromise; + { + const formattedResponse = await response.handle('test', context); + const textContent = formattedResponse[0] as {text: string}; + assert.ok( + textContent.text.includes( + `msgid=2 [issue] An element doesn't have an autocomplete attribute (count: 1)`, + ), + ); + } + }); + }); + it('work with primitive unhandled errors', async () => { await withBrowser(async (response, context) => { const page = await context.newPage(); diff --git a/tests/utils.ts b/tests/utils.ts index 99f82b055..a1f97b900 100644 --- a/tests/utils.ts +++ b/tests/utils.ts @@ -18,6 +18,7 @@ import {McpResponse} from '../src/McpResponse.js'; import {stableIdSymbol} from '../src/PageCollector.js'; const browsers = new Map(); +let context: McpContext | undefined; export async function withBrowser( cb: (response: McpResponse, context: McpContext) => Promise, @@ -48,7 +49,10 @@ export async function withBrowser( }), ); const response = new McpResponse(); - const context = await McpContext.from( + if (context) { + context.dispose(); + } + context = await McpContext.from( browser, logger('test'), { From 305d7d7d9e54c41ac3e299ae1976c5784c0b470d Mon Sep 17 00:00:00 2001 From: Nikolay Vitkov <34244704+Lightning00Blade@users.noreply.github.com> Date: Wed, 19 Nov 2025 07:56:34 +0100 Subject: [PATCH 074/456] chore: add title to the server.json (#572) Hopefully this is the property they read to show on the MCP registry. --- server.json | 1 + 1 file changed, 1 insertion(+) diff --git a/server.json b/server.json index 7cb6d0ad6..4dc7b2c4d 100644 --- a/server.json +++ b/server.json @@ -1,6 +1,7 @@ { "$schema": "https://static.modelcontextprotocol.io/schemas/2025-10-17/server.schema.json", "name": "io.github.ChromeDevTools/chrome-devtools-mcp", + "title": "Chrome DevTools MCP", "description": "MCP server for Chrome DevTools", "repository": { "url": "https://github.com/ChromeDevTools/chrome-devtools-mcp", From 08e9a9f42e6ff1a92c60b3e958b0817c7b785afc Mon Sep 17 00:00:00 2001 From: Alex Rudenko Date: Wed, 19 Nov 2025 09:28:31 +0100 Subject: [PATCH 075/456] refactor: disable issues in list_console_messages for now (#575) Disabled the feature until the detail tool is ready. --- src/PageCollector.ts | 4 ++ src/features.ts | 16 +++++ src/main.ts | 5 +- src/tools/console.ts | 7 ++- tests/PageCollector.test.ts | 8 ++- tests/tools/console.test.ts | 115 +++++++++++++++++++----------------- 6 files changed, 99 insertions(+), 56 deletions(-) create mode 100644 src/features.ts diff --git a/src/PageCollector.ts b/src/PageCollector.ts index cff2148d6..a25805f9a 100644 --- a/src/PageCollector.ts +++ b/src/PageCollector.ts @@ -16,6 +16,7 @@ import { } from '../node_modules/chrome-devtools-frontend/mcp/mcp.js'; import {FakeIssuesManager} from './DevtoolsUtils.js'; +import {features} from './features.js'; import {logger} from './logger.js'; import type { CDPSession, @@ -231,6 +232,9 @@ export class ConsoleCollector extends PageCollector< override addPage(page: Page): void { super.addPage(page); + if (!features.issues) { + return; + } if (!this.#subscribedPages.has(page)) { const subscriber = new PageIssueSubscriber(page); this.#subscribedPages.set(page, subscriber); diff --git a/src/features.ts b/src/features.ts new file mode 100644 index 000000000..6260edc74 --- /dev/null +++ b/src/features.ts @@ -0,0 +1,16 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ +let issuesEnabled = false; + +export const features = { + get issues() { + return issuesEnabled; + }, +}; + +export function setIssuesEnabled(value: boolean) { + issuesEnabled = value; +} diff --git a/src/main.ts b/src/main.ts index 779568764..b517b3731 100644 --- a/src/main.ts +++ b/src/main.ts @@ -9,6 +9,7 @@ import './polyfill.js'; import type {Channel} from './browser.js'; import {ensureBrowserConnected, ensureBrowserLaunched} from './browser.js'; import {parseArguments} from './cli.js'; +import {features} from './features.js'; import {loadIssueDescriptions} from './issue-descriptions.js'; import {logger, saveLogsToFile} from './logger.js'; import {McpContext} from './McpContext.js'; @@ -190,7 +191,9 @@ for (const tool of tools) { registerTool(tool); } -await loadIssueDescriptions(); +if (features.issues) { + await loadIssueDescriptions(); +} const transport = new StdioServerTransport(); await server.connect(transport); logger('Chrome DevTools MCP Server connected'); diff --git a/src/tools/console.ts b/src/tools/console.ts index ee6720183..f69f5d883 100644 --- a/src/tools/console.ts +++ b/src/tools/console.ts @@ -4,6 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +import {features} from '../features.js'; import {zod} from '../third_party/index.js'; import type {ConsoleMessageType} from '../third_party/index.js'; @@ -11,7 +12,7 @@ import {ToolCategory} from './categories.js'; import {defineTool} from './ToolDefinition.js'; type ConsoleResponseType = ConsoleMessageType | 'issue'; -const FILTERABLE_MESSAGE_TYPES: readonly [ +const FILTERABLE_MESSAGE_TYPES: [ ConsoleResponseType, ...ConsoleResponseType[], ] = [ @@ -37,6 +38,10 @@ const FILTERABLE_MESSAGE_TYPES: readonly [ 'issue', ]; +if (features.issues) { + FILTERABLE_MESSAGE_TYPES.push('issue'); +} + export const listConsoleMessages = defineTool({ name: 'list_console_messages', description: diff --git a/tests/PageCollector.test.ts b/tests/PageCollector.test.ts index 06438183e..ef31d0069 100644 --- a/tests/PageCollector.test.ts +++ b/tests/PageCollector.test.ts @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ import assert from 'node:assert'; -import {beforeEach, describe, it} from 'node:test'; +import {afterEach, beforeEach, describe, it} from 'node:test'; import type { Browser, @@ -17,6 +17,7 @@ import type { import sinon from 'sinon'; import {AggregatedIssue} from '../node_modules/chrome-devtools-frontend/mcp/mcp.js'; +import {setIssuesEnabled} from '../src/features.js'; import type {ListenerMap} from '../src/PageCollector.js'; import { ConsoleCollector, @@ -357,6 +358,11 @@ describe('ConsoleCollector', () => { }, }, }; + setIssuesEnabled(true); + }); + + afterEach(() => { + setIssuesEnabled(false); }); it('emits issues on page', async () => { diff --git a/tests/tools/console.test.ts b/tests/tools/console.test.ts index 770ccb4f0..73c6f092f 100644 --- a/tests/tools/console.test.ts +++ b/tests/tools/console.test.ts @@ -4,8 +4,9 @@ * SPDX-License-Identifier: Apache-2.0 */ import assert from 'node:assert'; -import {before, describe, it} from 'node:test'; +import {afterEach, before, beforeEach, describe, it} from 'node:test'; +import {setIssuesEnabled} from '../../src/features.js'; import {loadIssueDescriptions} from '../../src/issue-descriptions.js'; import { getConsoleMessage, @@ -41,40 +42,37 @@ describe('console', () => { }); }); - it('lists issues', async () => { + it('work with primitive unhandled errors', async () => { await withBrowser(async (response, context) => { const page = await context.newPage(); - const issuePromise = new Promise(resolve => { - page.once('issue', () => { - resolve(); - }); - }); - await page.setContent(''); - await issuePromise; + await page.setContent(''); await listConsoleMessages.handler({params: {}}, response, context); const formattedResponse = await response.handle('test', context); const textContent = formattedResponse[0] as {text: string}; assert.ok( - textContent.text.includes( - `msgid=1 [issue] An element doesn't have an autocomplete attribute (count: 1)`, - ), + textContent.text.includes('msgid=1 [error] undefined (0 args)'), ); }); }); - it('lists issues after a page reload', async () => { - await withBrowser(async (response, context) => { - const page = await context.newPage(); - const issuePromise = new Promise(resolve => { - page.once('issue', () => { - resolve(); + describe('issues', () => { + beforeEach(() => { + setIssuesEnabled(true); + }); + afterEach(() => { + setIssuesEnabled(false); + }); + it('lists issues', async () => { + await withBrowser(async (response, context) => { + const page = await context.newPage(); + const issuePromise = new Promise(resolve => { + page.once('issue', () => { + resolve(); + }); }); - }); - - await page.setContent(''); - await issuePromise; - await listConsoleMessages.handler({params: {}}, response, context); - { + await page.setContent(''); + await issuePromise; + await listConsoleMessages.handler({params: {}}, response, context); const formattedResponse = await response.handle('test', context); const textContent = formattedResponse[0] as {text: string}; assert.ok( @@ -82,38 +80,49 @@ describe('console', () => { `msgid=1 [issue] An element doesn't have an autocomplete attribute (count: 1)`, ), ); - } - - const anotherIssuePromise = new Promise(resolve => { - page.once('issue', () => { - resolve(); - }); }); - await page.reload(); - await page.setContent(''); - await anotherIssuePromise; - { - const formattedResponse = await response.handle('test', context); - const textContent = formattedResponse[0] as {text: string}; - assert.ok( - textContent.text.includes( - `msgid=2 [issue] An element doesn't have an autocomplete attribute (count: 1)`, - ), - ); - } }); - }); - it('work with primitive unhandled errors', async () => { - await withBrowser(async (response, context) => { - const page = await context.newPage(); - await page.setContent(''); - await listConsoleMessages.handler({params: {}}, response, context); - const formattedResponse = await response.handle('test', context); - const textContent = formattedResponse[0] as {text: string}; - assert.ok( - textContent.text.includes('msgid=1 [error] undefined (0 args)'), - ); + it('lists issues after a page reload', async () => { + await withBrowser(async (response, context) => { + const page = await context.newPage(); + const issuePromise = new Promise(resolve => { + page.once('issue', () => { + resolve(); + }); + }); + + await page.setContent(''); + await issuePromise; + await listConsoleMessages.handler({params: {}}, response, context); + { + const formattedResponse = await response.handle('test', context); + const textContent = formattedResponse[0] as {text: string}; + assert.ok( + textContent.text.includes( + `msgid=1 [issue] An element doesn't have an autocomplete attribute (count: 1)`, + ), + ); + } + + const anotherIssuePromise = new Promise(resolve => { + page.once('issue', () => { + resolve(); + }); + }); + await page.reload(); + await page.setContent(''); + await anotherIssuePromise; + { + const formattedResponse = await response.handle('test', context); + const textContent = formattedResponse[0] as {text: string}; + assert.ok( + textContent.text.includes( + `msgid=2 [issue] An element doesn't have an autocomplete attribute (count: 1)`, + ), + ); + } + }); }); }); }); From a5c40d14a9929e09a3f90c141aac336199df3f46 Mon Sep 17 00:00:00 2001 From: Alex Rudenko Date: Wed, 19 Nov 2025 10:32:54 +0100 Subject: [PATCH 076/456] chore: remove i18n mocks (#576) i18n can now run in node.js. --- scripts/post-build.ts | 75 ++++++---------------------- src/DevtoolsUtils.ts | 11 ++++ tests/trace-processing/parse.test.ts | 2 + tsconfig.json | 8 +-- 4 files changed, 31 insertions(+), 65 deletions(-) diff --git a/scripts/post-build.ts b/scripts/post-build.ts index b520d6a07..0010e22ef 100644 --- a/scripts/post-build.ts +++ b/scripts/post-build.ts @@ -53,69 +53,22 @@ function main(): void { // Create i18n mock const i18nDir = path.join(BUILD_DIR, devtoolsFrontEndCorePath, 'i18n'); - fs.mkdirSync(i18nDir, {recursive: true}); - const i18nFile = path.join(i18nDir, 'i18n.js'); - const i18nContent = ` -export const i18n = { - registerUIStrings: () => {}, - getLocalizedString: (_, str) => { - // So that the string passed in gets output verbatim. - return str; - }, - lockedLazyString: () => {}, - getLazilyComputedLocalizedString: () => ()=>{}, -}; - -// TODO(jacktfranklin): once the DocumentLatency insight does not depend on -// this method, we can remove this stub. -export const TimeUtilities = { - millisToString(x) { - const separator = '\xA0'; - const formatter = new Intl.NumberFormat('en-US', { - style: 'unit', - unitDisplay: 'narrow', - minimumFractionDigits: 0, - maximumFractionDigits: 1, - unit: 'millisecond', - }); - - const parts = formatter.formatToParts(x); - for (const part of parts) { - if (part.type === 'literal') { - if (part.value === ' ') { - part.value = separator; - } - } - } + const localesFile = path.join(i18nDir, 'locales.js'); + const localesContent = ` +export const LOCALES = [ + 'en-US', +]; - return parts.map(part => part.value).join(''); - } -}; - -// TODO(jacktfranklin): once the ImageDelivery insight does not depend on this method, we can remove this stub. -export const ByteUtilities = { - bytesToString(x) { - const separator = '\xA0'; - const formatter = new Intl.NumberFormat('en-US', { - style: 'unit', - unit: 'kilobyte', - unitDisplay: 'narrow', - minimumFractionDigits: 1, - maximumFractionDigits: 1, - }); - const parts = formatter.formatToParts(x / 1000); - for (const part of parts) { - if (part.type === 'literal') { - if (part.value === ' ') { - part.value = separator; - } - } - } +export const BUNDLED_LOCALES = [ + 'en-US', +]; - return parts.map(part => part.value).join(''); - } -};`; - writeFile(i18nFile, i18nContent); +export const DEFAULT_LOCALE = 'en-US'; + +export const REMOTE_FETCH_PATTERN = '@HOST@/remote/serve_file/@VERSION@/core/i18n/locales/@LOCALE@.json'; + +export const LOCAL_FETCH_PATTERN = './locales/@LOCALE@.json';`; + writeFile(localesFile, localesContent); // Create codemirror.next mock. const codeMirrorDir = path.join( diff --git a/src/DevtoolsUtils.ts b/src/DevtoolsUtils.ts index 27067ec0b..997cb86a3 100644 --- a/src/DevtoolsUtils.ts +++ b/src/DevtoolsUtils.ts @@ -8,6 +8,7 @@ import { type Issue, type IssuesManagerEventTypes, Common, + I18n, } from '../node_modules/chrome-devtools-frontend/mcp/mcp.js'; export function extractUrlLikeFromDevToolsTitle( @@ -67,3 +68,13 @@ export class FakeIssuesManager extends Common.ObjectWrapper return []; } } + +I18n.DevToolsLocale.DevToolsLocale.instance({ + create: true, + data: { + navigatorLanguage: 'en-US', + settingLanguage: 'en-US', + lookupClosestDevToolsLocale: l => l, + }, +}); +I18n.i18n.registerLocaleDataForTest('en-US', {}); diff --git a/tests/trace-processing/parse.test.ts b/tests/trace-processing/parse.test.ts index d329e29b8..79b4ecb57 100644 --- a/tests/trace-processing/parse.test.ts +++ b/tests/trace-processing/parse.test.ts @@ -11,6 +11,8 @@ import { parseRawTraceBuffer, } from '../../src/trace-processing/parse.js'; +import '../../src/DevtoolsUtils.js'; + import {loadTraceAsBuffer} from './fixtures/load.js'; describe('Trace parsing', async () => { diff --git a/tsconfig.json b/tsconfig.json index d4e0eaf4f..241806784 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -28,6 +28,7 @@ "tests/**/*.ts", "node_modules/chrome-devtools-frontend/front_end/core/common", "node_modules/chrome-devtools-frontend/front_end/core/host", + "node_modules/chrome-devtools-frontend/front_end/core/i18n", "node_modules/chrome-devtools-frontend/front_end/core/platform", "node_modules/chrome-devtools-frontend/front_end/core/protocol_client", "node_modules/chrome-devtools-frontend/front_end/core/root", @@ -57,14 +58,13 @@ "node_modules/chrome-devtools-frontend/front_end/models/trace", "node_modules/chrome-devtools-frontend/front_end/models/workspace", "node_modules/chrome-devtools-frontend/front_end/panels/issues/IssueAggregator.ts", + "node_modules/chrome-devtools-frontend/front_end/third_party/i18n", + "node_modules/chrome-devtools-frontend/front_end/third_party/intl-messageformat", "node_modules/chrome-devtools-frontend/front_end/third_party/legacy-javascript", "node_modules/chrome-devtools-frontend/front_end/third_party/marked", "node_modules/chrome-devtools-frontend/front_end/third_party/source-map-scopes-codec", "node_modules/chrome-devtools-frontend/front_end/third_party/third-party-web", - "node_modules/chrome-devtools-frontend/mcp/mcp.ts", - "node_modules/chrome-devtools-frontend/front_end/models/issues_manager", - "node_modules/chrome-devtools-frontend/front_end/third_party/marked", - "node_modules/chrome-devtools-frontend/front_end/panels/issues/IssueAggregator.ts" + "node_modules/chrome-devtools-frontend/mcp/mcp.ts" ], "exclude": ["node_modules/chrome-devtools-frontend/**/*.test.ts"] } From 3921e8a7c4b14743dbbd92c40495c2cc7565c8e9 Mon Sep 17 00:00:00 2001 From: browser-automation-bot <133232582+browser-automation-bot@users.noreply.github.com> Date: Wed, 19 Nov 2025 10:45:30 +0100 Subject: [PATCH 077/456] chore(main): release chrome-devtools-mcp 0.10.2 (#542) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit :robot: I have created a release *beep* *boop* --- ## [0.10.2](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.10.1...chrome-devtools-mcp-v0.10.2) (2025-11-19) ### 📄 Documentation * add Factory CLI configuration to MCP clients ([#523](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/523)) ([016e2fd](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/016e2fd6ee57447103f7385285dd503b5576a860)) ### ♻️ Chores * clear issue aggregator on page navigation ([#565](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/565)) ([c3784d1](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/c3784d1990a926f651951e4eef05520c5c448964)) * disable issues in list_console_messages for now ([#575](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/575)) ([08e9a9f](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/08e9a9f42e6ff1a92c60b3e958b0817c7b785afc)) * simplify issue management ([#564](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/564)) ([3b016f1](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/3b016f1a814b1a69750813548b3f35e79bfb6fef)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --- .release-please-manifest.json | 2 +- CHANGELOG.md | 14 ++++++++++++++ package-lock.json | 4 ++-- package.json | 2 +- server.json | 4 ++-- src/main.ts | 2 +- 6 files changed, 21 insertions(+), 7 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 30b6d45ad..582db2fa7 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.10.1" + ".": "0.10.2" } diff --git a/CHANGELOG.md b/CHANGELOG.md index b841f3b55..326dcc386 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## [0.10.2](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.10.1...chrome-devtools-mcp-v0.10.2) (2025-11-19) + + +### 📄 Documentation + +* add Factory CLI configuration to MCP clients ([#523](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/523)) ([016e2fd](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/016e2fd6ee57447103f7385285dd503b5576a860)) + + +### ♻️ Chores + +* clear issue aggregator on page navigation ([#565](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/565)) ([c3784d1](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/c3784d1990a926f651951e4eef05520c5c448964)) +* disable issues in list_console_messages for now ([#575](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/575)) ([08e9a9f](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/08e9a9f42e6ff1a92c60b3e958b0817c7b785afc)) +* simplify issue management ([#564](https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/564)) ([3b016f1](https://github.com/ChromeDevTools/chrome-devtools-mcp/commit/3b016f1a814b1a69750813548b3f35e79bfb6fef)) + ## [0.10.1](https://github.com/ChromeDevTools/chrome-devtools-mcp/compare/chrome-devtools-mcp-v0.10.0...chrome-devtools-mcp-v0.10.1) (2025-11-07) diff --git a/package-lock.json b/package-lock.json index 18f9d2349..ec4b7d265 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "chrome-devtools-mcp", - "version": "0.10.1", + "version": "0.10.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "chrome-devtools-mcp", - "version": "0.10.1", + "version": "0.10.2", "license": "Apache-2.0", "bin": { "chrome-devtools-mcp": "build/src/index.js" diff --git a/package.json b/package.json index 2c27fe34b..52bccd842 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "chrome-devtools-mcp", - "version": "0.10.1", + "version": "0.10.2", "description": "MCP server for Chrome DevTools", "type": "module", "bin": "./build/src/index.js", diff --git a/server.json b/server.json index 4dc7b2c4d..5610540c0 100644 --- a/server.json +++ b/server.json @@ -7,13 +7,13 @@ "url": "https://github.com/ChromeDevTools/chrome-devtools-mcp", "source": "github" }, - "version": "0.10.1", + "version": "0.10.2", "packages": [ { "registryType": "npm", "registryBaseUrl": "https://registry.npmjs.org", "identifier": "chrome-devtools-mcp", - "version": "0.10.1", + "version": "0.10.2", "transport": { "type": "stdio" }, diff --git a/src/main.ts b/src/main.ts index b517b3731..153b6f5da 100644 --- a/src/main.ts +++ b/src/main.ts @@ -35,7 +35,7 @@ import type {ToolDefinition} from './tools/ToolDefinition.js'; // If moved update release-please config // x-release-please-start-version -const VERSION = '0.10.1'; +const VERSION = '0.10.2'; // x-release-please-end export const args = parseArguments(VERSION); From 6f9182f4b60f1f6ff8d321fec35545712828686e Mon Sep 17 00:00:00 2001 From: Sebastian Benz Date: Wed, 19 Nov 2025 17:53:12 +0100 Subject: [PATCH 078/456] docs: add Antigravity config (#580) --- README.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/README.md b/README.md index 3de17ff7b..07f598bed 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,28 @@ amp mcp add chrome-devtools -- npx chrome-devtools-mcp@latest
+
+ Antigravity + +To use the Chrome DevTools MCP server, disable the built-in browser in the settings and add the following config to ` ~/.gemini/antigravity/mcp_config.json`: + +```bash +{ + "mcpServers": { + "chrome-devtools": { + "type": "stdio", + "command": "npx", + "args": [ + "chrome-devtools-mcp@latest", + "-y" + ] + } + } +} +``` + +
+
Claude Code Use the Claude Code CLI to add the Chrome DevTools MCP server (guide): From 9cb99ad3e65054f4ea12a39358719f6630a020d0 Mon Sep 17 00:00:00 2001 From: Alex Rudenko Date: Thu, 20 Nov 2025 14:19:29 +0100 Subject: [PATCH 079/456] fix: rename page content to latest page snapshot (#579) Refs https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/578 --- src/McpResponse.ts | 2 +- tests/McpResponse.test.js.snapshot | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/McpResponse.ts b/src/McpResponse.ts index 737456d33..8dc0cd07a 100644 --- a/src/McpResponse.ts +++ b/src/McpResponse.ts @@ -408,7 +408,7 @@ Call ${handleDialog.name} to handle it before continuing.`); } if (data.formattedSnapshot) { - response.push('## Page content'); + response.push('## Latest page snapshot'); response.push(data.formattedSnapshot); } diff --git a/tests/McpResponse.test.js.snapshot b/tests/McpResponse.test.js.snapshot index 79d43b8fc..61a417c81 100644 --- a/tests/McpResponse.test.js.snapshot +++ b/tests/McpResponse.test.js.snapshot @@ -88,7 +88,7 @@ exports[`McpResponse > list pages 1`] = ` exports[`McpResponse > returns correctly formatted snapshot for a simple tree 1`] = ` # test response -## Page content +## Latest page snapshot uid=1_0 RootWebArea "My test page" url="about:blank" uid=1_1 button "Click me" focusable focused uid=1_2 textbox value="Input" @@ -97,7 +97,7 @@ uid=1_0 RootWebArea "My test page" url="about:blank" exports[`McpResponse > returns values for textboxes 1`] = ` # test response -## Page content +## Latest page snapshot uid=1_0 RootWebArea "My test page" url="about:blank" uid=1_1 StaticText "username" uid=1_2 textbox "username" focusable focused value="mcp" @@ -106,7 +106,7 @@ uid=1_0 RootWebArea "My test page" url="about:blank" exports[`McpResponse > returns verbose snapshot 1`] = ` # test response -## Page content +## Latest page snapshot uid=1_0 RootWebArea "My test page" url="about:blank" uid=1_1 ignored uid=1_2 ignored @@ -118,7 +118,7 @@ uid=1_0 RootWebArea "My test page" url="about:blank" exports[`McpResponse > saves snapshot to file 1`] = ` # test response -## Page content +## Latest page snapshot Saved snapshot to `; From 2eaf2689c3360f88479f4cdab8ddde5899378e33 Mon Sep 17 00:00:00 2001 From: Alex Rudenko Date: Fri, 21 Nov 2025 07:32:19 +0100 Subject: [PATCH 080/456] fix: ignore quality for png (#589) Fixes https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/588 --- src/tools/screenshot.ts | 7 +++++-- tests/tools/screenshot.test.ts | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/tools/screenshot.ts b/src/tools/screenshot.ts index d8d3dfcfc..4312c02aa 100644 --- a/src/tools/screenshot.ts +++ b/src/tools/screenshot.ts @@ -62,10 +62,13 @@ export const screenshot = defineTool({ pageOrHandle = context.getSelectedPage(); } + const format = request.params.format; + const quality = format === 'png' ? undefined : request.params.quality; + const screenshot = await pageOrHandle.screenshot({ - type: request.params.format, + type: format, fullPage: request.params.fullPage, - quality: request.params.quality, + quality, optimizeForSpeed: true, // Bonus: optimize encoding for speed }); diff --git a/tests/tools/screenshot.test.ts b/tests/tools/screenshot.test.ts index c5b3a4605..ebf207e1f 100644 --- a/tests/tools/screenshot.test.ts +++ b/tests/tools/screenshot.test.ts @@ -30,6 +30,25 @@ describe('screenshot', () => { ); }); }); + it('ignores quality', async () => { + await withBrowser(async (response, context) => { + const fixture = screenshots.basic; + const page = context.getSelectedPage(); + await page.setContent(fixture.html); + await screenshot.handler( + {params: {format: 'png', quality: 0}}, + response, + context, + ); + + assert.equal(response.images.length, 1); + assert.equal(response.images[0].mimeType, 'image/png'); + assert.equal( + response.responseLines.at(0), + "Took a screenshot of the current page's viewport.", + ); + }); + }); it('with jpeg', async () => { await withBrowser(async (response, context) => { await screenshot.handler({params: {format: 'jpeg'}}, response, context); From 80e77fd9a35a3dc5c451cc5b070b8baa574c686c Mon Sep 17 00:00:00 2001 From: Alex Rudenko Date: Fri, 21 Nov 2025 11:14:44 +0100 Subject: [PATCH 081/456] fix: include a note about selected elements missing from the snapshot (#593) Closes https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/586 --- src/McpContext.ts | 7 ++ src/formatters/snapshotFormatter.ts | 21 +++- .../snapshotFormatter.test.js.snapshot | 20 ++++ tests/formatters/snapshotFormatter.test.ts | 100 ++++++++++++++++++ 4 files changed, 144 insertions(+), 4 deletions(-) create mode 100644 tests/formatters/snapshotFormatter.test.js.snapshot diff --git a/src/McpContext.ts b/src/McpContext.ts index 1c3c988d2..0bdf06637 100644 --- a/src/McpContext.ts +++ b/src/McpContext.ts @@ -42,6 +42,10 @@ export interface TextSnapshot { idToNode: Map; snapshotId: string; selectedElementUid?: string; + // It might happen that there is a selected element, but it is not part of the + // snapshot. This flag indicates if there is any selected element. + hasSelectedElement: boolean; + verbose: boolean; } interface McpContextOptions { @@ -529,9 +533,12 @@ export class McpContext implements Context { root: rootNodeWithId, snapshotId: String(snapshotId), idToNode, + hasSelectedElement: false, + verbose, }; const data = devtoolsData ?? (await this.getDevToolsData()); if (data?.cdpBackendNodeId) { + this.#textSnapshot.hasSelectedElement = true; this.#textSnapshot.selectedElementUid = this.resolveCdpElementId( data?.cdpBackendNodeId, ); diff --git a/src/formatters/snapshotFormatter.ts b/src/formatters/snapshotFormatter.ts index 42d567e50..5018381e6 100644 --- a/src/formatters/snapshotFormatter.ts +++ b/src/formatters/snapshotFormatter.ts @@ -10,7 +10,20 @@ export function formatSnapshotNode( snapshot?: TextSnapshot, depth = 0, ): string { - let result = ''; + const chunks: string[] = []; + + if (depth === 0) { + // Top-level content of the snapshot. + if ( + snapshot?.verbose && + snapshot?.hasSelectedElement && + !snapshot.selectedElementUid + ) { + chunks.push(`Note: there is a selected element in the DevTools Elements panel but it is not included into the current a11y tree snapshot. +Get a verbose snapshot to include all elements if you are interested in the selected element.\n\n`); + } + } + const attributes = getAttributes(root); const line = ' '.repeat(depth * 2) + @@ -19,13 +32,13 @@ export function formatSnapshotNode( ? ' [selected in the DevTools Elements panel]' : '') + '\n'; - result += line; + chunks.push(line); for (const child of root.children) { - result += formatSnapshotNode(child, snapshot, depth + 1); + chunks.push(formatSnapshotNode(child, snapshot, depth + 1)); } - return result; + return chunks.join(''); } function getAttributes(serializedAXNodeRoot: TextSnapshotNode): string[] { diff --git a/tests/formatters/snapshotFormatter.test.js.snapshot b/tests/formatters/snapshotFormatter.test.js.snapshot new file mode 100644 index 000000000..bb2e1ccbf --- /dev/null +++ b/tests/formatters/snapshotFormatter.test.js.snapshot @@ -0,0 +1,20 @@ +exports[`snapshotFormatter > does not include a note if the snapshot is already verbose 1`] = ` +Note: there is a selected element in the DevTools Elements panel but it is not included into the current a11y tree snapshot. +Get a verbose snapshot to include all elements if you are interested in the selected element. + +uid=1_1 checkbox "checkbox" checked + uid=1_2 statictext "text" + +`; + +exports[`snapshotFormatter > formats with DevTools data included into a snapshot 1`] = ` +uid=1_1 checkbox "checkbox" checked [selected in the DevTools Elements panel] + uid=1_2 statictext "text" + +`; + +exports[`snapshotFormatter > formats with DevTools data not included into a snapshot 1`] = ` +uid=1_1 checkbox "checkbox" checked + uid=1_2 statictext "text" + +`; diff --git a/tests/formatters/snapshotFormatter.test.ts b/tests/formatters/snapshotFormatter.test.ts index 1232929f3..3ec8d5df7 100644 --- a/tests/formatters/snapshotFormatter.test.ts +++ b/tests/formatters/snapshotFormatter.test.ts @@ -148,4 +148,104 @@ describe('snapshotFormatter', () => { `, ); }); + + it('formats with DevTools data not included into a snapshot', t => { + const node: TextSnapshotNode = { + id: '1_1', + role: 'checkbox', + name: 'checkbox', + checked: true, + children: [ + { + id: '1_2', + role: 'statictext', + name: 'text', + children: [], + elementHandle: async (): Promise | null> => { + return null; + }, + }, + ], + elementHandle: async (): Promise | null> => { + return null; + }, + }; + + const formatted = formatSnapshotNode(node, { + snapshotId: '1', + root: node, + idToNode: new Map(), + hasSelectedElement: true, + verbose: false, + }); + + t.assert.snapshot?.(formatted); + }); + + it('does not include a note if the snapshot is already verbose', t => { + const node: TextSnapshotNode = { + id: '1_1', + role: 'checkbox', + name: 'checkbox', + checked: true, + children: [ + { + id: '1_2', + role: 'statictext', + name: 'text', + children: [], + elementHandle: async (): Promise | null> => { + return null; + }, + }, + ], + elementHandle: async (): Promise | null> => { + return null; + }, + }; + + const formatted = formatSnapshotNode(node, { + snapshotId: '1', + root: node, + idToNode: new Map(), + hasSelectedElement: true, + verbose: true, + }); + + t.assert.snapshot?.(formatted); + }); + + it('formats with DevTools data included into a snapshot', t => { + const node: TextSnapshotNode = { + id: '1_1', + role: 'checkbox', + name: 'checkbox', + checked: true, + children: [ + { + id: '1_2', + role: 'statictext', + name: 'text', + children: [], + elementHandle: async (): Promise | null> => { + return null; + }, + }, + ], + elementHandle: async (): Promise | null> => { + return null; + }, + }; + + const formatted = formatSnapshotNode(node, { + snapshotId: '1', + root: node, + idToNode: new Map(), + hasSelectedElement: true, + selectedElementUid: '1_1', + verbose: false, + }); + + t.assert.snapshot?.(formatted); + }); }); From 144a513e5b56f583127c2f7b6c542a9c5986f910 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 21 Nov 2025 11:08:10 +0000 Subject: [PATCH 082/456] chore(deps-dev): bump chrome-devtools-frontend from 1.0.1544076 to 1.0.1547147 in the bundled-devtools group (#597) Bumps the bundled-devtools group with 1 update: [chrome-devtools-frontend](https://github.com/ChromeDevTools/devtools-frontend). Updates `chrome-devtools-frontend` from 1.0.1544076 to 1.0.1547147
Commits
  • b907f0c Lint all files
  • d875f63 Update Chrome (for Testing) PIN
  • db5bff7 Update DevTools DEPS (trusted)
  • 703da59 RPP: Support columns, pretty'd sources in Source's panel profile data
  • cd88537 [AI] Add line, column to AICallTree serialization
  • 36e5721 Allow quotes in response to self-XXX mitigation
  • c89fc0d Let lint decided what files to handle
  • 35151fe Update Chrome (for Testing) PIN
  • 25bf5bf Roll browser-protocol
  • 2df407c Replace devtools-console-insight-sources-list with view function.
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=chrome-devtools-frontend&package-manager=npm_and_yarn&previous-version=1.0.1544076&new-version=1.0.1547147)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
--------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Alex Rudenko --- package-lock.json | 8 ++++---- package.json | 2 +- tests/tools/performance.test.js.snapshot | 14 ++++++++------ tests/trace-processing/parse.test.js.snapshot | 14 ++++++++------ 4 files changed, 21 insertions(+), 17 deletions(-) diff --git a/package-lock.json b/package-lock.json index ec4b7d265..e2942e7a6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,7 +25,7 @@ "@types/yargs": "^17.0.33", "@typescript-eslint/eslint-plugin": "^8.43.0", "@typescript-eslint/parser": "^8.43.0", - "chrome-devtools-frontend": "1.0.1544076", + "chrome-devtools-frontend": "1.0.1547147", "core-js": "3.46.0", "debug": "4.4.3", "eslint": "^9.35.0", @@ -2299,9 +2299,9 @@ } }, "node_modules/chrome-devtools-frontend": { - "version": "1.0.1544076", - "resolved": "https://registry.npmjs.org/chrome-devtools-frontend/-/chrome-devtools-frontend-1.0.1544076.tgz", - "integrity": "sha512-sEhEZDPD3oqfY1MmpIhoZyoay4wPG9eq030Uv3B+faBshRpFj1ef5AjZv1yEtuv/OWDGZCWEmGRXJ+/K0mV3Cw==", + "version": "1.0.1547147", + "resolved": "https://registry.npmjs.org/chrome-devtools-frontend/-/chrome-devtools-frontend-1.0.1547147.tgz", + "integrity": "sha512-eUNXqCEo1Ioa5JEVVjbf3iZxoYjEswBV1GcokX/hrwSUYW9IVMtilqEvPXaD7i7gPJXHBK0tJUNZMAlK7+KClQ==", "dev": true, "license": "BSD-3-Clause" }, diff --git a/package.json b/package.json index 52bccd842..523df56e5 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "@types/yargs": "^17.0.33", "@typescript-eslint/eslint-plugin": "^8.43.0", "@typescript-eslint/parser": "^8.43.0", - "chrome-devtools-frontend": "1.0.1544076", + "chrome-devtools-frontend": "1.0.1547147", "core-js": "3.46.0", "debug": "4.4.3", "eslint": "^9.35.0", diff --git a/tests/tools/performance.test.js.snapshot b/tests/tools/performance.test.js.snapshot index 0b31b5f19..9cda7250b 100644 --- a/tests/tools/performance.test.js.snapshot +++ b/tests/tools/performance.test.js.snapshot @@ -109,7 +109,7 @@ Information on performance traces may contain main thread activity represented a Each call frame is presented in the following format: -'id;eventKey;name;duration;selfTime;urlIndex;childRange;[S]' +'id;eventKey;name;duration;selfTime;urlIndex;childRange;[line];[column];[S]' Key definitions: @@ -120,15 +120,17 @@ Key definitions: * selfTime: The time spent directly within the call frame, excluding its children's execution. * urlIndex: Index referencing the "All URLs" list. Empty if no specific script URL is associated. * childRange: Specifies the direct children of this node using their IDs. If empty ('' or 'S' at the end), the node has no children. If a single number (e.g., '4'), the node has one child with that ID. If in the format 'firstId-lastId' (e.g., '4-5'), it indicates a consecutive range of child IDs from 'firstId' to 'lastId', inclusive. +* line: An optional field for a call frame's line number. This is where the function is defined. +* column: An optional field for a call frame's column number. This is where the function is defined. * S: _Optional_. The letter 'S' terminates the line if that call frame was selected by the user. Example Call Tree: -1;r-123;main;500;100;; -2;r-124;update;200;50;;3 -3;p-49575-15428179-2834-374;animate;150;20;0;4-5;S -4;p-49575-15428179-3505-1162;calculatePosition;80;80;; -5;p-49575-15428179-5391-2767;applyStyles;50;50;; +1;r-123;main;500;100;0;1;; +2;r-124;update;200;50;;3;0;1; +3;p-49575-15428179-2834-374;animate;150;20;0;4-5;0;1;S +4;p-49575-15428179-3505-1162;calculatePosition;80;80;0;1;; +5;p-49575-15428179-5391-2767;applyStyles;50;50;0;1;; Network requests are formatted like this: diff --git a/tests/trace-processing/parse.test.js.snapshot b/tests/trace-processing/parse.test.js.snapshot index bed739d88..b15549e33 100644 --- a/tests/trace-processing/parse.test.js.snapshot +++ b/tests/trace-processing/parse.test.js.snapshot @@ -57,7 +57,7 @@ Information on performance traces may contain main thread activity represented a Each call frame is presented in the following format: -'id;eventKey;name;duration;selfTime;urlIndex;childRange;[S]' +'id;eventKey;name;duration;selfTime;urlIndex;childRange;[line];[column];[S]' Key definitions: @@ -68,15 +68,17 @@ Key definitions: * selfTime: The time spent directly within the call frame, excluding its children's execution. * urlIndex: Index referencing the "All URLs" list. Empty if no specific script URL is associated. * childRange: Specifies the direct children of this node using their IDs. If empty ('' or 'S' at the end), the node has no children. If a single number (e.g., '4'), the node has one child with that ID. If in the format 'firstId-lastId' (e.g., '4-5'), it indicates a consecutive range of child IDs from 'firstId' to 'lastId', inclusive. +* line: An optional field for a call frame's line number. This is where the function is defined. +* column: An optional field for a call frame's column number. This is where the function is defined. * S: _Optional_. The letter 'S' terminates the line if that call frame was selected by the user. Example Call Tree: -1;r-123;main;500;100;; -2;r-124;update;200;50;;3 -3;p-49575-15428179-2834-374;animate;150;20;0;4-5;S -4;p-49575-15428179-3505-1162;calculatePosition;80;80;; -5;p-49575-15428179-5391-2767;applyStyles;50;50;; +1;r-123;main;500;100;0;1;; +2;r-124;update;200;50;;3;0;1; +3;p-49575-15428179-2834-374;animate;150;20;0;4-5;0;1;S +4;p-49575-15428179-3505-1162;calculatePosition;80;80;0;1;; +5;p-49575-15428179-5391-2767;applyStyles;50;50;0;1;; Network requests are formatted like this: From 6cacdfb38199001bcb008c28abb936f543c4a6cb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 21 Nov 2025 12:35:45 +0100 Subject: [PATCH 083/456] chore(deps-dev): bump the bundled group across 1 directory with 2 updates (#598) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the bundled group with 2 updates in the / directory: [core-js](https://github.com/zloirock/core-js/tree/HEAD/packages/core-js) and [puppeteer](https://github.com/puppeteer/puppeteer). Updates `core-js` from 3.46.0 to 3.47.0
Changelog

Sourced from core-js's changelog.

3.47.0 - 2025.11.18

Commits
  • c1d2c7e v3.47.0
  • 400f00e move JSON.parse source text access to stable ES
  • 2b383e9 move joint iteration to stage 3
  • 21f984c move Iterator sequencing proposal to stable ES
  • 6add4cd add explicit "sideEffects": true to core-js and core-js-bundle packages
  • 0f6c98d update eslint-plugin-unicorn
  • fe04540 fix increasing .size in URLSearchParams.prototype.append polyfill in IE8-
  • See full diff in compare view

Updates `puppeteer` from 24.30.0 to 24.31.0
Release notes

Sourced from puppeteer's releases.

puppeteer-core: v24.31.0

24.31.0 (2025-11-20)

🎉 Features

  • webdriver: use emulation.setNetworkConditions for offline emulation (#14431) (a9e7c1d)
  • webdriver: use emulation.setUserAgentOverride instead of network interception (#14335) (e48ae6b)

🛠️ Fixes

puppeteer: v24.31.0

24.31.0 (2025-11-20)

♻️ Chores

  • puppeteer: Synchronize puppeteer versions

Dependencies

  • The following workspace dependencies were updated
    • dependencies
      • puppeteer-core bumped from 24.30.0 to 24.31.0
Changelog

Sourced from puppeteer's changelog.

24.31.0 (2025-11-20)

♻️ Chores

  • puppeteer: Synchronize puppeteer versions

Dependencies

  • The following workspace dependencies were updated
    • dependencies
      • puppeteer-core bumped from 24.30.0 to 24.31.0

🎉 Features

  • webdriver: use emulation.setNetworkConditions for offline emulation (#14431) (a9e7c1d)
  • webdriver: use emulation.setUserAgentOverride instead of network interception (#14335) (e48ae6b)

🛠️ Fixes

Commits
  • 1ad21f5 chore: release main (#14421)
  • 3b83e65 fix: TS fix for path in ScreenshotOptions (#14443)
  • 4437670 ci: update permission for actions (#14441)
  • 3a588e3 chore(deps-dev): bump the dev-dependencies group with 6 updates (#14439)
  • 5bcfd2d chore(deps): bump webdriver-bidi-protocol from 0.3.8 to 0.3.9 in the dependen...
  • b3b7317 chore(deps): bump js-yaml from 3.14.1 to 3.14.2 in /website (#14433)
  • 5826088 chore: extract DeviceRequestPrompt interface (#14432)
  • 7e1eed0 fix: roll to Firefox 145.0.1 (#14437)
  • bc5ef9d chore(deps-dev): bump glob from 10.4.5 to 10.5.0 (#14436)
  • a9e7c1d feat(webdriver): use emulation.setNetworkConditions for offline emulation (...
  • Additional commits viewable in compare view

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 32 ++++++++++++++++---------------- package.json | 4 ++-- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/package-lock.json b/package-lock.json index e2942e7a6..4bdc6c6a6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,14 +26,14 @@ "@typescript-eslint/eslint-plugin": "^8.43.0", "@typescript-eslint/parser": "^8.43.0", "chrome-devtools-frontend": "1.0.1547147", - "core-js": "3.46.0", + "core-js": "3.47.0", "debug": "4.4.3", "eslint": "^9.35.0", "eslint-import-resolver-typescript": "^4.4.4", "eslint-plugin-import": "^2.32.0", "globals": "^16.4.0", "prettier": "^3.6.2", - "puppeteer": "24.30.0", + "puppeteer": "24.31.0", "rollup": "4.53.2", "rollup-plugin-cleanup": "^3.2.1", "rollup-plugin-license": "^3.6.0", @@ -2419,9 +2419,9 @@ } }, "node_modules/core-js": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.46.0.tgz", - "integrity": "sha512-vDMm9B0xnqqZ8uSBpZ8sNtRtOdmfShrvT6h2TuQGLs0Is+cR0DYbj/KWP6ALVNbWPpqA/qPLoOuppJN07humpA==", + "version": "3.47.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.47.0.tgz", + "integrity": "sha512-c3Q2VVkGAUyupsjRnaNX6u8Dq2vAdzm9iuPj5FW0fRxzlxgq9Q39MDq10IvmQSpLgHQNyQzQmOo6bgGHmH3NNg==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -5472,9 +5472,9 @@ } }, "node_modules/puppeteer": { - "version": "24.30.0", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.30.0.tgz", - "integrity": "sha512-A5OtCi9WpiXBQgJ2vQiZHSyrAzQmO/WDsvghqlN4kgw21PhxA5knHUaUQq/N3EMt8CcvSS0RM+kmYLJmedR3TQ==", + "version": "24.31.0", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.31.0.tgz", + "integrity": "sha512-q8y5yLxLD8xdZdzNWqdOL43NbfvUOp60SYhaLZQwHC9CdKldxQKXOyJAciOr7oUJfyAH/KgB2wKvqT2sFKoVXA==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", @@ -5483,7 +5483,7 @@ "chromium-bidi": "11.0.0", "cosmiconfig": "^9.0.0", "devtools-protocol": "0.0.1521046", - "puppeteer-core": "24.30.0", + "puppeteer-core": "24.31.0", "typed-query-selector": "^2.12.0" }, "bin": { @@ -5494,9 +5494,9 @@ } }, "node_modules/puppeteer-core": { - "version": "24.30.0", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.30.0.tgz", - "integrity": "sha512-2S3Smy0t0W4wJnNvDe7W0bE7wDmZjfZ3ljfMgJd6hn2Hq/f0jgN+x9PULZo2U3fu5UUIJ+JP8cNUGllu8P91Pg==", + "version": "24.31.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.31.0.tgz", + "integrity": "sha512-pnAohhSZipWQoFpXuGV7xCZfaGhqcBR9C4pVrU0QSrcMi7tQMH9J9lDBqBvyMAHQqe8HCARuREqFuVKRQOgTvg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -5505,7 +5505,7 @@ "debug": "^4.4.3", "devtools-protocol": "0.0.1521046", "typed-query-selector": "^2.12.0", - "webdriver-bidi-protocol": "0.3.8", + "webdriver-bidi-protocol": "0.3.9", "ws": "^8.18.3" }, "engines": { @@ -6843,9 +6843,9 @@ } }, "node_modules/webdriver-bidi-protocol": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/webdriver-bidi-protocol/-/webdriver-bidi-protocol-0.3.8.tgz", - "integrity": "sha512-21Yi2GhGntMc671vNBCjiAeEVknXjVRoyu+k+9xOMShu+ZQfpGQwnBqbNz/Sv4GXZ6JmutlPAi2nIJcrymAWuQ==", + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/webdriver-bidi-protocol/-/webdriver-bidi-protocol-0.3.9.tgz", + "integrity": "sha512-uIYvlRQ0PwtZR1EzHlTMol1G0lAlmOe6wPykF9a77AK3bkpvZHzIVxRE2ThOx5vjy2zISe0zhwf5rzuUfbo1PQ==", "dev": true, "license": "Apache-2.0" }, diff --git a/package.json b/package.json index 523df56e5..86f29fa73 100644 --- a/package.json +++ b/package.json @@ -53,14 +53,14 @@ "@typescript-eslint/eslint-plugin": "^8.43.0", "@typescript-eslint/parser": "^8.43.0", "chrome-devtools-frontend": "1.0.1547147", - "core-js": "3.46.0", + "core-js": "3.47.0", "debug": "4.4.3", "eslint": "^9.35.0", "eslint-import-resolver-typescript": "^4.4.4", "eslint-plugin-import": "^2.32.0", "globals": "^16.4.0", "prettier": "^3.6.2", - "puppeteer": "24.30.0", + "puppeteer": "24.31.0", "rollup": "4.53.2", "rollup-plugin-cleanup": "^3.2.1", "rollup-plugin-license": "^3.6.0", From cc0a351ce003695c3fd379781a16f0526725de5d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Nov 2025 09:43:44 +0100 Subject: [PATCH 084/456] chore(deps-dev): bump the dev-dependencies group with 6 updates (#596) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the dev-dependencies group with 6 updates: | Package | From | To | | --- | --- | --- | | [@stylistic/eslint-plugin](https://github.com/eslint-stylistic/eslint-stylistic/tree/HEAD/packages/eslint-plugin) | `5.5.0` | `5.6.1` | | [@types/sinon](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/sinon) | `20.0.0` | `21.0.0` | | [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) | `8.46.4` | `8.47.0` | | [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) | `8.46.4` | `8.47.0` | | [rollup](https://github.com/rollup/rollup) | `4.53.2` | `4.53.3` | | [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) | `8.46.4` | `8.47.0` | Updates `@stylistic/eslint-plugin` from 5.5.0 to 5.6.1
Release notes

Sourced from @​stylistic/eslint-plugin's releases.

v5.6.1

5.6.1 (2025-11-19)

Bug Fixes

  • computed-property-spacing: guard with node.type instead of computed check (#1054) (c486861)

v5.6.0

5.6.0 (2025-11-18)

Features

  • arrow-spacing: support TSFunctionType and TSConstructorType (#1036) (a5380d2)
  • computed-property-spacing: support TSIndexedAccessType (#1047) (d6e7437)
  • no-whitespace-before-property: support TS nodes (#1046) (684ff76)
  • type-annotation-spacing: deprecate overrides.arrow in favor of arrow-spacing (#1037) (598bbb0)
  • update deps (#1052) (29d09cc)

Bug Fixes

  • type-generic-spacing: ignore spaces after new in TSConstructorType (#1044) (aa0c6ac)

Documentation

  • no-extra-parens: add missing ] in various examples. (#1048) (88248cf)
  • quotes: update deprecated options to latest and add tips (#1026) (dc949f8)

Build Related

  • deps: bump actions/download-artifact from 5 to 6 (#1033) (8845342)
  • deps: bump actions/setup-node from 5 to 6 (#1029) (aa2f991)
  • deps: bump actions/upload-artifact from 4 to 5 (#1034) (657e8ce)

Chores

  • indent: cleanup TestCaseError#nodeType (#1043) (3fcec3e)
  • indent: simplify logic in JSXText (#1027) (d83c5b0)
  • space-infix-ops: simplify logic (#1022) (6d57a7d)
  • type-annotation-spacing: simplify type definitions (#1031) (aa6a0a3)
  • type-annotation-spacing: use unindent for better collapse (#1032) (839502a)
  • type-generic-spacing: remove logic duplicated with comma-spacing (#1024) (361cfc8)
  • upgrade to vitest v4 (#1040) (c9d684b)
Changelog

Sourced from @​stylistic/eslint-plugin's changelog.

5.6.1 (2025-11-19)

Bug Fixes

  • computed-property-spacing: guard with node.type instead of computed check (#1054) (c486861)

5.6.0 (2025-11-18)

Features

  • arrow-spacing: support TSFunctionType and TSConstructorType (#1036) (a5380d2)
  • computed-property-spacing: support TSIndexedAccessType (#1047) (d6e7437)
  • no-whitespace-before-property: support TS nodes (#1046) (684ff76)
  • type-annotation-spacing: deprecate overrides.arrow in favor of arrow-spacing (#1037) (598bbb0)
  • update deps (#1052) (29d09cc)

Bug Fixes

  • type-generic-spacing: ignore spaces after new in TSConstructorType (#1044) (aa0c6ac)

Documentation

  • no-extra-parens: add missing ] in various examples. (#1048) (88248cf)
  • quotes: update deprecated options to latest and add tips (#1026) (dc949f8)

Build Related

  • deps: bump actions/download-artifact from 5 to 6 (#1033) (8845342)
  • deps: bump actions/setup-node from 5 to 6 (#1029) (aa2f991)
  • deps: bump actions/upload-artifact from 4 to 5 (#1034) (657e8ce)

Chores

  • indent: cleanup TestCaseError#nodeType (#1043) (3fcec3e)
  • indent: simplify logic in JSXText (#1027) (d83c5b0)
  • space-infix-ops: simplify logic (#1022) (6d57a7d)
  • type-annotation-spacing: simplify type definitions (#1031) (aa6a0a3)
  • type-annotation-spacing: use unindent for better collapse (#1032) (839502a)
  • type-generic-spacing: remove logic duplicated with comma-spacing (#1024) (361cfc8)
  • upgrade to vitest v4 (#1040) (c9d684b)
Commits
  • 52b2b2e chore: release v5.6.1 (main) (#1055)
  • c486861 fix(computed-property-spacing): guard with node.type instead of computed ...
  • 1f5526a chore: release v5.6.0 (main) (#1023)
  • 684ff76 feat(no-whitespace-before-property): support TS nodes (#1046)
  • d6e7437 feat(computed-property-spacing): support TSIndexedAccessType (#1047)
  • 88248cf docs(no-extra-parens): add missing ] in various examples. (#1048)
  • aa0c6ac fix(type-generic-spacing): ignore spaces after new in TSConstructorType ...
  • 3fcec3e test(indent): cleanup TestCaseError#nodeType (#1043)
  • 598bbb0 feat(type-annotation-spacing): deprecate overrides.arrow in favor of `arrow...
  • a5380d2 feat(arrow-spacing): support TSFunctionType and TSConstructorType (#1036)
  • Additional commits viewable in compare view

Updates `@types/sinon` from 20.0.0 to 21.0.0
Commits

Updates `@typescript-eslint/eslint-plugin` from 8.46.4 to 8.47.0
Release notes

Sourced from @​typescript-eslint/eslint-plugin's releases.

v8.47.0

8.47.0 (2025-11-17)

🚀 Features

  • eslint-plugin: [no-unused-private-class-members] new extension rule (#10913)

❤️ Thank You

You can read about our versioning strategy and releases on our website.

Changelog

Sourced from @​typescript-eslint/eslint-plugin's changelog.

8.47.0 (2025-11-17)

🚀 Features

  • eslint-plugin: [no-unused-private-class-members] new extension rule (#10913)

❤️ Thank You

You can read about our versioning strategy and releases on our website.

Commits
  • 28cf803 chore(release): publish 8.47.0
  • 6c6db24 feat(eslint-plugin): [no-unused-private-class-members] new extension rule (#1...
  • See full diff in compare view

Updates `@typescript-eslint/parser` from 8.46.4 to 8.47.0
Release notes

Sourced from @​typescript-eslint/parser's releases.

v8.47.0

8.47.0 (2025-11-17)

🚀 Features

  • eslint-plugin: [no-unused-private-class-members] new extension rule (#10913)

❤️ Thank You

You can read about our versioning strategy and releases on our website.

Changelog

Sourced from @​typescript-eslint/parser's changelog.

8.47.0 (2025-11-17)

This was a version bump only for parser to align it with other projects, there were no code changes.

You can read about our versioning strategy and releases on our website.

Commits

Updates `rollup` from 4.53.2 to 4.53.3
Release notes

Sourced from rollup's releases.

v4.53.3

4.53.3

2025-11-19

Bug Fixes

  • Fix an error where too many modules where flagged for having an unused external import (#6182)
  • Fix an error where an assignment was wrongly tree-shaken when mutating it (#6183)

Pull Requests

Changelog

Sourced from rollup's changelog.

4.53.3

2025-11-19

Bug Fixes

  • Fix an error where too many modules where flagged for having an unused external import (#6182)
  • Fix an error where an assignment was wrongly tree-shaken when mutating it (#6183)

Pull Requests

Commits
  • 998b595 4.53.3
  • ef834c2 Tracing the importers chain for exported variables in external module (#6182)
  • fb21d56 Check if left side is included when checking if assigning to an assignment ha...
  • 4b4581d Add test-install CI job to test packaging, installation and importing of roll...
  • 18ee41b fix(deps): lock file maintenance minor/patch updates (#6180)
  • f0a80d1 Re-enable TypeScript test (#6174)
  • See full diff in compare view

Updates `typescript-eslint` from 8.46.4 to 8.47.0
Release notes

Sourced from typescript-eslint's releases.

v8.47.0

8.47.0 (2025-11-17)

🚀 Features

  • eslint-plugin: [no-unused-private-class-members] new extension rule (#10913)

❤️ Thank You

You can read about our versioning strategy and releases on our website.

Changelog

Sourced from typescript-eslint's changelog.

8.47.0 (2025-11-17)

This was a version bump only for typescript-eslint to align it with other projects, there were no code changes.

You can read about our versioning strategy and releases on our website.

Commits

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 320 +++++++++++++++++++++++----------------------- package.json | 4 +- 2 files changed, 162 insertions(+), 162 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4bdc6c6a6..6570ba2ea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,7 +21,7 @@ "@types/debug": "^4.1.12", "@types/filesystem": "^0.0.36", "@types/node": "^24.3.3", - "@types/sinon": "^20.0.0", + "@types/sinon": "^21.0.0", "@types/yargs": "^17.0.33", "@typescript-eslint/eslint-plugin": "^8.43.0", "@typescript-eslint/parser": "^8.43.0", @@ -34,7 +34,7 @@ "globals": "^16.4.0", "prettier": "^3.6.2", "puppeteer": "24.31.0", - "rollup": "4.53.2", + "rollup": "4.53.3", "rollup-plugin-cleanup": "^3.2.1", "rollup-plugin-license": "^3.6.0", "sinon": "^21.0.0", @@ -713,9 +713,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.2.tgz", - "integrity": "sha512-yDPzwsgiFO26RJA4nZo8I+xqzh7sJTZIWQOxn+/XOdPE31lAvLIYCKqjV+lNH/vxE2L2iH3plKxDCRK6i+CwhA==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz", + "integrity": "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==", "cpu": [ "arm" ], @@ -727,9 +727,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.2.tgz", - "integrity": "sha512-k8FontTxIE7b0/OGKeSN5B6j25EuppBcWM33Z19JoVT7UTXFSo3D9CdU39wGTeb29NO3XxpMNauh09B+Ibw+9g==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz", + "integrity": "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==", "cpu": [ "arm64" ], @@ -741,9 +741,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.2.tgz", - "integrity": "sha512-A6s4gJpomNBtJ2yioj8bflM2oogDwzUiMl2yNJ2v9E7++sHrSrsQ29fOfn5DM/iCzpWcebNYEdXpaK4tr2RhfQ==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz", + "integrity": "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==", "cpu": [ "arm64" ], @@ -755,9 +755,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.2.tgz", - "integrity": "sha512-e6XqVmXlHrBlG56obu9gDRPW3O3hLxpwHpLsBJvuI8qqnsrtSZ9ERoWUXtPOkY8c78WghyPHZdmPhHLWNdAGEw==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.3.tgz", + "integrity": "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==", "cpu": [ "x64" ], @@ -769,9 +769,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.2.tgz", - "integrity": "sha512-v0E9lJW8VsrwPux5Qe5CwmH/CF/2mQs6xU1MF3nmUxmZUCHazCjLgYvToOk+YuuUqLQBio1qkkREhxhc656ViA==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz", + "integrity": "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==", "cpu": [ "arm64" ], @@ -783,9 +783,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.2.tgz", - "integrity": "sha512-ClAmAPx3ZCHtp6ysl4XEhWU69GUB1D+s7G9YjHGhIGCSrsg00nEGRRZHmINYxkdoJehde8VIsDC5t9C0gb6yqA==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz", + "integrity": "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==", "cpu": [ "x64" ], @@ -797,9 +797,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.2.tgz", - "integrity": "sha512-EPlb95nUsz6Dd9Qy13fI5kUPXNSljaG9FiJ4YUGU1O/Q77i5DYFW5KR8g1OzTcdZUqQQ1KdDqsTohdFVwCwjqg==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz", + "integrity": "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==", "cpu": [ "arm" ], @@ -811,9 +811,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.2.tgz", - "integrity": "sha512-BOmnVW+khAUX+YZvNfa0tGTEMVVEerOxN0pDk2E6N6DsEIa2Ctj48FOMfNDdrwinocKaC7YXUZ1pHlKpnkja/Q==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz", + "integrity": "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==", "cpu": [ "arm" ], @@ -825,9 +825,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.2.tgz", - "integrity": "sha512-Xt2byDZ+6OVNuREgBXr4+CZDJtrVso5woFtpKdGPhpTPHcNG7D8YXeQzpNbFRxzTVqJf7kvPMCub/pcGUWgBjA==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz", + "integrity": "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==", "cpu": [ "arm64" ], @@ -839,9 +839,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.2.tgz", - "integrity": "sha512-+LdZSldy/I9N8+klim/Y1HsKbJ3BbInHav5qE9Iy77dtHC/pibw1SR/fXlWyAk0ThnpRKoODwnAuSjqxFRDHUQ==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz", + "integrity": "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==", "cpu": [ "arm64" ], @@ -853,9 +853,9 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.2.tgz", - "integrity": "sha512-8ms8sjmyc1jWJS6WdNSA23rEfdjWB30LH8Wqj0Cqvv7qSHnvw6kgMMXRdop6hkmGPlyYBdRPkjJnj3KCUHV/uQ==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz", + "integrity": "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==", "cpu": [ "loong64" ], @@ -867,9 +867,9 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.2.tgz", - "integrity": "sha512-3HRQLUQbpBDMmzoxPJYd3W6vrVHOo2cVW8RUo87Xz0JPJcBLBr5kZ1pGcQAhdZgX9VV7NbGNipah1omKKe23/g==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz", + "integrity": "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==", "cpu": [ "ppc64" ], @@ -881,9 +881,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.2.tgz", - "integrity": "sha512-fMjKi+ojnmIvhk34gZP94vjogXNNUKMEYs+EDaB/5TG/wUkoeua7p7VCHnE6T2Tx+iaghAqQX8teQzcvrYpaQA==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz", + "integrity": "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==", "cpu": [ "riscv64" ], @@ -895,9 +895,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.2.tgz", - "integrity": "sha512-XuGFGU+VwUUV5kLvoAdi0Wz5Xbh2SrjIxCtZj6Wq8MDp4bflb/+ThZsVxokM7n0pcbkEr2h5/pzqzDYI7cCgLQ==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz", + "integrity": "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==", "cpu": [ "riscv64" ], @@ -909,9 +909,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.2.tgz", - "integrity": "sha512-w6yjZF0P+NGzWR3AXWX9zc0DNEGdtvykB03uhonSHMRa+oWA6novflo2WaJr6JZakG2ucsyb+rvhrKac6NIy+w==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz", + "integrity": "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==", "cpu": [ "s390x" ], @@ -923,9 +923,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.2.tgz", - "integrity": "sha512-yo8d6tdfdeBArzC7T/PnHd7OypfI9cbuZzPnzLJIyKYFhAQ8SvlkKtKBMbXDxe1h03Rcr7u++nFS7tqXz87Gtw==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz", + "integrity": "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==", "cpu": [ "x64" ], @@ -937,9 +937,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.2.tgz", - "integrity": "sha512-ah59c1YkCxKExPP8O9PwOvs+XRLKwh/mV+3YdKqQ5AMQ0r4M4ZDuOrpWkUaqO7fzAHdINzV9tEVu8vNw48z0lA==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz", + "integrity": "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==", "cpu": [ "x64" ], @@ -951,9 +951,9 @@ ] }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.2.tgz", - "integrity": "sha512-4VEd19Wmhr+Zy7hbUsFZ6YXEiP48hE//KPLCSVNY5RMGX2/7HZ+QkN55a3atM1C/BZCGIgqN+xrVgtdak2S9+A==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz", + "integrity": "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==", "cpu": [ "arm64" ], @@ -965,9 +965,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.2.tgz", - "integrity": "sha512-IlbHFYc/pQCgew/d5fslcy1KEaYVCJ44G8pajugd8VoOEI8ODhtb/j8XMhLpwHCMB3yk2J07ctup10gpw2nyMA==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.3.tgz", + "integrity": "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==", "cpu": [ "arm64" ], @@ -979,9 +979,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.2.tgz", - "integrity": "sha512-lNlPEGgdUfSzdCWU176ku/dQRnA7W+Gp8d+cWv73jYrb8uT7HTVVxq62DUYxjbaByuf1Yk0RIIAbDzp+CnOTFg==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz", + "integrity": "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==", "cpu": [ "ia32" ], @@ -993,9 +993,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.2.tgz", - "integrity": "sha512-S6YojNVrHybQis2lYov1sd+uj7K0Q05NxHcGktuMMdIQ2VixGwAfbJ23NnlvvVV1bdpR2m5MsNBViHJKcA4ADw==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz", + "integrity": "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==", "cpu": [ "x64" ], @@ -1007,9 +1007,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.2.tgz", - "integrity": "sha512-k+/Rkcyx//P6fetPoLMb8pBeqJBNGx81uuf7iljX9++yNBVRDQgD04L+SVXmXmh5ZP4/WOp4mWF0kmi06PW2tA==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz", + "integrity": "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==", "cpu": [ "x64" ], @@ -1069,14 +1069,14 @@ } }, "node_modules/@stylistic/eslint-plugin": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-5.5.0.tgz", - "integrity": "sha512-IeZF+8H0ns6prg4VrkhgL+yrvDXWDH2cKchrbh80ejG9dQgZWp10epHMbgRuQvgchLII/lfh6Xn3lu6+6L86Hw==", + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-5.6.1.tgz", + "integrity": "sha512-JCs+MqoXfXrRPGbGmho/zGS/jMcn3ieKl/A8YImqib76C8kjgZwq5uUFzc30lJkMvcchuRn6/v8IApLxli3Jyw==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.0", - "@typescript-eslint/types": "^8.46.1", + "@typescript-eslint/types": "^8.47.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "estraverse": "^5.3.0", @@ -1180,9 +1180,9 @@ "license": "MIT" }, "node_modules/@types/sinon": { - "version": "20.0.0", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-20.0.0.tgz", - "integrity": "sha512-etYGUC6IEevDGSWvR9WrECRA01ucR2/Oi9XMBUAdV0g4bLkNf4HlZWGiGlDOq5lgwXRwcV+PSeKgFcW4QzzYOg==", + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-21.0.0.tgz", + "integrity": "sha512-+oHKZ0lTI+WVLxx1IbJDNmReQaIsQJjN2e7UUrJHEeByG7bFeKJYsv1E75JxTQ9QKJDp21bAa/0W2Xo4srsDnw==", "dev": true, "license": "MIT", "dependencies": { @@ -1225,17 +1225,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.46.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.4.tgz", - "integrity": "sha512-R48VhmTJqplNyDxCyqqVkFSZIx1qX6PzwqgcXn1olLrzxcSBDlOsbtcnQuQhNtnNiJ4Xe5gREI1foajYaYU2Vg==", + "version": "8.47.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.47.0.tgz", + "integrity": "sha512-fe0rz9WJQ5t2iaLfdbDc9T80GJy0AeO453q8C3YCilnGozvOyCG5t+EZtg7j7D88+c3FipfP/x+wzGnh1xp8ZA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.46.4", - "@typescript-eslint/type-utils": "8.46.4", - "@typescript-eslint/utils": "8.46.4", - "@typescript-eslint/visitor-keys": "8.46.4", + "@typescript-eslint/scope-manager": "8.47.0", + "@typescript-eslint/type-utils": "8.47.0", + "@typescript-eslint/utils": "8.47.0", + "@typescript-eslint/visitor-keys": "8.47.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -1255,16 +1255,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.46.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.46.4.tgz", - "integrity": "sha512-tK3GPFWbirvNgsNKto+UmB/cRtn6TZfyw0D6IKrW55n6Vbs7KJoZtI//kpTKzE/DUmmnAFD8/Ca46s7Obs92/w==", + "version": "8.47.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.47.0.tgz", + "integrity": "sha512-lJi3PfxVmo0AkEY93ecfN+r8SofEqZNGByvHAI3GBLrvt1Cw6H5k1IM02nSzu0RfUafr2EvFSw0wAsZgubNplQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.46.4", - "@typescript-eslint/types": "8.46.4", - "@typescript-eslint/typescript-estree": "8.46.4", - "@typescript-eslint/visitor-keys": "8.46.4", + "@typescript-eslint/scope-manager": "8.47.0", + "@typescript-eslint/types": "8.47.0", + "@typescript-eslint/typescript-estree": "8.47.0", + "@typescript-eslint/visitor-keys": "8.47.0", "debug": "^4.3.4" }, "engines": { @@ -1280,14 +1280,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.46.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.46.4.tgz", - "integrity": "sha512-nPiRSKuvtTN+no/2N1kt2tUh/HoFzeEgOm9fQ6XQk4/ApGqjx0zFIIaLJ6wooR1HIoozvj2j6vTi/1fgAz7UYQ==", + "version": "8.47.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.47.0.tgz", + "integrity": "sha512-2X4BX8hUeB5JcA1TQJ7GjcgulXQ+5UkNb0DL8gHsHUHdFoiCTJoYLTpib3LtSDPZsRET5ygN4qqIWrHyYIKERA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.46.4", - "@typescript-eslint/types": "^8.46.4", + "@typescript-eslint/tsconfig-utils": "^8.47.0", + "@typescript-eslint/types": "^8.47.0", "debug": "^4.3.4" }, "engines": { @@ -1302,14 +1302,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.46.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.46.4.tgz", - "integrity": "sha512-tMDbLGXb1wC+McN1M6QeDx7P7c0UWO5z9CXqp7J8E+xGcJuUuevWKxuG8j41FoweS3+L41SkyKKkia16jpX7CA==", + "version": "8.47.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.47.0.tgz", + "integrity": "sha512-a0TTJk4HXMkfpFkL9/WaGTNuv7JWfFTQFJd6zS9dVAjKsojmv9HT55xzbEpnZoY+VUb+YXLMp+ihMLz/UlZfDg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.46.4", - "@typescript-eslint/visitor-keys": "8.46.4" + "@typescript-eslint/types": "8.47.0", + "@typescript-eslint/visitor-keys": "8.47.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1320,9 +1320,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.46.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.46.4.tgz", - "integrity": "sha512-+/XqaZPIAk6Cjg7NWgSGe27X4zMGqrFqZ8atJsX3CWxH/jACqWnrWI68h7nHQld0y+k9eTTjb9r+KU4twLoo9A==", + "version": "8.47.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.47.0.tgz", + "integrity": "sha512-ybUAvjy4ZCL11uryalkKxuT3w3sXJAuWhOoGS3T/Wu+iUu1tGJmk5ytSY8gbdACNARmcYEB0COksD2j6hfGK2g==", "dev": true, "license": "MIT", "engines": { @@ -1337,15 +1337,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.46.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.46.4.tgz", - "integrity": "sha512-V4QC8h3fdT5Wro6vANk6eojqfbv5bpwHuMsBcJUJkqs2z5XnYhJzyz9Y02eUmF9u3PgXEUiOt4w4KHR3P+z0PQ==", + "version": "8.47.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.47.0.tgz", + "integrity": "sha512-QC9RiCmZ2HmIdCEvhd1aJELBlD93ErziOXXlHEZyuBo3tBiAZieya0HLIxp+DoDWlsQqDawyKuNEhORyku+P8A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.46.4", - "@typescript-eslint/typescript-estree": "8.46.4", - "@typescript-eslint/utils": "8.46.4", + "@typescript-eslint/types": "8.47.0", + "@typescript-eslint/typescript-estree": "8.47.0", + "@typescript-eslint/utils": "8.47.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -1362,9 +1362,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.46.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.46.4.tgz", - "integrity": "sha512-USjyxm3gQEePdUwJBFjjGNG18xY9A2grDVGuk7/9AkjIF1L+ZrVnwR5VAU5JXtUnBL/Nwt3H31KlRDaksnM7/w==", + "version": "8.47.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.47.0.tgz", + "integrity": "sha512-nHAE6bMKsizhA2uuYZbEbmp5z2UpffNrPEqiKIeN7VsV6UY/roxanWfoRrf6x/k9+Obf+GQdkm0nPU+vnMXo9A==", "dev": true, "license": "MIT", "engines": { @@ -1376,16 +1376,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.46.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.46.4.tgz", - "integrity": "sha512-7oV2qEOr1d4NWNmpXLR35LvCfOkTNymY9oyW+lUHkmCno7aOmIf/hMaydnJBUTBMRCOGZh8YjkFOc8dadEoNGA==", + "version": "8.47.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.47.0.tgz", + "integrity": "sha512-k6ti9UepJf5NpzCjH31hQNLHQWupTRPhZ+KFF8WtTuTpy7uHPfeg2NM7cP27aCGajoEplxJDFVCEm9TGPYyiVg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.46.4", - "@typescript-eslint/tsconfig-utils": "8.46.4", - "@typescript-eslint/types": "8.46.4", - "@typescript-eslint/visitor-keys": "8.46.4", + "@typescript-eslint/project-service": "8.47.0", + "@typescript-eslint/tsconfig-utils": "8.47.0", + "@typescript-eslint/types": "8.47.0", + "@typescript-eslint/visitor-keys": "8.47.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -1405,16 +1405,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.46.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.46.4.tgz", - "integrity": "sha512-AbSv11fklGXV6T28dp2Me04Uw90R2iJ30g2bgLz529Koehrmkbs1r7paFqr1vPCZi7hHwYxYtxfyQMRC8QaVSg==", + "version": "8.47.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.47.0.tgz", + "integrity": "sha512-g7XrNf25iL4TJOiPqatNuaChyqt49a/onq5YsJ9+hXeugK+41LVg7AxikMfM02PC6jbNtZLCJj6AUcQXJS/jGQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.46.4", - "@typescript-eslint/types": "8.46.4", - "@typescript-eslint/typescript-estree": "8.46.4" + "@typescript-eslint/scope-manager": "8.47.0", + "@typescript-eslint/types": "8.47.0", + "@typescript-eslint/typescript-estree": "8.47.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1429,13 +1429,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.46.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.46.4.tgz", - "integrity": "sha512-/++5CYLQqsO9HFGLI7APrxBJYo+5OCMpViuhV8q5/Qa3o5mMrF//eQHks+PXcsAVaLdn817fMuS7zqoXNNZGaw==", + "version": "8.47.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.47.0.tgz", + "integrity": "sha512-SIV3/6eftCy1bNzCQoPmbWsRLujS8t5iDIZ4spZOBHqrM+yfX2ogg8Tt3PDTAVKw3sSCiUgg30uOAvK2r9zGjQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.46.4", + "@typescript-eslint/types": "8.47.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -5709,9 +5709,9 @@ } }, "node_modules/rollup": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.2.tgz", - "integrity": "sha512-MHngMYwGJVi6Fmnk6ISmnk7JAHRNF0UkuucA0CUW3N3a4KnONPEZz+vUanQP/ZC/iY1Qkf3bwPWzyY84wEks1g==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz", + "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==", "dev": true, "license": "MIT", "dependencies": { @@ -5725,28 +5725,28 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.53.2", - "@rollup/rollup-android-arm64": "4.53.2", - "@rollup/rollup-darwin-arm64": "4.53.2", - "@rollup/rollup-darwin-x64": "4.53.2", - "@rollup/rollup-freebsd-arm64": "4.53.2", - "@rollup/rollup-freebsd-x64": "4.53.2", - "@rollup/rollup-linux-arm-gnueabihf": "4.53.2", - "@rollup/rollup-linux-arm-musleabihf": "4.53.2", - "@rollup/rollup-linux-arm64-gnu": "4.53.2", - "@rollup/rollup-linux-arm64-musl": "4.53.2", - "@rollup/rollup-linux-loong64-gnu": "4.53.2", - "@rollup/rollup-linux-ppc64-gnu": "4.53.2", - "@rollup/rollup-linux-riscv64-gnu": "4.53.2", - "@rollup/rollup-linux-riscv64-musl": "4.53.2", - "@rollup/rollup-linux-s390x-gnu": "4.53.2", - "@rollup/rollup-linux-x64-gnu": "4.53.2", - "@rollup/rollup-linux-x64-musl": "4.53.2", - "@rollup/rollup-openharmony-arm64": "4.53.2", - "@rollup/rollup-win32-arm64-msvc": "4.53.2", - "@rollup/rollup-win32-ia32-msvc": "4.53.2", - "@rollup/rollup-win32-x64-gnu": "4.53.2", - "@rollup/rollup-win32-x64-msvc": "4.53.2", + "@rollup/rollup-android-arm-eabi": "4.53.3", + "@rollup/rollup-android-arm64": "4.53.3", + "@rollup/rollup-darwin-arm64": "4.53.3", + "@rollup/rollup-darwin-x64": "4.53.3", + "@rollup/rollup-freebsd-arm64": "4.53.3", + "@rollup/rollup-freebsd-x64": "4.53.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.53.3", + "@rollup/rollup-linux-arm-musleabihf": "4.53.3", + "@rollup/rollup-linux-arm64-gnu": "4.53.3", + "@rollup/rollup-linux-arm64-musl": "4.53.3", + "@rollup/rollup-linux-loong64-gnu": "4.53.3", + "@rollup/rollup-linux-ppc64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-musl": "4.53.3", + "@rollup/rollup-linux-s390x-gnu": "4.53.3", + "@rollup/rollup-linux-x64-gnu": "4.53.3", + "@rollup/rollup-linux-x64-musl": "4.53.3", + "@rollup/rollup-openharmony-arm64": "4.53.3", + "@rollup/rollup-win32-arm64-msvc": "4.53.3", + "@rollup/rollup-win32-ia32-msvc": "4.53.3", + "@rollup/rollup-win32-x64-gnu": "4.53.3", + "@rollup/rollup-win32-x64-msvc": "4.53.3", "fsevents": "~2.3.2" } }, @@ -6728,16 +6728,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.46.4", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.46.4.tgz", - "integrity": "sha512-KALyxkpYV5Ix7UhvjTwJXZv76VWsHG+NjNlt/z+a17SOQSiOcBdUXdbJdyXi7RPxrBFECtFOiPwUJQusJuCqrg==", + "version": "8.47.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.47.0.tgz", + "integrity": "sha512-Lwe8i2XQ3WoMjua/r1PHrCTpkubPYJCAfOurtn+mtTzqB6jNd+14n9UN1bJ4s3F49x9ixAm0FLflB/JzQ57M8Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.46.4", - "@typescript-eslint/parser": "8.46.4", - "@typescript-eslint/typescript-estree": "8.46.4", - "@typescript-eslint/utils": "8.46.4" + "@typescript-eslint/eslint-plugin": "8.47.0", + "@typescript-eslint/parser": "8.47.0", + "@typescript-eslint/typescript-estree": "8.47.0", + "@typescript-eslint/utils": "8.47.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" diff --git a/package.json b/package.json index 86f29fa73..d67a22684 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "@types/debug": "^4.1.12", "@types/filesystem": "^0.0.36", "@types/node": "^24.3.3", - "@types/sinon": "^20.0.0", + "@types/sinon": "^21.0.0", "@types/yargs": "^17.0.33", "@typescript-eslint/eslint-plugin": "^8.43.0", "@typescript-eslint/parser": "^8.43.0", @@ -61,7 +61,7 @@ "globals": "^16.4.0", "prettier": "^3.6.2", "puppeteer": "24.31.0", - "rollup": "4.53.2", + "rollup": "4.53.3", "rollup-plugin-cleanup": "^3.2.1", "rollup-plugin-license": "^3.6.0", "sinon": "^21.0.0", From 85ba2ddb1119f9e76c2bae292dd80f9a335a9763 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Nov 2025 10:24:59 +0000 Subject: [PATCH 085/456] chore(deps): bump actions/checkout from 5.0.0 to 6.0.0 in the all group (#600) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the all group with 1 update: [actions/checkout](https://github.com/actions/checkout). Updates `actions/checkout` from 5.0.0 to 6.0.0
Release notes

Sourced from actions/checkout's releases.

v6.0.0

What's Changed

Full Changelog: https://github.com/actions/checkout/compare/v5.0.0...v6.0.0

v6-beta

What's Changed

Updated persist-credentials to store the credentials under $RUNNER_TEMP instead of directly in the local git config.

This requires a minimum Actions Runner version of v2.329.0 to access the persisted credentials for Docker container action scenarios.

v5.0.1

What's Changed

Full Changelog: https://github.com/actions/checkout/compare/v5...v5.0.1

Changelog

Sourced from actions/checkout's changelog.

Changelog

V6.0.0

V5.0.1

V5.0.0

V4.3.1

V4.3.0

v4.2.2

v4.2.1

v4.2.0

v4.1.7

v4.1.6

v4.1.5

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/checkout&package-manager=github_actions&previous-version=5.0.0&new-version=6.0.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/pre-release.yml | 2 +- .github/workflows/presubmit.yml | 4 ++-- .github/workflows/publish-to-npm-on-tag.yml | 4 ++-- .github/workflows/run-tests.yml | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/pre-release.yml b/.github/workflows/pre-release.yml index 44750dd67..623b31935 100644 --- a/.github/workflows/pre-release.yml +++ b/.github/workflows/pre-release.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 with: fetch-depth: 2 diff --git a/.github/workflows/presubmit.yml b/.github/workflows/presubmit.yml index 45251394d..8149909e7 100644 --- a/.github/workflows/presubmit.yml +++ b/.github/workflows/presubmit.yml @@ -15,7 +15,7 @@ jobs: steps: - name: Check out repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 with: fetch-depth: 2 @@ -37,7 +37,7 @@ jobs: steps: - name: Check out repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 with: fetch-depth: 2 diff --git a/.github/workflows/publish-to-npm-on-tag.yml b/.github/workflows/publish-to-npm-on-tag.yml index 297990090..a21b97347 100644 --- a/.github/workflows/publish-to-npm-on-tag.yml +++ b/.github/workflows/publish-to-npm-on-tag.yml @@ -25,7 +25,7 @@ jobs: if: ${{ (github.event_name != 'workflow_dispatch') || (inputs.npm-publish && always()) }} steps: - name: Check out repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 with: fetch-depth: 2 @@ -58,7 +58,7 @@ jobs: if: ${{ (github.event_name != 'workflow_dispatch' && needs.publish-to-npm.result == 'success') || (inputs.mcp-publish && always()) }} steps: - name: Check out repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 with: fetch-depth: 2 diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 5649d2ca7..9526aaf22 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -26,7 +26,7 @@ jobs: - 24 steps: - name: Check out repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 with: fetch-depth: 2 From eea5b80165252c9c439cc96fcffbdc15ac163d6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Z=C3=BCnd?= Date: Mon, 24 Nov 2025 11:30:14 +0100 Subject: [PATCH 086/456] chore: change puppeteer/devtools connection adapter (#602) Rather then adapting on the transport layer, we adapt the puppeteer `CDPSession`/`Connection` and make them look like a `CDPConnection`. The class assumes that callers create a dedicated `CDPSession` to be used. The `PuppeteerDevToolsConnection` installs a generic `'*'` event listener on all child sessions to funnel all CDP events into DevTools via `CDPConnectionObservers`. As pointed out in the code comment, we don't need to recursively listen to `'sessionattached'` events on child sessions: Nested `sessionattached` events are reported on all parent sessions. While not strictly necessary, `PuppeteerDevToolsConnection` also uninstalls the CDP event listener when a session gets detached (modulo the root puppeteer `CDPSession`). --- src/DevToolsConnectionAdapter.ts | 110 +++++++++++++++++++++++++------ src/third_party/index.ts | 6 +- 2 files changed, 94 insertions(+), 22 deletions(-) diff --git a/src/DevToolsConnectionAdapter.ts b/src/DevToolsConnectionAdapter.ts index 917575403..2d6287421 100644 --- a/src/DevToolsConnectionAdapter.ts +++ b/src/DevToolsConnectionAdapter.ts @@ -4,38 +4,106 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {ConnectionTransport as DevToolsConnectionTransport} from '../node_modules/chrome-devtools-frontend/mcp/mcp.js'; +import type {CDPConnection as devtools} from '../node_modules/chrome-devtools-frontend/mcp/mcp.js'; -import {type ConnectionTransport} from './third_party/index.js'; +import type * as puppeteer from './third_party/index.js'; +import {CDPSessionEvent} from './third_party/index.js'; /** - * Allows a puppeteer {@link ConnectionTransport} to act like a DevTools {@link Connection}. + * This class makes a puppeteer connection look like DevTools CDPConnection. + * + * Since we connect "root" DevTools targets to specific pages, we scope everything to a puppeteer CDP session. + * + * We don't have to recursively listen for 'sessionattached' as the "root" CDP session sees all child session attached + * events, regardless how deeply nested they are. */ -export class DevToolsConnectionAdapter extends DevToolsConnectionTransport { - #transport: ConnectionTransport | null; - #onDisconnect: ((arg0: string) => void) | null = null; - - constructor(transport: ConnectionTransport) { - super(); - this.#transport = transport; - this.#transport.onclose = () => this.#onDisconnect?.(''); - this.#transport.onmessage = msg => this.onMessage?.(msg); +export class PuppeteerDevToolsConnection implements devtools.CDPConnection { + readonly #connection: puppeteer.Connection; + readonly #observers = new Set(); + readonly #sessionEventHandlers = new Map< + string, + puppeteer.Handler + >(); + + constructor(session: puppeteer.CDPSession) { + this.#connection = session.connection()!; + + session.on( + CDPSessionEvent.SessionAttached, + this.#startForwardingCdpEvents.bind(this), + ); + session.on( + CDPSessionEvent.SessionDetached, + this.#stopForwardingCdpEvents.bind(this), + ); + + this.#startForwardingCdpEvents(session); + } + + send( + method: T, + params: devtools.CommandParams, + sessionId: string | undefined, + ): Promise<{result: devtools.CommandResult} | {error: devtools.CDPError}> { + if (sessionId === undefined) { + throw new Error( + 'Attempting to send on the root session. This must not happen', + ); + } + const session = this.#connection.session(sessionId); + if (!session) { + throw new Error('Unknown session ' + sessionId); + } + // Rolled protocol version between puppeteer and DevTools doesn't necessarily match + /* eslint-disable @typescript-eslint/no-explicit-any */ + return session + .send(method as any, params) + .then(result => ({result})) + .catch(error => ({error})) as any; + /* eslint-enable @typescript-eslint/no-explicit-any */ + } + + observe(observer: devtools.CDPConnectionObserver): void { + this.#observers.add(observer); } - override setOnMessage(onMessage: (arg0: object | string) => void): void { - this.onMessage = onMessage; + unobserve(observer: devtools.CDPConnectionObserver): void { + this.#observers.delete(observer); } - override setOnDisconnect(onDisconnect: (arg0: string) => void): void { - this.#onDisconnect = onDisconnect; + #startForwardingCdpEvents(session: puppeteer.CDPSession): void { + const handler = this.#handleEvent.bind( + this, + session.id(), + ) as puppeteer.Handler; + this.#sessionEventHandlers.set(session.id(), handler); + session.on('*', handler); } - override sendRawMessage(message: string): void { - this.#transport?.send(message); + #stopForwardingCdpEvents(session: puppeteer.CDPSession): void { + const handler = this.#sessionEventHandlers.get(session.id()); + if (handler) { + session.off('*', handler); + } } - override async disconnect(): Promise { - this.#transport?.close(); - this.#transport = null; + #handleEvent( + sessionId: string, + type: string | symbol | number, + event: any, // eslint-disable-line @typescript-eslint/no-explicit-any + ): void { + if ( + typeof type === 'string' && + type !== CDPSessionEvent.SessionAttached && + type !== CDPSessionEvent.SessionDetached + ) { + this.#observers.forEach(observer => + observer.onEvent({ + method: type as devtools.Event, + sessionId, + params: event, + }), + ); + } } } diff --git a/src/third_party/index.ts b/src/third_party/index.ts index 49ef09c59..6facde826 100644 --- a/src/third_party/index.ts +++ b/src/third_party/index.ts @@ -20,7 +20,11 @@ export { type TextContent, } from '@modelcontextprotocol/sdk/types.js'; export {z as zod} from 'zod'; -export {Locator, PredefinedNetworkConditions} from 'puppeteer-core'; +export { + Locator, + PredefinedNetworkConditions, + CDPSessionEvent, +} from 'puppeteer-core'; export {default as puppeteer} from 'puppeteer-core'; export type * from 'puppeteer-core'; export type {CdpPage} from 'puppeteer-core/internal/cdp/Page.js'; From 94752ffade847671ebfd15e4013a5b5cdf8377df Mon Sep 17 00:00:00 2001 From: zyzyzyryxy <31672205+zyzyzyryxy@users.noreply.github.com> Date: Mon, 24 Nov 2025 13:38:30 +0100 Subject: [PATCH 087/456] fix: prevent dropping license notices on some files when publishing (#604) This PR prevents license notices being dropped when creating package for publication. This can happen when first import in the file is type-only import that gets removed during build. When there is no empty line between the license block comment and such import, the comment is treated as related to the import and gets removed alongside it. Adding an empty line between copyright notice and the import fixes the issue. Co-authored-by: Piotr Paulski --- src/McpContext.ts | 1 + src/McpResponse.ts | 1 + src/WaitForHelper.ts | 1 + src/formatters/snapshotFormatter.ts | 1 + src/logger.ts | 1 + src/third_party/index.ts | 1 + src/utils/keyboard.ts | 1 + tests/DevtoolsUtils.test.ts | 1 + tests/McpContext.test.ts | 1 + tests/McpResponse.test.ts | 1 + tests/PageCollector.test.ts | 1 + tests/browser.test.ts | 1 + tests/cli.test.ts | 1 + tests/index.test.ts | 1 + tests/server.ts | 1 + tests/setup.ts | 1 + tests/tools/console.test.ts | 1 + tests/tools/emulation.test.ts | 1 + tests/tools/input.test.ts | 1 + tests/tools/network.test.ts | 1 + tests/tools/pages.test.ts | 1 + tests/tools/performance.test.ts | 1 + tests/tools/screenshot.test.ts | 1 + tests/tools/script.test.ts | 1 + tests/tools/snapshot.test.ts | 1 + tests/trace-processing/parse.test.ts | 1 + tests/utils.ts | 1 + 27 files changed, 27 insertions(+) diff --git a/src/McpContext.ts b/src/McpContext.ts index 0bdf06637..22d54c948 100644 --- a/src/McpContext.ts +++ b/src/McpContext.ts @@ -3,6 +3,7 @@ * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ + import fs from 'node:fs/promises'; import os from 'node:os'; import path from 'node:path'; diff --git a/src/McpResponse.ts b/src/McpResponse.ts index 8dc0cd07a..0cf549257 100644 --- a/src/McpResponse.ts +++ b/src/McpResponse.ts @@ -3,6 +3,7 @@ * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ + import { AggregatedIssue, Marked, diff --git a/src/WaitForHelper.ts b/src/WaitForHelper.ts index 7a5edaf4e..44dbdd3c2 100644 --- a/src/WaitForHelper.ts +++ b/src/WaitForHelper.ts @@ -3,6 +3,7 @@ * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ + import {logger} from './logger.js'; import type {Page, Protocol, CdpPage} from './third_party/index.js'; diff --git a/src/formatters/snapshotFormatter.ts b/src/formatters/snapshotFormatter.ts index 5018381e6..15464dd22 100644 --- a/src/formatters/snapshotFormatter.ts +++ b/src/formatters/snapshotFormatter.ts @@ -3,6 +3,7 @@ * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ + import type {TextSnapshot, TextSnapshotNode} from '../McpContext.js'; export function formatSnapshotNode( diff --git a/src/logger.ts b/src/logger.ts index 072ece389..4c31f1ffb 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -3,6 +3,7 @@ * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ + import fs from 'node:fs'; import {debug} from './third_party/index.js'; diff --git a/src/third_party/index.ts b/src/third_party/index.ts index 6facde826..83194e38a 100644 --- a/src/third_party/index.ts +++ b/src/third_party/index.ts @@ -3,6 +3,7 @@ * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ + import 'core-js/modules/es.promise.with-resolvers.js'; import 'core-js/proposals/iterator-helpers.js'; diff --git a/src/utils/keyboard.ts b/src/utils/keyboard.ts index 55c56782f..51b0a3f7b 100644 --- a/src/utils/keyboard.ts +++ b/src/utils/keyboard.ts @@ -3,6 +3,7 @@ * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ + import type {KeyInput} from '../third_party/index.js'; // See the KeyInput type for the list of supported keys. diff --git a/tests/DevtoolsUtils.test.ts b/tests/DevtoolsUtils.test.ts index 2c1300841..40ecf2c31 100644 --- a/tests/DevtoolsUtils.test.ts +++ b/tests/DevtoolsUtils.test.ts @@ -3,6 +3,7 @@ * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ + import assert from 'node:assert'; import {describe, it} from 'node:test'; diff --git a/tests/McpContext.test.ts b/tests/McpContext.test.ts index 63887a05a..4b5de0911 100644 --- a/tests/McpContext.test.ts +++ b/tests/McpContext.test.ts @@ -3,6 +3,7 @@ * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ + import assert from 'node:assert'; import {describe, it} from 'node:test'; diff --git a/tests/McpResponse.test.ts b/tests/McpResponse.test.ts index 4cf522525..2c9516bcf 100644 --- a/tests/McpResponse.test.ts +++ b/tests/McpResponse.test.ts @@ -3,6 +3,7 @@ * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ + import assert from 'node:assert'; import {readFile, rm} from 'node:fs/promises'; import {tmpdir} from 'node:os'; diff --git a/tests/PageCollector.test.ts b/tests/PageCollector.test.ts index ef31d0069..bcf17ff54 100644 --- a/tests/PageCollector.test.ts +++ b/tests/PageCollector.test.ts @@ -3,6 +3,7 @@ * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ + import assert from 'node:assert'; import {afterEach, beforeEach, describe, it} from 'node:test'; diff --git a/tests/browser.test.ts b/tests/browser.test.ts index 8066da5fc..3c8e31ca8 100644 --- a/tests/browser.test.ts +++ b/tests/browser.test.ts @@ -3,6 +3,7 @@ * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ + import assert from 'node:assert'; import os from 'node:os'; import path from 'node:path'; diff --git a/tests/cli.test.ts b/tests/cli.test.ts index 19502e28c..c5c8b088b 100644 --- a/tests/cli.test.ts +++ b/tests/cli.test.ts @@ -3,6 +3,7 @@ * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ + import assert from 'node:assert'; import {describe, it} from 'node:test'; diff --git a/tests/index.test.ts b/tests/index.test.ts index 4bbe3ddd6..f31d1a84c 100644 --- a/tests/index.test.ts +++ b/tests/index.test.ts @@ -3,6 +3,7 @@ * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ + import assert from 'node:assert'; import fs from 'node:fs'; import {describe, it} from 'node:test'; diff --git a/tests/server.ts b/tests/server.ts index a0c6e318d..fb6fcd5b4 100644 --- a/tests/server.ts +++ b/tests/server.ts @@ -3,6 +3,7 @@ * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ + import http, { type IncomingMessage, type Server, diff --git a/tests/setup.ts b/tests/setup.ts index ce4e1b21b..f79b8735e 100644 --- a/tests/setup.ts +++ b/tests/setup.ts @@ -3,6 +3,7 @@ * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ + import '../src/polyfill.js'; import path from 'node:path'; diff --git a/tests/tools/console.test.ts b/tests/tools/console.test.ts index 73c6f092f..c7f6a97da 100644 --- a/tests/tools/console.test.ts +++ b/tests/tools/console.test.ts @@ -3,6 +3,7 @@ * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ + import assert from 'node:assert'; import {afterEach, before, beforeEach, describe, it} from 'node:test'; diff --git a/tests/tools/emulation.test.ts b/tests/tools/emulation.test.ts index 568631cc8..35b79ea67 100644 --- a/tests/tools/emulation.test.ts +++ b/tests/tools/emulation.test.ts @@ -3,6 +3,7 @@ * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ + import assert from 'node:assert'; import {describe, it} from 'node:test'; diff --git a/tests/tools/input.test.ts b/tests/tools/input.test.ts index b6f39622a..230b8a5dc 100644 --- a/tests/tools/input.test.ts +++ b/tests/tools/input.test.ts @@ -3,6 +3,7 @@ * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ + import assert from 'node:assert'; import fs from 'node:fs/promises'; import path from 'node:path'; diff --git a/tests/tools/network.test.ts b/tests/tools/network.test.ts index 2a6a1680d..740fb3a16 100644 --- a/tests/tools/network.test.ts +++ b/tests/tools/network.test.ts @@ -3,6 +3,7 @@ * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ + import assert from 'node:assert'; import {describe, it} from 'node:test'; diff --git a/tests/tools/pages.test.ts b/tests/tools/pages.test.ts index a4a76bcd7..d116e3523 100644 --- a/tests/tools/pages.test.ts +++ b/tests/tools/pages.test.ts @@ -3,6 +3,7 @@ * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ + import assert from 'node:assert'; import {describe, it} from 'node:test'; diff --git a/tests/tools/performance.test.ts b/tests/tools/performance.test.ts index 32425788b..900a10c28 100644 --- a/tests/tools/performance.test.ts +++ b/tests/tools/performance.test.ts @@ -3,6 +3,7 @@ * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ + import assert from 'node:assert'; import {describe, it, afterEach} from 'node:test'; diff --git a/tests/tools/screenshot.test.ts b/tests/tools/screenshot.test.ts index ebf207e1f..69e70fe13 100644 --- a/tests/tools/screenshot.test.ts +++ b/tests/tools/screenshot.test.ts @@ -3,6 +3,7 @@ * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ + import assert from 'node:assert'; import {rm, stat, mkdir, chmod, writeFile} from 'node:fs/promises'; import {tmpdir} from 'node:os'; diff --git a/tests/tools/script.test.ts b/tests/tools/script.test.ts index 981ca92e1..b6d52507d 100644 --- a/tests/tools/script.test.ts +++ b/tests/tools/script.test.ts @@ -3,6 +3,7 @@ * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ + import assert from 'node:assert'; import {describe, it} from 'node:test'; diff --git a/tests/tools/snapshot.test.ts b/tests/tools/snapshot.test.ts index 718ea998c..9dc48d94b 100644 --- a/tests/tools/snapshot.test.ts +++ b/tests/tools/snapshot.test.ts @@ -3,6 +3,7 @@ * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ + import assert from 'node:assert'; import {describe, it} from 'node:test'; diff --git a/tests/trace-processing/parse.test.ts b/tests/trace-processing/parse.test.ts index 79b4ecb57..d54ff24ba 100644 --- a/tests/trace-processing/parse.test.ts +++ b/tests/trace-processing/parse.test.ts @@ -3,6 +3,7 @@ * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ + import assert from 'node:assert'; import {describe, it} from 'node:test'; diff --git a/tests/utils.ts b/tests/utils.ts index a1f97b900..3a7173819 100644 --- a/tests/utils.ts +++ b/tests/utils.ts @@ -3,6 +3,7 @@ * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ + import logger from 'debug'; import type {Browser} from 'puppeteer'; import puppeteer, {Locator} from 'puppeteer'; From 5b84e763555d612d258efd0c9dacfbc48e7ed928 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Nov 2025 14:11:40 +0000 Subject: [PATCH 088/456] chore(deps-dev): bump chrome-devtools-frontend from 1.0.1547147 to 1.0.1547571 in the bundled-devtools group (#599) Bumps the bundled-devtools group with 1 update: [chrome-devtools-frontend](https://github.com/ChromeDevTools/devtools-frontend). Updates `chrome-devtools-frontend` from 1.0.1547147 to 1.0.1547571
Commits
  • 0832b98 Update DevTools DEPS (trusted)
  • f635ee3 [cleanup] Unify test names
  • 8d5fffb [Conversation History] Fix history context menu
  • c5b0eee Update DevTools DEPS (trusted)
  • 9b07794 Refactor code around AI Assistance
  • 482573e Fix for UI string in AI Assistance panel not being localized
  • 3acf724 [throttling] Update rule list items more cleanly to preserve focus
  • 26d4a81 Roll browser-protocol
  • c67929c [cleanup] Unify test naming
  • 1931fb3 Update Chrome (for Testing) PIN
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=chrome-devtools-frontend&package-manager=npm_and_yarn&previous-version=1.0.1547147&new-version=1.0.1547571)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
--------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Alex Rudenko --- package-lock.json | 8 ++++---- package.json | 2 +- src/McpResponse.ts | 5 +++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6570ba2ea..feb38eee9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,7 +25,7 @@ "@types/yargs": "^17.0.33", "@typescript-eslint/eslint-plugin": "^8.43.0", "@typescript-eslint/parser": "^8.43.0", - "chrome-devtools-frontend": "1.0.1547147", + "chrome-devtools-frontend": "1.0.1547571", "core-js": "3.47.0", "debug": "4.4.3", "eslint": "^9.35.0", @@ -2299,9 +2299,9 @@ } }, "node_modules/chrome-devtools-frontend": { - "version": "1.0.1547147", - "resolved": "https://registry.npmjs.org/chrome-devtools-frontend/-/chrome-devtools-frontend-1.0.1547147.tgz", - "integrity": "sha512-eUNXqCEo1Ioa5JEVVjbf3iZxoYjEswBV1GcokX/hrwSUYW9IVMtilqEvPXaD7i7gPJXHBK0tJUNZMAlK7+KClQ==", + "version": "1.0.1547571", + "resolved": "https://registry.npmjs.org/chrome-devtools-frontend/-/chrome-devtools-frontend-1.0.1547571.tgz", + "integrity": "sha512-NzfUHUzCu+4Vv68FuVZnacfGOqukqyG1hVj2ACjZM7BOJc+V3DY17CJJDRQilkVUYCQa3GskZUA8B9mYmwTbNg==", "dev": true, "license": "BSD-3-Clause" }, diff --git a/package.json b/package.json index d67a22684..a8f5c91b2 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "@types/yargs": "^17.0.33", "@typescript-eslint/eslint-plugin": "^8.43.0", "@typescript-eslint/parser": "^8.43.0", - "chrome-devtools-frontend": "1.0.1547147", + "chrome-devtools-frontend": "1.0.1547571", "core-js": "3.47.0", "debug": "4.4.3", "eslint": "^9.35.0", diff --git a/src/McpResponse.ts b/src/McpResponse.ts index 0cf549257..3fb839868 100644 --- a/src/McpResponse.ts +++ b/src/McpResponse.ts @@ -7,7 +7,7 @@ import { AggregatedIssue, Marked, - findTitleFromMarkdownAst, + MarkdownIssueDescription, } from '../node_modules/chrome-devtools-frontend/mcp/mcp.js'; import type {ConsoleMessageData} from './formatters/consoleFormatter.js'; @@ -319,7 +319,8 @@ export class McpResponse implements Response { return null; } const markdownAst = Marked.Marked.lexer(rawMarkdown); - const title = findTitleFromMarkdownAst(markdownAst); + const title = + MarkdownIssueDescription.findTitleFromMarkdownAst(markdownAst); if (!title) { logger('cannot read issue title from ' + filename); return null; From 326c279179cb2bf93ca67acd351dd5410dd1a250 Mon Sep 17 00:00:00 2001 From: Natasha Gorshunova <47688881+nattallius@users.noreply.github.com> Date: Tue, 25 Nov 2025 11:41:14 +0100 Subject: [PATCH 089/456] chore: report console issues in get_console_messages (#594) Co-authored-by: Natallia Harshunova --- src/DevtoolsUtils.ts | 48 ++++++++ src/McpContext.ts | 7 +- src/McpResponse.ts | 43 +++---- src/formatters/consoleFormatter.ts | 37 +++++- src/issue-descriptions.ts | 5 + tests/DevtoolsUtils.test.ts | 108 +++++++++++++++++- tests/McpResponse.test.ts | 44 +++++++ .../consoleFormatter.test.js.snapshot | 10 ++ tests/formatters/consoleFormatter.test.ts | 30 +++++ tests/tools/console.test.ts | 41 +++++++ 10 files changed, 339 insertions(+), 34 deletions(-) diff --git a/src/DevtoolsUtils.ts b/src/DevtoolsUtils.ts index 997cb86a3..eb7ad3655 100644 --- a/src/DevtoolsUtils.ts +++ b/src/DevtoolsUtils.ts @@ -6,11 +6,17 @@ import { type Issue, + type AggregatedIssue, type IssuesManagerEventTypes, + MarkdownIssueDescription, + Marked, Common, I18n, } from '../node_modules/chrome-devtools-frontend/mcp/mcp.js'; +import {ISSUE_UTILS} from './issue-descriptions.js'; +import {logger} from './logger.js'; + export function extractUrlLikeFromDevToolsTitle( title: string, ): string | undefined { @@ -69,6 +75,48 @@ export class FakeIssuesManager extends Common.ObjectWrapper } } +export function mapIssueToMessageObject(issue: AggregatedIssue) { + const count = issue.getAggregatedIssuesCount(); + const markdownDescription = issue.getDescription(); + const filename = markdownDescription?.file; + if (!markdownDescription) { + logger(`no description found for issue:` + issue.code); + return null; + } + const rawMarkdown = filename + ? ISSUE_UTILS.getIssueDescription(filename) + : null; + if (!rawMarkdown) { + logger(`no markdown ${filename} found for issue:` + issue.code); + return null; + } + let processedMarkdown: string; + let title: string | null; + + try { + processedMarkdown = MarkdownIssueDescription.substitutePlaceholders( + rawMarkdown, + markdownDescription.substitutions, + ); + const markdownAst = Marked.Marked.lexer(processedMarkdown); + title = MarkdownIssueDescription.findTitleFromMarkdownAst(markdownAst); + } catch { + logger('error parsing markdown for issue ' + issue.code()); + return null; + } + if (!title) { + logger('cannot read issue title from ' + filename); + return null; + } + return { + type: 'issue', + item: issue, + message: title, + count, + description: processedMarkdown, + }; +} + I18n.DevToolsLocale.DevToolsLocale.instance({ create: true, data: { diff --git a/src/McpContext.ts b/src/McpContext.ts index 22d54c948..7536fe784 100644 --- a/src/McpContext.ts +++ b/src/McpContext.ts @@ -199,8 +199,13 @@ export class McpContext implements Context { this.logger('no cdpBackendNodeId'); return; } + if (this.#textSnapshot === null) + throw new Error( + "The snapshot is not defined, can't resolve backendNodeId: " + + cdpBackendNodeId, + ); // TODO: index by backendNodeId instead. - const queue = [this.#textSnapshot?.root]; + const queue = [this.#textSnapshot.root]; while (queue.length) { const current = queue.pop()!; if (current.backendNodeId === cdpBackendNodeId) { diff --git a/src/McpResponse.ts b/src/McpResponse.ts index 3fb839868..b7de04405 100644 --- a/src/McpResponse.ts +++ b/src/McpResponse.ts @@ -4,12 +4,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { - AggregatedIssue, - Marked, - MarkdownIssueDescription, -} from '../node_modules/chrome-devtools-frontend/mcp/mcp.js'; +import {AggregatedIssue} from '../node_modules/chrome-devtools-frontend/mcp/mcp.js'; +import {mapIssueToMessageObject} from './DevtoolsUtils.js'; import type {ConsoleMessageData} from './formatters/consoleFormatter.js'; import { formatConsoleEventShort, @@ -23,8 +20,6 @@ import { getStatusFromRequest, } from './formatters/networkFormatter.js'; import {formatSnapshotNode} from './formatters/snapshotFormatter.js'; -import {getIssueDescription} from './issue-descriptions.js'; -import {logger} from './logger.js'; import type {McpContext} from './McpContext.js'; import type { ConsoleMessage, @@ -256,6 +251,16 @@ export class McpResponse implements Response { }), ), }; + } else if (message instanceof AggregatedIssue) { + const mappedIssueMessage = mapIssueToMessageObject(message); + if (!mappedIssueMessage) + throw new Error( + "Can't prpovide detals for the msgid " + consoleMessageStableId, + ); + consoleData = { + consoleMessageStableId, + ...mappedIssueMessage, + }; } else { consoleData = { consoleMessageStableId, @@ -309,29 +314,11 @@ export class McpResponse implements Response { }; } if (item instanceof AggregatedIssue) { - const count = item.getAggregatedIssuesCount(); - const filename = item.getDescription()?.file; - const rawMarkdown = filename - ? getIssueDescription(filename) - : null; - if (!rawMarkdown) { - logger(`no markdown ${filename} found for issue:` + item.code); - return null; - } - const markdownAst = Marked.Marked.lexer(rawMarkdown); - const title = - MarkdownIssueDescription.findTitleFromMarkdownAst(markdownAst); - if (!title) { - logger('cannot read issue title from ' + filename); - return null; - } + const mappedIssueMessage = mapIssueToMessageObject(item); + if (!mappedIssueMessage) return null; return { consoleMessageStableId, - type: 'issue', - item, - message: title, - count, - args: [], + ...mappedIssueMessage, }; } return { diff --git a/src/formatters/consoleFormatter.ts b/src/formatters/consoleFormatter.ts index e4eca9b1c..0e4672aa9 100644 --- a/src/formatters/consoleFormatter.ts +++ b/src/formatters/consoleFormatter.ts @@ -4,12 +4,15 @@ * SPDX-License-Identifier: Apache-2.0 */ +import type {AggregatedIssue} from '../../node_modules/chrome-devtools-frontend/mcp/mcp.js'; + export interface ConsoleMessageData { consoleMessageStableId: number; type?: string; - item?: unknown; + item?: AggregatedIssue; message?: string; count?: number; + description?: string; args?: string[]; } @@ -34,12 +37,12 @@ function getArgs(msg: ConsoleMessageData) { // The verbose format for a console message, including all details. export function formatConsoleEventVerbose(msg: ConsoleMessageData): string { + const aggregatedIssue = msg.item; const result = [ `ID: ${msg.consoleMessageStableId}`, - `Message: ${msg.type}> ${msg.message}`, - formatArgs(msg), + `Message: ${msg.type}> ${aggregatedIssue ? formatIssue(aggregatedIssue, msg.description) : msg.message}`, + aggregatedIssue ? undefined : formatArgs(msg), ].filter(line => !!line); - return result.join('\n'); } @@ -62,3 +65,29 @@ function formatArgs(consoleData: ConsoleMessageData): string { return result.join('\n'); } + +export function formatIssue( + issue: AggregatedIssue, + description?: string, +): string { + const result: string[] = []; + + let processedMarkdown = description?.trim(); + // Remove heading in order not to conflict with the whole console message response markdown + if (processedMarkdown?.startsWith('# ')) { + processedMarkdown = processedMarkdown.substring(2).trimStart(); + } + if (processedMarkdown) result.push(processedMarkdown); + + const links = issue.getDescription()?.links; + if (links && links.length > 0) { + result.push('Learn more:'); + for (const link of links) { + result.push(`[${link.linkTitle}](${link.link})`); + } + } + + if (result.length === 0) + return 'No details provided for the issue ' + issue.code(); + return result.join('\n'); +} diff --git a/src/issue-descriptions.ts b/src/issue-descriptions.ts index 21485e4b1..c13e405ee 100644 --- a/src/issue-descriptions.ts +++ b/src/issue-descriptions.ts @@ -47,3 +47,8 @@ export async function loadIssueDescriptions(): Promise { export function getIssueDescription(fileName: string): string | null { return issueDescriptions[fileName] ?? null; } + +export const ISSUE_UTILS = { + loadIssueDescriptions, + getIssueDescription, +}; diff --git a/tests/DevtoolsUtils.test.ts b/tests/DevtoolsUtils.test.ts index 40ecf2c31..fd1569d5f 100644 --- a/tests/DevtoolsUtils.test.ts +++ b/tests/DevtoolsUtils.test.ts @@ -5,12 +5,17 @@ */ import assert from 'node:assert'; -import {describe, it} from 'node:test'; +import {afterEach, describe, it} from 'node:test'; +import sinon from 'sinon'; + +import {AggregatedIssue} from '../node_modules/chrome-devtools-frontend/mcp/mcp.js'; import { extractUrlLikeFromDevToolsTitle, urlsEqual, + mapIssueToMessageObject, } from '../src/DevtoolsUtils.js'; +import {ISSUE_UTILS} from '../src/issue-descriptions.js'; describe('extractUrlFromDevToolsTitle', () => { it('deals with no trailing /', () => { @@ -70,3 +75,104 @@ describe('urlsEqual', () => { ); }); }); + +describe('mapIssueToMessageObject', () => { + const mockDescription = { + file: 'mock-issue.md', + substitutions: new Map([['PLACEHOLDER_VALUE', 'substitution value']]), + links: [ + {link: 'http://example.com/learnmore', linkTitle: 'Learn more'}, + { + link: 'http://example.com/another-learnmore', + linkTitle: 'Learn more 2', + }, + ], + }; + + afterEach(() => { + sinon.restore(); + }); + + it('maps aggregated issue with substituted description', () => { + const mockAggregatedIssue = sinon.createStubInstance(AggregatedIssue); + mockAggregatedIssue.getDescription.returns(mockDescription); + mockAggregatedIssue.getAggregatedIssuesCount.returns(1); + + const getIssueDescriptionStub = sinon.stub( + ISSUE_UTILS, + 'getIssueDescription', + ); + + getIssueDescriptionStub + .withArgs('mock-issue.md') + .returns( + '# Mock Issue Title\n\nThis is a mock issue description with a {PLACEHOLDER_VALUE}.', + ); + + const result = mapIssueToMessageObject(mockAggregatedIssue); + const expected = { + type: 'issue', + item: mockAggregatedIssue, + message: 'Mock Issue Title', + count: 1, + description: + '# Mock Issue Title\n\nThis is a mock issue description with a substitution value.', + }; + assert.deepStrictEqual(result, expected); + }); + + it('returns null for the issue with no description', () => { + const mockAggregatedIssue = sinon.createStubInstance(AggregatedIssue); + mockAggregatedIssue.getDescription.returns(null); + + const result = mapIssueToMessageObject(mockAggregatedIssue); + assert.equal(result, null); + }); + + it('returns null if there is no desciption file', () => { + const mockAggregatedIssue = sinon.createStubInstance(AggregatedIssue); + mockAggregatedIssue.getDescription.returns(mockDescription); + mockAggregatedIssue.getAggregatedIssuesCount.returns(1); + + const getIssueDescriptionStub = sinon.stub( + ISSUE_UTILS, + 'getIssueDescription', + ); + + getIssueDescriptionStub.withArgs('mock-issue.md').returns(null); + const result = mapIssueToMessageObject(mockAggregatedIssue); + assert.equal(result, null); + }); + + it("returns null if can't parse the title", () => { + const mockAggregatedIssue = sinon.createStubInstance(AggregatedIssue); + mockAggregatedIssue.getDescription.returns(mockDescription); + mockAggregatedIssue.getAggregatedIssuesCount.returns(1); + + const getIssueDescriptionStub = sinon.stub( + ISSUE_UTILS, + 'getIssueDescription', + ); + + getIssueDescriptionStub + .withArgs('mock-issue.md') + .returns('No title test {PLACEHOLDER_VALUE}'); + assert.deepStrictEqual(mapIssueToMessageObject(mockAggregatedIssue), null); + }); + + it('returns null if devtools utill function throws an error', () => { + const mockAggregatedIssue = sinon.createStubInstance(AggregatedIssue); + mockAggregatedIssue.getDescription.returns(mockDescription); + mockAggregatedIssue.getAggregatedIssuesCount.returns(1); + + const getIssueDescriptionStub = sinon.stub( + ISSUE_UTILS, + 'getIssueDescription', + ); + // An error will be thrown if placeholder doesn't start from PLACEHOLDER_ + getIssueDescriptionStub + .withArgs('mock-issue.md') + .returns('No title test {WRONG_PLACEHOLDER}'); + assert.deepStrictEqual(mapIssueToMessageObject(mockAggregatedIssue), null); + }); +}); diff --git a/tests/McpResponse.test.ts b/tests/McpResponse.test.ts index 2c9516bcf..b045454da 100644 --- a/tests/McpResponse.test.ts +++ b/tests/McpResponse.test.ts @@ -10,6 +10,10 @@ import {tmpdir} from 'node:os'; import {join} from 'node:path'; import {describe, it} from 'node:test'; +import sinon from 'sinon'; + +import {AggregatedIssue} from '../node_modules/chrome-devtools-frontend/mcp/mcp.js'; + import { getMockRequest, getMockResponse, @@ -295,6 +299,46 @@ describe('McpResponse', () => { t.assert.snapshot?.(result[0].text); }); }); + + it("doesn't list the issue message if mapping returns null", async () => { + await withBrowser(async (response, context) => { + const mockAggregatedIssue = sinon.createStubInstance(AggregatedIssue); + const mockDescription = { + file: 'not-existing-description-file.md', + links: [], + }; + mockAggregatedIssue.getDescription.returns(mockDescription); + response.setIncludeConsoleData(true); + context.getConsoleData = () => { + return [mockAggregatedIssue]; + }; + + const result = await response.handle('test', context); + const text = (result[0].text as string).toString(); + assert.ok(text.includes('')); + }); + }); + + it('throws error if mapping returns null on get issue details', async () => { + await withBrowser(async (response, context) => { + const mockAggregatedIssue = sinon.createStubInstance(AggregatedIssue); + const mockDescription = { + file: 'not-existing-description-file.md', + links: [], + }; + mockAggregatedIssue.getDescription.returns(mockDescription); + response.attachConsoleMessage(1); + context.getConsoleMessageById = () => { + return mockAggregatedIssue; + }; + + try { + await response.handle('test', context); + } catch (e) { + assert.ok(e.message.includes("Can't prpovide detals for the msgid 1")); + } + }); + }); }); describe('McpResponse network request filtering', () => { diff --git a/tests/formatters/consoleFormatter.test.js.snapshot b/tests/formatters/consoleFormatter.test.js.snapshot index 7e6d5d656..d6803572e 100644 --- a/tests/formatters/consoleFormatter.test.js.snapshot +++ b/tests/formatters/consoleFormatter.test.js.snapshot @@ -34,3 +34,13 @@ Message: log> Processing file: ### Arguments Arg #0: file.txt `; + +exports[`consoleFormatter > formats a console.log message with issue type 1`] = ` +ID: 5 +Message: issue> Mock Issue Title + +This is a mock issue description +Learn more: +[Learn more](http://example.com/learnmore) +[Learn more 2](http://example.com/another-learnmore) +`; diff --git a/tests/formatters/consoleFormatter.test.ts b/tests/formatters/consoleFormatter.test.ts index d5e71ba40..eabe2a44b 100644 --- a/tests/formatters/consoleFormatter.test.ts +++ b/tests/formatters/consoleFormatter.test.ts @@ -6,6 +6,9 @@ import {describe, it} from 'node:test'; +import sinon from 'sinon'; + +import {AggregatedIssue} from '../../node_modules/chrome-devtools-frontend/mcp/mcp.js'; import type {ConsoleMessageData} from '../../src/formatters/consoleFormatter.js'; import { formatConsoleEventShort, @@ -92,4 +95,31 @@ describe('consoleFormatter', () => { t.assert.snapshot?.(result); }); }); + + it('formats a console.log message with issue type', t => { + const mockAggregatedIssue = sinon.createStubInstance(AggregatedIssue); + const mockDescription = { + file: 'mock.md', + links: [ + {link: 'http://example.com/learnmore', linkTitle: 'Learn more'}, + { + link: 'http://example.com/another-learnmore', + linkTitle: 'Learn more 2', + }, + ], + }; + mockAggregatedIssue.getDescription.returns(mockDescription); + const mockDescriptionFileContent = + '# Mock Issue Title\n\nThis is a mock issue description'; + + const message: ConsoleMessageData = { + consoleMessageStableId: 5, + type: 'issue', + description: mockDescriptionFileContent, + item: mockAggregatedIssue, + }; + + const result = formatConsoleEventVerbose(message); + t.assert.snapshot?.(result); + }); }); diff --git a/tests/tools/console.test.ts b/tests/tools/console.test.ts index c7f6a97da..0724a503b 100644 --- a/tests/tools/console.test.ts +++ b/tests/tools/console.test.ts @@ -9,6 +9,7 @@ import {afterEach, before, beforeEach, describe, it} from 'node:test'; import {setIssuesEnabled} from '../../src/features.js'; import {loadIssueDescriptions} from '../../src/issue-descriptions.js'; +import {McpResponse} from '../../src/McpResponse.js'; import { getConsoleMessage, listConsoleMessages, @@ -150,5 +151,45 @@ describe('console', () => { ); }); }); + + describe('issues type', () => { + beforeEach(() => { + setIssuesEnabled(true); + }); + afterEach(() => { + setIssuesEnabled(false); + }); + + it('gets issue details', async () => { + await withBrowser(async (response, context) => { + const page = await context.newPage(); + const issuePromise = new Promise(resolve => { + page.once('issue', () => { + resolve(); + }); + }); + await page.setContent(''); + await issuePromise; + await listConsoleMessages.handler({params: {}}, response, context); + const response2 = new McpResponse(); + await getConsoleMessage.handler( + {params: {msgid: 1}}, + response2, + context, + ); + const formattedResponse = await response2.handle('test', context); + const textContent = formattedResponse[0] as {text: string}; + const learnMoreLinks = + '[HTML attribute: autocomplete](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete#values)'; + const detailsDescription = + "A form field has an `id` or `name` attribute that the browser's autofill recognizes. However, it doesn't have an `autocomplete` attribute assigned. This might prevent the browser from correctly autofilling the form.\n\nTo fix this issue, provide an `autocomplete` attribute."; + const title = + "Message: issue> An element doesn't have an autocomplete attribute"; + assert.ok(textContent.text.includes(title)); + assert.ok(textContent.text.includes(detailsDescription)); + assert.ok(textContent.text.includes(learnMoreLinks)); + }); + }); + }); }); }); From fae260888748ece77b368a13ee913153caffcef7 Mon Sep 17 00:00:00 2001 From: Michael Hablich Date: Tue, 25 Nov 2025 11:51:19 +0100 Subject: [PATCH 090/456] docs: Fix Antigravity docs (#605) --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 07f598bed..308d20729 100644 --- a/README.md +++ b/README.md @@ -66,16 +66,16 @@ amp mcp add chrome-devtools -- npx chrome-devtools-mcp@latest
Antigravity -To use the Chrome DevTools MCP server, disable the built-in browser in the settings and add the following config to ` ~/.gemini/antigravity/mcp_config.json`: +To use the Chrome DevTools MCP server follow the instructions from Antigravity's docs to install a custom MCP server. Add the following config to the MCP servers config: ```bash { "mcpServers": { "chrome-devtools": { - "type": "stdio", "command": "npx", "args": [ "chrome-devtools-mcp@latest", + "--browser-url=http://127.0.0.1:9222", "-y" ] } @@ -83,6 +83,8 @@ To use the Chrome DevTools MCP server, disable the built-in browser in the setti } ``` +This will make the Chrome DevTools MCP server automatically connect to the browser that Antigravity is using. If you are not using port 9222, make sure to adjust accordingly. +
From eb261fd48b6753db246d24b77e1f477dc7a9455e Mon Sep 17 00:00:00 2001 From: Alex Rudenko Date: Tue, 25 Nov 2025 16:20:35 +0100 Subject: [PATCH 091/456] refactor: avoid throwing in resolveCdpElementId (#606) We can return undefined as already supported by the function signature so that the callers do not have to try/catch. --- src/McpContext.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/McpContext.ts b/src/McpContext.ts index 7536fe784..1b0c586f4 100644 --- a/src/McpContext.ts +++ b/src/McpContext.ts @@ -199,11 +199,10 @@ export class McpContext implements Context { this.logger('no cdpBackendNodeId'); return; } - if (this.#textSnapshot === null) - throw new Error( - "The snapshot is not defined, can't resolve backendNodeId: " + - cdpBackendNodeId, - ); + if (this.#textSnapshot === null) { + this.logger('no text snapshot'); + return; + } // TODO: index by backendNodeId instead. const queue = [this.#textSnapshot.root]; while (queue.length) { From 52533d0c695354b816807de253f0ec17099aa9d7 Mon Sep 17 00:00:00 2001 From: Alex Rudenko Date: Wed, 26 Nov 2025 11:10:34 +0100 Subject: [PATCH 092/456] fix: ignore hash parts of URLs when finding DevTools (#608) Closes https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/607 --- src/DevtoolsUtils.ts | 8 ++++++++ tests/DevtoolsUtils.test.ts | 11 +++++++++++ 2 files changed, 19 insertions(+) diff --git a/src/DevtoolsUtils.ts b/src/DevtoolsUtils.ts index eb7ad3655..e26297877 100644 --- a/src/DevtoolsUtils.ts +++ b/src/DevtoolsUtils.ts @@ -37,6 +37,7 @@ export function urlsEqual(url1: string, url2: string): boolean { * 1. We do not care about the protocol. * 2. We do not care about trailing slashes. * 3. We do not care about "www". + * 4. We ignore the hash parts. * * For example, if the user types "record a trace on foo.com", we would want to * match a tab in the connected Chrome instance that is showing "www.foo.com/" @@ -56,6 +57,13 @@ function normalizeUrl(url: string): string { result = result.slice(4); } + // We use target URLs to locate DevTools but those often do + // no include hash. + const hashIdx = result.lastIndexOf('#'); + if (hashIdx !== -1) { + result = result.slice(0, hashIdx); + } + // Remove trailing slash if (result.endsWith('/')) { result = result.slice(0, -1); diff --git a/tests/DevtoolsUtils.test.ts b/tests/DevtoolsUtils.test.ts index fd1569d5f..947e55122 100644 --- a/tests/DevtoolsUtils.test.ts +++ b/tests/DevtoolsUtils.test.ts @@ -74,6 +74,17 @@ describe('urlsEqual', () => { false, ); }); + + it('ignores hash', () => { + assert.strictEqual( + urlsEqual('https://google.com/#', 'http://www.google.com'), + true, + ); + assert.strictEqual( + urlsEqual('https://google.com/#21', 'http://www.google.com#12'), + true, + ); + }); }); describe('mapIssueToMessageObject', () => { From 2d89865ddbff6e77332c6157f687dcc2f0bef892 Mon Sep 17 00:00:00 2001 From: Sebastian Benz Date: Wed, 26 Nov 2025 17:28:50 +0100 Subject: [PATCH 093/456] docs: update readme to explain agy's browser integration (#612) --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 308d20729..ab5db6aba 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,8 @@ To use the Chrome DevTools MCP server follow the instructions from ${aggregatedIssue ? formatIssue(aggregatedIssue, msg.description) : msg.message}`, + `Message: ${msg.type}> ${aggregatedIssue ? formatIssue(aggregatedIssue, msg.description, context) : msg.message}`, aggregatedIssue ? undefined : formatArgs(msg), ].filter(line => !!line); return result.join('\n'); @@ -65,10 +69,19 @@ function formatArgs(consoleData: ConsoleMessageData): string { return result.join('\n'); } - +interface IssueDetailsWithResources { + violatingNodeId?: number; + nodeId?: number; + documentNodeId?: number; + request?: { + requestId?: string; + url: string; + }; +} export function formatIssue( issue: AggregatedIssue, description?: string, + context?: McpContext, ): string { const result: string[] = []; @@ -87,6 +100,86 @@ export function formatIssue( } } + const issues: Array<{ + details?: () => IssueDetailsWithResources; + getDetails?: () => IssueDetailsWithResources; + }> = [ + ...issue.getCorsIssues(), + ...issue.getMixedContentIssues(), + ...issue.getGenericIssues(), + ...issue.getLowContrastIssues(), + ...issue.getElementAccessibilityIssues(), + ...issue.getQuirksModeIssues(), + ]; + const affectedResources: Array<{ + uid?: string; + data?: object; + request?: string | number; + }> = []; + for (const singleIssue of issues) { + if (!singleIssue.details && !singleIssue.getDetails) continue; + + let details = + singleIssue.details?.() as unknown as IssueDetailsWithResources; + if (!details) + details = + singleIssue.getDetails?.() as unknown as IssueDetailsWithResources; + if (!details) continue; + + let uid; + let request: number | string | undefined; + if (details.violatingNodeId && context) { + uid = context.resolveCdpElementId(details.violatingNodeId); + } + if (details.nodeId && context) { + uid = context.resolveCdpElementId(details.nodeId); + } + if (details.documentNodeId && context) { + uid = context.resolveCdpElementId(details.documentNodeId); + } + + if (details.request) { + request = details.request.url; + if (details.request.requestId && context) { + const resolvedId = context.resolveCdpRequestId( + details.request.requestId, + ); + if (resolvedId) { + request = resolvedId; + } + } + } + + // eslint-disable-next-line + const data = structuredClone(details) as any; + delete data.violatingNodeId; + delete data.nodeId; + delete data.documentNodeId; + delete data.errorType; + delete data.frameId; + delete data.request; + affectedResources.push({ + uid, + data: data, + request, + }); + } + if (affectedResources.length) { + result.push('### Affected resources'); + } + result.push( + ...affectedResources.map(item => { + const details = []; + if (item.uid) details.push(`uid=${item.uid}`); + if (item.request) { + details.push( + (typeof item.request === 'number' ? `reqid=` : 'url=') + item.request, + ); + } + if (item.data) details.push(`data=${JSON.stringify(item.data)}`); + return details.join(' '); + }), + ); if (result.length === 0) return 'No details provided for the issue ' + issue.code(); return result.join('\n'); diff --git a/tests/McpResponse.test.ts b/tests/McpResponse.test.ts index b045454da..9031f6ddb 100644 --- a/tests/McpResponse.test.ts +++ b/tests/McpResponse.test.ts @@ -10,11 +10,8 @@ import {tmpdir} from 'node:os'; import {join} from 'node:path'; import {describe, it} from 'node:test'; -import sinon from 'sinon'; - -import {AggregatedIssue} from '../node_modules/chrome-devtools-frontend/mcp/mcp.js'; - import { + getMockAggregatedIssue, getMockRequest, getMockResponse, html, @@ -302,7 +299,7 @@ describe('McpResponse', () => { it("doesn't list the issue message if mapping returns null", async () => { await withBrowser(async (response, context) => { - const mockAggregatedIssue = sinon.createStubInstance(AggregatedIssue); + const mockAggregatedIssue = getMockAggregatedIssue(); const mockDescription = { file: 'not-existing-description-file.md', links: [], @@ -321,7 +318,7 @@ describe('McpResponse', () => { it('throws error if mapping returns null on get issue details', async () => { await withBrowser(async (response, context) => { - const mockAggregatedIssue = sinon.createStubInstance(AggregatedIssue); + const mockAggregatedIssue = getMockAggregatedIssue(); const mockDescription = { file: 'not-existing-description-file.md', links: [], @@ -335,7 +332,7 @@ describe('McpResponse', () => { try { await response.handle('test', context); } catch (e) { - assert.ok(e.message.includes("Can't prpovide detals for the msgid 1")); + assert.ok(e.message.includes("Can't provide detals for the msgid 1")); } }); }); diff --git a/tests/formatters/consoleFormatter.test.js.snapshot b/tests/formatters/consoleFormatter.test.js.snapshot index d6803572e..2052e4352 100644 --- a/tests/formatters/consoleFormatter.test.js.snapshot +++ b/tests/formatters/consoleFormatter.test.js.snapshot @@ -43,4 +43,6 @@ This is a mock issue description Learn more: [Learn more](http://example.com/learnmore) [Learn more 2](http://example.com/another-learnmore) +### Affected resources +data={"violatingNodeAttribute":"test"} `; diff --git a/tests/formatters/consoleFormatter.test.ts b/tests/formatters/consoleFormatter.test.ts index eabe2a44b..8734a91fe 100644 --- a/tests/formatters/consoleFormatter.test.ts +++ b/tests/formatters/consoleFormatter.test.ts @@ -6,14 +6,12 @@ import {describe, it} from 'node:test'; -import sinon from 'sinon'; - -import {AggregatedIssue} from '../../node_modules/chrome-devtools-frontend/mcp/mcp.js'; import type {ConsoleMessageData} from '../../src/formatters/consoleFormatter.js'; import { formatConsoleEventShort, formatConsoleEventVerbose, } from '../../src/formatters/consoleFormatter.js'; +import {getMockAggregatedIssue} from '../utils.js'; describe('consoleFormatter', () => { describe('formatConsoleEventShort', () => { @@ -97,7 +95,15 @@ describe('consoleFormatter', () => { }); it('formats a console.log message with issue type', t => { - const mockAggregatedIssue = sinon.createStubInstance(AggregatedIssue); + const testGenericIssue = { + details: () => { + return { + violatingNodeId: 2, + violatingNodeAttribute: 'test', + }; + }, + }; + const mockAggregatedIssue = getMockAggregatedIssue(); const mockDescription = { file: 'mock.md', links: [ @@ -109,6 +115,8 @@ describe('consoleFormatter', () => { ], }; mockAggregatedIssue.getDescription.returns(mockDescription); + // @ts-expect-error generic issue stub bypass + mockAggregatedIssue.getGenericIssues.returns(new Set([testGenericIssue])); const mockDescriptionFileContent = '# Mock Issue Title\n\nThis is a mock issue description'; diff --git a/tests/tools/console.test.js.snapshot b/tests/tools/console.test.js.snapshot new file mode 100644 index 000000000..a9d01c1d7 --- /dev/null +++ b/tests/tools/console.test.js.snapshot @@ -0,0 +1,29 @@ +exports[`console > get_console_message > issues type > gets issue details with node id parsing 1`] = ` +# test response +ID: 1 +Message: issue> An element doesn't have an autocomplete attribute + +A form field has an \`id\` or \`name\` attribute that the browser's autofill recognizes. However, it doesn't have an \`autocomplete\` attribute assigned. This might prevent the browser from correctly autofilling the form. + +To fix this issue, provide an \`autocomplete\` attribute. +Learn more: +[HTML attribute: autocomplete](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete#values) +### Affected resources +uid=1_1 data={"violatingNodeAttribute":"name"} +`; + +exports[`console > get_console_message > issues type > gets issue details with request id parsing 1`] = ` +# test response +ID: +Message: issue> Ensure CORS response header values are valid + +A cross-origin resource sharing (CORS) request was blocked because of invalid or missing response headers of the request or the associated [preflight request](issueCorsPreflightRequest). + +To fix this issue, ensure the response to the CORS request and/or the associated [preflight request](issueCorsPreflightRequest) are not missing headers and use valid header values. + +Note that if an opaque response is sufficient, the request's mode can be set to \`no-cors\` to fetch the resource with CORS disabled; that way CORS headers are not required but the response content is inaccessible (opaque). +Learn more: +[Cross-Origin Resource Sharing (\`CORS\`)](https://web.dev/cross-origin-resource-sharing) +### Affected resources +reqid=1 data={"corsErrorStatus":{"corsError":"PreflightMissingAllowOriginHeader","failedParameter":""},"isWarning":false,"initiatorOrigin":"","clientSecurityState":{"initiatorIsSecureContext":false,"initiatorIPAddressSpace":"Loopback","privateNetworkRequestPolicy":"BlockFromInsecureToMorePrivate"}} +`; diff --git a/tests/tools/console.test.ts b/tests/tools/console.test.ts index 0724a503b..06bcbc18d 100644 --- a/tests/tools/console.test.ts +++ b/tests/tools/console.test.ts @@ -7,6 +7,7 @@ import assert from 'node:assert'; import {afterEach, before, beforeEach, describe, it} from 'node:test'; +import {AggregatedIssue} from '../../node_modules/chrome-devtools-frontend/mcp/mcp.js'; import {setIssuesEnabled} from '../../src/features.js'; import {loadIssueDescriptions} from '../../src/issue-descriptions.js'; import {McpResponse} from '../../src/McpResponse.js'; @@ -14,14 +15,14 @@ import { getConsoleMessage, listConsoleMessages, } from '../../src/tools/console.js'; +import {serverHooks} from '../server.js'; import {withBrowser} from '../utils.js'; describe('console', () => { + before(async () => { + await loadIssueDescriptions(); + }); describe('list_console_messages', () => { - before(async () => { - await loadIssueDescriptions(); - }); - it('list messages', async () => { await withBrowser(async (response, context) => { await listConsoleMessages.handler({params: {}}, response, context); @@ -153,6 +154,7 @@ describe('console', () => { }); describe('issues type', () => { + const server = serverHooks(); beforeEach(() => { setIssuesEnabled(true); }); @@ -160,7 +162,7 @@ describe('console', () => { setIssuesEnabled(false); }); - it('gets issue details', async () => { + it('gets issue details with node id parsing', async t => { await withBrowser(async (response, context) => { const page = await context.newPage(); const issuePromise = new Promise(resolve => { @@ -169,6 +171,7 @@ describe('console', () => { }); }); await page.setContent(''); + await context.createTextSnapshot(); await issuePromise; await listConsoleMessages.handler({params: {}}, response, context); const response2 = new McpResponse(); @@ -178,16 +181,64 @@ describe('console', () => { context, ); const formattedResponse = await response2.handle('test', context); - const textContent = formattedResponse[0] as {text: string}; - const learnMoreLinks = - '[HTML attribute: autocomplete](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete#values)'; - const detailsDescription = - "A form field has an `id` or `name` attribute that the browser's autofill recognizes. However, it doesn't have an `autocomplete` attribute assigned. This might prevent the browser from correctly autofilling the form.\n\nTo fix this issue, provide an `autocomplete` attribute."; - const title = - "Message: issue> An element doesn't have an autocomplete attribute"; - assert.ok(textContent.text.includes(title)); - assert.ok(textContent.text.includes(detailsDescription)); - assert.ok(textContent.text.includes(learnMoreLinks)); + t.assert.snapshot?.(formattedResponse[0].text); + }); + }); + it('gets issue details with request id parsing', async t => { + server.addRoute('/data.json', (_req, res) => { + res.setHeader('Content-Type', 'application/json'); + res.statusCode = 200; + res.end(JSON.stringify({data: 'test data'})); + }); + + await withBrowser(async (response, context) => { + const page = await context.newPage(); + const issuePromise = new Promise(resolve => { + page.once('issue', () => { + resolve(); + }); + }); + + const url = server.getRoute('/data.json'); + await page.setContent(` + + `); + await context.createTextSnapshot(); + await issuePromise; + const messages = context.getConsoleData(); + let issueMsg; + for (const message of messages) { + if (message instanceof AggregatedIssue) { + issueMsg = message; + break; + } + } + assert.ok(issueMsg); + const id = context.getConsoleMessageStableId(issueMsg); + assert.ok(id); + await listConsoleMessages.handler( + {params: {types: ['issue']}}, + response, + context, + ); + const response2 = new McpResponse(); + await getConsoleMessage.handler( + {params: {msgid: id}}, + response2, + context, + ); + const formattedResponse = await response2.handle('test', context); + const rawText = formattedResponse[0].text as string; + const sanitizedText = rawText.replaceAll(/ID: \d+/g, 'ID: '); + t.assert.snapshot?.(sanitizedText); }); }); }); diff --git a/tests/utils.ts b/tests/utils.ts index 3a7173819..8cd5d03ed 100644 --- a/tests/utils.ts +++ b/tests/utils.ts @@ -13,7 +13,9 @@ import type { HTTPResponse, LaunchOptions, } from 'puppeteer-core'; +import sinon from 'sinon'; +import {AggregatedIssue} from '../node_modules/chrome-devtools-frontend/mcp/mcp.js'; import {McpContext} from '../src/McpContext.js'; import {McpResponse} from '../src/McpResponse.js'; import {stableIdSymbol} from '../src/PageCollector.js'; @@ -180,3 +182,14 @@ export function stabilizeResponseOutput(text: unknown) { output = output.replaceAll(savedSnapshot, 'Saved snapshot to '); return output; } + +export function getMockAggregatedIssue(): sinon.SinonStubbedInstance { + const mockAggregatedIssue = sinon.createStubInstance(AggregatedIssue); + mockAggregatedIssue.getGenericIssues.returns(new Set()); + mockAggregatedIssue.getLowContrastIssues.returns(new Set()); + mockAggregatedIssue.getElementAccessibilityIssues.returns(new Set()); + mockAggregatedIssue.getQuirksModeIssues.returns(new Set()); + mockAggregatedIssue.getCorsIssues.returns(new Set()); + mockAggregatedIssue.getMixedContentIssues.returns(new Set()); + return mockAggregatedIssue; +} From c458fd1ebd9cb5ac4fc9d04cf7b98e6a1a53afac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 27 Nov 2025 07:59:21 +0000 Subject: [PATCH 095/456] chore(deps-dev): bump chrome-devtools-frontend from 1.0.1547571 to 1.0.1549484 in the bundled-devtools group (#615) Bumps the bundled-devtools group with 1 update: [chrome-devtools-frontend](https://github.com/ChromeDevTools/devtools-frontend). Updates `chrome-devtools-frontend` from 1.0.1547571 to 1.0.1549484
Commits
  • 368d4cc Update DevTools DEPS (trusted)
  • 380221d [ui] Pass 'Universe' to 'loadView' functions of view registrations
  • 2d2c0e5 [sdk] Pass PageResourceLoader to the source map loading helpers
  • 09ac2b7 [GreenDev]: Polish the AnnotationRepository API.
  • 174bfd0 Roll browser-protocol
  • 1669e7e [ui] Delay 'PreRegisteredView' instantiation
  • a4a64ee Move the utilities related to the markup highlighting to a dedicated component
  • a88394a Update the roll script to handle transitive dependencies
  • ca54db6 [GreenDev]: Add annotation indicator for the panels tab switcher.
  • 53e6e75 [AI Assistance] Fix icons getting cut off on small screens
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=chrome-devtools-frontend&package-manager=npm_and_yarn&previous-version=1.0.1547571&new-version=1.0.1549484)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
--------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Alex Rudenko --- package-lock.json | 20 +++++++++++++++----- package.json | 2 +- tsconfig.json | 2 +- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index feb38eee9..51df455d9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,7 +25,7 @@ "@types/yargs": "^17.0.33", "@typescript-eslint/eslint-plugin": "^8.43.0", "@typescript-eslint/parser": "^8.43.0", - "chrome-devtools-frontend": "1.0.1547571", + "chrome-devtools-frontend": "1.0.1549484", "core-js": "3.47.0", "debug": "4.4.3", "eslint": "^9.35.0", @@ -1260,6 +1260,7 @@ "integrity": "sha512-lJi3PfxVmo0AkEY93ecfN+r8SofEqZNGByvHAI3GBLrvt1Cw6H5k1IM02nSzu0RfUafr2EvFSw0wAsZgubNplQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.47.0", "@typescript-eslint/types": "8.47.0", @@ -1735,6 +1736,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2299,9 +2301,9 @@ } }, "node_modules/chrome-devtools-frontend": { - "version": "1.0.1547571", - "resolved": "https://registry.npmjs.org/chrome-devtools-frontend/-/chrome-devtools-frontend-1.0.1547571.tgz", - "integrity": "sha512-NzfUHUzCu+4Vv68FuVZnacfGOqukqyG1hVj2ACjZM7BOJc+V3DY17CJJDRQilkVUYCQa3GskZUA8B9mYmwTbNg==", + "version": "1.0.1549484", + "resolved": "https://registry.npmjs.org/chrome-devtools-frontend/-/chrome-devtools-frontend-1.0.1549484.tgz", + "integrity": "sha512-1ZUPHAxoqJ7qQvhIocdccl10KSLxrq74R+rWBDWJPggZ3pcgJEh82YjUN8ilx8P7/S+Aj2VukqU46NVAkQhKLA==", "dev": true, "license": "BSD-3-Clause" }, @@ -2651,7 +2653,8 @@ "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1521046.tgz", "integrity": "sha512-vhE6eymDQSKWUXwwA37NtTTVEzjtGVfDr3pRbsWEQ5onH/Snp2c+2xZHWJJawG/0hCCJLRGt4xVtEVUVILol4w==", "dev": true, - "license": "BSD-3-Clause" + "license": "BSD-3-Clause", + "peer": true }, "node_modules/diff": { "version": "7.0.0", @@ -2952,6 +2955,7 @@ "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -3122,6 +3126,7 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -3392,6 +3397,7 @@ "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.0", @@ -5714,6 +5720,7 @@ "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/estree": "1.0.8" }, @@ -6719,6 +6726,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -6794,6 +6802,7 @@ "dev": true, "hasInstallScript": true, "license": "MIT", + "peer": true, "dependencies": { "napi-postinstall": "^0.3.0" }, @@ -7092,6 +7101,7 @@ "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "dev": true, "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/package.json b/package.json index a8f5c91b2..b54211664 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "@types/yargs": "^17.0.33", "@typescript-eslint/eslint-plugin": "^8.43.0", "@typescript-eslint/parser": "^8.43.0", - "chrome-devtools-frontend": "1.0.1547571", + "chrome-devtools-frontend": "1.0.1549484", "core-js": "3.47.0", "debug": "4.4.3", "eslint": "^9.35.0", diff --git a/tsconfig.json b/tsconfig.json index 241806784..02d41107f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -64,7 +64,7 @@ "node_modules/chrome-devtools-frontend/front_end/third_party/marked", "node_modules/chrome-devtools-frontend/front_end/third_party/source-map-scopes-codec", "node_modules/chrome-devtools-frontend/front_end/third_party/third-party-web", - "node_modules/chrome-devtools-frontend/mcp/mcp.ts" + "node_modules/chrome-devtools-frontend/mcp" ], "exclude": ["node_modules/chrome-devtools-frontend/**/*.test.ts"] } From bff5c6569003fdbc207448d89a8be6a9a8172ca0 Mon Sep 17 00:00:00 2001 From: Alex Rudenko Date: Thu, 27 Nov 2025 11:07:19 +0100 Subject: [PATCH 096/456] fix: handle the case when all pages are filtered out (#616) Drive-by: allow attaching to chrome://inspect pages --- src/McpContext.ts | 5 ++++- src/browser.ts | 4 ++++ src/main.ts | 3 ++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/McpContext.ts b/src/McpContext.ts index 1b0c586f4..2490d3420 100644 --- a/src/McpContext.ts +++ b/src/McpContext.ts @@ -407,7 +407,10 @@ export class McpContext implements Context { ); }); - if (!this.#selectedPage || this.#pages.indexOf(this.#selectedPage) === -1) { + if ( + (!this.#selectedPage || this.#pages.indexOf(this.#selectedPage) === -1) && + this.#pages[0] + ) { this.selectPage(this.#pages[0]); } diff --git a/src/browser.ts b/src/browser.ts index 3abcbe00e..3120a413f 100644 --- a/src/browser.ts +++ b/src/browser.ts @@ -30,6 +30,10 @@ function makeTargetFilter() { if (target.url() === 'chrome://newtab/') { return true; } + // Could be the only page opened in the browser. + if (target.url().startsWith('chrome://inspect')) { + return true; + } for (const prefix of ignoredPrefixes) { if (target.url().startsWith(prefix)) { return false; diff --git a/src/main.ts b/src/main.ts index 153b6f5da..8edd06cc3 100644 --- a/src/main.ts +++ b/src/main.ts @@ -148,6 +148,7 @@ function registerTool(tool: ToolDefinition): void { content, }; } catch (error) { + logger(`${tool.name} response handling error:`, error, error.stack); const errorText = error instanceof Error ? error.message : String(error); @@ -162,7 +163,7 @@ function registerTool(tool: ToolDefinition): void { }; } } catch (err) { - logger(`${tool.name} error: ${err.message}`); + logger(`${tool.name} error:`, err, err.stack); throw err; } finally { guard.dispose(); From a67528a046746c7131d5265f6c94613d607aaf90 Mon Sep 17 00:00:00 2001 From: Alex Rudenko Date: Thu, 27 Nov 2025 16:40:58 +0100 Subject: [PATCH 097/456] fix: handle error messages that are not instanceof Error (#618) Closes https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/617 --- src/main.ts | 37 +++++++++++++++---------------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/src/main.ts b/src/main.ts index 8edd06cc3..1b7111fd0 100644 --- a/src/main.ts +++ b/src/main.ts @@ -142,29 +142,22 @@ function registerTool(tool: ToolDefinition): void { response, context, ); - try { - const content = await response.handle(tool.name, context); - return { - content, - }; - } catch (error) { - logger(`${tool.name} response handling error:`, error, error.stack); - const errorText = - error instanceof Error ? error.message : String(error); - - return { - content: [ - { - type: 'text', - text: errorText, - }, - ], - isError: true, - }; - } + const content = await response.handle(tool.name, context); + return { + content, + }; } catch (err) { - logger(`${tool.name} error:`, err, err.stack); - throw err; + logger(`${tool.name} error:`, err, err?.stack); + const errorText = err && 'message' in err ? err.message : String(err); + return { + content: [ + { + type: 'text', + text: errorText, + }, + ], + isError: true, + }; } finally { guard.dispose(); } From 8e90a92780d5e4775c8d2c22e2bae9310e15f8b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Z=C3=BCnd?= Date: Fri, 28 Nov 2025 07:52:52 +0100 Subject: [PATCH 098/456] chore: split withBrowser into withBrowser and withMcpContext (#620) I split this PR off from my "create one DevTools universe per page" PR in preparation. This allows tests to re-use browser instances without creating an `McpContext`. Drive-by: Move mocked browser/page into utils.ts. --- tests/McpContext.test.ts | 14 ++--- tests/McpResponse.test.ts | 64 +++++++++++------------ tests/PageCollector.test.ts | 62 +---------------------- tests/tools/console.test.ts | 18 +++---- tests/tools/emulation.test.ts | 18 +++---- tests/tools/input.test.ts | 28 +++++----- tests/tools/network.test.ts | 16 +++--- tests/tools/pages.test.ts | 34 ++++++------- tests/tools/performance.test.ts | 24 ++++----- tests/tools/screenshot.test.ts | 24 ++++----- tests/tools/script.test.ts | 16 +++--- tests/tools/snapshot.test.ts | 12 ++--- tests/utils.ts | 90 ++++++++++++++++++++++++++++----- 13 files changed, 212 insertions(+), 208 deletions(-) diff --git a/tests/McpContext.test.ts b/tests/McpContext.test.ts index 4b5de0911..e1c34f52c 100644 --- a/tests/McpContext.test.ts +++ b/tests/McpContext.test.ts @@ -11,11 +11,11 @@ import sinon from 'sinon'; import type {TraceResult} from '../src/trace-processing/parse.js'; -import {html, withBrowser} from './utils.js'; +import {html, withMcpContext} from './utils.js'; describe('McpContext', () => { it('list pages', async () => { - await withBrowser(async (_response, context) => { + await withMcpContext(async (_response, context) => { const page = context.getSelectedPage(); await page.setContent( html` { }); it('can store and retrieve performance traces', async () => { - await withBrowser(async (_response, context) => { + await withMcpContext(async (_response, context) => { const fakeTrace1 = {} as unknown as TraceResult; const fakeTrace2 = {} as unknown as TraceResult; context.storeTraceRecording(fakeTrace1); @@ -50,7 +50,7 @@ describe('McpContext', () => { }); it('should update default timeout when cpu throttling changes', async () => { - await withBrowser(async (_response, context) => { + await withMcpContext(async (_response, context) => { const page = await context.newPage(); const timeoutBefore = page.getDefaultTimeout(); context.setCpuThrottlingRate(2); @@ -60,7 +60,7 @@ describe('McpContext', () => { }); it('should update default timeout when network conditions changes', async () => { - await withBrowser(async (_response, context) => { + await withMcpContext(async (_response, context) => { const page = await context.newPage(); const timeoutBefore = page.getDefaultNavigationTimeout(); context.setNetworkConditions('Slow 3G'); @@ -70,7 +70,7 @@ describe('McpContext', () => { }); it('should call waitForEventsAfterAction with correct multipliers', async () => { - await withBrowser(async (_response, context) => { + await withMcpContext(async (_response, context) => { const page = await context.newPage(); context.setCpuThrottlingRate(2); @@ -86,7 +86,7 @@ describe('McpContext', () => { }); it('should should detect open DevTools pages', async () => { - await withBrowser( + await withMcpContext( async (_response, context) => { const page = await context.newPage(); // TODO: we do not know when the CLI flag to auto open DevTools will run diff --git a/tests/McpResponse.test.ts b/tests/McpResponse.test.ts index 9031f6ddb..5feb66fdc 100644 --- a/tests/McpResponse.test.ts +++ b/tests/McpResponse.test.ts @@ -16,12 +16,12 @@ import { getMockResponse, html, stabilizeResponseOutput, - withBrowser, + withMcpContext, } from './utils.js'; describe('McpResponse', () => { it('list pages', async t => { - await withBrowser(async (response, context) => { + await withMcpContext(async (response, context) => { response.setIncludePages(true); const result = await response.handle('test', context); assert.equal(result[0].type, 'text'); @@ -30,7 +30,7 @@ describe('McpResponse', () => { }); it('allows response text lines to be added', async t => { - await withBrowser(async (response, context) => { + await withMcpContext(async (response, context) => { response.appendResponseLine('Testing 1'); response.appendResponseLine('Testing 2'); const result = await response.handle('test', context); @@ -40,7 +40,7 @@ describe('McpResponse', () => { }); it('does not include anything in response if snapshot is null', async () => { - await withBrowser(async (response, context) => { + await withMcpContext(async (response, context) => { const page = context.getSelectedPage(); page.accessibility.snapshot = async () => null; const result = await response.handle('test', context); @@ -50,7 +50,7 @@ describe('McpResponse', () => { }); it('returns correctly formatted snapshot for a simple tree', async t => { - await withBrowser(async (response, context) => { + await withMcpContext(async (response, context) => { const page = context.getSelectedPage(); await page.setContent( html` { }); it('returns values for textboxes', async t => { - await withBrowser(async (response, context) => { + await withMcpContext(async (response, context) => { const page = context.getSelectedPage(); await page.setContent( html`