Add SPA session validation and buglist, update migration docs

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

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

6
node_modules/playwright/README.md generated vendored
View File

@@ -1,6 +1,6 @@
# 🎭 Playwright
[![npm version](https://img.shields.io/npm/v/playwright.svg)](https://www.npmjs.com/package/playwright) <!-- GEN:chromium-version-badge -->[![Chromium version](https://img.shields.io/badge/chromium-141.0.7390.37-blue.svg?logo=google-chrome)](https://www.chromium.org/Home)<!-- GEN:stop --> <!-- GEN:firefox-version-badge -->[![Firefox version](https://img.shields.io/badge/firefox-142.0.1-blue.svg?logo=firefoxbrowser)](https://www.mozilla.org/en-US/firefox/new/)<!-- GEN:stop --> <!-- GEN:webkit-version-badge -->[![WebKit version](https://img.shields.io/badge/webkit-26.0-blue.svg?logo=safari)](https://webkit.org/)<!-- GEN:stop --> [![Join Discord](https://img.shields.io/badge/join-discord-informational)](https://aka.ms/playwright/discord)
[![npm version](https://img.shields.io/npm/v/playwright.svg)](https://www.npmjs.com/package/playwright) <!-- GEN:chromium-version-badge -->[![Chromium version](https://img.shields.io/badge/chromium-143.0.7499.4-blue.svg?logo=google-chrome)](https://www.chromium.org/Home)<!-- GEN:stop --> <!-- GEN:firefox-version-badge -->[![Firefox version](https://img.shields.io/badge/firefox-144.0.2-blue.svg?logo=firefoxbrowser)](https://www.mozilla.org/en-US/firefox/new/)<!-- GEN:stop --> <!-- GEN:webkit-version-badge -->[![WebKit version](https://img.shields.io/badge/webkit-26.0-blue.svg?logo=safari)](https://webkit.org/)<!-- GEN:stop --> [![Join Discord](https://img.shields.io/badge/join-discord-informational)](https://aka.ms/playwright/discord)
## [Documentation](https://playwright.dev) | [API reference](https://playwright.dev/docs/api/class-playwright)
@@ -8,9 +8,9 @@ Playwright is a framework for Web Testing and Automation. It allows testing [Chr
| | Linux | macOS | Windows |
| :--- | :---: | :---: | :---: |
| Chromium <!-- GEN:chromium-version -->141.0.7390.37<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| Chromium <!-- GEN:chromium-version -->143.0.7499.4<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| WebKit <!-- GEN:webkit-version -->26.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| Firefox <!-- GEN:firefox-version -->142.0.1<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| Firefox <!-- GEN:firefox-version -->144.0.2<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
Headless execution is supported for all browsers on all platforms. Check out [system requirements](https://playwright.dev/docs/intro#system-requirements) for details.

View File

@@ -5,7 +5,6 @@ THIRD-PARTY SOFTWARE NOTICES AND INFORMATION
This project incorporates components from the projects listed below. The original copyright notices and the licenses under which Microsoft received such components are set forth below. Microsoft reserves all rights not expressly granted herein, whether by implication, estoppel or otherwise.
- @ampproject/remapping@2.2.1 (https://github.com/ampproject/remapping)
- @babel/code-frame@7.24.7 (https://github.com/babel/babel)
- @babel/code-frame@7.27.1 (https://github.com/babel/babel)
- @babel/compat-data@7.28.0 (https://github.com/babel/babel)
- @babel/core@7.28.0 (https://github.com/babel/babel)
@@ -22,11 +21,10 @@ This project incorporates components from the projects listed below. The origina
- @babel/helper-replace-supers@7.27.1 (https://github.com/babel/babel)
- @babel/helper-skip-transparent-expression-wrappers@7.27.1 (https://github.com/babel/babel)
- @babel/helper-string-parser@7.27.1 (https://github.com/babel/babel)
- @babel/helper-validator-identifier@7.24.7 (https://github.com/babel/babel)
- @babel/helper-validator-identifier@7.27.1 (https://github.com/babel/babel)
- @babel/helper-validator-identifier@7.28.5 (https://github.com/babel/babel)
- @babel/helper-validator-option@7.27.1 (https://github.com/babel/babel)
- @babel/helpers@7.28.2 (https://github.com/babel/babel)
- @babel/highlight@7.24.7 (https://github.com/babel/babel)
- @babel/parser@7.28.0 (https://github.com/babel/babel)
- @babel/plugin-proposal-decorators@7.28.0 (https://github.com/babel/babel)
- @babel/plugin-syntax-async-generators@7.8.4 (https://github.com/babel/babel/tree/master/packages/babel-plugin-syntax-async-generators)
@@ -55,26 +53,28 @@ This project incorporates components from the projects listed below. The origina
- @babel/template@7.27.2 (https://github.com/babel/babel)
- @babel/traverse@7.28.0 (https://github.com/babel/babel)
- @babel/types@7.28.2 (https://github.com/babel/babel)
- @jest/expect-utils@29.7.0 (https://github.com/jestjs/jest)
- @jest/schemas@29.6.3 (https://github.com/jestjs/jest)
- @jest/types@29.6.3 (https://github.com/jestjs/jest)
- @jest/diff-sequences@30.0.1 (https://github.com/jestjs/jest)
- @jest/expect-utils@30.2.0 (https://github.com/jestjs/jest)
- @jest/get-type@30.1.0 (https://github.com/jestjs/jest)
- @jest/pattern@30.0.1 (https://github.com/jestjs/jest)
- @jest/schemas@30.0.5 (https://github.com/jestjs/jest)
- @jest/types@30.2.0 (https://github.com/jestjs/jest)
- @jridgewell/gen-mapping@0.3.12 (https://github.com/jridgewell/sourcemaps)
- @jridgewell/resolve-uri@3.1.1 (https://github.com/jridgewell/resolve-uri)
- @jridgewell/sourcemap-codec@1.5.4 (https://github.com/jridgewell/sourcemaps)
- @jridgewell/trace-mapping@0.3.29 (https://github.com/jridgewell/sourcemaps)
- @modelcontextprotocol/sdk@1.17.5 (https://github.com/modelcontextprotocol/typescript-sdk)
- @sinclair/typebox@0.27.8 (https://github.com/sinclairzx81/typebox)
- @sinclair/typebox@0.34.41 (https://github.com/sinclairzx81/typebox)
- @types/istanbul-lib-coverage@2.0.6 (https://github.com/DefinitelyTyped/DefinitelyTyped)
- @types/istanbul-lib-report@3.0.3 (https://github.com/DefinitelyTyped/DefinitelyTyped)
- @types/istanbul-reports@3.0.4 (https://github.com/DefinitelyTyped/DefinitelyTyped)
- @types/node@22.5.4 (https://github.com/DefinitelyTyped/DefinitelyTyped)
- @types/node@24.9.2 (https://github.com/DefinitelyTyped/DefinitelyTyped)
- @types/stack-utils@2.0.3 (https://github.com/DefinitelyTyped/DefinitelyTyped)
- @types/yargs-parser@21.0.3 (https://github.com/DefinitelyTyped/DefinitelyTyped)
- @types/yargs@17.0.33 (https://github.com/DefinitelyTyped/DefinitelyTyped)
- @types/yargs@17.0.34 (https://github.com/DefinitelyTyped/DefinitelyTyped)
- accepts@2.0.0 (https://github.com/jshttp/accepts)
- ajv@6.12.6 (https://github.com/ajv-validator/ajv)
- ansi-colors@4.1.3 (https://github.com/doowb/ansi-colors)
- ansi-styles@3.2.1 (https://github.com/chalk/ansi-styles)
- ansi-styles@4.3.0 (https://github.com/chalk/ansi-styles)
- ansi-styles@5.2.0 (https://github.com/chalk/ansi-styles)
- anymatch@3.1.3 (https://github.com/micromatch/anymatch)
@@ -87,14 +87,11 @@ This project incorporates components from the projects listed below. The origina
- call-bind-apply-helpers@1.0.2 (https://github.com/ljharb/call-bind-apply-helpers)
- call-bound@1.0.4 (https://github.com/ljharb/call-bound)
- caniuse-lite@1.0.30001731 (https://github.com/browserslist/caniuse-lite)
- chalk@2.4.2 (https://github.com/chalk/chalk)
- chalk@4.1.2 (https://github.com/chalk/chalk)
- chokidar@3.6.0 (https://github.com/paulmillr/chokidar)
- ci-info@3.9.0 (https://github.com/watson/ci-info)
- ci-info@4.3.1 (https://github.com/watson/ci-info)
- codemirror@5.65.18 (https://github.com/codemirror/CodeMirror)
- color-convert@1.9.3 (https://github.com/Qix-/color-convert)
- color-convert@2.0.1 (https://github.com/Qix-/color-convert)
- color-name@1.1.3 (https://github.com/dfcreative/color-name)
- color-name@1.1.4 (https://github.com/colorjs/color-name)
- content-disposition@1.0.0 (https://github.com/jshttp/content-disposition)
- content-type@1.0.5 (https://github.com/jshttp/content-type)
@@ -105,7 +102,6 @@ This project incorporates components from the projects listed below. The origina
- cross-spawn@7.0.6 (https://github.com/moxystudio/node-cross-spawn)
- debug@4.4.0 (https://github.com/debug-js/debug)
- depd@2.0.0 (https://github.com/dougwilson/nodejs-depd)
- diff-sequences@29.6.3 (https://github.com/jestjs/jest)
- dunder-proto@1.0.1 (https://github.com/es-shims/dunder-proto)
- ee-first@1.1.1 (https://github.com/jonathanong/ee-first)
- electron-to-chromium@1.5.192 (https://github.com/kilian/electron-to-chromium)
@@ -116,11 +112,11 @@ This project incorporates components from the projects listed below. The origina
- es-object-atoms@1.1.1 (https://github.com/ljharb/es-object-atoms)
- escalade@3.2.0 (https://github.com/lukeed/escalade)
- escape-html@1.0.3 (https://github.com/component/escape-html)
- escape-string-regexp@1.0.5 (https://github.com/sindresorhus/escape-string-regexp)
- escape-string-regexp@2.0.0 (https://github.com/sindresorhus/escape-string-regexp)
- etag@1.8.1 (https://github.com/jshttp/etag)
- eventsource-parser@3.0.3 (https://github.com/rexxars/eventsource-parser)
- eventsource@3.0.7 (git://git@github.com/EventSource/eventsource)
- expect@30.2.0 (https://github.com/jestjs/jest)
- express-rate-limit@7.5.1 (https://github.com/express-rate-limit/express-rate-limit)
- express@5.1.0 (https://github.com/expressjs/express)
- fast-deep-equal@3.1.3 (https://github.com/epoberezkin/fast-deep-equal)
@@ -137,7 +133,6 @@ This project incorporates components from the projects listed below. The origina
- glob-parent@5.1.2 (https://github.com/gulpjs/glob-parent)
- gopd@1.2.0 (https://github.com/ljharb/gopd)
- graceful-fs@4.2.11 (https://github.com/isaacs/node-graceful-fs)
- has-flag@3.0.0 (https://github.com/sindresorhus/has-flag)
- has-flag@4.0.0 (https://github.com/sindresorhus/has-flag)
- has-symbols@1.1.0 (https://github.com/inspect-js/has-symbols)
- hasown@2.0.2 (https://github.com/inspect-js/hasOwn)
@@ -151,12 +146,12 @@ This project incorporates components from the projects listed below. The origina
- is-number@7.0.0 (https://github.com/jonschlinkert/is-number)
- is-promise@4.0.0 (https://github.com/then/is-promise)
- isexe@2.0.0 (https://github.com/isaacs/isexe)
- jest-diff@29.7.0 (https://github.com/jestjs/jest)
- jest-get-type@29.6.3 (https://github.com/jestjs/jest)
- jest-matcher-utils@29.7.0 (https://github.com/jestjs/jest)
- jest-message-util@29.7.0 (https://github.com/jestjs/jest)
- jest-mock@29.7.0 (https://github.com/jestjs/jest)
- jest-util@29.7.0 (https://github.com/jestjs/jest)
- jest-diff@30.2.0 (https://github.com/jestjs/jest)
- jest-matcher-utils@30.2.0 (https://github.com/jestjs/jest)
- jest-message-util@30.2.0 (https://github.com/jestjs/jest)
- jest-mock@30.2.0 (https://github.com/jestjs/jest)
- jest-regex-util@30.0.1 (https://github.com/jestjs/jest)
- jest-util@30.2.0 (https://github.com/jestjs/jest)
- js-tokens@4.0.0 (https://github.com/lydell/js-tokens)
- jsesc@3.1.0 (https://github.com/mathiasbynens/jsesc)
- json-schema-traverse@0.4.1 (https://github.com/epoberezkin/json-schema-traverse)
@@ -179,11 +174,11 @@ This project incorporates components from the projects listed below. The origina
- parseurl@1.3.3 (https://github.com/pillarjs/parseurl)
- path-key@3.1.1 (https://github.com/sindresorhus/path-key)
- path-to-regexp@8.2.0 (https://github.com/pillarjs/path-to-regexp)
- picocolors@1.1.0 (https://github.com/alexeyraspopov/picocolors)
- picocolors@1.1.1 (https://github.com/alexeyraspopov/picocolors)
- picomatch@2.3.1 (https://github.com/micromatch/picomatch)
- picomatch@4.0.3 (https://github.com/micromatch/picomatch)
- pkce-challenge@5.0.0 (https://github.com/crouchcd/pkce-challenge)
- pretty-format@29.7.0 (https://github.com/jestjs/jest)
- pretty-format@30.2.0 (https://github.com/jestjs/jest)
- proxy-addr@2.0.7 (https://github.com/jshttp/proxy-addr)
- punycode@2.3.1 (https://github.com/mathiasbynens/punycode.js)
- qs@6.14.0 (https://github.com/ljharb/qs)
@@ -211,12 +206,11 @@ This project incorporates components from the projects listed below. The origina
- statuses@2.0.1 (https://github.com/jshttp/statuses)
- statuses@2.0.2 (https://github.com/jshttp/statuses)
- stoppable@1.1.0 (https://github.com/hunterloftis/stoppable)
- supports-color@5.5.0 (https://github.com/chalk/supports-color)
- supports-color@7.2.0 (https://github.com/chalk/supports-color)
- to-regex-range@5.0.1 (https://github.com/micromatch/to-regex-range)
- toidentifier@1.0.1 (https://github.com/component/toidentifier)
- type-is@2.0.1 (https://github.com/jshttp/type-is)
- undici-types@6.19.8 (https://github.com/nodejs/undici)
- undici-types@7.16.0 (https://github.com/nodejs/undici)
- unpipe@1.0.0 (https://github.com/stream-utils/unpipe)
- update-browserslist-db@1.1.3 (https://github.com/browserslist/update-db)
- uri-js@4.4.1 (https://github.com/garycourt/uri-js)
@@ -433,33 +427,6 @@ Apache License
=========================================
END OF @ampproject/remapping@2.2.1 AND INFORMATION
%% @babel/code-frame@7.24.7 NOTICES AND INFORMATION BEGIN HERE
=========================================
MIT License
Copyright (c) 2014-present Sebastian McKenzie and other contributors
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
=========================================
END OF @babel/code-frame@7.24.7 AND INFORMATION
%% @babel/code-frame@7.27.1 NOTICES AND INFORMATION BEGIN HERE
=========================================
MIT License
@@ -892,33 +859,6 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
=========================================
END OF @babel/helper-string-parser@7.27.1 AND INFORMATION
%% @babel/helper-validator-identifier@7.24.7 NOTICES AND INFORMATION BEGIN HERE
=========================================
MIT License
Copyright (c) 2014-present Sebastian McKenzie and other contributors
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
=========================================
END OF @babel/helper-validator-identifier@7.24.7 AND INFORMATION
%% @babel/helper-validator-identifier@7.27.1 NOTICES AND INFORMATION BEGIN HERE
=========================================
MIT License
@@ -946,6 +886,33 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
=========================================
END OF @babel/helper-validator-identifier@7.27.1 AND INFORMATION
%% @babel/helper-validator-identifier@7.28.5 NOTICES AND INFORMATION BEGIN HERE
=========================================
MIT License
Copyright (c) 2014-present Sebastian McKenzie and other contributors
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
=========================================
END OF @babel/helper-validator-identifier@7.28.5 AND INFORMATION
%% @babel/helper-validator-option@7.27.1 NOTICES AND INFORMATION BEGIN HERE
=========================================
MIT License
@@ -1001,33 +968,6 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
=========================================
END OF @babel/helpers@7.28.2 AND INFORMATION
%% @babel/highlight@7.24.7 NOTICES AND INFORMATION BEGIN HERE
=========================================
MIT License
Copyright (c) 2014-present Sebastian McKenzie and other contributors
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
=========================================
END OF @babel/highlight@7.24.7 AND INFORMATION
%% @babel/parser@7.28.0 NOTICES AND INFORMATION BEGIN HERE
=========================================
Copyright (C) 2012-2014 by various contributors (see AUTHORS)
@@ -1781,11 +1721,12 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
=========================================
END OF @babel/types@7.28.2 AND INFORMATION
%% @jest/expect-utils@29.7.0 NOTICES AND INFORMATION BEGIN HERE
%% @jest/diff-sequences@30.0.1 NOTICES AND INFORMATION BEGIN HERE
=========================================
MIT License
Copyright (c) Meta Platforms, Inc. and affiliates.
Copyright Contributors to the Jest project.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -1805,13 +1746,14 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
=========================================
END OF @jest/expect-utils@29.7.0 AND INFORMATION
END OF @jest/diff-sequences@30.0.1 AND INFORMATION
%% @jest/schemas@29.6.3 NOTICES AND INFORMATION BEGIN HERE
%% @jest/expect-utils@30.2.0 NOTICES AND INFORMATION BEGIN HERE
=========================================
MIT License
Copyright (c) Meta Platforms, Inc. and affiliates.
Copyright Contributors to the Jest project.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -1831,13 +1773,14 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
=========================================
END OF @jest/schemas@29.6.3 AND INFORMATION
END OF @jest/expect-utils@30.2.0 AND INFORMATION
%% @jest/types@29.6.3 NOTICES AND INFORMATION BEGIN HERE
%% @jest/get-type@30.1.0 NOTICES AND INFORMATION BEGIN HERE
=========================================
MIT License
Copyright (c) Meta Platforms, Inc. and affiliates.
Copyright Contributors to the Jest project.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -1857,7 +1800,88 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
=========================================
END OF @jest/types@29.6.3 AND INFORMATION
END OF @jest/get-type@30.1.0 AND INFORMATION
%% @jest/pattern@30.0.1 NOTICES AND INFORMATION BEGIN HERE
=========================================
MIT License
Copyright (c) Meta Platforms, Inc. and affiliates.
Copyright Contributors to the Jest project.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
=========================================
END OF @jest/pattern@30.0.1 AND INFORMATION
%% @jest/schemas@30.0.5 NOTICES AND INFORMATION BEGIN HERE
=========================================
MIT License
Copyright (c) Meta Platforms, Inc. and affiliates.
Copyright Contributors to the Jest project.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
=========================================
END OF @jest/schemas@30.0.5 AND INFORMATION
%% @jest/types@30.2.0 NOTICES AND INFORMATION BEGIN HERE
=========================================
MIT License
Copyright (c) Meta Platforms, Inc. and affiliates.
Copyright Contributors to the Jest project.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
=========================================
END OF @jest/types@30.2.0 AND INFORMATION
%% @jridgewell/gen-mapping@0.3.12 NOTICES AND INFORMATION BEGIN HERE
=========================================
@@ -1981,13 +2005,15 @@ SOFTWARE.
=========================================
END OF @modelcontextprotocol/sdk@1.17.5 AND INFORMATION
%% @sinclair/typebox@0.27.8 NOTICES AND INFORMATION BEGIN HERE
%% @sinclair/typebox@0.34.41 NOTICES AND INFORMATION BEGIN HERE
=========================================
TypeBox: JSON Schema Type Builder with Static Type Resolution for TypeScript
TypeBox
Json Schema Type Builder with Static Type Resolution for TypeScript
The MIT License (MIT)
Copyright (c) 2017-2023 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
Copyright (c) 2017-2025 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -2007,7 +2033,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
=========================================
END OF @sinclair/typebox@0.27.8 AND INFORMATION
END OF @sinclair/typebox@0.34.41 AND INFORMATION
%% @types/istanbul-lib-coverage@2.0.6 NOTICES AND INFORMATION BEGIN HERE
=========================================
@@ -2087,7 +2113,7 @@ MIT License
=========================================
END OF @types/istanbul-reports@3.0.4 AND INFORMATION
%% @types/node@22.5.4 NOTICES AND INFORMATION BEGIN HERE
%% @types/node@24.9.2 NOTICES AND INFORMATION BEGIN HERE
=========================================
MIT License
@@ -2111,7 +2137,7 @@ MIT License
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE
=========================================
END OF @types/node@22.5.4 AND INFORMATION
END OF @types/node@24.9.2 AND INFORMATION
%% @types/stack-utils@2.0.3 NOTICES AND INFORMATION BEGIN HERE
=========================================
@@ -2165,7 +2191,7 @@ MIT License
=========================================
END OF @types/yargs-parser@21.0.3 AND INFORMATION
%% @types/yargs@17.0.33 NOTICES AND INFORMATION BEGIN HERE
%% @types/yargs@17.0.34 NOTICES AND INFORMATION BEGIN HERE
=========================================
MIT License
@@ -2189,7 +2215,7 @@ MIT License
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE
=========================================
END OF @types/yargs@17.0.33 AND INFORMATION
END OF @types/yargs@17.0.34 AND INFORMATION
%% accepts@2.0.0 NOTICES AND INFORMATION BEGIN HERE
=========================================
@@ -2271,20 +2297,6 @@ THE SOFTWARE.
=========================================
END OF ansi-colors@4.1.3 AND INFORMATION
%% ansi-styles@3.2.1 NOTICES AND INFORMATION BEGIN HERE
=========================================
MIT License
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
=========================================
END OF ansi-styles@3.2.1 AND INFORMATION
%% ansi-styles@4.3.0 NOTICES AND INFORMATION BEGIN HERE
=========================================
MIT License
@@ -2932,20 +2944,6 @@ Creative Commons may be contacted at creativecommons.org.
=========================================
END OF caniuse-lite@1.0.30001731 AND INFORMATION
%% chalk@2.4.2 NOTICES AND INFORMATION BEGIN HERE
=========================================
MIT License
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
=========================================
END OF chalk@2.4.2 AND INFORMATION
%% chalk@4.1.2 NOTICES AND INFORMATION BEGIN HERE
=========================================
MIT License
@@ -2986,7 +2984,7 @@ THE SOFTWARE.
=========================================
END OF chokidar@3.6.0 AND INFORMATION
%% ci-info@3.9.0 NOTICES AND INFORMATION BEGIN HERE
%% ci-info@4.3.1 NOTICES AND INFORMATION BEGIN HERE
=========================================
The MIT License (MIT)
@@ -3010,7 +3008,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
=========================================
END OF ci-info@3.9.0 AND INFORMATION
END OF ci-info@4.3.1 AND INFORMATION
%% codemirror@5.65.18 NOTICES AND INFORMATION BEGIN HERE
=========================================
@@ -3038,31 +3036,6 @@ THE SOFTWARE.
=========================================
END OF codemirror@5.65.18 AND INFORMATION
%% color-convert@1.9.3 NOTICES AND INFORMATION BEGIN HERE
=========================================
Copyright (c) 2011-2016 Heather Arthur <fayearthur@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
=========================================
END OF color-convert@1.9.3 AND INFORMATION
%% color-convert@2.0.1 NOTICES AND INFORMATION BEGIN HERE
=========================================
Copyright (c) 2011-2016 Heather Arthur <fayearthur@gmail.com>
@@ -3088,19 +3061,6 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
=========================================
END OF color-convert@2.0.1 AND INFORMATION
%% color-name@1.1.3 NOTICES AND INFORMATION BEGIN HERE
=========================================
The MIT License (MIT)
Copyright (c) 2015 Dmitry Ivanov
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
=========================================
END OF color-name@1.1.3 AND INFORMATION
%% color-name@1.1.4 NOTICES AND INFORMATION BEGIN HERE
=========================================
The MIT License (MIT)
@@ -3355,32 +3315,6 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
=========================================
END OF depd@2.0.0 AND INFORMATION
%% diff-sequences@29.6.3 NOTICES AND INFORMATION BEGIN HERE
=========================================
MIT License
Copyright (c) Meta Platforms, Inc. and affiliates.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
=========================================
END OF diff-sequences@29.6.3 AND INFORMATION
%% dunder-proto@1.0.1 NOTICES AND INFORMATION BEGIN HERE
=========================================
MIT License
@@ -3617,32 +3551,6 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
=========================================
END OF escape-html@1.0.3 AND INFORMATION
%% escape-string-regexp@1.0.5 NOTICES AND INFORMATION BEGIN HERE
=========================================
The MIT License (MIT)
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
=========================================
END OF escape-string-regexp@1.0.5 AND INFORMATION
%% escape-string-regexp@2.0.0 NOTICES AND INFORMATION BEGIN HERE
=========================================
MIT License
@@ -3737,6 +3645,33 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
=========================================
END OF eventsource@3.0.7 AND INFORMATION
%% expect@30.2.0 NOTICES AND INFORMATION BEGIN HERE
=========================================
MIT License
Copyright (c) Meta Platforms, Inc. and affiliates.
Copyright Contributors to the Jest project.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
=========================================
END OF expect@30.2.0 AND INFORMATION
%% express-rate-limit@7.5.1 NOTICES AND INFORMATION BEGIN HERE
=========================================
# MIT License
@@ -4119,20 +4054,6 @@ IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
=========================================
END OF graceful-fs@4.2.11 AND INFORMATION
%% has-flag@3.0.0 NOTICES AND INFORMATION BEGIN HERE
=========================================
MIT License
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
=========================================
END OF has-flag@3.0.0 AND INFORMATION
%% has-flag@4.0.0 NOTICES AND INFORMATION BEGIN HERE
=========================================
MIT License
@@ -4431,11 +4352,12 @@ IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
=========================================
END OF isexe@2.0.0 AND INFORMATION
%% jest-diff@29.7.0 NOTICES AND INFORMATION BEGIN HERE
%% jest-diff@30.2.0 NOTICES AND INFORMATION BEGIN HERE
=========================================
MIT License
Copyright (c) Meta Platforms, Inc. and affiliates.
Copyright Contributors to the Jest project.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -4455,13 +4377,14 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
=========================================
END OF jest-diff@29.7.0 AND INFORMATION
END OF jest-diff@30.2.0 AND INFORMATION
%% jest-get-type@29.6.3 NOTICES AND INFORMATION BEGIN HERE
%% jest-matcher-utils@30.2.0 NOTICES AND INFORMATION BEGIN HERE
=========================================
MIT License
Copyright (c) Meta Platforms, Inc. and affiliates.
Copyright Contributors to the Jest project.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -4481,13 +4404,14 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
=========================================
END OF jest-get-type@29.6.3 AND INFORMATION
END OF jest-matcher-utils@30.2.0 AND INFORMATION
%% jest-matcher-utils@29.7.0 NOTICES AND INFORMATION BEGIN HERE
%% jest-message-util@30.2.0 NOTICES AND INFORMATION BEGIN HERE
=========================================
MIT License
Copyright (c) Meta Platforms, Inc. and affiliates.
Copyright Contributors to the Jest project.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -4507,13 +4431,14 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
=========================================
END OF jest-matcher-utils@29.7.0 AND INFORMATION
END OF jest-message-util@30.2.0 AND INFORMATION
%% jest-message-util@29.7.0 NOTICES AND INFORMATION BEGIN HERE
%% jest-mock@30.2.0 NOTICES AND INFORMATION BEGIN HERE
=========================================
MIT License
Copyright (c) Meta Platforms, Inc. and affiliates.
Copyright Contributors to the Jest project.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -4533,13 +4458,14 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
=========================================
END OF jest-message-util@29.7.0 AND INFORMATION
END OF jest-mock@30.2.0 AND INFORMATION
%% jest-mock@29.7.0 NOTICES AND INFORMATION BEGIN HERE
%% jest-regex-util@30.0.1 NOTICES AND INFORMATION BEGIN HERE
=========================================
MIT License
Copyright (c) Meta Platforms, Inc. and affiliates.
Copyright Contributors to the Jest project.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -4559,13 +4485,14 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
=========================================
END OF jest-mock@29.7.0 AND INFORMATION
END OF jest-regex-util@30.0.1 AND INFORMATION
%% jest-util@29.7.0 NOTICES AND INFORMATION BEGIN HERE
%% jest-util@30.2.0 NOTICES AND INFORMATION BEGIN HERE
=========================================
MIT License
Copyright (c) Meta Platforms, Inc. and affiliates.
Copyright Contributors to the Jest project.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -4585,7 +4512,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
=========================================
END OF jest-util@29.7.0 AND INFORMATION
END OF jest-util@30.2.0 AND INFORMATION
%% js-tokens@4.0.0 NOTICES AND INFORMATION BEGIN HERE
=========================================
@@ -5138,26 +5065,6 @@ THE SOFTWARE.
=========================================
END OF path-to-regexp@8.2.0 AND INFORMATION
%% picocolors@1.1.0 NOTICES AND INFORMATION BEGIN HERE
=========================================
ISC License
Copyright (c) 2021-2024 Oleksii Raspopov, Kostiantyn Denysov, Anton Verinov
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
=========================================
END OF picocolors@1.1.0 AND INFORMATION
%% picocolors@1.1.1 NOTICES AND INFORMATION BEGIN HERE
=========================================
ISC License
@@ -5204,6 +5111,32 @@ THE SOFTWARE.
=========================================
END OF picomatch@2.3.1 AND INFORMATION
%% picomatch@4.0.3 NOTICES AND INFORMATION BEGIN HERE
=========================================
The MIT License (MIT)
Copyright (c) 2017-present, Jon Schlinkert.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
=========================================
END OF picomatch@4.0.3 AND INFORMATION
%% pkce-challenge@5.0.0 NOTICES AND INFORMATION BEGIN HERE
=========================================
MIT License
@@ -5230,11 +5163,12 @@ SOFTWARE.
=========================================
END OF pkce-challenge@5.0.0 AND INFORMATION
%% pretty-format@29.7.0 NOTICES AND INFORMATION BEGIN HERE
%% pretty-format@30.2.0 NOTICES AND INFORMATION BEGIN HERE
=========================================
MIT License
Copyright (c) Meta Platforms, Inc. and affiliates.
Copyright Contributors to the Jest project.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -5254,7 +5188,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
=========================================
END OF pretty-format@29.7.0 AND INFORMATION
END OF pretty-format@30.2.0 AND INFORMATION
%% proxy-addr@2.0.7 NOTICES AND INFORMATION BEGIN HERE
=========================================
@@ -5935,20 +5869,6 @@ THE SOFTWARE.
=========================================
END OF stoppable@1.1.0 AND INFORMATION
%% supports-color@5.5.0 NOTICES AND INFORMATION BEGIN HERE
=========================================
MIT License
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
=========================================
END OF supports-color@5.5.0 AND INFORMATION
%% supports-color@7.2.0 NOTICES AND INFORMATION BEGIN HERE
=========================================
MIT License
@@ -6043,7 +5963,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
=========================================
END OF type-is@2.0.1 AND INFORMATION
%% undici-types@6.19.8 NOTICES AND INFORMATION BEGIN HERE
%% undici-types@7.16.0 NOTICES AND INFORMATION BEGIN HERE
=========================================
MIT License
@@ -6067,7 +5987,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
=========================================
END OF undici-types@6.19.8 AND INFORMATION
END OF undici-types@7.16.0 AND INFORMATION
%% unpipe@1.0.0 NOTICES AND INFORMATION BEGIN HERE
=========================================
@@ -6272,6 +6192,6 @@ END OF zod@3.25.76 AND INFORMATION
SUMMARY BEGIN HERE
=========================================
Total Packages: 222
Total Packages: 216
=========================================
END OF SUMMARY

34
node_modules/playwright/lib/agents/copilot-setup-steps.yml generated vendored Executable file
View File

@@ -0,0 +1,34 @@
name: "Copilot Setup Steps"
on:
workflow_dispatch:
push:
paths:
- .github/workflows/copilot-setup-steps.yml
pull_request:
paths:
- .github/workflows/copilot-setup-steps.yml
jobs:
copilot-setup-steps:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: lts/*
- name: Install dependencies
run: npm ci
- name: Install Playwright Browsers
run: npx playwright install --with-deps
# Customize this step as needed
- name: Build application
run: npx run build

462
node_modules/playwright/lib/agents/generateAgents.js generated vendored Normal file → Executable file
View File

@@ -28,18 +28,25 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var generateAgents_exports = {};
__export(generateAgents_exports, {
initClaudeCodeRepo: () => initClaudeCodeRepo,
initOpencodeRepo: () => initOpencodeRepo,
initVSCodeRepo: () => initVSCodeRepo
ClaudeGenerator: () => ClaudeGenerator,
CopilotGenerator: () => CopilotGenerator,
OpencodeGenerator: () => OpencodeGenerator,
VSCodeGenerator: () => VSCodeGenerator
});
module.exports = __toCommonJS(generateAgents_exports);
var import_fs = __toESM(require("fs"));
var import_path = __toESM(require("path"));
var import_utilsBundle = require("playwright-core/lib/utilsBundle");
var import_utils = require("playwright-core/lib/utils");
var import_seed = require("../mcp/test/seed");
class AgentParser {
static async loadAgents() {
const files = await import_fs.default.promises.readdir(__dirname);
return Promise.all(files.filter((file) => file.endsWith(".agent.md")).map((file) => AgentParser.parseFile(import_path.default.join(__dirname, file))));
}
static async parseFile(filePath) {
const rawMarkdown = await import_fs.default.promises.readFile(filePath, "utf-8");
const { header, content } = this.extractYamlAndContent(rawMarkdown);
const source = await import_fs.default.promises.readFile(filePath, "utf-8");
const { header, content } = this.extractYamlAndContent(source);
const { instructions, examples } = this.extractInstructionsAndExamples(content);
return { header, instructions, examples };
}
@@ -84,180 +91,305 @@ class AgentParser {
return { instructions, examples };
}
}
const claudeToolMap = /* @__PURE__ */ new Map([
["ls", ["Glob"]],
["grep", ["Grep"]],
["read", ["Read"]],
["edit", ["Edit", "MultiEdit"]],
["write", ["Write"]]
]);
const commonMcpServers = {
playwrightTest: {
type: "local",
command: "npx",
args: ["playwright", "run-test-mcp-server"]
class ClaudeGenerator {
static async init(config, projectName, prompts) {
await initRepo(config, projectName, {
promptsFolder: prompts ? ".claude/prompts" : void 0
});
const agents = await AgentParser.loadAgents();
await import_fs.default.promises.mkdir(".claude/agents", { recursive: true });
for (const agent of agents)
await writeFile(`.claude/agents/${agent.header.name}.md`, ClaudeGenerator.agentSpec(agent), "\u{1F916}", "agent definition");
await writeFile(".mcp.json", JSON.stringify({
mcpServers: {
"playwright-test": {
command: "npx",
args: ["playwright", "run-test-mcp-server"]
}
}
}, null, 2), "\u{1F527}", "mcp configuration");
initRepoDone();
}
};
function saveAsClaudeCode(agent) {
function asClaudeTool(tool) {
const [first, second] = tool.split("/");
if (!second)
return (claudeToolMap.get(first) || [first]).join(", ");
return `mcp__${first}__${second}`;
}
const lines = [];
lines.push(`---`);
lines.push(`name: playwright-test-${agent.header.name}`);
lines.push(`description: ${agent.header.description}. Examples: ${agent.examples.map((example) => `<example>${example}</example>`).join("")}`);
lines.push(`tools: ${agent.header.tools.map((tool) => asClaudeTool(tool)).join(", ")}`);
lines.push(`model: ${agent.header.model}`);
lines.push(`color: ${agent.header.color}`);
lines.push(`---`);
lines.push("");
lines.push(agent.instructions);
return lines.join("\n");
}
const opencodeToolMap = /* @__PURE__ */ new Map([
["ls", ["ls", "glob"]],
["grep", ["grep"]],
["read", ["read"]],
["edit", ["edit"]],
["write", ["write"]]
]);
function saveAsOpencodeJson(agents) {
function asOpencodeTool(tools, tool) {
const [first, second] = tool.split("/");
if (!second) {
for (const tool2 of opencodeToolMap.get(first) || [first])
tools[tool2] = true;
} else {
tools[`${first}*${second}`] = true;
static agentSpec(agent) {
const claudeToolMap = /* @__PURE__ */ new Map([
["search", ["Glob", "Grep", "Read", "LS"]],
["edit", ["Edit", "MultiEdit", "Write"]]
]);
function asClaudeTool(tool) {
const [first, second] = tool.split("/");
if (!second)
return (claudeToolMap.get(first) || [first]).join(", ");
return `mcp__${first}__${second}`;
}
}
const result = {};
result["$schema"] = "https://opencode.ai/config.json";
result["mcp"] = {};
result["tools"] = {
"playwright*": false
};
result["agent"] = {};
for (const agent of agents) {
const tools = {};
result["agent"]["playwright-test-" + agent.header.name] = {
description: agent.header.description,
mode: "subagent",
prompt: `{file:.opencode/prompts/playwright-test-${agent.header.name}.md}`,
tools
const examples = agent.examples.length ? ` Examples: ${agent.examples.map((example) => `<example>${example}</example>`).join("")}` : "";
const lines = [];
const header = {
name: agent.header.name,
description: agent.header.description + examples,
tools: agent.header.tools.map((tool) => asClaudeTool(tool)).join(", "),
model: agent.header.model,
color: agent.header.color
};
for (const tool of agent.header.tools)
asOpencodeTool(tools, tool);
lines.push(`---`);
lines.push(import_utilsBundle.yaml.stringify(header, { lineWidth: 1e5 }) + `---`);
lines.push("");
lines.push(agent.instructions);
return lines.join("\n");
}
const server = commonMcpServers.playwrightTest;
result["mcp"]["playwright-test"] = {
type: server.type,
command: [server.command, ...server.args],
enabled: true
};
return JSON.stringify(result, null, 2);
}
async function loadAgents() {
const files = await import_fs.default.promises.readdir(__dirname);
return Promise.all(files.filter((file) => file.endsWith(".md")).map((file) => AgentParser.parseFile(import_path.default.join(__dirname, file))));
class OpencodeGenerator {
static async init(config, projectName, prompts) {
await initRepo(config, projectName, {
defaultAgentName: "Build",
promptsFolder: prompts ? ".opencode/prompts" : void 0
});
const agents = await AgentParser.loadAgents();
for (const agent of agents) {
const prompt = [agent.instructions];
prompt.push("");
prompt.push(...agent.examples.map((example) => `<example>${example}</example>`));
await writeFile(`.opencode/prompts/${agent.header.name}.md`, prompt.join("\n"), "\u{1F916}", "agent definition");
}
await writeFile("opencode.json", OpencodeGenerator.configuration(agents), "\u{1F527}", "opencode configuration");
initRepoDone();
}
static configuration(agents) {
const opencodeToolMap = /* @__PURE__ */ new Map([
["search", ["ls", "glob", "grep", "read"]],
["edit", ["edit", "write"]]
]);
const asOpencodeTool = (tools, tool) => {
const [first, second] = tool.split("/");
if (!second) {
for (const tool2 of opencodeToolMap.get(first) || [first])
tools[tool2] = true;
} else {
tools[`${first}*${second}`] = true;
}
};
const result = {};
result["$schema"] = "https://opencode.ai/config.json";
result["mcp"] = {};
result["tools"] = {
"playwright*": false
};
result["agent"] = {};
for (const agent of agents) {
const tools = {};
result["agent"][agent.header.name] = {
description: agent.header.description,
mode: "subagent",
prompt: `{file:.opencode/prompts/${agent.header.name}.md}`,
tools
};
for (const tool of agent.header.tools)
asOpencodeTool(tools, tool);
}
result["mcp"]["playwright-test"] = {
type: "local",
command: ["npx", "playwright", "run-test-mcp-server"],
enabled: true
};
return JSON.stringify(result, null, 2);
}
}
async function writeFile(filePath, content) {
console.log(`Writing file: ${filePath}`);
class CopilotGenerator {
static async init(config, projectName, prompts) {
await initRepo(config, projectName, {
defaultAgentName: "agent",
promptsFolder: prompts ? ".github/prompts" : void 0,
promptSuffix: "prompt"
});
const agents = await AgentParser.loadAgents();
await import_fs.default.promises.mkdir(".github/agents", { recursive: true });
for (const agent of agents)
await writeFile(`.github/agents/${agent.header.name}.agent.md`, CopilotGenerator.agentSpec(agent), "\u{1F916}", "agent definition");
await deleteFile(`.github/chatmodes/ \u{1F3AD} planner.chatmode.md`, "legacy planner chatmode");
await deleteFile(`.github/chatmodes/\u{1F3AD} generator.chatmode.md`, "legacy generator chatmode");
await deleteFile(`.github/chatmodes/\u{1F3AD} healer.chatmode.md`, "legacy healer chatmode");
await deleteFile(`.github/agents/ \u{1F3AD} planner.agent.md`, "legacy planner agent");
await deleteFile(`.github/agents/\u{1F3AD} generator.agent.md`, "legacy generator agent");
await deleteFile(`.github/agents/\u{1F3AD} healer.agent.md`, "legacy healer agent");
await VSCodeGenerator.appendToMCPJson();
const mcpConfig = { mcpServers: CopilotGenerator.mcpServers };
if (!import_fs.default.existsSync(".github/copilot-setup-steps.yml")) {
const yaml2 = import_fs.default.readFileSync(import_path.default.join(__dirname, "copilot-setup-steps.yml"), "utf-8");
await writeFile(".github/workflows/copilot-setup-steps.yml", yaml2, "\u{1F527}", "GitHub Copilot setup steps");
}
console.log("");
console.log("");
console.log(" \u{1F527} TODO: GitHub > Settings > Copilot > Coding agent > MCP configuration");
console.log("------------------------------------------------------------------");
console.log(JSON.stringify(mcpConfig, null, 2));
console.log("------------------------------------------------------------------");
initRepoDone();
}
static agentSpec(agent) {
const examples = agent.examples.length ? ` Examples: ${agent.examples.map((example) => `<example>${example}</example>`).join("")}` : "";
const lines = [];
const header = {
"name": agent.header.name,
"description": agent.header.description + examples,
"tools": agent.header.tools,
"model": "Claude Sonnet 4",
"mcp-servers": CopilotGenerator.mcpServers
};
lines.push(`---`);
lines.push(import_utilsBundle.yaml.stringify(header) + `---`);
lines.push("");
lines.push(agent.instructions);
lines.push("");
return lines.join("\n");
}
static {
this.mcpServers = {
"playwright-test": {
"type": "stdio",
"command": "npx",
"args": [
"playwright",
"run-test-mcp-server"
],
"tools": ["*"]
}
};
}
}
class VSCodeGenerator {
static async init(config, projectName) {
await initRepo(config, projectName, {
promptsFolder: void 0
});
const agents = await AgentParser.loadAgents();
const nameMap = /* @__PURE__ */ new Map([
["playwright-test-planner", " \u{1F3AD} planner"],
["playwright-test-generator", "\u{1F3AD} generator"],
["playwright-test-healer", "\u{1F3AD} healer"]
]);
await import_fs.default.promises.mkdir(".github/chatmodes", { recursive: true });
for (const agent of agents)
await writeFile(`.github/chatmodes/${nameMap.get(agent.header.name)}.chatmode.md`, VSCodeGenerator.agentSpec(agent), "\u{1F916}", "chatmode definition");
await VSCodeGenerator.appendToMCPJson();
initRepoDone();
}
static async appendToMCPJson() {
await import_fs.default.promises.mkdir(".vscode", { recursive: true });
const mcpJsonPath = ".vscode/mcp.json";
let mcpJson = {
servers: {},
inputs: []
};
try {
mcpJson = JSON.parse(import_fs.default.readFileSync(mcpJsonPath, "utf8"));
} catch {
}
if (!mcpJson.servers)
mcpJson.servers = {};
mcpJson.servers["playwright-test"] = {
type: "stdio",
command: "npx",
args: ["playwright", "run-test-mcp-server"]
};
await writeFile(mcpJsonPath, JSON.stringify(mcpJson, null, 2), "\u{1F527}", "mcp configuration");
}
static agentSpec(agent) {
const vscodeToolMap = /* @__PURE__ */ new Map([
["search", ["search/listDirectory", "search/fileSearch", "search/textSearch"]],
["read", ["search/readFile"]],
["edit", ["edit/editFiles"]],
["write", ["edit/createFile", "edit/createDirectory"]]
]);
const vscodeToolsOrder = ["edit/createFile", "edit/createDirectory", "edit/editFiles", "search/fileSearch", "search/textSearch", "search/listDirectory", "search/readFile"];
const vscodeMcpName = "playwright-test";
function asVscodeTool(tool) {
const [first, second] = tool.split("/");
if (second)
return `${vscodeMcpName}/${second}`;
return vscodeToolMap.get(first) || first;
}
const tools = agent.header.tools.map(asVscodeTool).flat().sort((a, b) => {
const indexA = vscodeToolsOrder.indexOf(a);
const indexB = vscodeToolsOrder.indexOf(b);
if (indexA === -1 && indexB === -1)
return a.localeCompare(b);
if (indexA === -1)
return 1;
if (indexB === -1)
return -1;
return indexA - indexB;
}).map((tool) => `'${tool}'`).join(", ");
const lines = [];
lines.push(`---`);
lines.push(`description: ${agent.header.description}.`);
lines.push(`tools: [${tools}]`);
lines.push(`---`);
lines.push("");
lines.push(agent.instructions);
for (const example of agent.examples)
lines.push(`<example>${example}</example>`);
lines.push("");
return lines.join("\n");
}
}
async function writeFile(filePath, content, icon, description) {
console.log(` ${icon} ${import_path.default.relative(process.cwd(), filePath)} ${import_utilsBundle.colors.dim("- " + description)}`);
await (0, import_utils.mkdirIfNeeded)(filePath);
await import_fs.default.promises.writeFile(filePath, content, "utf-8");
}
async function initClaudeCodeRepo() {
const agents = await loadAgents();
await import_fs.default.promises.mkdir(".claude/agents", { recursive: true });
for (const agent of agents)
await writeFile(`.claude/agents/playwright-test-${agent.header.name}.md`, saveAsClaudeCode(agent));
await writeFile(".mcp.json", JSON.stringify({
mcpServers: {
"playwright-test": {
command: commonMcpServers.playwrightTest.command,
args: commonMcpServers.playwrightTest.args
}
}
}, null, 2));
}
const vscodeToolMap = /* @__PURE__ */ new Map([
["ls", ["search/listDirectory", "search/fileSearch"]],
["grep", ["search/textSearch"]],
["read", ["search/readFile"]],
["edit", ["edit/editFiles"]],
["write", ["edit/createFile", "edit/createDirectory"]]
]);
const vscodeToolsOrder = ["edit/createFile", "edit/createDirectory", "edit/editFiles", "search/fileSearch", "search/textSearch", "search/listDirectory", "search/readFile"];
const vscodeMcpName = "playwright-test";
function saveAsVSCodeChatmode(agent) {
function asVscodeTool(tool) {
const [first, second] = tool.split("/");
if (second)
return `${vscodeMcpName}/${second}`;
return vscodeToolMap.get(first) || first;
}
const tools = agent.header.tools.map(asVscodeTool).flat().sort((a, b) => {
const indexA = vscodeToolsOrder.indexOf(a);
const indexB = vscodeToolsOrder.indexOf(b);
if (indexA === -1 && indexB === -1)
return a.localeCompare(b);
if (indexA === -1)
return 1;
if (indexB === -1)
return -1;
return indexA - indexB;
}).map((tool) => `'${tool}'`).join(", ");
const lines = [];
lines.push(`---`);
lines.push(`description: ${agent.header.description}.`);
lines.push(`tools: [${tools}]`);
lines.push(`---`);
lines.push("");
lines.push(agent.instructions);
for (const example of agent.examples)
lines.push(`<example>${example}</example>`);
return lines.join("\n");
}
async function initVSCodeRepo() {
const agents = await loadAgents();
await import_fs.default.promises.mkdir(".github/chatmodes", { recursive: true });
for (const agent of agents)
await writeFile(`.github/chatmodes/${agent.header.name === "planner" ? " " : ""}\u{1F3AD} ${agent.header.name}.chatmode.md`, saveAsVSCodeChatmode(agent));
await import_fs.default.promises.mkdir(".vscode", { recursive: true });
const mcpJsonPath = ".vscode/mcp.json";
let mcpJson = {
servers: {},
inputs: []
};
async function deleteFile(filePath, description) {
try {
mcpJson = JSON.parse(import_fs.default.readFileSync(mcpJsonPath, "utf8"));
if (!import_fs.default.existsSync(filePath))
return;
} catch {
return;
}
if (!mcpJson.servers)
mcpJson.servers = {};
mcpJson.servers["playwright-test"] = {
type: "stdio",
command: commonMcpServers.playwrightTest.command,
args: commonMcpServers.playwrightTest.args
};
await writeFile(mcpJsonPath, JSON.stringify(mcpJson, null, 2));
console.log(` \u2702\uFE0F ${import_path.default.relative(process.cwd(), filePath)} ${import_utilsBundle.colors.dim("- " + description)}`);
await import_fs.default.promises.unlink(filePath);
}
async function initOpencodeRepo() {
const agents = await loadAgents();
await import_fs.default.promises.mkdir(".opencode/prompts", { recursive: true });
for (const agent of agents) {
const prompt = [agent.instructions];
prompt.push("");
prompt.push(...agent.examples.map((example) => `<example>${example}</example>`));
await writeFile(`.opencode/prompts/playwright-test-${agent.header.name}.md`, prompt.join("\n"));
async function initRepo(config, projectName, options) {
const project = (0, import_seed.seedProject)(config, projectName);
console.log(` \u{1F3AD} Using project "${project.project.name}" as a primary project`);
if (!import_fs.default.existsSync("specs")) {
await import_fs.default.promises.mkdir("specs");
await writeFile(import_path.default.join("specs", "README.md"), `# Specs
This is a directory for test plans.
`, "\u{1F4DD}", "directory for test plans");
}
await writeFile("opencode.json", saveAsOpencodeJson(agents));
let seedFile = await (0, import_seed.findSeedFile)(project);
if (!seedFile) {
seedFile = (0, import_seed.defaultSeedFile)(project);
await writeFile(seedFile, import_seed.seedFileContent, "\u{1F331}", "default environment seed file");
}
if (options.promptsFolder) {
await import_fs.default.promises.mkdir(options.promptsFolder, { recursive: true });
for (const promptFile of await import_fs.default.promises.readdir(__dirname)) {
if (!promptFile.endsWith(".prompt.md"))
continue;
const shortName = promptFile.replace(".prompt.md", "");
const fileName = options.promptSuffix ? `${shortName}.${options.promptSuffix}.md` : `${shortName}.md`;
const content = await loadPrompt(promptFile, {
defaultAgentName: "default",
...options,
seedFile: import_path.default.relative(process.cwd(), seedFile)
});
await writeFile(import_path.default.join(options.promptsFolder, fileName), content, "\u{1F4DD}", "prompt template");
}
}
}
function initRepoDone() {
console.log(" \u2705 Done.");
}
async function loadPrompt(file, params) {
const content = await import_fs.default.promises.readFile(import_path.default.join(__dirname, file), "utf-8");
return Object.entries(params).reduce((acc, [key, value]) => {
return acc.replace(new RegExp(`\\\${${key}}`, "g"), value);
}, content);
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
initClaudeCodeRepo,
initOpencodeRepo,
initVSCodeRepo
ClaudeGenerator,
CopilotGenerator,
OpencodeGenerator,
VSCodeGenerator
});

View File

@@ -0,0 +1,31 @@
---
agent: ${defaultAgentName}
description: Produce test coverage
---
Parameters:
- Task: the task to perform
- Seed file (optional): the seed file to use, defaults to `${seedFile}`
- Test plan file (optional): the test plan file to write, under `specs/` folder.
1. Call #playwright-test-planner subagent with prompt:
<plan>
<task-text><!-- the task --></task-text>
<seed-file><!-- path to seed file --></seed-file>
<plan-file><!-- path to test plan file to generate --></plan-file>
</plan>
2. For each test case from the test plan file (1.1, 1.2, ...), one after another, not in parallel, call #playwright-test-generator subagent with prompt:
<generate>
<test-suite><!-- Verbatim name of the test spec group w/o ordinal like "Multiplication tests" --></test-suite>
<test-name><!-- Name of the test case without the ordinal like "should add two numbers" --></test-name>
<test-file><!-- Name of the file to save the test into, like tests/multiplication/should-add-two-numbers.spec.ts --></test-file>
<seed-file><!-- Seed file path from test plan --></seed-file>
<body><!-- Test case content including steps and expectations --></body>
</generate>
3. Call #playwright-test-healer subagent with prompt:
<heal>Run all tests and fix the failing ones one after another.</heal>

View File

@@ -0,0 +1,8 @@
---
agent: playwright-test-generator
description: Generate test plan
---
Generate tests for the test plan's bullet 1.1 Add item to card.
Test plan: `specs/coverage.plan.md`

View File

@@ -1,12 +1,10 @@
---
name: generator
name: playwright-test-generator
description: Use this agent when you need to create automated browser tests using Playwright
model: sonnet
color: blue
tools:
- ls
- grep
- read
- search
- playwright-test/browser_click
- playwright-test/browser_drag
- playwright-test/browser_evaluate
@@ -81,22 +79,10 @@ application behavior.
</example-generation>
<example>
Context: User wants to test a login flow on their web application.
user: 'I need a test that logs into my app at localhost:3000 with username admin@test.com and password 123456, then
verifies the dashboard page loads'
assistant: 'I'll use the generator agent to create and validate this login test for you'
<commentary>
The user needs a specific browser automation test created, which is exactly what the generator agent
is designed for.
</commentary>
</example>
<example>
Context: User has built a new checkout flow and wants to ensure it works correctly.
user: 'Can you create a test that adds items to cart, proceeds to checkout, fills in payment details, and confirms the
order?'
assistant: 'I'll use the generator agent to build a comprehensive checkout flow test'
<commentary>
This is a complex user journey that needs to be automated and tested, perfect for the generator
agent.
</commentary>
Context: User wants to generate a test for the test plan item.
<test-suite><!-- Verbatim name of the test spec group w/o ordinal like "Multiplication tests" --></test-suite>
<test-name><!-- Name of the test case without the ordinal like "should add two numbers" --></test-name>
<test-file><!-- Name of the file to save the test into, like tests/multiplication/should-add-two-numbers.spec.ts --></test-file>
<seed-file><!-- Seed file path from test plan --></seed-file>
<body><!-- Test case content including steps and expectations --></body>
</example>

View File

@@ -0,0 +1,6 @@
---
agent: playwright-test-healer
description: Fix tests
---
Run all my tests and fix the failing ones.

View File

@@ -1,13 +1,10 @@
---
name: healer
name: playwright-test-healer
description: Use this agent when you need to debug and fix failing Playwright tests
color: red
model: sonnet
color: red
tools:
- ls
- grep
- read
- write
- search
- edit
- playwright-test/browser_console_messages
- playwright-test/browser_evaluate
@@ -24,8 +21,8 @@ resolving Playwright test failures. Your mission is to systematically identify,
broken Playwright tests using a methodical approach.
Your workflow:
1. **Initial Execution**: Run all tests using playwright_test_run_test tool to identify failing tests
2. **Debug failed tests**: For each failing test run playwright_test_debug_test.
1. **Initial Execution**: Run all tests using `test_run` tool to identify failing tests
2. **Debug failed tests**: For each failing test run `test_debug`.
3. **Error Investigation**: When the test pauses on errors, use available Playwright MCP tools to:
- Examine the error details
- Capture page snapshot to understand the context
@@ -56,23 +53,3 @@ Key principles:
of the expected behavior.
- Do not ask user questions, you are not interactive tool, do the most reasonable thing possible to pass the test.
- Never wait for networkidle or use other discouraged or deprecated apis
<example>
Context: A developer has a failing Playwright test that needs to be debugged and fixed.
user: 'The login test is failing, can you fix it?'
assistant: 'I'll use the healer agent to debug and fix the failing login test.'
<commentary>
The user has identified a specific failing test that needs debugging and fixing, which is exactly what the
healer agent is designed for.
</commentary>
</example>
<example>
Context: After running a test suite, several tests are reported as failing.
user: 'Test user-registration.spec.ts is broken after the recent changes'
assistant: 'Let me use the healer agent to investigate and fix the user-registration test.'
<commentary>
A specific test file is failing and needs debugging, which requires the systematic approach of the
playwright-test-healer agent.
</commentary>
</example>

View File

@@ -0,0 +1,9 @@
---
agent: playwright-test-planner
description: Create test plan
---
Create test plan for "add to cart" functionality of my app.
- Seed file: `${seedFile}`
- Test plan: `specs/coverage.plan.md`

View File

@@ -1,13 +1,10 @@
---
name: planner
name: playwright-test-planner
description: Use this agent when you need to create comprehensive test plan for a web application or website
model: sonnet
color: green
tools:
- ls
- grep
- read
- write
- search
- playwright-test/browser_click
- playwright-test/browser_close
- playwright-test/browser_console_messages
@@ -26,6 +23,7 @@ tools:
- playwright-test/browser_type
- playwright-test/browser_wait_for
- playwright-test/planner_setup_page
- playwright-test/planner_save_plan
---
You are an expert web test planner with extensive experience in quality assurance, user experience testing, and test
@@ -38,7 +36,7 @@ You will:
- Invoke the `planner_setup_page` tool once to set up page before using any other tools
- Explore the browser snapshot
- Do not take screenshots unless absolutely necessary
- Use browser_* tools to navigate and discover interface
- Use `browser_*` tools to navigate and discover interface
- Thoroughly explore the interface, identifying all interactive elements, forms, navigation paths, and functionality
2. **Analyze User Flows**
@@ -63,48 +61,7 @@ You will:
5. **Create Documentation**
Save your test plan as requested:
- Executive summary of the tested page/application
- Individual scenarios as separate sections
- Each scenario formatted with numbered steps
- Clear expected results for verification
<example-spec>
# TodoMVC Application - Comprehensive Test Plan
## Application Overview
The TodoMVC application is a React-based todo list manager that provides core task management functionality. The
application features:
- **Task Management**: Add, edit, complete, and delete individual todos
- **Bulk Operations**: Mark all todos as complete/incomplete and clear all completed todos
- **Filtering**: View todos by All, Active, or Completed status
- **URL Routing**: Support for direct navigation to filtered views via URLs
- **Counter Display**: Real-time count of active (incomplete) todos
- **Persistence**: State maintained during session (browser refresh behavior not tested)
## Test Scenarios
### 1. Adding New Todos
**Seed:** `tests/seed.spec.ts`
#### 1.1 Add Valid Todo
**Steps:**
1. Click in the "What needs to be done?" input field
2. Type "Buy groceries"
3. Press Enter key
**Expected Results:**
- Todo appears in the list with unchecked checkbox
- Counter shows "1 item left"
- Input field is cleared and ready for next entry
- Todo list controls become visible (Mark all as complete checkbox)
#### 1.2
...
</example-spec>
Submit your test plan using `planner_save_plan` tool.
**Quality Standards**:
- Write steps that are specific enough for any tester to follow
@@ -113,23 +70,3 @@ application features:
**Output Format**: Always save the complete test plan as a markdown file with clear headings, numbered steps, and
professional formatting suitable for sharing with development and QA teams.
<example>
Context: User wants to test a new e-commerce checkout flow.
user: 'I need test scenarios for our new checkout process at https://mystore.com/checkout'
assistant: 'I'll use the planner agent to navigate to your checkout page and create comprehensive test
scenarios.'
<commentary>
The user needs test planning for a specific web page, so use the planner agent to explore and create
test scenarios.
</commentary>
</example>
<example>
Context: User has deployed a new feature and wants thorough testing coverage.
user: 'Can you help me test our new user dashboard at https://app.example.com/dashboard?'
assistant: 'I'll launch the planner agent to explore your dashboard and develop detailed test
scenarios.'
<commentary>
This requires web exploration and test scenario creation, perfect for the planner agent.
</commentary>
</example>

6
node_modules/playwright/lib/common/config.js generated vendored Normal file → Executable file
View File

@@ -69,6 +69,11 @@ class FullConfigInternal {
this.globalSetups = (Array.isArray(userConfig.globalSetup) ? userConfig.globalSetup : [userConfig.globalSetup]).map((s) => resolveScript(s, configDir)).filter((script) => script !== void 0);
this.globalTeardowns = (Array.isArray(userConfig.globalTeardown) ? userConfig.globalTeardown : [userConfig.globalTeardown]).map((s) => resolveScript(s, configDir)).filter((script) => script !== void 0);
userConfig.metadata = userConfig.metadata || {};
const globalTags = Array.isArray(userConfig.tag) ? userConfig.tag : userConfig.tag ? [userConfig.tag] : [];
for (const tag of globalTags) {
if (tag[0] !== "@")
throw new Error(`Tag must start with "@" symbol, got "${tag}" instead.`);
}
this.config = {
configFile: resolvedConfigFile,
rootDir: pathResolve(configDir, userConfig.testDir) || configDir,
@@ -91,6 +96,7 @@ class FullConfigInternal {
quiet: takeFirst(configCLIOverrides.quiet, userConfig.quiet, false),
projects: [],
shard: takeFirst(configCLIOverrides.shard, userConfig.shard, null),
tags: globalTags,
updateSnapshots: takeFirst(configCLIOverrides.updateSnapshots, userConfig.updateSnapshots, "missing"),
updateSourceMethod: takeFirst(configCLIOverrides.updateSourceMethod, userConfig.updateSourceMethod, "patch"),
version: require("../../package.json").version,

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

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

9
node_modules/playwright/lib/common/expectBundle.js generated vendored Normal file → Executable file
View File

@@ -22,17 +22,11 @@ __export(expectBundle_exports, {
EXPECTED_COLOR: () => EXPECTED_COLOR,
INVERTED_COLOR: () => INVERTED_COLOR,
RECEIVED_COLOR: () => RECEIVED_COLOR,
asymmetricMatchers: () => asymmetricMatchers,
expect: () => expect,
matcherUtils: () => matcherUtils,
mock: () => mock,
printReceived: () => printReceived
});
module.exports = __toCommonJS(expectBundle_exports);
const expect = require("./expectBundleImpl").expect;
const mock = require("./expectBundleImpl").mock;
const asymmetricMatchers = require("./expectBundleImpl").asymmetricMatchers;
const matcherUtils = require("./expectBundleImpl").matcherUtils;
const EXPECTED_COLOR = require("./expectBundleImpl").EXPECTED_COLOR;
const INVERTED_COLOR = require("./expectBundleImpl").INVERTED_COLOR;
const RECEIVED_COLOR = require("./expectBundleImpl").RECEIVED_COLOR;
@@ -44,9 +38,6 @@ const printReceived = require("./expectBundleImpl").printReceived;
EXPECTED_COLOR,
INVERTED_COLOR,
RECEIVED_COLOR,
asymmetricMatchers,
expect,
matcherUtils,
mock,
printReceived
});

504
node_modules/playwright/lib/common/expectBundleImpl.js generated vendored Normal file → Executable file

File diff suppressed because one or more lines are too long

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

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

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

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

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

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

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

5
node_modules/playwright/lib/common/testLoader.js generated vendored Normal file → Executable file
View File

@@ -42,12 +42,13 @@ var import_transform = require("../transform/transform");
var import_util2 = require("../util");
const defaultTimeout = 3e4;
const cachedFileSuites = /* @__PURE__ */ new Map();
async function loadTestFile(file, rootDir, testErrors) {
async function loadTestFile(file, config, testErrors) {
if (cachedFileSuites.has(file))
return cachedFileSuites.get(file);
const suite = new import_test.Suite(import_path.default.relative(rootDir, file) || import_path.default.basename(file), "file");
const suite = new import_test.Suite(import_path.default.relative(config.config.rootDir, file) || import_path.default.basename(file), "file");
suite._requireFile = file;
suite.location = { file, line: 0, column: 0 };
suite._tags = [...config.config.tags];
(0, import_globals.setCurrentlyLoadingFileSuite)(suite);
if (!(0, import_globals.isWorkerProcess)()) {
(0, import_compilationCache.startCollectingFileDeps)();

15
node_modules/playwright/lib/common/testType.js generated vendored Normal file → Executable file
View File

@@ -29,6 +29,7 @@ var import_globals = require("./globals");
var import_test = require("./test");
var import_expect = require("../matchers/expect");
var import_transform = require("../transform/transform");
var import_validators = require("./validators");
const testTypeSymbol = Symbol("testType");
class TestTypeImpl {
constructor(fixtures) {
@@ -96,7 +97,7 @@ class TestTypeImpl {
body = fn;
details = fnOrDetails;
}
const validatedDetails = validateTestDetails(details, location);
const validatedDetails = (0, import_validators.validateTestDetails)(details, location);
const test = new import_test.TestCase(title, body, this, location);
test._requireFile = suite._requireFile;
test.annotations.push(...validatedDetails.annotations);
@@ -130,7 +131,7 @@ class TestTypeImpl {
details = fnOrDetails;
body = fn;
}
const validatedDetails = validateTestDetails(details, location);
const validatedDetails = (0, import_validators.validateTestDetails)(details, location);
const child = new import_test.Suite(title, "describe");
child._requireFile = suite._requireFile;
child.location = location;
@@ -276,16 +277,6 @@ See https://playwright.dev/docs/intro for more information about Playwright Test
);
}
}
function validateTestDetails(details, location) {
const originalAnnotations = Array.isArray(details.annotation) ? details.annotation : details.annotation ? [details.annotation] : [];
const annotations = originalAnnotations.map((annotation) => ({ ...annotation, location }));
const tags = Array.isArray(details.tag) ? details.tag : details.tag ? [details.tag] : [];
for (const tag of tags) {
if (tag[0] !== "@")
throw new Error(`Tag must start with "@" symbol, got "${tag}" instead.`);
}
return { annotations, tags };
}
const rootTestType = new TestTypeImpl([]);
function mergeTests(...tests) {
let result = rootTestType;

68
node_modules/playwright/lib/common/validators.js generated vendored Executable file
View File

@@ -0,0 +1,68 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var validators_exports = {};
__export(validators_exports, {
validateTestAnnotation: () => validateTestAnnotation,
validateTestDetails: () => validateTestDetails
});
module.exports = __toCommonJS(validators_exports);
var import_utilsBundle = require("playwright-core/lib/utilsBundle");
const testAnnotationSchema = import_utilsBundle.zod.object({
type: import_utilsBundle.zod.string(),
description: import_utilsBundle.zod.string().optional()
});
const testDetailsSchema = import_utilsBundle.zod.object({
tag: import_utilsBundle.zod.union([
import_utilsBundle.zod.string().optional(),
import_utilsBundle.zod.array(import_utilsBundle.zod.string())
]).transform((val) => Array.isArray(val) ? val : val !== void 0 ? [val] : []).refine((val) => val.every((v) => v.startsWith("@")), {
message: "Tag must start with '@'"
}),
annotation: import_utilsBundle.zod.union([
testAnnotationSchema,
import_utilsBundle.zod.array(testAnnotationSchema).optional()
]).transform((val) => Array.isArray(val) ? val : val !== void 0 ? [val] : [])
});
function validateTestAnnotation(annotation) {
try {
return testAnnotationSchema.parse(annotation);
} catch (error) {
throwZodError(error);
}
}
function validateTestDetails(details, location) {
try {
const parsedDetails = testDetailsSchema.parse(details);
return {
annotations: parsedDetails.annotation.map((a) => ({ ...a, location })),
tags: parsedDetails.tag,
location
};
} catch (error) {
throwZodError(error);
}
}
function throwZodError(error) {
throw new Error(error.issues.map((i) => i.message).join("\n"));
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
validateTestAnnotation,
validateTestDetails
});

0
node_modules/playwright/lib/fsWatcher.js generated vendored Normal file → Executable file
View File

18
node_modules/playwright/lib/index.js generated vendored Normal file → Executable file
View File

@@ -89,8 +89,8 @@ const playwrightFixtures = {
await use(options);
playwright._defaultLaunchOptions = void 0;
}, { scope: "worker", auto: true, box: true }],
browser: [async ({ playwright, browserName, _browserOptions, connectOptions }, use, testInfo) => {
if (!["chromium", "firefox", "webkit", "_bidiChromium", "_bidiFirefox"].includes(browserName))
browser: [async ({ playwright, browserName, _browserOptions, connectOptions }, use) => {
if (!["chromium", "firefox", "webkit"].includes(browserName))
throw new Error(`Unexpected browserName "${browserName}", must be one of "chromium", "firefox" or "webkit"`);
if (connectOptions) {
const browser2 = await playwright[browserName].connect({
@@ -220,7 +220,7 @@ const playwrightFixtures = {
if ((0, import_utils.debugMode)() === "inspector")
testInfo._setDebugMode();
playwright._defaultContextOptions = _combinedContextOptions;
playwright._defaultContextTimeout = testInfo._pauseOnError() ? 5e3 : actionTimeout || 0;
playwright._defaultContextTimeout = actionTimeout || 0;
playwright._defaultContextNavigationTimeout = navigationTimeout || 0;
await use();
playwright._defaultContextOptions = void 0;
@@ -242,8 +242,8 @@ const playwrightFixtures = {
if (zone && zone.category === "expect" && isExpectCall) {
if (zone.apiName)
data.apiName = zone.apiName;
if (zone.title)
data.title = zone.title;
if (zone.shortTitle || zone.title)
data.title = zone.shortTitle ?? zone.title;
data.stepId = zone.stepId;
return;
}
@@ -380,13 +380,13 @@ const playwrightFixtures = {
attachConnectedHeaderIfNeeded(testInfo, browserImpl);
if (!_reuseContext) {
const { context: context2, close } = await _contextFactory();
testInfo._onDidFinishTestFunctions.unshift(() => (0, import_browserBackend.runBrowserBackendAtEnd)(context2, testInfo.errors[0]?.message));
testInfo._onCustomMessageCallback = (0, import_browserBackend.createCustomMessageHandler)(testInfo, context2);
await use(context2);
await close();
return;
}
const context = await browserImpl._wrapApiCall(() => browserImpl._newContextForReuse(), { internal: true });
testInfo._onDidFinishTestFunctions.unshift(() => (0, import_browserBackend.runBrowserBackendAtEnd)(context, testInfo.errors[0]?.message));
testInfo._onCustomMessageCallback = (0, import_browserBackend.createCustomMessageHandler)(testInfo, context);
await use(context);
const closeReason = testInfo.status === "timedOut" ? "Test timeout of " + testInfo.timeout + "ms exceeded." : "Test ended.";
await browserImpl._wrapApiCall(() => browserImpl._disconnectFromReusedContext(closeReason), { internal: true });
@@ -560,7 +560,7 @@ class ArtifactsRecorder {
}
async willStartTest(testInfo) {
this._testInfo = testInfo;
testInfo._onDidFinishTestFunctions.push(() => this.didFinishTestFunction());
testInfo._onDidFinishTestFunctionCallback = () => this.didFinishTestFunction();
this._screenshotRecorder.fixOrdinal();
await Promise.all(this._playwright._allContexts().map((context) => this.didCreateBrowserContext(context)));
const existingApiRequests = Array.from(this._playwright.request._contexts);
@@ -586,7 +586,7 @@ class ArtifactsRecorder {
return;
try {
await page._wrapApiCall(async () => {
this._pageSnapshot = await page._snapshotForAI({ timeout: 5e3 });
this._pageSnapshot = (await page._snapshotForAI({ timeout: 5e3 })).full;
}, { internal: true });
} catch {
}

0
node_modules/playwright/lib/internalsForTest.js generated vendored Normal file → Executable file
View File

0
node_modules/playwright/lib/isomorphic/events.js generated vendored Normal file → Executable file
View File

0
node_modules/playwright/lib/isomorphic/folders.js generated vendored Normal file → Executable file
View File

0
node_modules/playwright/lib/isomorphic/stringInternPool.js generated vendored Normal file → Executable file
View File

1
node_modules/playwright/lib/isomorphic/teleReceiver.js generated vendored Normal file → Executable file
View File

@@ -445,6 +445,7 @@ const baseFullConfig = {
rootDir: "",
quiet: false,
shard: null,
tags: [],
updateSnapshots: "missing",
updateSourceMethod: "patch",
version: "",

0
node_modules/playwright/lib/isomorphic/teleSuiteUpdater.js generated vendored Normal file → Executable file
View File

14
node_modules/playwright/lib/isomorphic/testServerConnection.js generated vendored Normal file → Executable file
View File

@@ -29,10 +29,16 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
var testServerConnection_exports = {};
__export(testServerConnection_exports, {
TestServerConnection: () => TestServerConnection,
TestServerConnectionClosedError: () => TestServerConnectionClosedError,
WebSocketTestServerTransport: () => WebSocketTestServerTransport
});
module.exports = __toCommonJS(testServerConnection_exports);
var events = __toESM(require("./events"));
class TestServerConnectionClosedError extends Error {
constructor() {
super("Test server connection closed");
}
}
class WebSocketTestServerTransport {
constructor(url) {
this._ws = new WebSocket(url);
@@ -63,6 +69,7 @@ class TestServerConnection {
this._onStdioEmitter = new events.EventEmitter();
this._onTestFilesChangedEmitter = new events.EventEmitter();
this._onLoadTraceRequestedEmitter = new events.EventEmitter();
this._onTestPausedEmitter = new events.EventEmitter();
this._lastId = 0;
this._callbacks = /* @__PURE__ */ new Map();
this._isClosed = false;
@@ -71,6 +78,7 @@ class TestServerConnection {
this.onStdio = this._onStdioEmitter.event;
this.onTestFilesChanged = this._onTestFilesChangedEmitter.event;
this.onLoadTraceRequested = this._onLoadTraceRequestedEmitter.event;
this.onTestPaused = this._onTestPausedEmitter.event;
this._transport = transport;
this._transport.onmessage((data) => {
const message = JSON.parse(data);
@@ -98,6 +106,9 @@ class TestServerConnection {
this._isClosed = true;
this._onCloseEmitter.fire();
clearInterval(pingInterval);
for (const callback of this._callbacks.values())
callback.reject(new TestServerConnectionClosedError());
this._callbacks.clear();
});
}
isClosed() {
@@ -127,6 +138,8 @@ class TestServerConnection {
this._onTestFilesChangedEmitter.fire(params);
else if (method === "loadTraceRequested")
this._onLoadTraceRequestedEmitter.fire(params);
else if (method === "testPaused")
this._onTestPausedEmitter.fire(params);
}
async initialize(params) {
await this._sendMessage("initialize", params);
@@ -207,5 +220,6 @@ class TestServerConnection {
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
TestServerConnection,
TestServerConnectionClosedError,
WebSocketTestServerTransport
});

0
node_modules/playwright/lib/isomorphic/testServerInterface.js generated vendored Normal file → Executable file
View File

0
node_modules/playwright/lib/isomorphic/testTree.js generated vendored Normal file → Executable file
View File

0
node_modules/playwright/lib/isomorphic/types.d.js generated vendored Normal file → Executable file
View File

2
node_modules/playwright/lib/loader/loaderMain.js generated vendored Normal file → Executable file
View File

@@ -42,7 +42,7 @@ class LoaderMain extends import_process.ProcessRunner {
async loadTestFile(params) {
const testErrors = [];
const config = await this._config();
const fileSuite = await (0, import_testLoader.loadTestFile)(params.file, config.config.rootDir, testErrors);
const fileSuite = await (0, import_testLoader.loadTestFile)(params.file, config, testErrors);
this._poolBuilder.buildPools(fileSuite);
return { fileSuite: fileSuite._deepSerialize(), testErrors };
}

25
node_modules/playwright/lib/matchers/expect.js generated vendored Normal file → Executable file
View File

@@ -43,7 +43,7 @@ const printReceivedStringContainExpectedResult = (received, result) => result ==
result[0].length
);
function createMatchers(actual, info, prefix) {
return new Proxy((0, import_expectBundle.expect)(actual), new ExpectMetaInfoProxyHandler(info, prefix));
return new Proxy((0, import_expectBundle.expect)(actual), new ExpectMetaInfoProxyHandler(actual, info, prefix));
}
const userMatchersSymbol = Symbol("userMatchers");
function qualifiedMatcherName(qualifier, matcherName) {
@@ -185,11 +185,14 @@ const customMatchers = {
toMatchSnapshot: import_toMatchSnapshot.toMatchSnapshot
};
class ExpectMetaInfoProxyHandler {
constructor(info, prefix) {
constructor(actual, info, prefix) {
this._actual = actual;
this._info = { ...info };
this._prefix = prefix;
}
get(target, matcherName, receiver) {
if (matcherName === "toThrowError")
matcherName = "toThrow";
let matcher = Reflect.get(target, matcherName, receiver);
if (typeof matcherName !== "string")
return matcher;
@@ -220,15 +223,17 @@ class ExpectMetaInfoProxyHandler {
if (!testInfo)
return matcher.call(target, ...args);
const customMessage = this._info.message || "";
const argsSuffix = computeArgsSuffix(matcherName, args);
const defaultTitle = `${this._info.poll ? "poll " : ""}${this._info.isSoft ? "soft " : ""}${this._info.isNot ? "not " : ""}${matcherName}${argsSuffix}`;
const title = customMessage || `Expect ${(0, import_utils.escapeWithQuotes)(defaultTitle, '"')}`;
const apiName = `expect${this._info.poll ? ".poll " : ""}${this._info.isSoft ? ".soft " : ""}${this._info.isNot ? ".not" : ""}.${matcherName}${argsSuffix}`;
const suffixes = (0, import_matchers.computeMatcherTitleSuffix)(matcherName, this._actual, args);
const defaultTitle = `${this._info.poll ? "poll " : ""}${this._info.isSoft ? "soft " : ""}${this._info.isNot ? "not " : ""}${matcherName}${suffixes.short || ""}`;
const shortTitle = customMessage || `Expect ${(0, import_utils.escapeWithQuotes)(defaultTitle, '"')}`;
const longTitle = shortTitle + (suffixes.long || "");
const apiName = `expect${this._info.poll ? ".poll " : ""}${this._info.isSoft ? ".soft " : ""}${this._info.isNot ? ".not" : ""}.${matcherName}${suffixes.short || ""}`;
const stackFrames = (0, import_util.filteredStackTrace)((0, import_utils.captureRawStack)());
const stepInfo = {
category: "expect",
apiName,
title,
title: longTitle,
shortTitle,
params: args[0] ? { expected: args[0] } : void 0,
infectParentStepsWithError: this._info.isSoft
};
@@ -299,12 +304,6 @@ async function pollMatcher(qualifiedMatcherName2, info, prefix, ...args) {
throw new Error(message);
}
}
function computeArgsSuffix(matcherName, args) {
let value = "";
if (matcherName === "toHaveScreenshot")
value = (0, import_toMatchSnapshot.toHaveScreenshotStepTitle)(...args);
return value ? `(${value})` : "";
}
const expect = createExpect({}, [], {}).extend(customMatchers);
function mergeExpects(...expects) {
let merged = expect;

0
node_modules/playwright/lib/matchers/matcherHint.js generated vendored Normal file → Executable file
View File

16
node_modules/playwright/lib/matchers/matchers.js generated vendored Normal file → Executable file
View File

@@ -18,6 +18,7 @@ var __copyProps = (to, from, except, desc) => {
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var matchers_exports = {};
__export(matchers_exports, {
computeMatcherTitleSuffix: () => computeMatcherTitleSuffix,
toBeAttached: () => toBeAttached,
toBeChecked: () => toBeChecked,
toBeDisabled: () => toBeDisabled,
@@ -56,6 +57,7 @@ var import_toBeTruthy = require("./toBeTruthy");
var import_toEqual = require("./toEqual");
var import_toHaveURL = require("./toHaveURL");
var import_toMatchText = require("./toMatchText");
var import_toMatchSnapshot = require("./toMatchSnapshot");
var import_config = require("../common/config");
var import_globals = require("../common/globals");
var import_testInfo = require("../worker/testInfo");
@@ -332,8 +334,22 @@ async function toPass(callback, options = {}) {
}
return { pass: !this.isNot, message: () => "" };
}
function computeMatcherTitleSuffix(matcherName, receiver, args) {
if (matcherName === "toHaveScreenshot") {
const title = (0, import_toMatchSnapshot.toHaveScreenshotStepTitle)(...args);
return { short: title ? `(${title})` : "" };
}
if (receiver && typeof receiver === "object" && receiver.constructor?.name === "Locator") {
try {
return { long: " " + (0, import_utils.asLocatorDescription)("javascript", receiver._selector) };
} catch {
}
}
return {};
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
computeMatcherTitleSuffix,
toBeAttached,
toBeChecked,
toBeDisabled,

0
node_modules/playwright/lib/matchers/toBeTruthy.js generated vendored Normal file → Executable file
View File

0
node_modules/playwright/lib/matchers/toEqual.js generated vendored Normal file → Executable file
View File

0
node_modules/playwright/lib/matchers/toHaveURL.js generated vendored Normal file → Executable file
View File

0
node_modules/playwright/lib/matchers/toMatchAriaSnapshot.js generated vendored Normal file → Executable file
View File

0
node_modules/playwright/lib/matchers/toMatchSnapshot.js generated vendored Normal file → Executable file
View File

0
node_modules/playwright/lib/matchers/toMatchText.js generated vendored Normal file → Executable file
View File

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

22
node_modules/playwright/lib/mcp/program.js generated vendored Normal file → Executable file
View File

@@ -31,7 +31,9 @@ __export(program_exports, {
decorateCommand: () => decorateCommand
});
module.exports = __toCommonJS(program_exports);
var import_fs = __toESM(require("fs"));
var import_utilsBundle = require("playwright-core/lib/utilsBundle");
var import_server = require("playwright-core/lib/server");
var mcpServer = __toESM(require("./sdk/server"));
var import_config = require("./browser/config");
var import_watchdog = require("./browser/watchdog");
@@ -40,13 +42,23 @@ var import_proxyBackend = require("./sdk/proxyBackend");
var import_browserServerBackend = require("./browser/browserServerBackend");
var import_extensionContextFactory = require("./extension/extensionContextFactory");
function decorateCommand(command, version) {
command.option("--allowed-hosts <hosts...>", "comma-separated list of hosts this server is allowed to serve from. Defaults to the host the server is bound to. Pass '*' to disable the host check.", import_config.commaSeparatedList).option("--allowed-origins <origins>", "semicolon-separated list of origins to allow the browser to request. Default is to allow all.", import_config.semicolonSeparatedList).option("--blocked-origins <origins>", "semicolon-separated list of origins to block the browser from requesting. Blocklist is evaluated before allowlist. If used without the allowlist, requests not matching the blocklist are still allowed.", import_config.semicolonSeparatedList).option("--block-service-workers", "block service workers").option("--browser <browser>", "browser or chrome channel to use, possible values: chrome, firefox, webkit, msedge.").option("--caps <caps>", "comma-separated list of additional capabilities to enable, possible values: vision, pdf.", import_config.commaSeparatedList).option("--cdp-endpoint <endpoint>", "CDP endpoint to connect to.").option("--cdp-header <headers...>", "CDP headers to send with the connect request, multiple can be specified.", import_config.headerParser).option("--config <path>", "path to the configuration file.").option("--device <device>", 'device to emulate, for example: "iPhone 15"').option("--executable-path <path>", "path to the browser executable.").option("--extension", 'Connect to a running browser instance (Edge/Chrome only). Requires the "Playwright MCP Bridge" browser extension to be installed.').option("--grant-permissions <permissions...>", 'List of permissions to grant to the browser context, for example "geolocation", "clipboard-read", "clipboard-write".', import_config.commaSeparatedList).option("--headless", "run browser in headless mode, headed by default").option("--host <host>", "host to bind server to. Default is localhost. Use 0.0.0.0 to bind to all interfaces.").option("--ignore-https-errors", "ignore https errors").option("--init-script <path...>", "path to JavaScript file to add as an initialization script. The script will be evaluated in every page before any of the page's scripts. Can be specified multiple times.").option("--isolated", "keep the browser profile in memory, do not save it to disk.").option("--image-responses <mode>", 'whether to send image responses to the client. Can be "allow" or "omit", Defaults to "allow".').option("--no-sandbox", "disable the sandbox for all process types that are normally sandboxed.").option("--output-dir <path>", "path to the directory for output files.").option("--port <port>", "port to listen on for SSE transport.").option("--proxy-bypass <bypass>", 'comma-separated domains to bypass proxy, for example ".com,chromium.org,.domain.com"').option("--proxy-server <proxy>", 'specify proxy server, for example "http://myproxy:3128" or "socks5://myproxy:8080"').option("--save-session", "Whether to save the Playwright MCP session into the output directory.").option("--save-trace", "Whether to save the Playwright Trace of the session into the output directory.").option("--save-video <size>", 'Whether to save the video of the session into the output directory. For example "--save-video=800x600"', import_config.resolutionParser.bind(null, "--save-video")).option("--secrets <path>", "path to a file containing secrets in the dotenv format", import_config.dotenvFileLoader).option("--shared-browser-context", "reuse the same browser context between all connected HTTP clients.").option("--storage-state <path>", "path to the storage state file for isolated sessions.").option("--timeout-action <timeout>", "specify action timeout in milliseconds, defaults to 5000ms", import_config.numberParser).option("--timeout-navigation <timeout>", "specify navigation timeout in milliseconds, defaults to 60000ms", import_config.numberParser).option("--user-agent <ua string>", "specify user agent string").option("--user-data-dir <path>", "path to the user data directory. If not specified, a temporary directory will be created.").option("--viewport-size <size>", 'specify browser viewport size in pixels, for example "1280x720"', import_config.resolutionParser.bind(null, "--viewport-size")).addOption(new import_utilsBundle.ProgramOption("--connect-tool", "Allow to switch between different browser connection methods.").hideHelp()).addOption(new import_utilsBundle.ProgramOption("--vision", "Legacy option, use --caps=vision instead").hideHelp()).action(async (options) => {
command.option("--allowed-hosts <hosts...>", "comma-separated list of hosts this server is allowed to serve from. Defaults to the host the server is bound to. Pass '*' to disable the host check.", import_config.commaSeparatedList).option("--block-service-workers", "block service workers").option("--browser <browser>", "browser or chrome channel to use, possible values: chrome, firefox, webkit, msedge.").option("--caps <caps>", "comma-separated list of additional capabilities to enable, possible values: vision, pdf.", import_config.commaSeparatedList).option("--cdp-endpoint <endpoint>", "CDP endpoint to connect to.").option("--cdp-header <headers...>", "CDP headers to send with the connect request, multiple can be specified.", import_config.headerParser).option("--config <path>", "path to the configuration file.").option("--device <device>", 'device to emulate, for example: "iPhone 15"').option("--executable-path <path>", "path to the browser executable.").option("--extension", 'Connect to a running browser instance (Edge/Chrome only). Requires the "Playwright MCP Bridge" browser extension to be installed.').option("--grant-permissions <permissions...>", 'List of permissions to grant to the browser context, for example "geolocation", "clipboard-read", "clipboard-write".', import_config.commaSeparatedList).option("--headless", "run browser in headless mode, headed by default").option("--host <host>", "host to bind server to. Default is localhost. Use 0.0.0.0 to bind to all interfaces.").option("--ignore-https-errors", "ignore https errors").option("--init-page <path...>", "path to TypeScript file to evaluate on Playwright page object").option("--init-script <path...>", "path to JavaScript file to add as an initialization script. The script will be evaluated in every page before any of the page's scripts. Can be specified multiple times.").option("--isolated", "keep the browser profile in memory, do not save it to disk.").option("--image-responses <mode>", 'whether to send image responses to the client. Can be "allow" or "omit", Defaults to "allow".').option("--no-sandbox", "disable the sandbox for all process types that are normally sandboxed.").option("--output-dir <path>", "path to the directory for output files.").option("--port <port>", "port to listen on for SSE transport.").option("--proxy-bypass <bypass>", 'comma-separated domains to bypass proxy, for example ".com,chromium.org,.domain.com"').option("--proxy-server <proxy>", 'specify proxy server, for example "http://myproxy:3128" or "socks5://myproxy:8080"').option("--save-session", "Whether to save the Playwright MCP session into the output directory.").option("--save-trace", "Whether to save the Playwright Trace of the session into the output directory.").option("--save-video <size>", 'Whether to save the video of the session into the output directory. For example "--save-video=800x600"', import_config.resolutionParser.bind(null, "--save-video")).option("--secrets <path>", "path to a file containing secrets in the dotenv format", import_config.dotenvFileLoader).option("--shared-browser-context", "reuse the same browser context between all connected HTTP clients.").option("--storage-state <path>", "path to the storage state file for isolated sessions.").option("--test-id-attribute <attribute>", 'specify the attribute to use for test ids, defaults to "data-testid"').option("--timeout-action <timeout>", "specify action timeout in milliseconds, defaults to 5000ms", import_config.numberParser).option("--timeout-navigation <timeout>", "specify navigation timeout in milliseconds, defaults to 60000ms", import_config.numberParser).option("--user-agent <ua string>", "specify user agent string").option("--user-data-dir <path>", "path to the user data directory. If not specified, a temporary directory will be created.").option("--viewport-size <size>", 'specify browser viewport size in pixels, for example "1280x720"', import_config.resolutionParser.bind(null, "--viewport-size")).addOption(new import_utilsBundle.ProgramOption("--connect-tool", "Allow to switch between different browser connection methods.").hideHelp()).addOption(new import_utilsBundle.ProgramOption("--vision", "Legacy option, use --caps=vision instead").hideHelp()).action(async (options) => {
(0, import_watchdog.setupExitWatchdog)();
if (options.vision) {
console.error("The --vision option is deprecated, use --caps=vision instead");
options.caps = "vision";
}
const config = await (0, import_config.resolveCLIConfig)(options);
if (config.saveVideo && !checkFfmpeg()) {
console.error(import_utilsBundle.colors.red(`
Error: ffmpeg required to save the video is not installed.`));
console.error(`
Please run the command below. It will install a local copy of ffmpeg and will not change any system-wide settings.`);
console.error(`
npx playwright install ffmpeg
`);
process.exit(1);
}
const browserContextFactory = (0, import_browserContextFactory.contextFactory)(config);
const extensionContextFactory = new import_extensionContextFactory.ExtensionContextFactory(config.browser.launchOptions.channel || "chrome", config.browser.userDataDir, config.browser.launchOptions.executablePath);
if (options.extension) {
@@ -90,6 +102,14 @@ function decorateCommand(command, version) {
await mcpServer.start(factory, config.server);
});
}
function checkFfmpeg() {
try {
const executable = import_server.registry.findExecutable("ffmpeg");
return import_fs.default.existsSync(executable.executablePath("javascript"));
} catch (error) {
return false;
}
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
decorateCommand

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

4
node_modules/playwright/lib/mcp/sdk/exports.js generated vendored Normal file → Executable file
View File

@@ -20,13 +20,11 @@ __reExport(exports_exports, require("./proxyBackend"), module.exports);
__reExport(exports_exports, require("./server"), module.exports);
__reExport(exports_exports, require("./tool"), module.exports);
__reExport(exports_exports, require("./http"), module.exports);
__reExport(exports_exports, require("./mdb"), module.exports);
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
...require("./inProcessTransport"),
...require("./proxyBackend"),
...require("./server"),
...require("./tool"),
...require("./http"),
...require("./mdb")
...require("./http")
});

11
node_modules/playwright/lib/mcp/sdk/http.js generated vendored Normal file → Executable file
View File

@@ -67,11 +67,12 @@ function httpAddressToString(address) {
resolvedHost = "localhost";
return `http://${resolvedHost}:${resolvedPort}`;
}
async function installHttpTransport(httpServer, serverBackendFactory, allowedHosts) {
async function installHttpTransport(httpServer, serverBackendFactory, unguessableUrl, allowedHosts) {
const url = httpAddressToString(httpServer.address());
const host = new URL(url).host;
allowedHosts = (allowedHosts || [host]).map((h) => h.toLowerCase());
const allowAnyHost = allowedHosts.includes("*");
const pathPrefix = unguessableUrl ? `/${import_crypto.default.randomUUID()}` : "";
const sseSessions = /* @__PURE__ */ new Map();
const streamableSessions = /* @__PURE__ */ new Map();
httpServer.on("request", async (req, res) => {
@@ -86,7 +87,12 @@ async function installHttpTransport(httpServer, serverBackendFactory, allowedHos
return res.end("Access is only allowed at " + allowedHosts.join(", "));
}
}
const url2 = new URL(`http://localhost${req.url}`);
if (!req.url?.startsWith(pathPrefix)) {
res.statusCode = 404;
return res.end("Not found");
}
const path = req.url?.slice(pathPrefix.length);
const url2 = new URL(`http://localhost${path}`);
if (url2.pathname === "/killkillkill" && req.method === "GET") {
res.statusCode = 200;
res.end("Killing process");
@@ -98,6 +104,7 @@ async function installHttpTransport(httpServer, serverBackendFactory, allowedHos
else
await handleStreamable(serverBackendFactory, req, res, streamableSessions);
});
return `${url}${pathPrefix}`;
}
async function handleSSE(serverBackendFactory, req, res, url, sessions) {
if (req.method === "POST") {

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

View File

@@ -1,208 +0,0 @@
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var mdb_exports = {};
__export(mdb_exports, {
MDBBackend: () => MDBBackend,
runMainBackend: () => runMainBackend,
runOnPauseBackendLoop: () => runOnPauseBackendLoop
});
module.exports = __toCommonJS(mdb_exports);
var import_utilsBundle = require("playwright-core/lib/utilsBundle");
var import_utils = require("playwright-core/lib/utils");
var import_tool = require("./tool");
var mcpBundle = __toESM(require("./bundle"));
var mcpServer = __toESM(require("./server"));
var mcpHttp = __toESM(require("./http"));
const mdbDebug = (0, import_utilsBundle.debug)("pw:mcp:mdb");
const errorsDebug = (0, import_utilsBundle.debug)("pw:mcp:errors");
const z = mcpBundle.z;
class MDBBackend {
constructor(mainBackend) {
this._progress = [];
this._mainBackend = mainBackend;
this._progressCallback = (params) => {
if (params.message)
this._progress.push({ type: "text", text: params.message });
};
}
async initialize(server, clientInfo) {
if (!this._clientInfo) {
this._clientInfo = clientInfo;
await this._mainBackend.initialize?.(server, clientInfo);
}
}
async listTools() {
return await this._mainBackend.listTools();
}
async callTool(name, args) {
if (name === pushToolsSchema.name) {
await this._createOnPauseClient(pushToolsSchema.inputSchema.parse(args || {}));
return { content: [{ type: "text", text: "Tools pushed" }] };
}
if (this._onPauseClient?.tools.find((tool) => tool.name === name)) {
const result2 = await this._onPauseClient.client.callTool({
name,
arguments: args
});
await this._mainBackend.afterCallTool?.(name, args, result2);
return result2;
}
await this._onPauseClient?.transport.terminateSession().catch(errorsDebug);
await this._onPauseClient?.client.close().catch(errorsDebug);
this._onPauseClient = void 0;
const resultPromise = new import_utils.ManualPromise();
const interruptPromise = new import_utils.ManualPromise();
this._interruptPromise = interruptPromise;
this._mainBackend.callTool(name, args, this._progressCallback).then((result2) => {
resultPromise.resolve(result2);
}).catch((e) => {
resultPromise.resolve({ content: [{ type: "text", text: String(e) }], isError: true });
});
const result = await Promise.race([interruptPromise, resultPromise]);
if (interruptPromise.isDone())
mdbDebug("client call intercepted", result);
else
mdbDebug("client call result", result);
result.content.unshift(...this._progress);
this._progress.length = 0;
return result;
}
async _createOnPauseClient(params) {
if (this._onPauseClient)
await this._onPauseClient.client.close().catch(errorsDebug);
this._onPauseClient = await this._createClient(params.mcpUrl);
this._interruptPromise?.resolve({
content: [{
type: "text",
text: params.introMessage || ""
}]
});
this._interruptPromise = void 0;
}
async _createClient(url) {
const client = new mcpBundle.Client({ name: "Interrupting client", version: "0.0.0" }, { capabilities: { roots: {} } });
client.setRequestHandler(mcpBundle.ListRootsRequestSchema, () => ({ roots: this._clientInfo?.roots || [] }));
client.setRequestHandler(mcpBundle.PingRequestSchema, () => ({}));
client.setNotificationHandler(mcpBundle.ProgressNotificationSchema, (notification) => {
if (notification.method === "notifications/progress") {
const { message } = notification.params;
if (message)
this._progress.push({ type: "text", text: message });
}
});
const transport = new mcpBundle.StreamableHTTPClientTransport(new URL(url));
await client.connect(transport);
const { tools } = await client.listTools();
return { client, tools, transport };
}
}
const pushToolsSchema = (0, import_tool.defineToolSchema)({
name: "mdb_push_tools",
title: "Push MCP tools to the tools stack",
description: "Push MCP tools to the tools stack",
inputSchema: z.object({
mcpUrl: z.string(),
introMessage: z.string().optional()
}),
type: "readOnly"
});
async function runMainBackend(backendFactory, options) {
const mdbBackend = new MDBBackend(backendFactory.create());
const factory = {
...backendFactory,
create: () => mdbBackend
};
const url = await startAsHttp(factory, { port: options?.port || 0 });
process.env.PLAYWRIGHT_DEBUGGER_MCP = url;
if (options?.port !== void 0)
return url;
await mcpServer.connect(factory, new mcpBundle.StdioServerTransport(), false);
}
async function runOnPauseBackendLoop(backend, introMessage) {
const wrappedBackend = new ServerBackendWithCloseListener(backend);
const factory = {
name: "on-pause-backend",
nameInConfig: "on-pause-backend",
version: "0.0.0",
create: () => wrappedBackend
};
const httpServer = await mcpHttp.startHttpServer({ port: 0 });
await mcpHttp.installHttpTransport(httpServer, factory);
const url = mcpHttp.httpAddressToString(httpServer.address());
const client = new mcpBundle.Client({ name: "Pushing client", version: "0.0.0" });
client.setRequestHandler(mcpBundle.PingRequestSchema, () => ({}));
const transport = new mcpBundle.StreamableHTTPClientTransport(new URL(process.env.PLAYWRIGHT_DEBUGGER_MCP));
await client.connect(transport);
const pushToolsResult = await client.callTool({
name: pushToolsSchema.name,
arguments: {
mcpUrl: url,
introMessage
}
});
if (pushToolsResult.isError)
errorsDebug("Failed to push tools", pushToolsResult.content);
await transport.terminateSession();
await client.close();
await wrappedBackend.waitForClosed();
httpServer.close();
}
async function startAsHttp(backendFactory, options) {
const httpServer = await mcpHttp.startHttpServer(options);
await mcpHttp.installHttpTransport(httpServer, backendFactory);
return mcpHttp.httpAddressToString(httpServer.address());
}
class ServerBackendWithCloseListener {
constructor(backend) {
this._serverClosedPromise = new import_utils.ManualPromise();
this._backend = backend;
}
async initialize(server, clientInfo) {
await this._backend.initialize?.(server, clientInfo);
}
async listTools() {
return this._backend.listTools();
}
async callTool(name, args, progress) {
return this._backend.callTool(name, args, progress);
}
serverClosed(server) {
this._backend.serverClosed?.(server);
this._serverClosedPromise.resolve();
}
async waitForClosed() {
await this._serverClosedPromise;
}
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
MDBBackend,
runMainBackend,
runOnPauseBackendLoop
});

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

@@ -40,7 +40,7 @@ class ProxyBackend {
this._mcpProviders = mcpProviders;
this._contextSwitchTool = this._defineContextSwitchTool();
}
async initialize(server, clientInfo) {
async initialize(clientInfo) {
this._clientInfo = clientInfo;
}
async listTools() {

18
node_modules/playwright/lib/mcp/sdk/server.js generated vendored Normal file → Executable file
View File

@@ -41,6 +41,7 @@ var mcpBundle = __toESM(require("./bundle"));
var import_http = require("./http");
var import_inProcessTransport = require("./inProcessTransport");
const serverDebug = (0, import_utilsBundle.debug)("pw:mcp:server");
const serverDebugResponse = (0, import_utilsBundle.debug)("pw:mcp:server:response");
async function connect(factory, transport, runHeartbeat) {
const server = createServer(factory.name, factory.version, factory.create(), runHeartbeat);
await server.connect(transport);
@@ -81,7 +82,10 @@ function createServer(name, version, backend, runHeartbeat) {
if (!initializePromise)
initializePromise = initializeServer(server, backend, runHeartbeat);
await initializePromise;
return mergeTextParts(await backend.callTool(request.params.name, request.params.arguments || {}, progress));
const toolResult = await backend.callTool(request.params.name, request.params.arguments || {}, progress);
const mergedResult = mergeTextParts(toolResult);
serverDebugResponse("callResult", mergedResult);
return mergedResult;
} catch (error) {
return {
content: [{ type: "text", text: "### Result\n" + String(error) }],
@@ -108,7 +112,7 @@ const initializeServer = async (server, backend, runHeartbeat) => {
roots: clientRoots,
timestamp: Date.now()
};
await backend.initialize?.(server, clientInfo);
await backend.initialize?.(clientInfo);
if (runHeartbeat)
startHeartbeat(server);
};
@@ -138,8 +142,7 @@ async function start(serverBackendFactory, options) {
return;
}
const httpServer = await (0, import_http.startHttpServer)(options);
const url = (0, import_http.httpAddressToString)(httpServer.address());
await (0, import_http.installHttpTransport)(httpServer, serverBackendFactory, options.allowedHosts);
const url = await (0, import_http.installHttpTransport)(httpServer, serverBackendFactory, false, options.allowedHosts);
const mcpConfig = { mcpServers: {} };
mcpConfig.mcpServers[serverBackendFactory.nameInConfig] = {
url: `${url}/mcp`
@@ -157,7 +160,12 @@ function firstRootPath(clientInfo) {
return void 0;
const firstRootUri = clientInfo.roots[0]?.uri;
const url = firstRootUri ? new URL(firstRootUri) : void 0;
return url ? (0, import_url.fileURLToPath)(url) : void 0;
try {
return url ? (0, import_url.fileURLToPath)(url) : void 0;
} catch (error) {
serverDebug(error);
return void 0;
}
}
function mergeTextParts(result) {
const content = [];

8
node_modules/playwright/lib/mcp/sdk/tool.js generated vendored Normal file → Executable file
View File

@@ -23,16 +23,12 @@ __export(tool_exports, {
});
module.exports = __toCommonJS(tool_exports);
var import_bundle = require("../sdk/bundle");
const typesWithIntent = ["action", "assertion", "input"];
function toMcpTool(tool, options) {
const inputSchema = options?.addIntent && typesWithIntent.includes(tool.type) ? tool.inputSchema.extend({
intent: import_bundle.z.string().describe("The intent of the call, for example the test step description plan idea")
}) : tool.inputSchema;
function toMcpTool(tool) {
const readOnly = tool.type === "readOnly" || tool.type === "assertion";
return {
name: tool.name,
description: tool.description,
inputSchema: (0, import_bundle.zodToJsonSchema)(inputSchema, { strictUnions: true }),
inputSchema: (0, import_bundle.zodToJsonSchema)(tool.inputSchema, { strictUnions: true }),
annotations: {
title: tool.title,
readOnlyHint: readOnly,

76
node_modules/playwright/lib/mcp/test/browserBackend.js generated vendored Normal file → Executable file
View File

@@ -1,9 +1,7 @@
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
@@ -17,38 +15,54 @@ var __copyProps = (to, from, except, desc) => {
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var browserBackend_exports = {};
__export(browserBackend_exports, {
runBrowserBackendAtEnd: () => runBrowserBackendAtEnd
createCustomMessageHandler: () => createCustomMessageHandler
});
module.exports = __toCommonJS(browserBackend_exports);
var mcp = __toESM(require("../sdk/exports"));
var import_globals = require("../../common/globals");
var import_util = require("../../util");
var import_config = require("../browser/config");
var import_browserServerBackend = require("../browser/browserServerBackend");
var import_tab = require("../browser/tab");
async function runBrowserBackendAtEnd(context, errorMessage) {
const testInfo = (0, import_globals.currentTestInfo)();
if (!testInfo)
return;
const shouldPause = errorMessage ? testInfo?._pauseOnError() : testInfo?._pauseAtEnd();
if (!shouldPause)
return;
var import_util = require("../../util");
function createCustomMessageHandler(testInfo, context) {
let backend;
return async (data) => {
if (data.initialize) {
if (backend)
throw new Error("MCP backend is already initialized");
backend = new import_browserServerBackend.BrowserServerBackend({ ...import_config.defaultConfig, capabilities: ["testing"] }, identityFactory(context));
await backend.initialize(data.initialize.clientInfo);
const pausedMessage = await generatePausedMessage(testInfo, context);
return { initialize: { pausedMessage } };
}
if (data.listTools) {
if (!backend)
throw new Error("MCP backend is not initialized");
return { listTools: await backend.listTools() };
}
if (data.callTool) {
if (!backend)
throw new Error("MCP backend is not initialized");
return { callTool: await backend.callTool(data.callTool.name, data.callTool.arguments) };
}
if (data.close) {
backend?.serverClosed();
backend = void 0;
return { close: {} };
}
throw new Error("Unknown MCP request");
};
}
async function generatePausedMessage(testInfo, context) {
const lines = [];
if (errorMessage)
lines.push(`### Paused on error:`, (0, import_util.stripAnsiEscapes)(errorMessage));
else
if (testInfo.errors.length) {
lines.push(`### Paused on error:`);
for (const error of testInfo.errors)
lines.push((0, import_util.stripAnsiEscapes)(error.message || ""));
} else {
lines.push(`### Paused at end of test. ready for interaction`);
}
for (let i = 0; i < context.pages().length; i++) {
const page = context.pages()[i];
const stateSuffix = context.pages().length > 1 ? i + 1 + " of " + context.pages().length : "state";
@@ -58,7 +72,7 @@ async function runBrowserBackendAtEnd(context, errorMessage) {
`- Page URL: ${page.url()}`,
`- Page Title: ${await page.title()}`.trim()
);
let console = errorMessage ? await import_tab.Tab.collectConsoleMessages(page) : [];
let console = testInfo.errors.length ? await import_tab.Tab.collectConsoleMessages(page) : [];
console = console.filter((msg) => !msg.type || msg.type === "error");
if (console.length) {
lines.push("- Console Messages:");
@@ -68,18 +82,14 @@ async function runBrowserBackendAtEnd(context, errorMessage) {
lines.push(
`- Page Snapshot:`,
"```yaml",
await page._snapshotForAI(),
(await page._snapshotForAI()).full,
"```"
);
}
lines.push("");
if (errorMessage)
if (testInfo.errors.length)
lines.push(`### Task`, `Try recovering from the error prior to continuing`);
const config = {
...import_config.defaultConfig,
capabilities: ["testing"]
};
await mcp.runOnPauseBackendLoop(new import_browserServerBackend.BrowserServerBackend(config, identityFactory(context)), lines.join("\n"));
return lines.join("\n");
}
function identityFactory(browserContext) {
return {
@@ -94,5 +104,5 @@ function identityFactory(browserContext) {
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
runBrowserBackendAtEnd
createCustomMessageHandler
});

6
node_modules/playwright/lib/mcp/test/generatorTools.js generated vendored Normal file → Executable file
View File

@@ -50,11 +50,11 @@ const setupPage = (0, import_testTool.defineTestTool)({
}),
type: "readOnly"
},
handle: async (context, params, progress) => {
handle: async (context, params) => {
const seed = await context.getOrCreateSeedFile(params.seedFile, params.project);
context.generatorJournal = new import_testContext.GeneratorJournal(context.rootPath, params.plan, seed);
await context.runSeedTest(seed.file, seed.projectName, progress);
return { content: [] };
const { output, status } = await context.runSeedTest(seed.file, seed.projectName);
return { content: [{ type: "text", text: output }], isError: status !== "paused" };
}
});
const generatorReadLog = (0, import_testTool.defineTestTool)({

108
node_modules/playwright/lib/mcp/test/plannerTools.js generated vendored Normal file → Executable file
View File

@@ -1,7 +1,9 @@
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
@@ -15,12 +17,24 @@ var __copyProps = (to, from, except, desc) => {
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var plannerTools_exports = {};
__export(plannerTools_exports, {
setupPage: () => setupPage
saveTestPlan: () => saveTestPlan,
setupPage: () => setupPage,
submitTestPlan: () => submitTestPlan
});
module.exports = __toCommonJS(plannerTools_exports);
var import_fs = __toESM(require("fs"));
var import_path = __toESM(require("path"));
var import_bundle = require("../sdk/bundle");
var import_testTool = require("./testTool");
const setupPage = (0, import_testTool.defineTestTool)({
@@ -34,13 +48,97 @@ const setupPage = (0, import_testTool.defineTestTool)({
}),
type: "readOnly"
},
handle: async (context, params, progress) => {
handle: async (context, params) => {
const seed = await context.getOrCreateSeedFile(params.seedFile, params.project);
await context.runSeedTest(seed.file, seed.projectName, progress);
return { content: [] };
const { output, status } = await context.runSeedTest(seed.file, seed.projectName);
return { content: [{ type: "text", text: output }], isError: status !== "paused" };
}
});
const planSchema = import_bundle.z.object({
overview: import_bundle.z.string().describe("A brief overview of the application to be tested"),
suites: import_bundle.z.array(import_bundle.z.object({
name: import_bundle.z.string().describe("The name of the suite"),
seedFile: import_bundle.z.string().describe("A seed file that was used to setup the page for testing."),
tests: import_bundle.z.array(import_bundle.z.object({
name: import_bundle.z.string().describe("The name of the test"),
file: import_bundle.z.string().describe('The file the test should be saved to, for example: "tests/<suite-name>/<test-name>.spec.ts".'),
steps: import_bundle.z.array(import_bundle.z.string().describe(`The steps to be executed to perform the test. For example: 'Click on the "Submit" button'`)),
expectedResults: import_bundle.z.array(import_bundle.z.string().describe("The expected results of the steps for test to verify."))
}))
}))
});
const submitTestPlan = (0, import_testTool.defineTestTool)({
schema: {
name: "planner_submit_plan",
title: "Submit test plan",
description: "Submit the test plan to the test planner",
inputSchema: planSchema,
type: "readOnly"
},
handle: async (context, params) => {
return {
content: [{
type: "text",
text: JSON.stringify(params, null, 2)
}]
};
}
});
const saveTestPlan = (0, import_testTool.defineTestTool)({
schema: {
name: "planner_save_plan",
title: "Save test plan as markdown file",
description: "Save the test plan as a markdown file",
inputSchema: planSchema.extend({
name: import_bundle.z.string().describe('The name of the test plan, for example: "Test Plan".'),
fileName: import_bundle.z.string().describe('The file to save the test plan to, for example: "spec/test.plan.md". Relative to the workspace root.')
}),
type: "readOnly"
},
handle: async (context, params) => {
const lines = [];
lines.push(`# ${params.name}`);
lines.push(``);
lines.push(`## Application Overview`);
lines.push(``);
lines.push(params.overview);
lines.push(``);
lines.push(`## Test Scenarios`);
for (let i = 0; i < params.suites.length; i++) {
lines.push(``);
const suite = params.suites[i];
lines.push(`### ${i + 1}. ${suite.name}`);
lines.push(``);
lines.push(`**Seed:** \`${suite.seedFile}\``);
for (let j = 0; j < suite.tests.length; j++) {
lines.push(``);
const test = suite.tests[j];
lines.push(`#### ${i + 1}.${j + 1}. ${test.name}`);
lines.push(``);
lines.push(`**File:** \`${test.file}\``);
lines.push(``);
lines.push(`**Steps:**`);
for (let k = 0; k < test.steps.length; k++)
lines.push(` ${k + 1}. ${test.steps[k]}`);
lines.push(``);
lines.push(`**Expected Results:**`);
for (const result of test.expectedResults)
lines.push(` - ${result}`);
}
}
lines.push(``);
await import_fs.default.promises.writeFile(import_path.default.resolve(context.rootPath, params.fileName), lines.join("\n"));
return {
content: [{
type: "text",
text: `Test plan saved to ${params.fileName}`
}]
};
}
});
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
setupPage
saveTestPlan,
setupPage,
submitTestPlan
});

40
node_modules/playwright/lib/mcp/test/seed.js generated vendored Normal file → Executable file
View File

@@ -28,7 +28,10 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var seed_exports = {};
__export(seed_exports, {
ensureSeedTest: () => ensureSeedTest,
defaultSeedFile: () => defaultSeedFile,
ensureSeedFile: () => ensureSeedFile,
findSeedFile: () => findSeedFile,
seedFileContent: () => seedFileContent,
seedProject: () => seedProject
});
module.exports = __toCommonJS(seed_exports);
@@ -44,29 +47,36 @@ function seedProject(config, projectName) {
throw new Error(`Project ${projectName} not found`);
return project;
}
async function ensureSeedTest(project, logNew) {
async function findSeedFile(project) {
const files = await (0, import_projectUtils.collectFilesForProject)(project);
const seed = files.find((file) => import_path.default.basename(file).includes("seed"));
if (seed)
return seed;
return files.find((file) => import_path.default.basename(file).includes("seed"));
}
function defaultSeedFile(project) {
const testDir = project.project.testDir;
const seedFile = import_path.default.resolve(testDir, "seed.spec.ts");
if (logNew) {
console.log(`Writing file: ${import_path.default.relative(process.cwd(), seedFile)}`);
}
await (0, import_utils.mkdirIfNeeded)(seedFile);
await import_fs.default.promises.writeFile(seedFile, `import { test, expect } from '@playwright/test';
return import_path.default.resolve(testDir, "seed.spec.ts");
}
async function ensureSeedFile(project) {
const seedFile = await findSeedFile(project);
if (seedFile)
return seedFile;
const seedFilePath = defaultSeedFile(project);
await (0, import_utils.mkdirIfNeeded)(seedFilePath);
await import_fs.default.promises.writeFile(seedFilePath, seedFileContent);
return seedFilePath;
}
const seedFileContent = `import { test, expect } from '@playwright/test';
test.describe('Test group', () => {
test('seed', async ({ page }) => {
// generate code here.
});
});
`);
return seedFile;
}
`;
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
ensureSeedTest,
defaultSeedFile,
ensureSeedFile,
findSeedFile,
seedFileContent,
seedProject
});

13
node_modules/playwright/lib/mcp/test/streams.js generated vendored Normal file → Executable file
View File

@@ -22,14 +22,19 @@ __export(streams_exports, {
});
module.exports = __toCommonJS(streams_exports);
var import_stream = require("stream");
var import_util = require("../../util");
class StringWriteStream extends import_stream.Writable {
constructor(progress) {
constructor(output, stdio) {
super();
this._progress = progress;
this._output = output;
this._prefix = stdio === "stdout" ? "" : "[err] ";
}
_write(chunk, encoding, callback) {
const text = chunk.toString();
this._progress({ message: text.endsWith("\n") ? text.slice(0, -1) : text });
let text = (0, import_util.stripAnsiEscapes)(chunk.toString());
if (text.endsWith("\n"))
text = text.slice(0, -1);
if (text)
this._output.push(this._prefix + text);
callback();
}
}

Some files were not shown because too many files have changed in this diff Show More